diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2008-10-27 11:08:29 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2008-10-27 11:08:29 +0000 |
commit | a3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255 (patch) | |
tree | 6b19387c81e2d865a57cf3357340deffdbc5e858 | |
parent | 9b624fd1d6f66bbe60d738522d9fc5fd547ef3af (diff) | |
download | redmine-a3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255.tar.gz redmine-a3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255.zip |
Makes wiki text formatter pluggable.
Original patch #2025 by Yuki Sonoda slightly edited.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1955 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r-- | app/controllers/wiki_controller.rb | 9 | ||||
-rw-r--r-- | app/helpers/application_helper.rb | 28 | ||||
-rw-r--r-- | app/views/layouts/base.rhtml | 2 | ||||
-rw-r--r-- | app/views/mailer/layout.text.html.rhtml | 2 | ||||
-rw-r--r-- | app/views/settings/_general.rhtml | 2 | ||||
-rw-r--r-- | lib/redmine.rb | 5 | ||||
-rw-r--r-- | lib/redmine/plugin.rb | 10 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting.rb | 193 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting/textile/formatter.rb | 183 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting/textile/helper.rb | 43 | ||||
-rw-r--r-- | public/javascripts/jstoolbar/jstoolbar.js | 179 | ||||
-rw-r--r-- | public/javascripts/jstoolbar/textile.js | 200 | ||||
-rw-r--r-- | test/unit/helpers/application_helper_test.rb | 7 |
13 files changed, 513 insertions, 350 deletions
diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 114010dff..04bc33a82 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c72763dbb..aad2c5bec 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/views/layouts/base.rhtml b/app/views/layouts/base.rhtml index 86d23d62f..8cdfcb8e6 100644 --- a/app/views/layouts/base.rhtml +++ b/app/views/layouts/base.rhtml @@ -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%' ); } diff --git a/app/views/mailer/layout.text.html.rhtml b/app/views/mailer/layout.text.html.rhtml index c95c94501..12fb654d6 100644 --- a/app/views/mailer/layout.text.html.rhtml +++ b/app/views/mailer/layout.text.html.rhtml @@ -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> diff --git a/app/views/settings/_general.rhtml b/app/views/settings/_general.rhtml index bb56c43db..f8d4e67fb 100644 --- a/app/views/settings/_general.rhtml +++ b/app/views/settings/_general.rhtml @@ -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> diff --git a/lib/redmine.rb b/lib/redmine.rb index 1503c1d41..eeb12e00a 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -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 diff --git a/lib/redmine/plugin.rb b/lib/redmine/plugin.rb index 8123003cc..87a6ce4d3 100644 --- a/lib/redmine/plugin.rb +++ b/lib/redmine/plugin.rb @@ -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? diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index 7dffff492..1525dbc19 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -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>/) - mail - 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 diff --git a/lib/redmine/wiki_formatting/textile/formatter.rb b/lib/redmine/wiki_formatting/textile/formatter.rb new file mode 100644 index 000000000..c936803d5 --- /dev/null +++ b/lib/redmine/wiki_formatting/textile/formatter.rb @@ -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>/) + mail + else + %{<a href="mailto:#{mail}" class="email">#{mail}</a>} + end + end + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb new file mode 100644 index 000000000..af021f990 --- /dev/null +++ b/lib/redmine/wiki_formatting/textile/helper.rb @@ -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 diff --git a/public/javascripts/jstoolbar/jstoolbar.js b/public/javascripts/jstoolbar/jstoolbar.js index 64c460217..66669ceee 100644 --- a/public/javascripts/jstoolbar/jstoolbar.js +++ b/public/javascripts/jstoolbar/jstoolbar.js @@ -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("!", "!") } - } -} diff --git a/public/javascripts/jstoolbar/textile.js b/public/javascripts/jstoolbar/textile.js new file mode 100644 index 000000000..c461b9d2e --- /dev/null +++ b/public/javascripts/jstoolbar/textile.js @@ -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("!", "!") } + } +} diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index c3e8ca2d7..13fa262f1 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -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 = '' |