##// END OF EJS Templates
Display wiki syntax quick ref link within the jstoolbar (closes #629, #767)....
Jean-Philippe Lang -
r1176:c61424e57a0f
parent child
Show More
@@ -1,461 +1,466
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module ApplicationHelper
18 module ApplicationHelper
19 include Redmine::WikiFormatting::Macros::Definitions
19 include Redmine::WikiFormatting::Macros::Definitions
20
20
21 def current_role
21 def current_role
22 @current_role ||= User.current.role_for_project(@project)
22 @current_role ||= User.current.role_for_project(@project)
23 end
23 end
24
24
25 # Return true if user is authorized for controller/action, otherwise false
25 # Return true if user is authorized for controller/action, otherwise false
26 def authorize_for(controller, action)
26 def authorize_for(controller, action)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
28 end
28 end
29
29
30 # Display a link if user is authorized
30 # Display a link if user is authorized
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
33 end
33 end
34
34
35 # Display a link to user's account page
35 # Display a link to user's account page
36 def link_to_user(user)
36 def link_to_user(user)
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
38 end
38 end
39
39
40 def link_to_issue(issue)
40 def link_to_issue(issue)
41 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
41 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
42 end
42 end
43
43
44 def toggle_link(name, id, options={})
44 def toggle_link(name, id, options={})
45 onclick = "Element.toggle('#{id}'); "
45 onclick = "Element.toggle('#{id}'); "
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
47 onclick << "return false;"
47 onclick << "return false;"
48 link_to(name, "#", :onclick => onclick)
48 link_to(name, "#", :onclick => onclick)
49 end
49 end
50
50
51 def show_and_goto_link(name, id, options={})
51 def show_and_goto_link(name, id, options={})
52 onclick = "Element.show('#{id}'); "
52 onclick = "Element.show('#{id}'); "
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
54 onclick << "Element.scrollTo('#{id}'); "
54 onclick << "Element.scrollTo('#{id}'); "
55 onclick << "return false;"
55 onclick << "return false;"
56 link_to(name, "#", options.merge(:onclick => onclick))
56 link_to(name, "#", options.merge(:onclick => onclick))
57 end
57 end
58
58
59 def image_to_function(name, function, html_options = {})
59 def image_to_function(name, function, html_options = {})
60 html_options.symbolize_keys!
60 html_options.symbolize_keys!
61 tag(:input, html_options.merge({
61 tag(:input, html_options.merge({
62 :type => "image", :src => image_path(name),
62 :type => "image", :src => image_path(name),
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
64 }))
64 }))
65 end
65 end
66
66
67 def prompt_to_remote(name, text, param, url, html_options = {})
67 def prompt_to_remote(name, text, param, url, html_options = {})
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
69 link_to name, {}, html_options
69 link_to name, {}, html_options
70 end
70 end
71
71
72 def format_date(date)
72 def format_date(date)
73 return nil unless date
73 return nil unless date
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
76 date.strftime(@date_format)
76 date.strftime(@date_format)
77 end
77 end
78
78
79 def format_time(time, include_date = true)
79 def format_time(time, include_date = true)
80 return nil unless time
80 return nil unless time
81 time = time.to_time if time.is_a?(String)
81 time = time.to_time if time.is_a?(String)
82 zone = User.current.time_zone
82 zone = User.current.time_zone
83 if time.utc?
83 if time.utc?
84 local = zone ? zone.adjust(time) : time.getlocal
84 local = zone ? zone.adjust(time) : time.getlocal
85 else
85 else
86 local = zone ? zone.adjust(time.getutc) : time
86 local = zone ? zone.adjust(time.getutc) : time
87 end
87 end
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
91 end
91 end
92
92
93 def html_hours(text)
93 def html_hours(text)
94 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
94 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
95 end
95 end
96
96
97 def authoring(created, author)
97 def authoring(created, author)
98 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
98 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
99 l(:label_added_time_by, author || 'Anonymous', time_tag)
99 l(:label_added_time_by, author || 'Anonymous', time_tag)
100 end
100 end
101
101
102 def l_or_humanize(s)
102 def l_or_humanize(s)
103 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
103 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
104 end
104 end
105
105
106 def day_name(day)
106 def day_name(day)
107 l(:general_day_names).split(',')[day-1]
107 l(:general_day_names).split(',')[day-1]
108 end
108 end
109
109
110 def month_name(month)
110 def month_name(month)
111 l(:actionview_datehelper_select_month_names).split(',')[month-1]
111 l(:actionview_datehelper_select_month_names).split(',')[month-1]
112 end
112 end
113
113
114 def pagination_links_full(paginator, count=nil, options={})
114 def pagination_links_full(paginator, count=nil, options={})
115 page_param = options.delete(:page_param) || :page
115 page_param = options.delete(:page_param) || :page
116 url_param = params.dup
116 url_param = params.dup
117 # don't reuse params if filters are present
117 # don't reuse params if filters are present
118 url_param.clear if url_param.has_key?(:set_filter)
118 url_param.clear if url_param.has_key?(:set_filter)
119
119
120 html = ''
120 html = ''
121 html << link_to_remote(('&#171; ' + l(:label_previous)),
121 html << link_to_remote(('&#171; ' + l(:label_previous)),
122 {:update => 'content',
122 {:update => 'content',
123 :url => url_param.merge(page_param => paginator.current.previous),
123 :url => url_param.merge(page_param => paginator.current.previous),
124 :complete => 'window.scrollTo(0,0)'},
124 :complete => 'window.scrollTo(0,0)'},
125 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
125 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
126
126
127 html << (pagination_links_each(paginator, options) do |n|
127 html << (pagination_links_each(paginator, options) do |n|
128 link_to_remote(n.to_s,
128 link_to_remote(n.to_s,
129 {:url => {:params => url_param.merge(page_param => n)},
129 {:url => {:params => url_param.merge(page_param => n)},
130 :update => 'content',
130 :update => 'content',
131 :complete => 'window.scrollTo(0,0)'},
131 :complete => 'window.scrollTo(0,0)'},
132 {:href => url_for(:params => url_param.merge(page_param => n))})
132 {:href => url_for(:params => url_param.merge(page_param => n))})
133 end || '')
133 end || '')
134
134
135 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
135 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
136 {:update => 'content',
136 {:update => 'content',
137 :url => url_param.merge(page_param => paginator.current.next),
137 :url => url_param.merge(page_param => paginator.current.next),
138 :complete => 'window.scrollTo(0,0)'},
138 :complete => 'window.scrollTo(0,0)'},
139 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
139 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
140
140
141 unless count.nil?
141 unless count.nil?
142 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
142 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
143 end
143 end
144
144
145 html
145 html
146 end
146 end
147
147
148 def per_page_links(selected=nil)
148 def per_page_links(selected=nil)
149 url_param = params.dup
149 url_param = params.dup
150 url_param.clear if url_param.has_key?(:set_filter)
150 url_param.clear if url_param.has_key?(:set_filter)
151
151
152 links = Setting.per_page_options_array.collect do |n|
152 links = Setting.per_page_options_array.collect do |n|
153 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
153 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
154 {:href => url_for(url_param.merge(:per_page => n))})
154 {:href => url_for(url_param.merge(:per_page => n))})
155 end
155 end
156 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
156 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
157 end
157 end
158
158
159 def html_title(*args)
159 def html_title(*args)
160 if args.empty?
160 if args.empty?
161 title = []
161 title = []
162 title << @project.name if @project
162 title << @project.name if @project
163 title += @html_title if @html_title
163 title += @html_title if @html_title
164 title << Setting.app_title
164 title << Setting.app_title
165 title.compact.join(' - ')
165 title.compact.join(' - ')
166 else
166 else
167 @html_title ||= []
167 @html_title ||= []
168 @html_title += args
168 @html_title += args
169 end
169 end
170 end
170 end
171
171
172 def accesskey(s)
172 def accesskey(s)
173 Redmine::AccessKeys.key_for s
173 Redmine::AccessKeys.key_for s
174 end
174 end
175
175
176 # Formats text according to system settings.
176 # Formats text according to system settings.
177 # 2 ways to call this method:
177 # 2 ways to call this method:
178 # * with a String: textilizable(text, options)
178 # * with a String: textilizable(text, options)
179 # * with an object and one of its attribute: textilizable(issue, :description, options)
179 # * with an object and one of its attribute: textilizable(issue, :description, options)
180 def textilizable(*args)
180 def textilizable(*args)
181 options = args.last.is_a?(Hash) ? args.pop : {}
181 options = args.last.is_a?(Hash) ? args.pop : {}
182 case args.size
182 case args.size
183 when 1
183 when 1
184 obj = nil
184 obj = nil
185 text = args.shift
185 text = args.shift
186 when 2
186 when 2
187 obj = args.shift
187 obj = args.shift
188 text = obj.send(args.shift).to_s
188 text = obj.send(args.shift).to_s
189 else
189 else
190 raise ArgumentError, 'invalid arguments to textilizable'
190 raise ArgumentError, 'invalid arguments to textilizable'
191 end
191 end
192 return '' if text.blank?
192 return '' if text.blank?
193
193
194 only_path = options.delete(:only_path) == false ? false : true
194 only_path = options.delete(:only_path) == false ? false : true
195
195
196 # when using an image link, try to use an attachment, if possible
196 # when using an image link, try to use an attachment, if possible
197 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
197 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
198
198
199 if attachments
199 if attachments
200 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
200 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
201 style = $1
201 style = $1
202 filename = $6
202 filename = $6
203 rf = Regexp.new(filename, Regexp::IGNORECASE)
203 rf = Regexp.new(filename, Regexp::IGNORECASE)
204 # search for the picture in attachments
204 # search for the picture in attachments
205 if found = attachments.detect { |att| att.filename =~ rf }
205 if found = attachments.detect { |att| att.filename =~ rf }
206 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found.id
206 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found.id
207 "!#{style}#{image_url}!"
207 "!#{style}#{image_url}!"
208 else
208 else
209 "!#{style}#{filename}!"
209 "!#{style}#{filename}!"
210 end
210 end
211 end
211 end
212 end
212 end
213
213
214 text = (Setting.text_formatting == 'textile') ?
214 text = (Setting.text_formatting == 'textile') ?
215 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
215 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
216 simple_format(auto_link(h(text)))
216 simple_format(auto_link(h(text)))
217
217
218 # different methods for formatting wiki links
218 # different methods for formatting wiki links
219 case options[:wiki_links]
219 case options[:wiki_links]
220 when :local
220 when :local
221 # used for local links to html files
221 # used for local links to html files
222 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
222 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
223 when :anchor
223 when :anchor
224 # used for single-file wiki export
224 # used for single-file wiki export
225 format_wiki_link = Proc.new {|project, title| "##{title}" }
225 format_wiki_link = Proc.new {|project, title| "##{title}" }
226 else
226 else
227 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
227 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
228 end
228 end
229
229
230 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
230 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
231
231
232 # Wiki links
232 # Wiki links
233 #
233 #
234 # Examples:
234 # Examples:
235 # [[mypage]]
235 # [[mypage]]
236 # [[mypage|mytext]]
236 # [[mypage|mytext]]
237 # wiki links can refer other project wikis, using project name or identifier:
237 # wiki links can refer other project wikis, using project name or identifier:
238 # [[project:]] -> wiki starting page
238 # [[project:]] -> wiki starting page
239 # [[project:|mytext]]
239 # [[project:|mytext]]
240 # [[project:mypage]]
240 # [[project:mypage]]
241 # [[project:mypage|mytext]]
241 # [[project:mypage|mytext]]
242 text = text.gsub(/(!)?(\[\[([^\]\|]+)(\|([^\]\|]+))?\]\])/) do |m|
242 text = text.gsub(/(!)?(\[\[([^\]\|]+)(\|([^\]\|]+))?\]\])/) do |m|
243 link_project = project
243 link_project = project
244 esc, all, page, title = $1, $2, $3, $5
244 esc, all, page, title = $1, $2, $3, $5
245 if esc.nil?
245 if esc.nil?
246 if page =~ /^([^\:]+)\:(.*)$/
246 if page =~ /^([^\:]+)\:(.*)$/
247 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
247 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
248 page = $2
248 page = $2
249 title ||= $1 if page.blank?
249 title ||= $1 if page.blank?
250 end
250 end
251
251
252 if link_project && link_project.wiki
252 if link_project && link_project.wiki
253 # check if page exists
253 # check if page exists
254 wiki_page = link_project.wiki.find_page(page)
254 wiki_page = link_project.wiki.find_page(page)
255 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
255 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
256 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
256 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
257 else
257 else
258 # project or wiki doesn't exist
258 # project or wiki doesn't exist
259 title || page
259 title || page
260 end
260 end
261 else
261 else
262 all
262 all
263 end
263 end
264 end
264 end
265
265
266 # Redmine links
266 # Redmine links
267 #
267 #
268 # Examples:
268 # Examples:
269 # Issues:
269 # Issues:
270 # #52 -> Link to issue #52
270 # #52 -> Link to issue #52
271 # Changesets:
271 # Changesets:
272 # r52 -> Link to revision 52
272 # r52 -> Link to revision 52
273 # Documents:
273 # Documents:
274 # document#17 -> Link to document with id 17
274 # document#17 -> Link to document with id 17
275 # document:Greetings -> Link to the document with title "Greetings"
275 # document:Greetings -> Link to the document with title "Greetings"
276 # document:"Some document" -> Link to the document with title "Some document"
276 # document:"Some document" -> Link to the document with title "Some document"
277 # Versions:
277 # Versions:
278 # version#3 -> Link to version with id 3
278 # version#3 -> Link to version with id 3
279 # version:1.0.0 -> Link to version named "1.0.0"
279 # version:1.0.0 -> Link to version named "1.0.0"
280 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
280 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
281 # Attachments:
281 # Attachments:
282 # attachment:file.zip -> Link to the attachment of the current object named file.zip
282 # attachment:file.zip -> Link to the attachment of the current object named file.zip
283 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
283 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
284 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
284 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
285 link = nil
285 link = nil
286 if esc.nil?
286 if esc.nil?
287 if prefix.nil? && sep == 'r'
287 if prefix.nil? && sep == 'r'
288 if project && (changeset = project.changesets.find_by_revision(oid))
288 if project && (changeset = project.changesets.find_by_revision(oid))
289 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid},
289 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid},
290 :class => 'changeset',
290 :class => 'changeset',
291 :title => truncate(changeset.comments, 100))
291 :title => truncate(changeset.comments, 100))
292 end
292 end
293 elsif sep == '#'
293 elsif sep == '#'
294 oid = oid.to_i
294 oid = oid.to_i
295 case prefix
295 case prefix
296 when nil
296 when nil
297 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
297 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
298 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
298 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
299 :class => 'issue',
299 :class => 'issue',
300 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
300 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
301 link = content_tag('del', link) if issue.closed?
301 link = content_tag('del', link) if issue.closed?
302 end
302 end
303 when 'document'
303 when 'document'
304 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
304 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
305 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
305 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
306 :class => 'document'
306 :class => 'document'
307 end
307 end
308 when 'version'
308 when 'version'
309 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
309 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
310 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
310 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
311 :class => 'version'
311 :class => 'version'
312 end
312 end
313 end
313 end
314 elsif sep == ':'
314 elsif sep == ':'
315 # removes the double quotes if any
315 # removes the double quotes if any
316 name = oid.gsub(%r{^"(.*)"$}, "\\1")
316 name = oid.gsub(%r{^"(.*)"$}, "\\1")
317 case prefix
317 case prefix
318 when 'document'
318 when 'document'
319 if project && document = project.documents.find_by_title(name)
319 if project && document = project.documents.find_by_title(name)
320 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
320 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
321 :class => 'document'
321 :class => 'document'
322 end
322 end
323 when 'version'
323 when 'version'
324 if project && version = project.versions.find_by_name(name)
324 if project && version = project.versions.find_by_name(name)
325 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
325 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
326 :class => 'version'
326 :class => 'version'
327 end
327 end
328 when 'attachment'
328 when 'attachment'
329 if attachments && attachment = attachments.detect {|a| a.filename == name }
329 if attachments && attachment = attachments.detect {|a| a.filename == name }
330 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
330 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
331 :class => 'attachment'
331 :class => 'attachment'
332 end
332 end
333 end
333 end
334 end
334 end
335 end
335 end
336 leading + (link || "#{prefix}#{sep}#{oid}")
336 leading + (link || "#{prefix}#{sep}#{oid}")
337 end
337 end
338
338
339 text
339 text
340 end
340 end
341
341
342 # Same as Rails' simple_format helper without using paragraphs
342 # Same as Rails' simple_format helper without using paragraphs
343 def simple_format_without_paragraph(text)
343 def simple_format_without_paragraph(text)
344 text.to_s.
344 text.to_s.
345 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
345 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
346 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
346 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
347 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
347 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
348 end
348 end
349
349
350 def error_messages_for(object_name, options = {})
350 def error_messages_for(object_name, options = {})
351 options = options.symbolize_keys
351 options = options.symbolize_keys
352 object = instance_variable_get("@#{object_name}")
352 object = instance_variable_get("@#{object_name}")
353 if object && !object.errors.empty?
353 if object && !object.errors.empty?
354 # build full_messages here with controller current language
354 # build full_messages here with controller current language
355 full_messages = []
355 full_messages = []
356 object.errors.each do |attr, msg|
356 object.errors.each do |attr, msg|
357 next if msg.nil?
357 next if msg.nil?
358 msg = msg.first if msg.is_a? Array
358 msg = msg.first if msg.is_a? Array
359 if attr == "base"
359 if attr == "base"
360 full_messages << l(msg)
360 full_messages << l(msg)
361 else
361 else
362 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
362 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
363 end
363 end
364 end
364 end
365 # retrieve custom values error messages
365 # retrieve custom values error messages
366 if object.errors[:custom_values]
366 if object.errors[:custom_values]
367 object.custom_values.each do |v|
367 object.custom_values.each do |v|
368 v.errors.each do |attr, msg|
368 v.errors.each do |attr, msg|
369 next if msg.nil?
369 next if msg.nil?
370 msg = msg.first if msg.is_a? Array
370 msg = msg.first if msg.is_a? Array
371 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
371 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
372 end
372 end
373 end
373 end
374 end
374 end
375 content_tag("div",
375 content_tag("div",
376 content_tag(
376 content_tag(
377 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
377 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
378 ) +
378 ) +
379 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
379 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
380 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
380 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
381 )
381 )
382 else
382 else
383 ""
383 ""
384 end
384 end
385 end
385 end
386
386
387 def lang_options_for_select(blank=true)
387 def lang_options_for_select(blank=true)
388 (blank ? [["(auto)", ""]] : []) +
388 (blank ? [["(auto)", ""]] : []) +
389 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
389 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
390 end
390 end
391
391
392 def label_tag_for(name, option_tags = nil, options = {})
392 def label_tag_for(name, option_tags = nil, options = {})
393 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
393 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
394 content_tag("label", label_text)
394 content_tag("label", label_text)
395 end
395 end
396
396
397 def labelled_tabular_form_for(name, object, options, &proc)
397 def labelled_tabular_form_for(name, object, options, &proc)
398 options[:html] ||= {}
398 options[:html] ||= {}
399 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
399 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
400 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
400 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
401 end
401 end
402
402
403 def check_all_links(form_name)
403 def check_all_links(form_name)
404 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
404 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
405 " | " +
405 " | " +
406 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
406 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
407 end
407 end
408
408
409 def progress_bar(pcts, options={})
409 def progress_bar(pcts, options={})
410 pcts = [pcts, pcts] unless pcts.is_a?(Array)
410 pcts = [pcts, pcts] unless pcts.is_a?(Array)
411 pcts[1] = pcts[1] - pcts[0]
411 pcts[1] = pcts[1] - pcts[0]
412 pcts << (100 - pcts[1] - pcts[0])
412 pcts << (100 - pcts[1] - pcts[0])
413 width = options[:width] || '100px;'
413 width = options[:width] || '100px;'
414 legend = options[:legend] || ''
414 legend = options[:legend] || ''
415 content_tag('table',
415 content_tag('table',
416 content_tag('tr',
416 content_tag('tr',
417 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
417 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
418 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
418 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
419 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
419 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
420 ), :class => 'progress', :style => "width: #{width};") +
420 ), :class => 'progress', :style => "width: #{width};") +
421 content_tag('p', legend, :class => 'pourcent')
421 content_tag('p', legend, :class => 'pourcent')
422 end
422 end
423
423
424 def context_menu_link(name, url, options={})
424 def context_menu_link(name, url, options={})
425 options[:class] ||= ''
425 options[:class] ||= ''
426 if options.delete(:selected)
426 if options.delete(:selected)
427 options[:class] << ' icon-checked disabled'
427 options[:class] << ' icon-checked disabled'
428 options[:disabled] = true
428 options[:disabled] = true
429 end
429 end
430 if options.delete(:disabled)
430 if options.delete(:disabled)
431 options.delete(:method)
431 options.delete(:method)
432 options.delete(:confirm)
432 options.delete(:confirm)
433 options.delete(:onclick)
433 options.delete(:onclick)
434 options[:class] << ' disabled'
434 options[:class] << ' disabled'
435 url = '#'
435 url = '#'
436 end
436 end
437 link_to name, url, options
437 link_to name, url, options
438 end
438 end
439
439
440 def calendar_for(field_id)
440 def calendar_for(field_id)
441 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
441 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
442 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
442 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
443 end
443 end
444
444
445 def wikitoolbar_for(field_id)
445 def wikitoolbar_for(field_id)
446 return '' unless Setting.text_formatting == 'textile'
446 return '' unless Setting.text_formatting == 'textile'
447
448 help_link = l(:setting_text_formatting) + ': ' +
449 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
450 :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;")
451
447 javascript_include_tag('jstoolbar/jstoolbar') +
452 javascript_include_tag('jstoolbar/jstoolbar') +
448 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
453 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
449 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
454 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
450 end
455 end
451
456
452 def content_for(name, content = nil, &block)
457 def content_for(name, content = nil, &block)
453 @has_content ||= {}
458 @has_content ||= {}
454 @has_content[name] = true
459 @has_content[name] = true
455 super(name, content, &block)
460 super(name, content, &block)
456 end
461 end
457
462
458 def has_content?(name)
463 def has_content?(name)
459 (@has_content && @has_content[name]) || false
464 (@has_content && @has_content[name]) || false
460 end
465 end
461 end
466 end
@@ -1,30 +1,26
1 <h2><%= @page.pretty_title %></h2>
1 <h2><%= @page.pretty_title %></h2>
2
2
3 <% form_for :content, @content, :url => {:action => 'edit', :page => @page.title}, :html => {:id => 'wiki_form'} do |f| %>
3 <% form_for :content, @content, :url => {:action => 'edit', :page => @page.title}, :html => {:id => 'wiki_form'} do |f| %>
4 <%= f.hidden_field :version %>
4 <%= f.hidden_field :version %>
5 <%= error_messages_for 'content' %>
5 <%= error_messages_for 'content' %>
6 <div class="contextual">
6
7 <%= l(:setting_text_formatting) %>:
8 <%= link_to l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
9 :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;" %>
10 </div>
11 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
7 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
12 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
8 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
13 <p><%= submit_tag l(:button_save) %>
9 <p><%= submit_tag l(:button_save) %>
14 <%= link_to_remote l(:label_preview),
10 <%= link_to_remote l(:label_preview),
15 { :url => { :controller => 'wiki', :action => 'preview', :id => @project, :page => @page.title },
11 { :url => { :controller => 'wiki', :action => 'preview', :id => @project, :page => @page.title },
16 :method => 'post',
12 :method => 'post',
17 :update => 'preview',
13 :update => 'preview',
18 :with => "Form.serialize('wiki_form')",
14 :with => "Form.serialize('wiki_form')",
19 :complete => "Element.scrollTo('preview')"
15 :complete => "Element.scrollTo('preview')"
20 }, :accesskey => accesskey(:preview) %></p>
16 }, :accesskey => accesskey(:preview) %></p>
21 <%= wikitoolbar_for 'content_text' %>
17 <%= wikitoolbar_for 'content_text' %>
22 <% end %>
18 <% end %>
23
19
24 <div id="preview" class="wiki"></div>
20 <div id="preview" class="wiki"></div>
25
21
26 <% content_for :header_tags do %>
22 <% content_for :header_tags do %>
27 <%= stylesheet_link_tag 'scm' %>
23 <%= stylesheet_link_tag 'scm' %>
28 <% end %>
24 <% end %>
29
25
30 <% html_title @page.pretty_title %>
26 <% html_title @page.pretty_title %>
@@ -1,59 +1,64
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml">
2 <html xmlns="http://www.w3.org/1999/xhtml">
3 <head>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <meta http-equiv="Content-Style-Type" content="text/css" />
5 <meta http-equiv="Content-Style-Type" content="text/css" />
6 <title>Wiki formatting</title>
6 <title>Wiki formatting</title>
7 <link rel="stylesheet" href="html.css" type="text/css" />
8 <style type="text/css">
7 <style type="text/css">
9 h1 { font-family: Verdana, sans-serif; font-size: 14px; text-align: center; color: #444; }
8 h1 { font-family: Verdana, sans-serif; font-size: 14px; text-align: center; color: #444; }
10 table { font-family: Verdana, sans-serif; font-size: 12px; color: #444; }
9 body { font-family: Verdana, sans-serif; font-size: 12px; color: #444; }
10 table th { padding-top: 1em; }
11 table td { vertical-align: top; background-color: #f5f5f5; height: 2em; vertical-align: middle;}
11 table td { vertical-align: top; background-color: #f5f5f5; height: 2em; vertical-align: middle;}
12 table td code { font-size: 1.2em; }
12 table td code { font-size: 1.2em; }
13 table td h1 { font-size: 1.8em; text-align: left; }
13 table td h1 { font-size: 1.8em; text-align: left; }
14 table td h2 { font-size: 1.4em; text-align: left; }
14 table td h2 { font-size: 1.4em; text-align: left; }
15 table td h3 { font-size: 1.2em; text-align: left; }
15 table td h3 { font-size: 1.2em; text-align: left; }
16
16
17 </style>
17 </style>
18 </head>
18 </head>
19 <body>
19 <body>
20
20
21 <h1>Wiki Syntax Quick Reference</h1>
21 <h1>Wiki Syntax Quick Reference</h1>
22
22
23 <table width="100%">
23 <table width="100%">
24 <tr><th colspan="3">Font Styles</th></tr>
24 <tr><th colspan="3">Font Styles</th></tr>
25 <tr><th><img src="../../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td width="50%">*Strong*</td><td width="50%"><strong>Strong</strong></td></tr>
25 <tr><th><img src="../../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td width="50%">*Strong*</td><td width="50%"><strong>Strong</strong></td></tr>
26 <tr><th><img src="../../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>_Italic_</td><td><em>Italic</em></td></tr>
26 <tr><th><img src="../../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>_Italic_</td><td><em>Italic</em></td></tr>
27 <tr><th><img src="../../images/jstoolbar/bt_ins.png" style="border: 1px solid #bbb;" alt="Underline" /></th><td>+Underline+</td><td><ins>Underline</ins></td></tr>
27 <tr><th><img src="../../images/jstoolbar/bt_ins.png" style="border: 1px solid #bbb;" alt="Underline" /></th><td>+Underline+</td><td><ins>Underline</ins></td></tr>
28 <tr><th><img src="../../images/jstoolbar/bt_del.png" style="border: 1px solid #bbb;" alt="Deleted" /></th><td>-Deleted-</td><td><del>Deleted</del></td></tr>
28 <tr><th><img src="../../images/jstoolbar/bt_del.png" style="border: 1px solid #bbb;" alt="Deleted" /></th><td>-Deleted-</td><td><del>Deleted</del></td></tr>
29 <tr><th></th><td>??Quote??</td><td><cite>Quote</cite></td></tr>
29 <tr><th></th><td>??Quote??</td><td><cite>Quote</cite></td></tr>
30 <tr><th><img src="../../images/jstoolbar/bt_code.png" style="border: 1px solid #bbb;" alt="Inline Code" /></th><td>@Inline Code@</td><td><code>Inline Code</code></td></tr>
30 <tr><th><img src="../../images/jstoolbar/bt_code.png" style="border: 1px solid #bbb;" alt="Inline Code" /></th><td>@Inline Code@</td><td><code>Inline Code</code></td></tr>
31 <tr><th><img src="../../images/jstoolbar/bt_pre.png" style="border: 1px solid #bbb;" alt="Preformatted text" /></th><td>&lt;pre><br />&nbsp;lines<br />&nbsp;of code<br />&lt;/pre></td><td>
31 <tr><th><img src="../../images/jstoolbar/bt_pre.png" style="border: 1px solid #bbb;" alt="Preformatted text" /></th><td>&lt;pre><br />&nbsp;lines<br />&nbsp;of code<br />&lt;/pre></td><td>
32 <pre>
32 <pre>
33 lines
33 lines
34 of code
34 of code
35 </pre>
35 </pre>
36 </td></tr>
36 </td></tr>
37
37
38 <tr><th colspan="3">Lists</th></tr>
38 <tr><th colspan="3">Lists</th></tr>
39 <tr><th><img src="../../images/jstoolbar/bt_ul.png" style="border: 1px solid #bbb;" alt="Unordered list" /></th><td>* Item 1<br />* Item 2</td><td><ul><li>Item 1</li><li>Item 2</li></ul></td></tr>
39 <tr><th><img src="../../images/jstoolbar/bt_ul.png" style="border: 1px solid #bbb;" alt="Unordered list" /></th><td>* Item 1<br />* Item 2</td><td><ul><li>Item 1</li><li>Item 2</li></ul></td></tr>
40 <tr><th><img src="../../images/jstoolbar/bt_ol.png" style="border: 1px solid #bbb;" alt="Ordered list" /></th><td># Item 1<br /># Item 2</td><td><ol><li>Item 1</li><li>Item 2</li></ol></td></tr>
40 <tr><th><img src="../../images/jstoolbar/bt_ol.png" style="border: 1px solid #bbb;" alt="Ordered list" /></th><td># Item 1<br /># Item 2</td><td><ol><li>Item 1</li><li>Item 2</li></ol></td></tr>
41
41
42 <tr><th colspan="3">Headings</th></tr>
42 <tr><th colspan="3">Headings</th></tr>
43 <tr><th><img src="../../images/jstoolbar/bt_h1.png" style="border: 1px solid #bbb;" alt="Heading 1" /></th><td>h1. Title 1</td><td><h1>Title 1</h1></td></tr>
43 <tr><th><img src="../../images/jstoolbar/bt_h1.png" style="border: 1px solid #bbb;" alt="Heading 1" /></th><td>h1. Title 1</td><td><h1>Title 1</h1></td></tr>
44 <tr><th><img src="../../images/jstoolbar/bt_h2.png" style="border: 1px solid #bbb;" alt="Heading 2" /></th><td>h2. Title 2</td><td><h2>Title 2</h2></td></tr>
44 <tr><th><img src="../../images/jstoolbar/bt_h2.png" style="border: 1px solid #bbb;" alt="Heading 2" /></th><td>h2. Title 2</td><td><h2>Title 2</h2></td></tr>
45 <tr><th><img src="../../images/jstoolbar/bt_h3.png" style="border: 1px solid #bbb;" alt="Heading 3" /></th><td>h3. Title 3</td><td><h3>Title 3</h3></td></tr>
45 <tr><th><img src="../../images/jstoolbar/bt_h3.png" style="border: 1px solid #bbb;" alt="Heading 3" /></th><td>h3. Title 3</td><td><h3>Title 3</h3></td></tr>
46
46
47 <tr><th colspan="3">Links</th></tr>
47 <tr><th colspan="3">Links</th></tr>
48 <tr><th></th><td>http://foo.bar</td><td><a href="#">http://foo.bar</a></td></tr>
48 <tr><th></th><td>http://foo.bar</td><td><a href="#">http://foo.bar</a></td></tr>
49 <tr><th></th><td>"Foo":http://foo.bar</td><td><a href="#">Foo</a></td></tr>
50
51 <tr><th colspan="3">Redmine links</th></tr>
49 <tr><th><img src="../../images/jstoolbar/bt_link.png" style="border: 1px solid #bbb;" alt="Link to a Wiki page" /></th><td>[[Wiki page]]</td><td><a href="#">Wiki page</a></td></tr>
52 <tr><th><img src="../../images/jstoolbar/bt_link.png" style="border: 1px solid #bbb;" alt="Link to a Wiki page" /></th><td>[[Wiki page]]</td><td><a href="#">Wiki page</a></td></tr>
50 <tr><th></th><td>Issue #12</td><td>Issue <a href="#">#12</a></td></tr>
53 <tr><th></th><td>Issue #12</td><td>Issue <a href="#">#12</a></td></tr>
51 <tr><th></th><td>Revision r43</td><td>Revision <a href="#">r43</a></td></tr>
54 <tr><th></th><td>Revision r43</td><td>Revision <a href="#">r43</a></td></tr>
52
55
53 <tr><th colspan="3">Inline images</th></tr>
56 <tr><th colspan="3">Inline images</th></tr>
54 <tr><th><img src="../../images/jstoolbar/bt_img.png" style="border: 1px solid #bbb;" alt="Image" /></th><td>!<em>image_url</em>!</td><td></td></tr>
57 <tr><th><img src="../../images/jstoolbar/bt_img.png" style="border: 1px solid #bbb;" alt="Image" /></th><td>!<em>image_url</em>!</td><td></td></tr>
55 <tr><th></th><td>!<em>attached_image</em>!</td><td></td></tr>
58 <tr><th></th><td>!<em>attached_image</em>!</td><td></td></tr>
56 </table>
59 </table>
57
60
61 <p><a href="http://www.redmine.org/wiki/redmine/RedmineWikiFormatting" onclick="window.open('http://www.redmine.org/wiki/redmine/RedmineWikiFormatting', '', ''); return false;">More Information</a></p>
62
58 </body>
63 </body>
59 </html>
64 </html>
@@ -1,517 +1,528
1 /* ***** BEGIN LICENSE BLOCK *****
1 /* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of DotClear.
2 * This file is part of DotClear.
3 * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All
3 * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All
4 * rights reserved.
4 * rights reserved.
5 *
5 *
6 * DotClear is free software; you can redistribute it and/or modify
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
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
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
9 * (at your option) any later version.
10 *
10 *
11 * DotClear is distributed in the hope that it will be useful,
11 * DotClear is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
14 * GNU General Public License for more details.
15 *
15 *
16 * You should have received a copy of the GNU General Public License
16 * You should have received a copy of the GNU General Public License
17 * along with DotClear; if not, write to the Free Software
17 * along with DotClear; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
19 *
20 * ***** END LICENSE BLOCK *****
20 * ***** END LICENSE BLOCK *****
21 */
21 */
22
22
23 /* Modified by JP LANG for textile formatting */
23 /* Modified by JP LANG for textile formatting */
24
24
25 function jsToolBar(textarea) {
25 function jsToolBar(textarea) {
26 if (!document.createElement) { return; }
26 if (!document.createElement) { return; }
27
27
28 if (!textarea) { return; }
28 if (!textarea) { return; }
29
29
30 if ((typeof(document["selection"]) == "undefined")
30 if ((typeof(document["selection"]) == "undefined")
31 && (typeof(textarea["setSelectionRange"]) == "undefined")) {
31 && (typeof(textarea["setSelectionRange"]) == "undefined")) {
32 return;
32 return;
33 }
33 }
34
34
35 this.textarea = textarea;
35 this.textarea = textarea;
36
36
37 this.editor = document.createElement('div');
37 this.editor = document.createElement('div');
38 this.editor.className = 'jstEditor';
38 this.editor.className = 'jstEditor';
39
39
40 this.textarea.parentNode.insertBefore(this.editor,this.textarea);
40 this.textarea.parentNode.insertBefore(this.editor,this.textarea);
41 this.editor.appendChild(this.textarea);
41 this.editor.appendChild(this.textarea);
42
42
43 this.toolbar = document.createElement("div");
43 this.toolbar = document.createElement("div");
44 this.toolbar.className = 'jstElements';
44 this.toolbar.className = 'jstElements';
45 this.editor.parentNode.insertBefore(this.toolbar,this.editor);
45 this.editor.parentNode.insertBefore(this.toolbar,this.editor);
46
46
47 // Dragable resizing (only for gecko)
47 // Dragable resizing (only for gecko)
48 if (this.editor.addEventListener)
48 if (this.editor.addEventListener)
49 {
49 {
50 this.handle = document.createElement('div');
50 this.handle = document.createElement('div');
51 this.handle.className = 'jstHandle';
51 this.handle.className = 'jstHandle';
52 var dragStart = this.resizeDragStart;
52 var dragStart = this.resizeDragStart;
53 var This = this;
53 var This = this;
54 this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
54 this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
55 // fix memory leak in Firefox (bug #241518)
55 // fix memory leak in Firefox (bug #241518)
56 window.addEventListener('unload',function() {
56 window.addEventListener('unload',function() {
57 var del = This.handle.parentNode.removeChild(This.handle);
57 var del = This.handle.parentNode.removeChild(This.handle);
58 delete(This.handle);
58 delete(This.handle);
59 },false);
59 },false);
60
60
61 this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
61 this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
62 }
62 }
63
63
64 this.context = null;
64 this.context = null;
65 this.toolNodes = {}; // lorsque la toolbar est dessinΓ©e , cet objet est garni
65 this.toolNodes = {}; // lorsque la toolbar est dessinΓ©e , cet objet est garni
66 // de raccourcis vers les Γ©lΓ©ments DOM correspondants aux outils.
66 // de raccourcis vers les Γ©lΓ©ments DOM correspondants aux outils.
67 }
67 }
68
68
69 function jsButton(title, fn, scope, className) {
69 function jsButton(title, fn, scope, className) {
70 if(typeof jsToolBar.strings == 'undefined') {
70 if(typeof jsToolBar.strings == 'undefined') {
71 this.title = title || null;
71 this.title = title || null;
72 } else {
72 } else {
73 this.title = jsToolBar.strings[title] || title || null;
73 this.title = jsToolBar.strings[title] || title || null;
74 }
74 }
75 this.fn = fn || function(){};
75 this.fn = fn || function(){};
76 this.scope = scope || null;
76 this.scope = scope || null;
77 this.className = className || null;
77 this.className = className || null;
78 }
78 }
79 jsButton.prototype.draw = function() {
79 jsButton.prototype.draw = function() {
80 if (!this.scope) return null;
80 if (!this.scope) return null;
81
81
82 var button = document.createElement('button');
82 var button = document.createElement('button');
83 button.setAttribute('type','button');
83 button.setAttribute('type','button');
84 button.tabIndex = 200;
84 button.tabIndex = 200;
85 if (this.className) button.className = this.className;
85 if (this.className) button.className = this.className;
86 button.title = this.title;
86 button.title = this.title;
87 var span = document.createElement('span');
87 var span = document.createElement('span');
88 span.appendChild(document.createTextNode(this.title));
88 span.appendChild(document.createTextNode(this.title));
89 button.appendChild(span);
89 button.appendChild(span);
90
90
91 if (this.icon != undefined) {
91 if (this.icon != undefined) {
92 button.style.backgroundImage = 'url('+this.icon+')';
92 button.style.backgroundImage = 'url('+this.icon+')';
93 }
93 }
94 if (typeof(this.fn) == 'function') {
94 if (typeof(this.fn) == 'function') {
95 var This = this;
95 var This = this;
96 button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
96 button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
97 }
97 }
98 return button;
98 return button;
99 }
99 }
100
100
101 function jsSpace(id) {
101 function jsSpace(id) {
102 this.id = id || null;
102 this.id = id || null;
103 this.width = null;
103 this.width = null;
104 }
104 }
105 jsSpace.prototype.draw = function() {
105 jsSpace.prototype.draw = function() {
106 var span = document.createElement('span');
106 var span = document.createElement('span');
107 if (this.id) span.id = this.id;
107 if (this.id) span.id = this.id;
108 span.appendChild(document.createTextNode(String.fromCharCode(160)));
108 span.appendChild(document.createTextNode(String.fromCharCode(160)));
109 span.className = 'jstSpacer';
109 span.className = 'jstSpacer';
110 if (this.width) span.style.marginRight = this.width+'px';
110 if (this.width) span.style.marginRight = this.width+'px';
111
111
112 return span;
112 return span;
113 }
113 }
114
114
115 function jsCombo(title, options, scope, fn, className) {
115 function jsCombo(title, options, scope, fn, className) {
116 this.title = title || null;
116 this.title = title || null;
117 this.options = options || null;
117 this.options = options || null;
118 this.scope = scope || null;
118 this.scope = scope || null;
119 this.fn = fn || function(){};
119 this.fn = fn || function(){};
120 this.className = className || null;
120 this.className = className || null;
121 }
121 }
122 jsCombo.prototype.draw = function() {
122 jsCombo.prototype.draw = function() {
123 if (!this.scope || !this.options) return null;
123 if (!this.scope || !this.options) return null;
124
124
125 var select = document.createElement('select');
125 var select = document.createElement('select');
126 if (this.className) select.className = className;
126 if (this.className) select.className = className;
127 select.title = this.title;
127 select.title = this.title;
128
128
129 for (var o in this.options) {
129 for (var o in this.options) {
130 //var opt = this.options[o];
130 //var opt = this.options[o];
131 var option = document.createElement('option');
131 var option = document.createElement('option');
132 option.value = o;
132 option.value = o;
133 option.appendChild(document.createTextNode(this.options[o]));
133 option.appendChild(document.createTextNode(this.options[o]));
134 select.appendChild(option);
134 select.appendChild(option);
135 }
135 }
136
136
137 var This = this;
137 var This = this;
138 select.onchange = function() {
138 select.onchange = function() {
139 try {
139 try {
140 This.fn.call(This.scope, this.value);
140 This.fn.call(This.scope, this.value);
141 } catch (e) { alert(e); }
141 } catch (e) { alert(e); }
142
142
143 return false;
143 return false;
144 }
144 }
145
145
146 return select;
146 return select;
147 }
147 }
148
148
149
149
150 jsToolBar.prototype = {
150 jsToolBar.prototype = {
151 base_url: '',
151 base_url: '',
152 mode: 'wiki',
152 mode: 'wiki',
153 elements: {},
153 elements: {},
154 help_link: '',
154
155
155 getMode: function() {
156 getMode: function() {
156 return this.mode;
157 return this.mode;
157 },
158 },
158
159
159 setMode: function(mode) {
160 setMode: function(mode) {
160 this.mode = mode || 'wiki';
161 this.mode = mode || 'wiki';
161 },
162 },
162
163
163 switchMode: function(mode) {
164 switchMode: function(mode) {
164 mode = mode || 'wiki';
165 mode = mode || 'wiki';
165 this.draw(mode);
166 this.draw(mode);
166 },
167 },
167
168
169 setHelpLink: function(link) {
170 this.help_link = link;
171 },
172
168 button: function(toolName) {
173 button: function(toolName) {
169 var tool = this.elements[toolName];
174 var tool = this.elements[toolName];
170 if (typeof tool.fn[this.mode] != 'function') return null;
175 if (typeof tool.fn[this.mode] != 'function') return null;
171 var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName);
176 var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName);
172 if (tool.icon != undefined) b.icon = tool.icon;
177 if (tool.icon != undefined) b.icon = tool.icon;
173 return b;
178 return b;
174 },
179 },
175 space: function(toolName) {
180 space: function(toolName) {
176 var tool = new jsSpace(toolName)
181 var tool = new jsSpace(toolName)
177 if (this.elements[toolName].width !== undefined)
182 if (this.elements[toolName].width !== undefined)
178 tool.width = this.elements[toolName].width;
183 tool.width = this.elements[toolName].width;
179 return tool;
184 return tool;
180 },
185 },
181 combo: function(toolName) {
186 combo: function(toolName) {
182 var tool = this.elements[toolName];
187 var tool = this.elements[toolName];
183 var length = tool[this.mode].list.length;
188 var length = tool[this.mode].list.length;
184
189
185 if (typeof tool[this.mode].fn != 'function' || length == 0) {
190 if (typeof tool[this.mode].fn != 'function' || length == 0) {
186 return null;
191 return null;
187 } else {
192 } else {
188 var options = {};
193 var options = {};
189 for (var i=0; i < length; i++) {
194 for (var i=0; i < length; i++) {
190 var opt = tool[this.mode].list[i];
195 var opt = tool[this.mode].list[i];
191 options[opt] = tool.options[opt];
196 options[opt] = tool.options[opt];
192 }
197 }
193 return new jsCombo(tool.title, options, this, tool[this.mode].fn);
198 return new jsCombo(tool.title, options, this, tool[this.mode].fn);
194 }
199 }
195 },
200 },
196 draw: function(mode) {
201 draw: function(mode) {
197 this.setMode(mode);
202 this.setMode(mode);
198
203
199 // Empty toolbar
204 // Empty toolbar
200 while (this.toolbar.hasChildNodes()) {
205 while (this.toolbar.hasChildNodes()) {
201 this.toolbar.removeChild(this.toolbar.firstChild)
206 this.toolbar.removeChild(this.toolbar.firstChild)
202 }
207 }
203 this.toolNodes = {}; // vide les raccourcis DOM/**/
208 this.toolNodes = {}; // vide les raccourcis DOM/**/
204
209
210 var h = document.createElement('div');
211 h.className = 'help'
212 h.innerHTML = this.help_link;
213 '<a href="/help/wiki_syntax.html" onclick="window.open(\'/help/wiki_syntax.html\', \'\', \'resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\'); return false;">Aide</a>';
214 this.toolbar.appendChild(h);
215
205 // Draw toolbar elements
216 // Draw toolbar elements
206 var b, tool, newTool;
217 var b, tool, newTool;
207
218
208 for (var i in this.elements) {
219 for (var i in this.elements) {
209 b = this.elements[i];
220 b = this.elements[i];
210
221
211 var disabled =
222 var disabled =
212 b.type == undefined || b.type == ''
223 b.type == undefined || b.type == ''
213 || (b.disabled != undefined && b.disabled)
224 || (b.disabled != undefined && b.disabled)
214 || (b.context != undefined && b.context != null && b.context != this.context);
225 || (b.context != undefined && b.context != null && b.context != this.context);
215
226
216 if (!disabled && typeof this[b.type] == 'function') {
227 if (!disabled && typeof this[b.type] == 'function') {
217 tool = this[b.type](i);
228 tool = this[b.type](i);
218 if (tool) newTool = tool.draw();
229 if (tool) newTool = tool.draw();
219 if (newTool) {
230 if (newTool) {
220 this.toolNodes[i] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
231 this.toolNodes[i] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
221 this.toolbar.appendChild(newTool);
232 this.toolbar.appendChild(newTool);
222 }
233 }
223 }
234 }
224 }
235 }
225 },
236 },
226
237
227 singleTag: function(stag,etag) {
238 singleTag: function(stag,etag) {
228 stag = stag || null;
239 stag = stag || null;
229 etag = etag || stag;
240 etag = etag || stag;
230
241
231 if (!stag || !etag) { return; }
242 if (!stag || !etag) { return; }
232
243
233 this.encloseSelection(stag,etag);
244 this.encloseSelection(stag,etag);
234 },
245 },
235
246
236 encloseLineSelection: function(prefix, suffix, fn) {
247 encloseLineSelection: function(prefix, suffix, fn) {
237 this.textarea.focus();
248 this.textarea.focus();
238
249
239 prefix = prefix || '';
250 prefix = prefix || '';
240 suffix = suffix || '';
251 suffix = suffix || '';
241
252
242 var start, end, sel, scrollPos, subst, res;
253 var start, end, sel, scrollPos, subst, res;
243
254
244 if (typeof(document["selection"]) != "undefined") {
255 if (typeof(document["selection"]) != "undefined") {
245 sel = document.selection.createRange().text;
256 sel = document.selection.createRange().text;
246 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
257 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
247 start = this.textarea.selectionStart;
258 start = this.textarea.selectionStart;
248 end = this.textarea.selectionEnd;
259 end = this.textarea.selectionEnd;
249 scrollPos = this.textarea.scrollTop;
260 scrollPos = this.textarea.scrollTop;
250 // go to the start of the line
261 // go to the start of the line
251 start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
262 start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
252 // go to the end of the line
263 // go to the end of the line
253 end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
264 end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
254 sel = this.textarea.value.substring(start, end);
265 sel = this.textarea.value.substring(start, end);
255 }
266 }
256
267
257 if (sel.match(/ $/)) { // exclude ending space char, if any
268 if (sel.match(/ $/)) { // exclude ending space char, if any
258 sel = sel.substring(0, sel.length - 1);
269 sel = sel.substring(0, sel.length - 1);
259 suffix = suffix + " ";
270 suffix = suffix + " ";
260 }
271 }
261
272
262 if (typeof(fn) == 'function') {
273 if (typeof(fn) == 'function') {
263 res = (sel) ? fn.call(this,sel) : fn('');
274 res = (sel) ? fn.call(this,sel) : fn('');
264 } else {
275 } else {
265 res = (sel) ? sel : '';
276 res = (sel) ? sel : '';
266 }
277 }
267
278
268 subst = prefix + res + suffix;
279 subst = prefix + res + suffix;
269
280
270 if (typeof(document["selection"]) != "undefined") {
281 if (typeof(document["selection"]) != "undefined") {
271 document.selection.createRange().text = subst;
282 document.selection.createRange().text = subst;
272 var range = this.textarea.createTextRange();
283 var range = this.textarea.createTextRange();
273 range.collapse(false);
284 range.collapse(false);
274 range.move('character', -suffix.length);
285 range.move('character', -suffix.length);
275 range.select();
286 range.select();
276 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
287 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
277 this.textarea.value = this.textarea.value.substring(0, start) + subst +
288 this.textarea.value = this.textarea.value.substring(0, start) + subst +
278 this.textarea.value.substring(end);
289 this.textarea.value.substring(end);
279 if (sel) {
290 if (sel) {
280 this.textarea.setSelectionRange(start + subst.length, start + subst.length);
291 this.textarea.setSelectionRange(start + subst.length, start + subst.length);
281 } else {
292 } else {
282 this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
293 this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
283 }
294 }
284 this.textarea.scrollTop = scrollPos;
295 this.textarea.scrollTop = scrollPos;
285 }
296 }
286 },
297 },
287
298
288 encloseSelection: function(prefix, suffix, fn) {
299 encloseSelection: function(prefix, suffix, fn) {
289 this.textarea.focus();
300 this.textarea.focus();
290
301
291 prefix = prefix || '';
302 prefix = prefix || '';
292 suffix = suffix || '';
303 suffix = suffix || '';
293
304
294 var start, end, sel, scrollPos, subst, res;
305 var start, end, sel, scrollPos, subst, res;
295
306
296 if (typeof(document["selection"]) != "undefined") {
307 if (typeof(document["selection"]) != "undefined") {
297 sel = document.selection.createRange().text;
308 sel = document.selection.createRange().text;
298 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
309 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
299 start = this.textarea.selectionStart;
310 start = this.textarea.selectionStart;
300 end = this.textarea.selectionEnd;
311 end = this.textarea.selectionEnd;
301 scrollPos = this.textarea.scrollTop;
312 scrollPos = this.textarea.scrollTop;
302 sel = this.textarea.value.substring(start, end);
313 sel = this.textarea.value.substring(start, end);
303 }
314 }
304
315
305 if (sel.match(/ $/)) { // exclude ending space char, if any
316 if (sel.match(/ $/)) { // exclude ending space char, if any
306 sel = sel.substring(0, sel.length - 1);
317 sel = sel.substring(0, sel.length - 1);
307 suffix = suffix + " ";
318 suffix = suffix + " ";
308 }
319 }
309
320
310 if (typeof(fn) == 'function') {
321 if (typeof(fn) == 'function') {
311 res = (sel) ? fn.call(this,sel) : fn('');
322 res = (sel) ? fn.call(this,sel) : fn('');
312 } else {
323 } else {
313 res = (sel) ? sel : '';
324 res = (sel) ? sel : '';
314 }
325 }
315
326
316 subst = prefix + res + suffix;
327 subst = prefix + res + suffix;
317
328
318 if (typeof(document["selection"]) != "undefined") {
329 if (typeof(document["selection"]) != "undefined") {
319 document.selection.createRange().text = subst;
330 document.selection.createRange().text = subst;
320 var range = this.textarea.createTextRange();
331 var range = this.textarea.createTextRange();
321 range.collapse(false);
332 range.collapse(false);
322 range.move('character', -suffix.length);
333 range.move('character', -suffix.length);
323 range.select();
334 range.select();
324 // this.textarea.caretPos -= suffix.length;
335 // this.textarea.caretPos -= suffix.length;
325 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
336 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
326 this.textarea.value = this.textarea.value.substring(0, start) + subst +
337 this.textarea.value = this.textarea.value.substring(0, start) + subst +
327 this.textarea.value.substring(end);
338 this.textarea.value.substring(end);
328 if (sel) {
339 if (sel) {
329 this.textarea.setSelectionRange(start + subst.length, start + subst.length);
340 this.textarea.setSelectionRange(start + subst.length, start + subst.length);
330 } else {
341 } else {
331 this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
342 this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
332 }
343 }
333 this.textarea.scrollTop = scrollPos;
344 this.textarea.scrollTop = scrollPos;
334 }
345 }
335 },
346 },
336
347
337 stripBaseURL: function(url) {
348 stripBaseURL: function(url) {
338 if (this.base_url != '') {
349 if (this.base_url != '') {
339 var pos = url.indexOf(this.base_url);
350 var pos = url.indexOf(this.base_url);
340 if (pos == 0) {
351 if (pos == 0) {
341 url = url.substr(this.base_url.length);
352 url = url.substr(this.base_url.length);
342 }
353 }
343 }
354 }
344
355
345 return url;
356 return url;
346 }
357 }
347 };
358 };
348
359
349 /** Resizer
360 /** Resizer
350 -------------------------------------------------------- */
361 -------------------------------------------------------- */
351 jsToolBar.prototype.resizeSetStartH = function() {
362 jsToolBar.prototype.resizeSetStartH = function() {
352 this.dragStartH = this.textarea.offsetHeight + 0;
363 this.dragStartH = this.textarea.offsetHeight + 0;
353 };
364 };
354 jsToolBar.prototype.resizeDragStart = function(event) {
365 jsToolBar.prototype.resizeDragStart = function(event) {
355 var This = this;
366 var This = this;
356 this.dragStartY = event.clientY;
367 this.dragStartY = event.clientY;
357 this.resizeSetStartH();
368 this.resizeSetStartH();
358 document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
369 document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
359 document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
370 document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
360 };
371 };
361
372
362 jsToolBar.prototype.resizeDragMove = function(event) {
373 jsToolBar.prototype.resizeDragMove = function(event) {
363 this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
374 this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
364 };
375 };
365
376
366 jsToolBar.prototype.resizeDragStop = function(event) {
377 jsToolBar.prototype.resizeDragStop = function(event) {
367 document.removeEventListener('mousemove', this.dragMoveHdlr, false);
378 document.removeEventListener('mousemove', this.dragMoveHdlr, false);
368 document.removeEventListener('mouseup', this.dragStopHdlr, false);
379 document.removeEventListener('mouseup', this.dragStopHdlr, false);
369 };
380 };
370
381
371 // Elements definition ------------------------------------
382 // Elements definition ------------------------------------
372
383
373 // strong
384 // strong
374 jsToolBar.prototype.elements.strong = {
385 jsToolBar.prototype.elements.strong = {
375 type: 'button',
386 type: 'button',
376 title: 'Strong',
387 title: 'Strong',
377 fn: {
388 fn: {
378 wiki: function() { this.singleTag('*') }
389 wiki: function() { this.singleTag('*') }
379 }
390 }
380 }
391 }
381
392
382 // em
393 // em
383 jsToolBar.prototype.elements.em = {
394 jsToolBar.prototype.elements.em = {
384 type: 'button',
395 type: 'button',
385 title: 'Italic',
396 title: 'Italic',
386 fn: {
397 fn: {
387 wiki: function() { this.singleTag("_") }
398 wiki: function() { this.singleTag("_") }
388 }
399 }
389 }
400 }
390
401
391 // ins
402 // ins
392 jsToolBar.prototype.elements.ins = {
403 jsToolBar.prototype.elements.ins = {
393 type: 'button',
404 type: 'button',
394 title: 'Underline',
405 title: 'Underline',
395 fn: {
406 fn: {
396 wiki: function() { this.singleTag('+') }
407 wiki: function() { this.singleTag('+') }
397 }
408 }
398 }
409 }
399
410
400 // del
411 // del
401 jsToolBar.prototype.elements.del = {
412 jsToolBar.prototype.elements.del = {
402 type: 'button',
413 type: 'button',
403 title: 'Deleted',
414 title: 'Deleted',
404 fn: {
415 fn: {
405 wiki: function() { this.singleTag('-') }
416 wiki: function() { this.singleTag('-') }
406 }
417 }
407 }
418 }
408
419
409 // code
420 // code
410 jsToolBar.prototype.elements.code = {
421 jsToolBar.prototype.elements.code = {
411 type: 'button',
422 type: 'button',
412 title: 'Code',
423 title: 'Code',
413 fn: {
424 fn: {
414 wiki: function() { this.singleTag('@') }
425 wiki: function() { this.singleTag('@') }
415 }
426 }
416 }
427 }
417
428
418 // spacer
429 // spacer
419 jsToolBar.prototype.elements.space1 = {type: 'space'}
430 jsToolBar.prototype.elements.space1 = {type: 'space'}
420
431
421 // headings
432 // headings
422 jsToolBar.prototype.elements.h1 = {
433 jsToolBar.prototype.elements.h1 = {
423 type: 'button',
434 type: 'button',
424 title: 'Heading 1',
435 title: 'Heading 1',
425 fn: {
436 fn: {
426 wiki: function() {
437 wiki: function() {
427 this.encloseLineSelection('h1. ', '',function(str) {
438 this.encloseLineSelection('h1. ', '',function(str) {
428 str = str.replace(/^h\d+\.\s+/, '')
439 str = str.replace(/^h\d+\.\s+/, '')
429 return str;
440 return str;
430 });
441 });
431 }
442 }
432 }
443 }
433 }
444 }
434 jsToolBar.prototype.elements.h2 = {
445 jsToolBar.prototype.elements.h2 = {
435 type: 'button',
446 type: 'button',
436 title: 'Heading 2',
447 title: 'Heading 2',
437 fn: {
448 fn: {
438 wiki: function() {
449 wiki: function() {
439 this.encloseLineSelection('h2. ', '',function(str) {
450 this.encloseLineSelection('h2. ', '',function(str) {
440 str = str.replace(/^h\d+\.\s+/, '')
451 str = str.replace(/^h\d+\.\s+/, '')
441 return str;
452 return str;
442 });
453 });
443 }
454 }
444 }
455 }
445 }
456 }
446 jsToolBar.prototype.elements.h3 = {
457 jsToolBar.prototype.elements.h3 = {
447 type: 'button',
458 type: 'button',
448 title: 'Heading 3',
459 title: 'Heading 3',
449 fn: {
460 fn: {
450 wiki: function() {
461 wiki: function() {
451 this.encloseLineSelection('h3. ', '',function(str) {
462 this.encloseLineSelection('h3. ', '',function(str) {
452 str = str.replace(/^h\d+\.\s+/, '')
463 str = str.replace(/^h\d+\.\s+/, '')
453 return str;
464 return str;
454 });
465 });
455 }
466 }
456 }
467 }
457 }
468 }
458
469
459 // spacer
470 // spacer
460 jsToolBar.prototype.elements.space2 = {type: 'space'}
471 jsToolBar.prototype.elements.space2 = {type: 'space'}
461
472
462 // ul
473 // ul
463 jsToolBar.prototype.elements.ul = {
474 jsToolBar.prototype.elements.ul = {
464 type: 'button',
475 type: 'button',
465 title: 'Unordered list',
476 title: 'Unordered list',
466 fn: {
477 fn: {
467 wiki: function() {
478 wiki: function() {
468 this.encloseLineSelection('','',function(str) {
479 this.encloseLineSelection('','',function(str) {
469 str = str.replace(/\r/g,'');
480 str = str.replace(/\r/g,'');
470 return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
481 return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
471 });
482 });
472 }
483 }
473 }
484 }
474 }
485 }
475
486
476 // ol
487 // ol
477 jsToolBar.prototype.elements.ol = {
488 jsToolBar.prototype.elements.ol = {
478 type: 'button',
489 type: 'button',
479 title: 'Ordered list',
490 title: 'Ordered list',
480 fn: {
491 fn: {
481 wiki: function() {
492 wiki: function() {
482 this.encloseLineSelection('','',function(str) {
493 this.encloseLineSelection('','',function(str) {
483 str = str.replace(/\r/g,'');
494 str = str.replace(/\r/g,'');
484 return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
495 return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
485 });
496 });
486 }
497 }
487 }
498 }
488 }
499 }
489
500
490 // pre
501 // pre
491 jsToolBar.prototype.elements.pre = {
502 jsToolBar.prototype.elements.pre = {
492 type: 'button',
503 type: 'button',
493 title: 'Preformatted text',
504 title: 'Preformatted text',
494 fn: {
505 fn: {
495 wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
506 wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
496 }
507 }
497 }
508 }
498
509
499 // spacer
510 // spacer
500 jsToolBar.prototype.elements.space3 = {type: 'space'}
511 jsToolBar.prototype.elements.space3 = {type: 'space'}
501
512
502 // wiki page
513 // wiki page
503 jsToolBar.prototype.elements.link = {
514 jsToolBar.prototype.elements.link = {
504 type: 'button',
515 type: 'button',
505 title: 'Wiki link',
516 title: 'Wiki link',
506 fn: {
517 fn: {
507 wiki: function() { this.encloseSelection("[[", "]]") }
518 wiki: function() { this.encloseSelection("[[", "]]") }
508 }
519 }
509 }
520 }
510 // image
521 // image
511 jsToolBar.prototype.elements.img = {
522 jsToolBar.prototype.elements.img = {
512 type: 'button',
523 type: 'button',
513 title: 'Image',
524 title: 'Image',
514 fn: {
525 fn: {
515 wiki: function() { this.encloseSelection("!", "!") }
526 wiki: function() { this.encloseSelection("!", "!") }
516 }
527 }
517 }
528 }
@@ -1,93 +1,95
1 .jstEditor {
1 .jstEditor {
2 padding-left: 0px;
2 padding-left: 0px;
3 }
3 }
4 .jstEditor textarea, .jstEditor iframe {
4 .jstEditor textarea, .jstEditor iframe {
5 margin: 0;
5 margin: 0;
6 }
6 }
7
7
8 .jstHandle {
8 .jstHandle {
9 height: 10px;
9 height: 10px;
10 font-size: 0.1em;
10 font-size: 0.1em;
11 cursor: s-resize;
11 cursor: s-resize;
12 /*background: transparent url(img/resizer.png) no-repeat 45% 50%;*/
12 /*background: transparent url(img/resizer.png) no-repeat 45% 50%;*/
13 }
13 }
14
14
15 .jstElements {
15 .jstElements {
16 padding: 3px 3px;
16 padding: 3px 3px;
17 }
17 }
18
18
19 .jstElements button {
19 .jstElements button {
20 margin-right : 6px;
20 margin-right : 6px;
21 width : 24px;
21 width : 24px;
22 height: 24px;
22 height: 24px;
23 padding: 4px;
23 padding: 4px;
24 border-style: solid;
24 border-style: solid;
25 border-width: 1px;
25 border-width: 1px;
26 border-color: #ddd;
26 border-color: #ddd;
27 background-color : #f7f7f7;
27 background-color : #f7f7f7;
28 background-position : 50% 50%;
28 background-position : 50% 50%;
29 background-repeat: no-repeat;
29 background-repeat: no-repeat;
30 }
30 }
31 .jstElements button:hover {
31 .jstElements button:hover {
32 border-color : #000;
32 border-color : #000;
33 }
33 }
34 .jstElements button span {
34 .jstElements button span {
35 display : none;
35 display : none;
36 }
36 }
37 .jstElements span {
37 .jstElements span {
38 display : inline;
38 display : inline;
39 }
39 }
40
40
41 .jstSpacer {
41 .jstSpacer {
42 width : 0px;
42 width : 0px;
43 font-size: 1px;
43 font-size: 1px;
44 margin-right: 4px;
44 margin-right: 4px;
45 }
45 }
46
46
47 .jstElements .help { float: right; margin-right: 1em; padding-top: 8px; font-size: 0.9em; }
48
47 /* Buttons
49 /* Buttons
48 -------------------------------------------------------- */
50 -------------------------------------------------------- */
49 .jstb_strong {
51 .jstb_strong {
50 background-image: url(../images/jstoolbar/bt_strong.png);
52 background-image: url(../images/jstoolbar/bt_strong.png);
51 }
53 }
52 .jstb_em {
54 .jstb_em {
53 background-image: url(../images/jstoolbar/bt_em.png);
55 background-image: url(../images/jstoolbar/bt_em.png);
54 }
56 }
55 .jstb_ins {
57 .jstb_ins {
56 background-image: url(../images/jstoolbar/bt_ins.png);
58 background-image: url(../images/jstoolbar/bt_ins.png);
57 }
59 }
58 .jstb_del {
60 .jstb_del {
59 background-image: url(../images/jstoolbar/bt_del.png);
61 background-image: url(../images/jstoolbar/bt_del.png);
60 }
62 }
61 .jstb_quote {
63 .jstb_quote {
62 background-image: url(../images/jstoolbar/bt_quote.png);
64 background-image: url(../images/jstoolbar/bt_quote.png);
63 }
65 }
64 .jstb_code {
66 .jstb_code {
65 background-image: url(../images/jstoolbar/bt_code.png);
67 background-image: url(../images/jstoolbar/bt_code.png);
66 }
68 }
67 .jstb_br {
69 .jstb_br {
68 background-image: url(../images/jstoolbar/bt_br.png);
70 background-image: url(../images/jstoolbar/bt_br.png);
69 }
71 }
70 .jstb_h1 {
72 .jstb_h1 {
71 background-image: url(../images/jstoolbar/bt_h1.png);
73 background-image: url(../images/jstoolbar/bt_h1.png);
72 }
74 }
73 .jstb_h2 {
75 .jstb_h2 {
74 background-image: url(../images/jstoolbar/bt_h2.png);
76 background-image: url(../images/jstoolbar/bt_h2.png);
75 }
77 }
76 .jstb_h3 {
78 .jstb_h3 {
77 background-image: url(../images/jstoolbar/bt_h3.png);
79 background-image: url(../images/jstoolbar/bt_h3.png);
78 }
80 }
79 .jstb_ul {
81 .jstb_ul {
80 background-image: url(../images/jstoolbar/bt_ul.png);
82 background-image: url(../images/jstoolbar/bt_ul.png);
81 }
83 }
82 .jstb_ol {
84 .jstb_ol {
83 background-image: url(../images/jstoolbar/bt_ol.png);
85 background-image: url(../images/jstoolbar/bt_ol.png);
84 }
86 }
85 .jstb_pre {
87 .jstb_pre {
86 background-image: url(../images/jstoolbar/bt_pre.png);
88 background-image: url(../images/jstoolbar/bt_pre.png);
87 }
89 }
88 .jstb_link {
90 .jstb_link {
89 background-image: url(../images/jstoolbar/bt_link.png);
91 background-image: url(../images/jstoolbar/bt_link.png);
90 }
92 }
91 .jstb_img {
93 .jstb_img {
92 background-image: url(../images/jstoolbar/bt_img.png);
94 background-image: url(../images/jstoolbar/bt_img.png);
93 }
95 }
General Comments 0
You need to be logged in to leave comments. Login now