Original patch #2025 by Yuki Sonoda slightly edited. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1955 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.8.0-RC1
@@ -63,7 +63,7 @@ class WikiController < ApplicationController | |||
@page.content = WikiContent.new(:page => @page) if @page.new_record? | |||
@content = @page.content_for_version(params[:version]) | |||
@content.text = "h1. #{@page.pretty_title}" if @content.text.blank? | |||
@content.text = initial_page_content(@page) if @content.text.blank? | |||
# don't keep previous comment | |||
@content.comments = nil | |||
if request.get? | |||
@@ -208,4 +208,11 @@ private | |||
def editable?(page = @page) | |||
page.editable_by?(User.current) | |||
end | |||
# Returns the default content of a new wiki page | |||
def initial_page_content(page) | |||
helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) | |||
extend helper unless self.instance_of?(helper) | |||
helper.instance_method(:initial_page_content).bind(self).call(page) | |||
end | |||
end |
@@ -17,10 +17,14 @@ | |||
require 'coderay' | |||
require 'coderay/helpers/file_type' | |||
require 'forwardable' | |||
module ApplicationHelper | |||
include Redmine::WikiFormatting::Macros::Definitions | |||
extend Forwardable | |||
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter | |||
def current_role | |||
@current_role ||= User.current.role_for_project(@project) | |||
end | |||
@@ -259,9 +263,7 @@ module ApplicationHelper | |||
end | |||
end | |||
text = (Setting.text_formatting == 'textile') ? | |||
Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } : | |||
simple_format(auto_link(h(text))) | |||
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) } | |||
# different methods for formatting wiki links | |||
case options[:wiki_links] | |||
@@ -549,18 +551,6 @@ module ApplicationHelper | |||
end | |||
end | |||
def wikitoolbar_for(field_id) | |||
return '' unless Setting.text_formatting == 'textile' | |||
help_link = l(:setting_text_formatting) + ': ' + | |||
link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'), | |||
:onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;") | |||
javascript_include_tag('jstoolbar/jstoolbar') + | |||
javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") + | |||
javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();") | |||
end | |||
def content_for(name, content = nil, &block) | |||
@has_content ||= {} | |||
@has_content[name] = true | |||
@@ -570,4 +560,12 @@ module ApplicationHelper | |||
def has_content?(name) | |||
(@has_content && @has_content[name]) || false | |||
end | |||
private | |||
def wiki_helper | |||
helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) | |||
extend helper | |||
return self | |||
end | |||
end |
@@ -7,7 +7,7 @@ | |||
<meta name="keywords" content="issue,bug,tracker" /> | |||
<%= stylesheet_link_tag 'application', :media => 'all' %> | |||
<%= javascript_include_tag :defaults %> | |||
<%= stylesheet_link_tag 'jstoolbar' %> | |||
<%= heads_for_wiki_formatter %> | |||
<!--[if IE]> | |||
<style type="text/css"> | |||
* html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); } |
@@ -32,6 +32,6 @@ hr { | |||
<body> | |||
<%= yield %> | |||
<hr /> | |||
<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.emails_footer) %></span> | |||
<span class="footer"><%= Redmine::WikiFormatting.to_html(Setting.text_formatting, Setting.emails_footer) %></span> | |||
</body> | |||
</html> |
@@ -39,7 +39,7 @@ | |||
<%= select_tag 'settings[protocol]', options_for_select(['http', 'https'], Setting.protocol) %></p> | |||
<p><label><%= l(:setting_text_formatting) %></label> | |||
<%= select_tag 'settings[text_formatting]', options_for_select([[l(:label_none), "0"], ["textile", "textile"]], Setting.text_formatting) %></p> | |||
<%= select_tag 'settings[text_formatting]', options_for_select([[l(:label_none), "0"], *Redmine::WikiFormatting.format_names.collect{|name| [name, name]} ], Setting.text_formatting.to_sym) %></p> | |||
<p><label><%= l(:setting_wiki_compression) %></label> | |||
<%= select_tag 'settings[wiki_compression]', options_for_select( [[l(:label_none), 0], ["gzip", "gzip"]], Setting.wiki_compression) %></p> |
@@ -6,6 +6,7 @@ require 'redmine/core_ext' | |||
require 'redmine/themes' | |||
require 'redmine/hook' | |||
require 'redmine/plugin' | |||
require 'redmine/wiki_formatting' | |||
begin | |||
require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick) | |||
@@ -150,3 +151,7 @@ Redmine::Activity.map do |activity| | |||
activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false | |||
activity.register :messages, :default => false | |||
end | |||
Redmine::WikiFormatting.map do |format| | |||
format.register :textile, Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting::Textile::Helper | |||
end |
@@ -148,6 +148,16 @@ module Redmine #:nodoc: | |||
def activity_provider(*args) | |||
Redmine::Activity.register(*args) | |||
end | |||
# Registers a wiki formatter. | |||
# | |||
# Parameters: | |||
# * +name+ - human-readable name | |||
# * +formatter+ - formatter class, which should have an instance method +to_html+ | |||
# * +helper+ - helper module, which will be included by wiki pages | |||
def wiki_format_provider(name, formatter, helper) | |||
Redmine::WikiFormatting.register(name, formatter, helper) | |||
end | |||
# Returns +true+ if the plugin can be configured. | |||
def configurable? |
@@ -1,5 +1,5 @@ | |||
# redMine - project management software | |||
# Copyright (C) 2006-2007 Jean-Philippe Lang | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2008 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
@@ -15,176 +15,65 @@ | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
require 'redcloth3' | |||
require 'coderay' | |||
module Redmine | |||
module WikiFormatting | |||
private | |||
class TextileFormatter < RedCloth3 | |||
# auto_link rule after textile rules so that it doesn't break !image_url! tags | |||
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros] | |||
@@formatters = {} | |||
class << self | |||
def map | |||
yield self | |||
end | |||
def initialize(*args) | |||
super | |||
self.hard_breaks=true | |||
self.no_span_caps=true | |||
def register(name, formatter, helper) | |||
raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name] | |||
@@formatters[name.to_sym] = {:formatter => formatter, :helper => helper} | |||
end | |||
def to_html(*rules, &block) | |||
@toc = [] | |||
@macros_runner = block | |||
super(*RULES).to_s | |||
def formatter_for(name) | |||
entry = @@formatters[name.to_sym] | |||
(entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter | |||
end | |||
private | |||
# Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. | |||
# <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | |||
def hard_break( text ) | |||
text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks | |||
def helper_for(name) | |||
entry = @@formatters[name.to_sym] | |||
(entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper | |||
end | |||
# Patch to add code highlighting support to RedCloth | |||
def smooth_offtags( text ) | |||
unless @pre_list.empty? | |||
## replace <pre> content | |||
text.gsub!(/<redpre#(\d+)>/) do | |||
content = @pre_list[$1.to_i] | |||
if content.match(/<code\s+class="(\w+)">\s?(.+)/m) | |||
content = "<code class=\"#{$1} CodeRay\">" + | |||
CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline) | |||
end | |||
content | |||
end | |||
end | |||
def format_names | |||
@@formatters.keys.map | |||
end | |||
# Patch to add 'table of content' support to RedCloth | |||
def textile_p_withtoc(tag, atts, cite, content) | |||
# removes wiki links from the item | |||
toc_item = content.gsub(/(\[\[|\]\])/, '') | |||
# removes styles | |||
# eg. %{color:red}Triggers% => Triggers | |||
toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1' | |||
def to_html(format, text, options = {}, &block) | |||
formatter_for(format).new(text).to_html(&block) | |||
end | |||
end | |||
# Default formatter module | |||
module NullFormatter | |||
class Formatter | |||
include ActionView::Helpers::TagHelper | |||
include ActionView::Helpers::TextHelper | |||
# replaces non word caracters by dashes | |||
anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-') | |||
unless anchor.blank? | |||
if tag =~ /^h(\d)$/ | |||
@toc << [$1.to_i, anchor, toc_item] | |||
end | |||
atts << " id=\"#{anchor}\"" | |||
content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a>" | |||
def initialize(text) | |||
@text = text | |||
end | |||
textile_p(tag, atts, cite, content) | |||
end | |||
alias :textile_h1 :textile_p_withtoc | |||
alias :textile_h2 :textile_p_withtoc | |||
alias :textile_h3 :textile_p_withtoc | |||
def inline_toc(text) | |||
text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do | |||
div_class = 'toc' | |||
div_class << ' right' if $1 == '>' | |||
div_class << ' left' if $1 == '<' | |||
out = "<ul class=\"#{div_class}\">" | |||
@toc.each do |heading| | |||
level, anchor, toc_item = heading | |||
out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n" | |||
end | |||
out << '</ul>' | |||
out | |||
def to_html(*args) | |||
simple_format(auto_link(CGI::escapeHTML(@text))) | |||
end | |||
end | |||
MACROS_RE = / | |||
(!)? # escaping | |||
( | |||
\{\{ # opening tag | |||
([\w]+) # macro name | |||
(\(([^\}]*)\))? # optional arguments | |||
\}\} # closing tag | |||
) | |||
/x unless const_defined?(:MACROS_RE) | |||
def inline_macros(text) | |||
text.gsub!(MACROS_RE) do | |||
esc, all, macro = $1, $2, $3.downcase | |||
args = ($5 || '').split(',').each(&:strip) | |||
if esc.nil? | |||
begin | |||
@macros_runner.call(macro, args) | |||
rescue => e | |||
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>" | |||
end || all | |||
else | |||
all | |||
end | |||
module Helper | |||
def wikitoolbar_for(field_id) | |||
end | |||
end | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+.*?>| # leading HTML tag, or | |||
[^=<>!:'"/]| # leading punctuation, or | |||
^ # beginning of line | |||
) | |||
( | |||
(?:https?://)| # protocol spec, or | |||
(?:ftp://)| | |||
(?:www\.) # www.* | |||
) | |||
( | |||
(\S+?) # url | |||
(\/)? # slash | |||
) | |||
([^\w\=\/;\(\)]*?) # post | |||
(?=<|\s|$) | |||
}x unless const_defined?(:AUTO_LINK_RE) | |||
# Turns all urls into clickable links (code from Rails). | |||
def inline_auto_link(text) | |||
text.gsub!(AUTO_LINK_RE) do | |||
all, leading, proto, url, post = $&, $1, $2, $3, $6 | |||
if leading =~ /<a\s/i || leading =~ /![<>=]?/ | |||
# don't replace URL's that are already linked | |||
# and URL's prefixed with ! !> !< != (textile images) | |||
all | |||
else | |||
# Idea below : an URL with unbalanced parethesis and | |||
# ending by ')' is put into external parenthesis | |||
if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) ) | |||
url=url[0..-2] # discard closing parenth from url | |||
post = ")"+post # add closing parenth to post | |||
end | |||
%(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post}) | |||
end | |||
def heads_for_wiki_formatter | |||
end | |||
end | |||
# Turns all email addresses into clickable links (code from Rails). | |||
def inline_auto_mailto(text) | |||
text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | |||
mail = $1 | |||
if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/) | |||
else | |||
%{<a href="mailto:#{mail}" class="email">#{mail}</a>} | |||
end | |||
def initial_page_content(page) | |||
page.pretty_title.to_s | |||
end | |||
end | |||
end | |||
public | |||
def self.to_html(text, options = {}, &block) | |||
TextileFormatter.new(text).to_html(&block) | |||
end | |||
end | |||
end |
@@ -0,0 +1,183 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2008 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
require 'redcloth3' | |||
require 'coderay' | |||
module Redmine | |||
module WikiFormatting | |||
module Textile | |||
class Formatter < RedCloth3 | |||
# auto_link rule after textile rules so that it doesn't break !image_url! tags | |||
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros] | |||
def initialize(*args) | |||
super | |||
self.hard_breaks=true | |||
self.no_span_caps=true | |||
end | |||
def to_html(*rules, &block) | |||
@toc = [] | |||
@macros_runner = block | |||
super(*RULES).to_s | |||
end | |||
private | |||
# Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. | |||
# <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | |||
def hard_break( text ) | |||
text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks | |||
end | |||
# Patch to add code highlighting support to RedCloth | |||
def smooth_offtags( text ) | |||
unless @pre_list.empty? | |||
## replace <pre> content | |||
text.gsub!(/<redpre#(\d+)>/) do | |||
content = @pre_list[$1.to_i] | |||
if content.match(/<code\s+class="(\w+)">\s?(.+)/m) | |||
content = "<code class=\"#{$1} CodeRay\">" + | |||
CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline) | |||
end | |||
content | |||
end | |||
end | |||
end | |||
# Patch to add 'table of content' support to RedCloth | |||
def textile_p_withtoc(tag, atts, cite, content) | |||
# removes wiki links from the item | |||
toc_item = content.gsub(/(\[\[|\]\])/, '') | |||
# removes styles | |||
# eg. %{color:red}Triggers% => Triggers | |||
toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1' | |||
# replaces non word caracters by dashes | |||
anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-') | |||
unless anchor.blank? | |||
if tag =~ /^h(\d)$/ | |||
@toc << [$1.to_i, anchor, toc_item] | |||
end | |||
atts << " id=\"#{anchor}\"" | |||
content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a>" | |||
end | |||
textile_p(tag, atts, cite, content) | |||
end | |||
alias :textile_h1 :textile_p_withtoc | |||
alias :textile_h2 :textile_p_withtoc | |||
alias :textile_h3 :textile_p_withtoc | |||
def inline_toc(text) | |||
text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do | |||
div_class = 'toc' | |||
div_class << ' right' if $1 == '>' | |||
div_class << ' left' if $1 == '<' | |||
out = "<ul class=\"#{div_class}\">" | |||
@toc.each do |heading| | |||
level, anchor, toc_item = heading | |||
out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n" | |||
end | |||
out << '</ul>' | |||
out | |||
end | |||
end | |||
MACROS_RE = / | |||
(!)? # escaping | |||
( | |||
\{\{ # opening tag | |||
([\w]+) # macro name | |||
(\(([^\}]*)\))? # optional arguments | |||
\}\} # closing tag | |||
) | |||
/x unless const_defined?(:MACROS_RE) | |||
def inline_macros(text) | |||
text.gsub!(MACROS_RE) do | |||
esc, all, macro = $1, $2, $3.downcase | |||
args = ($5 || '').split(',').each(&:strip) | |||
if esc.nil? | |||
begin | |||
@macros_runner.call(macro, args) | |||
rescue => e | |||
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>" | |||
end || all | |||
else | |||
all | |||
end | |||
end | |||
end | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+.*?>| # leading HTML tag, or | |||
[^=<>!:'"/]| # leading punctuation, or | |||
^ # beginning of line | |||
) | |||
( | |||
(?:https?://)| # protocol spec, or | |||
(?:ftp://)| | |||
(?:www\.) # www.* | |||
) | |||
( | |||
(\S+?) # url | |||
(\/)? # slash | |||
) | |||
([^\w\=\/;\(\)]*?) # post | |||
(?=<|\s|$) | |||
}x unless const_defined?(:AUTO_LINK_RE) | |||
# Turns all urls into clickable links (code from Rails). | |||
def inline_auto_link(text) | |||
text.gsub!(AUTO_LINK_RE) do | |||
all, leading, proto, url, post = $&, $1, $2, $3, $6 | |||
if leading =~ /<a\s/i || leading =~ /![<>=]?/ | |||
# don't replace URL's that are already linked | |||
# and URL's prefixed with ! !> !< != (textile images) | |||
all | |||
else | |||
# Idea below : an URL with unbalanced parethesis and | |||
# ending by ')' is put into external parenthesis | |||
if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) ) | |||
url=url[0..-2] # discard closing parenth from url | |||
post = ")"+post # add closing parenth to post | |||
end | |||
%(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post}) | |||
end | |||
end | |||
end | |||
# Turns all email addresses into clickable links (code from Rails). | |||
def inline_auto_mailto(text) | |||
text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | |||
mail = $1 | |||
if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/) | |||
else | |||
%{<a href="mailto:#{mail}" class="email">#{mail}</a>} | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end |
@@ -0,0 +1,43 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2008 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
module Redmine | |||
module WikiFormatting | |||
module Textile | |||
module Helper | |||
def wikitoolbar_for(field_id) | |||
help_link = l(:setting_text_formatting) + ': ' + | |||
link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'), | |||
:onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;") | |||
javascript_include_tag('jstoolbar/jstoolbar') + | |||
javascript_include_tag('jstoolbar/textile') + | |||
javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") + | |||
javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();") | |||
end | |||
def initial_page_content(page) | |||
"h1. #{@page.pretty_title}" | |||
end | |||
def heads_for_wiki_formatter | |||
stylesheet_link_tag 'jstoolbar' | |||
end | |||
end | |||
end | |||
end | |||
end |
@@ -378,182 +378,3 @@ jsToolBar.prototype.resizeDragStop = function(event) { | |||
document.removeEventListener('mousemove', this.dragMoveHdlr, false); | |||
document.removeEventListener('mouseup', this.dragStopHdlr, false); | |||
}; | |||
// Elements definition ------------------------------------ | |||
// strong | |||
jsToolBar.prototype.elements.strong = { | |||
type: 'button', | |||
title: 'Strong', | |||
fn: { | |||
wiki: function() { this.singleTag('*') } | |||
} | |||
} | |||
// em | |||
jsToolBar.prototype.elements.em = { | |||
type: 'button', | |||
title: 'Italic', | |||
fn: { | |||
wiki: function() { this.singleTag("_") } | |||
} | |||
} | |||
// ins | |||
jsToolBar.prototype.elements.ins = { | |||
type: 'button', | |||
title: 'Underline', | |||
fn: { | |||
wiki: function() { this.singleTag('+') } | |||
} | |||
} | |||
// del | |||
jsToolBar.prototype.elements.del = { | |||
type: 'button', | |||
title: 'Deleted', | |||
fn: { | |||
wiki: function() { this.singleTag('-') } | |||
} | |||
} | |||
// code | |||
jsToolBar.prototype.elements.code = { | |||
type: 'button', | |||
title: 'Code', | |||
fn: { | |||
wiki: function() { this.singleTag('@') } | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space1 = {type: 'space'} | |||
// headings | |||
jsToolBar.prototype.elements.h1 = { | |||
type: 'button', | |||
title: 'Heading 1', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h1. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
jsToolBar.prototype.elements.h2 = { | |||
type: 'button', | |||
title: 'Heading 2', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h2. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
jsToolBar.prototype.elements.h3 = { | |||
type: 'button', | |||
title: 'Heading 3', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h3. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space2 = {type: 'space'} | |||
// ul | |||
jsToolBar.prototype.elements.ul = { | |||
type: 'button', | |||
title: 'Unordered list', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^)[#-]?\s*/g,"$1* "); | |||
}); | |||
} | |||
} | |||
} | |||
// ol | |||
jsToolBar.prototype.elements.ol = { | |||
type: 'button', | |||
title: 'Ordered list', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^)[*-]?\s*/g,"$1# "); | |||
}); | |||
} | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space3 = {type: 'space'} | |||
// bq | |||
jsToolBar.prototype.elements.bq = { | |||
type: 'button', | |||
title: 'Quote', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^) *([^\n]*)/g,"$1> $2"); | |||
}); | |||
} | |||
} | |||
} | |||
// unbq | |||
jsToolBar.prototype.elements.unbq = { | |||
type: 'button', | |||
title: 'Unquote', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^) *[>]? *([^\n]*)/g,"$1$2"); | |||
}); | |||
} | |||
} | |||
} | |||
// pre | |||
jsToolBar.prototype.elements.pre = { | |||
type: 'button', | |||
title: 'Preformatted text', | |||
fn: { | |||
wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') } | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space4 = {type: 'space'} | |||
// wiki page | |||
jsToolBar.prototype.elements.link = { | |||
type: 'button', | |||
title: 'Wiki link', | |||
fn: { | |||
wiki: function() { this.encloseSelection("[[", "]]") } | |||
} | |||
} | |||
// image | |||
jsToolBar.prototype.elements.img = { | |||
type: 'button', | |||
title: 'Image', | |||
fn: { | |||
wiki: function() { this.encloseSelection("!", "!") } | |||
} | |||
} |
@@ -0,0 +1,200 @@ | |||
/* ***** BEGIN LICENSE BLOCK ***** | |||
* This file is part of DotClear. | |||
* Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All | |||
* rights reserved. | |||
* | |||
* DotClear is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* DotClear is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with DotClear; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
* | |||
* ***** END LICENSE BLOCK ***** | |||
*/ | |||
/* Modified by JP LANG for textile formatting */ | |||
// strong | |||
jsToolBar.prototype.elements.strong = { | |||
type: 'button', | |||
title: 'Strong', | |||
fn: { | |||
wiki: function() { this.singleTag('*') } | |||
} | |||
} | |||
// em | |||
jsToolBar.prototype.elements.em = { | |||
type: 'button', | |||
title: 'Italic', | |||
fn: { | |||
wiki: function() { this.singleTag("_") } | |||
} | |||
} | |||
// ins | |||
jsToolBar.prototype.elements.ins = { | |||
type: 'button', | |||
title: 'Underline', | |||
fn: { | |||
wiki: function() { this.singleTag('+') } | |||
} | |||
} | |||
// del | |||
jsToolBar.prototype.elements.del = { | |||
type: 'button', | |||
title: 'Deleted', | |||
fn: { | |||
wiki: function() { this.singleTag('-') } | |||
} | |||
} | |||
// code | |||
jsToolBar.prototype.elements.code = { | |||
type: 'button', | |||
title: 'Code', | |||
fn: { | |||
wiki: function() { this.singleTag('@') } | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space1 = {type: 'space'} | |||
// headings | |||
jsToolBar.prototype.elements.h1 = { | |||
type: 'button', | |||
title: 'Heading 1', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h1. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
jsToolBar.prototype.elements.h2 = { | |||
type: 'button', | |||
title: 'Heading 2', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h2. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
jsToolBar.prototype.elements.h3 = { | |||
type: 'button', | |||
title: 'Heading 3', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('h3. ', '',function(str) { | |||
str = str.replace(/^h\d+\.\s+/, '') | |||
return str; | |||
}); | |||
} | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space2 = {type: 'space'} | |||
// ul | |||
jsToolBar.prototype.elements.ul = { | |||
type: 'button', | |||
title: 'Unordered list', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^)[#-]?\s*/g,"$1* "); | |||
}); | |||
} | |||
} | |||
} | |||
// ol | |||
jsToolBar.prototype.elements.ol = { | |||
type: 'button', | |||
title: 'Ordered list', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^)[*-]?\s*/g,"$1# "); | |||
}); | |||
} | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space3 = {type: 'space'} | |||
// bq | |||
jsToolBar.prototype.elements.bq = { | |||
type: 'button', | |||
title: 'Quote', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^) *([^\n]*)/g,"$1> $2"); | |||
}); | |||
} | |||
} | |||
} | |||
// unbq | |||
jsToolBar.prototype.elements.unbq = { | |||
type: 'button', | |||
title: 'Unquote', | |||
fn: { | |||
wiki: function() { | |||
this.encloseLineSelection('','',function(str) { | |||
str = str.replace(/\r/g,''); | |||
return str.replace(/(\n|^) *[>]? *([^\n]*)/g,"$1$2"); | |||
}); | |||
} | |||
} | |||
} | |||
// pre | |||
jsToolBar.prototype.elements.pre = { | |||
type: 'button', | |||
title: 'Preformatted text', | |||
fn: { | |||
wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') } | |||
} | |||
} | |||
// spacer | |||
jsToolBar.prototype.elements.space4 = {type: 'space'} | |||
// wiki page | |||
jsToolBar.prototype.elements.link = { | |||
type: 'button', | |||
title: 'Wiki link', | |||
fn: { | |||
wiki: function() { this.encloseSelection("[[", "]]") } | |||
} | |||
} | |||
// image | |||
jsToolBar.prototype.elements.img = { | |||
type: 'button', | |||
title: 'Image', | |||
fn: { | |||
wiki: function() { this.encloseSelection("!", "!") } | |||
} | |||
} |
@@ -350,6 +350,13 @@ EXPECTED | |||
assert textilizable(text).match(/Unknow project/) | |||
end | |||
def test_default_formatter | |||
Setting.text_formatting = 'unknown' | |||
text = 'a *link*: http://www.example.net/' | |||
assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text) | |||
Setting.text_formatting = 'textile' | |||
end | |||
def test_date_format_default | |||
today = Date.today | |||
Setting.date_format = '' |