Project

General

Profile

Patch #2025 » make-wiki-formatters-pluggable.patch

makes wiki formatting pluggable - Yuki Sonoda, 2008-10-12 05:19

View differences:

app/controllers/wiki_controller.rb
63 63
    @page.content = WikiContent.new(:page => @page) if @page.new_record?
64 64
    
65 65
    @content = @page.content_for_version(params[:version])
66
    @content.text = "h1. #{@page.pretty_title}" if @content.text.blank?
66
    @content.text = initial_page_content(@page) if @content.text.blank?
67 67
    # don't keep previous comment
68 68
    @content.comments = nil
69 69
    if request.get?
......
208 208
  def editable?(page = @page)
209 209
    page.editable_by?(User.current)
210 210
  end
211

  
212
  def initial_page_content(page)
213
    helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
214
    helper ||= ApplicationHelper::NullFormattingHelper
215
    extend helper unless self.instance_of?(helper)
216
    helper.instance_method(:initial_page_content).bind(self).call(page)
217
  end
211 218
end
app/helpers/application_helper.rb
17 17

  
18 18
require 'coderay'
19 19
require 'coderay/helpers/file_type'
20
require 'forwardable'
20 21

  
21 22
module ApplicationHelper
22 23
  include Redmine::WikiFormatting::Macros::Definitions
23 24

  
25
  extend Forwardable
26
  def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
27

  
24 28
  def current_role
25 29
    @current_role ||= User.current.role_for_project(@project)
26 30
  end
......
257 261
      end
258 262
    end
259 263
    
260
    text = (Setting.text_formatting == 'textile') ?
261
      Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
262
      simple_format(auto_link(h(text)))
264
    text = (Setting.text_formatting == '0') ?
265
      simple_format(auto_link(h(text))) :
266
      Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
263 267

  
264 268
    # different methods for formatting wiki links
265 269
    case options[:wiki_links]
......
547 551
    end
548 552
  end
549 553
  
550
  def wikitoolbar_for(field_id)
551
    return '' unless Setting.text_formatting == 'textile'
552
    
553
    help_link = l(:setting_text_formatting) + ': ' +
554
      link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
555
                              :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;")
556

  
557
    javascript_include_tag('jstoolbar/jstoolbar') +
558
      javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
559
      javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
560
  end
561
  
562 554
  def content_for(name, content = nil, &block)
563 555
    @has_content ||= {}
564 556
    @has_content[name] = true
......
568 560
  def has_content?(name)
569 561
    (@has_content && @has_content[name]) || false
570 562
  end
563

  
564
  private
565
  def wiki_helper
566
    helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
567
    helper ||= NullFormattingHelper
568
    extend helper
569
    return self
570
  end
571

  
572
  module NullFormattingHelper
573
    def wikitoolbar_for(field_id); '' end
574
    def heads_for_wiki_formatter; '' end
575
    def initial_page_content(page); @page.pretty_title end
576
  end
571 577
end
app/helpers/textile_helper.rb
1
module TextileHelper
2
  def wikitoolbar_for(field_id)
3
    help_link = l(:setting_text_formatting) + ': ' +
4
      link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
5
              :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;")
6

  
7
    javascript_include_tag('jstoolbar/jstoolbar') +
8
      javascript_include_tag('jstoolbar/textile') +
9
      javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
10
    javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
11
  end
12

  
13
  def initial_page_content(page)
14
    "h1. #{@page.pretty_title}"
15
  end
16

  
17
  def heads_for_wiki_formatter; '' end
18
end
app/views/layouts/base.rhtml
8 8
<%= stylesheet_link_tag 'application', :media => 'all' %>
9 9
<%= javascript_include_tag :defaults %>
10 10
<%= stylesheet_link_tag 'jstoolbar' %>
11
<%= heads_for_wiki_formatter %>
11 12
<!--[if IE]>
12 13
    <style type="text/css">
13 14
      * html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
app/views/settings/_general.rhtml
39 39
<%= select_tag 'settings[protocol]', options_for_select(['http', 'https'], Setting.protocol) %></p>
40 40

  
41 41
<p><label><%= l(:setting_text_formatting) %></label>
42
<%= select_tag 'settings[text_formatting]', options_for_select([[l(:label_none), "0"], ["textile", "textile"]], Setting.text_formatting) %></p>
42
<%= 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>
43 43

  
44 44
<p><label><%= l(:setting_wiki_compression) %></label>
45 45
<%= select_tag 'settings[wiki_compression]', options_for_select( [[l(:label_none), 0], ["gzip", "gzip"]], Setting.wiki_compression) %></p>
lib/redmine.rb
6 6
require 'redmine/themes'
7 7
require 'redmine/hook'
8 8
require 'redmine/plugin'
9
require 'redmine/wiki_formatting'
9 10

  
10 11
begin
11 12
  require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
......
149 150
  activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
150 151
  activity.register :messages, :default => false
151 152
end
153

  
154
Redmine::WikiFormatting.map do |format|
155
  format.register :textile, Redmine::WikiFormatting::TextileFormatter, TextileHelper
156
end
lib/redmine/plugin.rb
143 143
      Redmine::Activity.register(*args)
144 144
    end
145 145

  
146
    # Registers a wiki formatter.
147
    #
148
    # Parameters:
149
    # [+name+] human-readable name
150
    # [+formatter+] formatter class, which should have instance_method as to_html(text)
151
    # [+helper+] helper module, which will be included by wiki pages.
152
    def wiki_format_provider(name, formatter, helper)
153
      Redmine::WikiFormatting.register(name, formatter, helper)
154
    end
155

  
146 156
    # Returns +true+ if the plugin can be configured.
147 157
    def configurable?
148 158
      settings && settings.is_a?(Hash) && !settings[:partial].blank?
lib/redmine/wiki_formatting.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
# 
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
# 
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
require 'redcloth3'
19
require 'coderay'
20

  
21 1
module Redmine
22 2
  module WikiFormatting
23
  
24
  private
25
  
26
    class TextileFormatter < RedCloth3
27
      
28
      # auto_link rule after textile rules so that it doesn't break !image_url! tags
29
      RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
30
      
31
      def initialize(*args)
32
        super
33
        self.hard_breaks=true
34
        self.no_span_caps=true
35
      end
36
      
37
      def to_html(*rules, &block)
38
        @toc = []
39
        @macros_runner = block
40
        super(*RULES).to_s
41
      end
42

  
43
    private
44

  
45
      # Patch for RedCloth.  Fixed in RedCloth r128 but _why hasn't released it yet.
46
      # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
47
      def hard_break( text ) 
48
        text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks 
49
      end
50
      
51
      # Patch to add code highlighting support to RedCloth
52
      def smooth_offtags( text )
53
        unless @pre_list.empty?
54
          ## replace <pre> content
55
          text.gsub!(/<redpre#(\d+)>/) do
56
            content = @pre_list[$1.to_i]
57
            if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
58
              content = "<code class=\"#{$1} CodeRay\">" + 
59
                CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
60
            end
61
            content
62
          end
63
        end
64
      end
65
      
66
      # Patch to add 'table of content' support to RedCloth
67
      def textile_p_withtoc(tag, atts, cite, content)
68
        # removes wiki links from the item
69
        toc_item = content.gsub(/(\[\[|\]\])/, '')
70
        # removes styles
71
        # eg. %{color:red}Triggers% => Triggers
72
        toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
73
        
74
        # replaces non word caracters by dashes
75
        anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
76

  
77
        unless anchor.blank?
78
          if tag =~ /^h(\d)$/
79
            @toc << [$1.to_i, anchor, toc_item]
80
          end
81
          atts << " id=\"#{anchor}\""
82
          content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
83
        end
84
        textile_p(tag, atts, cite, content)
85
      end
86

  
87
      alias :textile_h1 :textile_p_withtoc
88
      alias :textile_h2 :textile_p_withtoc
89
      alias :textile_h3 :textile_p_withtoc
90
      
91
      def inline_toc(text)
92
        text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
93
          div_class = 'toc'
94
          div_class << ' right' if $1 == '>'
95
          div_class << ' left' if $1 == '<'
96
          out = "<ul class=\"#{div_class}\">"
97
          @toc.each do |heading|
98
            level, anchor, toc_item = heading
99
            out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
100
          end
101
          out << '</ul>'
102
          out
103
        end
104
      end
105
      
106
      MACROS_RE = /
107
                    (!)?                        # escaping
108
                    (
109
                    \{\{                        # opening tag
110
                    ([\w]+)                     # macro name
111
                    (\(([^\}]*)\))?             # optional arguments
112
                    \}\}                        # closing tag
113
                    )
114
                  /x unless const_defined?(:MACROS_RE)
115
      
116
      def inline_macros(text)
117
        text.gsub!(MACROS_RE) do
118
          esc, all, macro = $1, $2, $3.downcase
119
          args = ($5 || '').split(',').each(&:strip)
120
          if esc.nil?
121
            begin
122
              @macros_runner.call(macro, args)
123
            rescue => e
124
              "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
125
            end || all
126
          else
127
            all
128
          end
129
        end
130
      end
131
      
132
      AUTO_LINK_RE = %r{
133
                        (                          # leading text
134
                          <\w+.*?>|                # leading HTML tag, or
135
                          [^=<>!:'"/]|             # leading punctuation, or 
136
                          ^                        # beginning of line
137
                        )
138
                        (
139
                          (?:https?://)|           # protocol spec, or
140
                          (?:ftp://)|
141
                          (?:www\.)                # www.*
142
                        )
143
                        (
144
                          (\S+?)                   # url
145
                          (\/)?                    # slash
146
                        )
147
                        ([^\w\=\/;\(\)]*?)               # post
148
                        (?=<|\s|$)
149
                       }x unless const_defined?(:AUTO_LINK_RE)
3
    @@formatters = {}
150 4

  
151
      # Turns all urls into clickable links (code from Rails).
152
      def inline_auto_link(text)
153
        text.gsub!(AUTO_LINK_RE) do
154
          all, leading, proto, url, post = $&, $1, $2, $3, $6
155
          if leading =~ /<a\s/i || leading =~ /![<>=]?/
156
            # don't replace URL's that are already linked
157
            # and URL's prefixed with ! !> !< != (textile images)
158
            all
159
          else
160
            # Idea below : an URL with unbalanced parethesis and
161
            # ending by ')' is put into external parenthesis
162
            if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
163
              url=url[0..-2] # discard closing parenth from url
164
              post = ")"+post # add closing parenth to post
165
            end
166
            %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
167
          end
168
        end
169
      end
170

  
171
      # Turns all email addresses into clickable links (code from Rails).
172
      def inline_auto_mailto(text)
173
        text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
174
          mail = $1
175
          if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
176
            mail
177
          else
178
            %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
179
          end
180
        end
181
      end
5
    def self.map
6
      yield self
7
    end
8
    def self.register(name, formatter, helper)
9
      raise ArgumentError, "format name `#{name}' is already taken" if @@formatters[name]
10
      @@formatters[name.to_sym] = {:formatter => formatter, :helper => helper}
11
    end
12
    def self.formatter_for(name)
13
      entry = @@formatters[name.to_sym]
14
      return entry && entry[:formatter]
15
    end
16
    def self.helper_for(name)
17
      entry = @@formatters[name.to_sym]
18
      return entry && entry[:helper]
19
    end
20
    def self.format_names
21
      @@formatters.keys.map
182 22
    end
183
    
184
  public
185
  
186
    def self.to_html(text, options = {}, &block)
187
      TextileFormatter.new(text).to_html(&block)
23
    def self.to_html(format, text, options = {}, &block)
24
      (formatter_for(format) || TextileFormatter).new(text).to_html(&block)
188 25
    end
189 26
  end
190 27
end
lib/redmine/wiki_formatting/textile_formatter.rb
1
# redMine - project management software
2
# Copyright (C) 2006-2007  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
# 
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
# 
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
require 'redcloth3'
19
require 'coderay'
20

  
21
module Redmine
22
  module WikiFormatting
23
    class TextileFormatter < RedCloth3
24
      
25
      # auto_link rule after textile rules so that it doesn't break !image_url! tags
26
      RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
27
      
28
      def initialize(*args)
29
        super
30
        self.hard_breaks=true
31
        self.no_span_caps=true
32
      end
33
      
34
      def to_html(*rules, &block)
35
        @toc = []
36
        @macros_runner = block
37
        super(*RULES).to_s
38
      end
39

  
40
    private
41

  
42
      # Patch for RedCloth.  Fixed in RedCloth r128 but _why hasn't released it yet.
43
      # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
44
      def hard_break( text ) 
45
        text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks 
46
      end
47
      
48
      # Patch to add code highlighting support to RedCloth
49
      def smooth_offtags( text )
50
        unless @pre_list.empty?
51
          ## replace <pre> content
52
          text.gsub!(/<redpre#(\d+)>/) do
53
            content = @pre_list[$1.to_i]
54
            if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
55
              content = "<code class=\"#{$1} CodeRay\">" + 
56
                CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
57
            end
58
            content
59
          end
60
        end
61
      end
62
      
63
      # Patch to add 'table of content' support to RedCloth
64
      def textile_p_withtoc(tag, atts, cite, content)
65
        # removes wiki links from the item
66
        toc_item = content.gsub(/(\[\[|\]\])/, '')
67
        # removes styles
68
        # eg. %{color:red}Triggers% => Triggers
69
        toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
70
        
71
        # replaces non word caracters by dashes
72
        anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
73

  
74
        unless anchor.blank?
75
          if tag =~ /^h(\d)$/
76
            @toc << [$1.to_i, anchor, toc_item]
77
          end
78
          atts << " id=\"#{anchor}\""
79
          content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
80
        end
81
        textile_p(tag, atts, cite, content)
82
      end
83

  
84
      alias :textile_h1 :textile_p_withtoc
85
      alias :textile_h2 :textile_p_withtoc
86
      alias :textile_h3 :textile_p_withtoc
87
      
88
      def inline_toc(text)
89
        text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
90
          div_class = 'toc'
91
          div_class << ' right' if $1 == '>'
92
          div_class << ' left' if $1 == '<'
93
          out = "<ul class=\"#{div_class}\">"
94
          @toc.each do |heading|
95
            level, anchor, toc_item = heading
96
            out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
97
          end
98
          out << '</ul>'
99
          out
100
        end
101
      end
102
      
103
      MACROS_RE = /
104
                    (!)?                        # escaping
105
                    (
106
                    \{\{                        # opening tag
107
                    ([\w]+)                     # macro name
108
                    (\(([^\}]*)\))?             # optional arguments
109
                    \}\}                        # closing tag
110
                    )
111
                  /x unless const_defined?(:MACROS_RE)
112
      
113
      def inline_macros(text)
114
        text.gsub!(MACROS_RE) do
115
          esc, all, macro = $1, $2, $3.downcase
116
          args = ($5 || '').split(',').each(&:strip)
117
          if esc.nil?
118
            begin
119
              @macros_runner.call(macro, args)
120
            rescue => e
121
              "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
122
            end || all
123
          else
124
            all
125
          end
126
        end
127
      end
128
      
129
      AUTO_LINK_RE = %r{
130
                        (                          # leading text
131
                          <\w+.*?>|                # leading HTML tag, or
132
                          [^=<>!:'"/]|             # leading punctuation, or 
133
                          ^                        # beginning of line
134
                        )
135
                        (
136
                          (?:https?://)|           # protocol spec, or
137
                          (?:ftp://)|
138
                          (?:www\.)                # www.*
139
                        )
140
                        (
141
                          (\S+?)                   # url
142
                          (\/)?                    # slash
143
                        )
144
                        ([^\w\=\/;\(\)]*?)               # post
145
                        (?=<|\s|$)
146
                       }x unless const_defined?(:AUTO_LINK_RE)
147

  
148
      # Turns all urls into clickable links (code from Rails).
149
      def inline_auto_link(text)
150
        text.gsub!(AUTO_LINK_RE) do
151
          all, leading, proto, url, post = $&, $1, $2, $3, $6
152
          if leading =~ /<a\s/i || leading =~ /![<>=]?/
153
            # don't replace URL's that are already linked
154
            # and URL's prefixed with ! !> !< != (textile images)
155
            all
156
          else
157
            # Idea below : an URL with unbalanced parethesis and
158
            # ending by ')' is put into external parenthesis
159
            if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
160
              url=url[0..-2] # discard closing parenth from url
161
              post = ")"+post # add closing parenth to post
162
            end
163
            %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
164
          end
165
        end
166
      end
167

  
168
      # Turns all email addresses into clickable links (code from Rails).
169
      def inline_auto_mailto(text)
170
        text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
171
          mail = $1
172
          if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
173
            mail
174
          else
175
            %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
176
          end
177
        end
178
      end
179
    end
180
  end
181
end
public/javascripts/jstoolbar/jstoolbar.js
378 378
	document.removeEventListener('mousemove', this.dragMoveHdlr, false);
379 379
	document.removeEventListener('mouseup', this.dragStopHdlr, false);
380 380
};
381

  
382
// Elements definition ------------------------------------
383

  
384
// strong
385
jsToolBar.prototype.elements.strong = {
386
	type: 'button',
387
	title: 'Strong',
388
	fn: {
389
		wiki: function() { this.singleTag('*') }
390
	}
391
}
392

  
393
// em
394
jsToolBar.prototype.elements.em = {
395
	type: 'button',
396
	title: 'Italic',
397
	fn: {
398
		wiki: function() { this.singleTag("_") }
399
	}
400
}
401

  
402
// ins
403
jsToolBar.prototype.elements.ins = {
404
	type: 'button',
405
	title: 'Underline',
406
	fn: {
407
		wiki: function() { this.singleTag('+') }
408
	}
409
}
410

  
411
// del
412
jsToolBar.prototype.elements.del = {
413
	type: 'button',
414
	title: 'Deleted',
415
	fn: {
416
		wiki: function() { this.singleTag('-') }
417
	}
418
}
419

  
420
// code
421
jsToolBar.prototype.elements.code = {
422
	type: 'button',
423
	title: 'Code',
424
	fn: {
425
		wiki: function() { this.singleTag('@') }
426
	}
427
}
428

  
429
// spacer
430
jsToolBar.prototype.elements.space1 = {type: 'space'}
431

  
432
// headings
433
jsToolBar.prototype.elements.h1 = {
434
	type: 'button',
435
	title: 'Heading 1',
436
	fn: {
437
		wiki: function() { 
438
		  this.encloseLineSelection('h1. ', '',function(str) {
439
		    str = str.replace(/^h\d+\.\s+/, '')
440
		    return str;
441
		  });
442
		}
443
	}
444
}
445
jsToolBar.prototype.elements.h2 = {
446
	type: 'button',
447
	title: 'Heading 2',
448
	fn: {
449
		wiki: function() { 
450
		  this.encloseLineSelection('h2. ', '',function(str) {
451
		    str = str.replace(/^h\d+\.\s+/, '')
452
		    return str;
453
		  });
454
		}
455
	}
456
}
457
jsToolBar.prototype.elements.h3 = {
458
	type: 'button',
459
	title: 'Heading 3',
460
	fn: {
461
		wiki: function() { 
462
		  this.encloseLineSelection('h3. ', '',function(str) {
463
		    str = str.replace(/^h\d+\.\s+/, '')
464
		    return str;
465
		  });
466
		}
467
	}
468
}
469

  
470
// spacer
471
jsToolBar.prototype.elements.space2 = {type: 'space'}
472

  
473
// ul
474
jsToolBar.prototype.elements.ul = {
475
	type: 'button',
476
	title: 'Unordered list',
477
	fn: {
478
		wiki: function() {
479
			this.encloseLineSelection('','',function(str) {
480
				str = str.replace(/\r/g,'');
481
				return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
482
			});
483
		}
484
	}
485
}
486

  
487
// ol
488
jsToolBar.prototype.elements.ol = {
489
	type: 'button',
490
	title: 'Ordered list',
491
	fn: {
492
		wiki: function() {
493
			this.encloseLineSelection('','',function(str) {
494
				str = str.replace(/\r/g,'');
495
				return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
496
			});
497
		}
498
	}
499
}
500

  
501
// spacer
502
jsToolBar.prototype.elements.space3 = {type: 'space'}
503

  
504
// bq
505
jsToolBar.prototype.elements.bq = {
506
	type: 'button',
507
	title: 'Quote',
508
	fn: {
509
		wiki: function() {
510
			this.encloseLineSelection('','',function(str) {
511
				str = str.replace(/\r/g,'');
512
				return str.replace(/(\n|^) *([^\n]*)/g,"$1> $2");
513
			});
514
		}
515
	}
516
}
517

  
518
// unbq
519
jsToolBar.prototype.elements.unbq = {
520
	type: 'button',
521
	title: 'Unquote',
522
	fn: {
523
		wiki: function() {
524
			this.encloseLineSelection('','',function(str) {
525
				str = str.replace(/\r/g,'');
526
				return str.replace(/(\n|^) *[>]? *([^\n]*)/g,"$1$2");
527
			});
528
		}
529
	}
530
}
531

  
532
// pre
533
jsToolBar.prototype.elements.pre = {
534
	type: 'button',
535
	title: 'Preformatted text',
536
	fn: {
537
		wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
538
	}
539
}
540

  
541
// spacer
542
jsToolBar.prototype.elements.space4 = {type: 'space'}
543

  
544
// wiki page
545
jsToolBar.prototype.elements.link = {
546
	type: 'button',
547
	title: 'Wiki link',
548
	fn: {
549
		wiki: function() { this.encloseSelection("[[", "]]") }
550
	}
551
}
552
// image
553
jsToolBar.prototype.elements.img = {
554
	type: 'button',
555
	title: 'Image',
556
	fn: {
557
		wiki: function() { this.encloseSelection("!", "!") }
558
	}
559
}
public/javascripts/jstoolbar/textile.js
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * This file is part of DotClear.
3
 * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All
4
 * rights reserved.
5
 *
6
 * DotClear is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 * 
11
 * DotClear is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with DotClear; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 *
20
 * ***** END LICENSE BLOCK *****
21
*/
22

  
23
// Elements definition ------------------------------------
24

  
25
// strong
26
jsToolBar.prototype.elements.strong = {
27
	type: 'button',
28
	title: 'Strong',
29
	fn: {
30
		wiki: function() { this.singleTag('*') }
31
	}
32
}
33

  
34
// em
35
jsToolBar.prototype.elements.em = {
36
	type: 'button',
37
	title: 'Italic',
38
	fn: {
39
		wiki: function() { this.singleTag("_") }
40
	}
41
}
42

  
43
// ins
44
jsToolBar.prototype.elements.ins = {
45
	type: 'button',
46
	title: 'Underline',
47
	fn: {
48
		wiki: function() { this.singleTag('+') }
49
	}
50
}
51

  
52
// del
53
jsToolBar.prototype.elements.del = {
54
	type: 'button',
55
	title: 'Deleted',
56
	fn: {
57
		wiki: function() { this.singleTag('-') }
58
	}
59
}
60

  
61
// code
62
jsToolBar.prototype.elements.code = {
63
	type: 'button',
64
	title: 'Code',
65
	fn: {
66
		wiki: function() { this.singleTag('@') }
67
	}
68
}
69

  
70
// spacer
71
jsToolBar.prototype.elements.space1 = {type: 'space'}
72

  
73
// headings
74
jsToolBar.prototype.elements.h1 = {
75
	type: 'button',
76
	title: 'Heading 1',
77
	fn: {
78
		wiki: function() { 
79
		  this.encloseLineSelection('h1. ', '',function(str) {
80
		    str = str.replace(/^h\d+\.\s+/, '')
81
		    return str;
82
		  });
83
		}
84
	}
85
}
86
jsToolBar.prototype.elements.h2 = {
87
	type: 'button',
88
	title: 'Heading 2',
89
	fn: {
90
		wiki: function() { 
91
		  this.encloseLineSelection('h2. ', '',function(str) {
92
		    str = str.replace(/^h\d+\.\s+/, '')
93
		    return str;
94
		  });
95
		}
96
	}
97
}
98
jsToolBar.prototype.elements.h3 = {
99
	type: 'button',
100
	title: 'Heading 3',
101
	fn: {
102
		wiki: function() { 
103
		  this.encloseLineSelection('h3. ', '',function(str) {
104
		    str = str.replace(/^h\d+\.\s+/, '')
105
		    return str;
106
		  });
107
		}
108
	}
109
}
110

  
111
// spacer
112
jsToolBar.prototype.elements.space2 = {type: 'space'}
113

  
114
// ul
115
jsToolBar.prototype.elements.ul = {
116
	type: 'button',
117
	title: 'Unordered list',
118
	fn: {
119
		wiki: function() {
120
			this.encloseLineSelection('','',function(str) {
121
				str = str.replace(/\r/g,'');
122
				return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
123
			});
124
		}
125
	}
126
}
127

  
128
// ol
129
jsToolBar.prototype.elements.ol = {
130
	type: 'button',
131
	title: 'Ordered list',
132
	fn: {
133
		wiki: function() {
134
			this.encloseLineSelection('','',function(str) {
135
				str = str.replace(/\r/g,'');
136
				return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
137
			});
138
		}
139
	}
140
}
141

  
142
// spacer
143
jsToolBar.prototype.elements.space3 = {type: 'space'}
144

  
145
// bq
146
jsToolBar.prototype.elements.bq = {
147
	type: 'button',
148
	title: 'Quote',
149
	fn: {
150
		wiki: function() {
151
			this.encloseLineSelection('','',function(str) {
152
				str = str.replace(/\r/g,'');
153
				return str.replace(/(\n|^) *([^\n]*)/g,"$1> $2");
154
			});
155
		}
156
	}
157
}
158

  
159
// unbq
160
jsToolBar.prototype.elements.unbq = {
161
	type: 'button',
162
	title: 'Unquote',
163
	fn: {
164
		wiki: function() {
165
			this.encloseLineSelection('','',function(str) {
166
				str = str.replace(/\r/g,'');
167
				return str.replace(/(\n|^) *[>]? *([^\n]*)/g,"$1$2");
168
			});
169
		}
170
	}
171
}
172

  
173
// pre
174
jsToolBar.prototype.elements.pre = {
175
	type: 'button',
176
	title: 'Preformatted text',
177
	fn: {
178
		wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
179
	}
180
}
181

  
182
// spacer
183
jsToolBar.prototype.elements.space4 = {type: 'space'}
184

  
185
// wiki page
186
jsToolBar.prototype.elements.link = {
187
	type: 'button',
188
	title: 'Wiki link',
189
	fn: {
190
		wiki: function() { this.encloseSelection("[[", "]]") }
191
	}
192
}
193
// image
194
jsToolBar.prototype.elements.img = {
195
	type: 'button',
196
	title: 'Image',
197
	fn: {
198
		wiki: function() { this.encloseSelection("!", "!") }
199
	}
200
}
(1-1/2)