|
1 /** |
|
2 * FormatControls.js |
|
3 * |
|
4 * Copyright, Moxiecode Systems AB |
|
5 * Released under LGPL License. |
|
6 * |
|
7 * License: http://www.tinymce.com/license |
|
8 * Contributing: http://www.tinymce.com/contributing |
|
9 */ |
|
10 |
|
11 /** |
|
12 * Internal class containing all TinyMCE specific control types such as |
|
13 * format listboxes, fontlist boxes, toolbar buttons etc. |
|
14 * |
|
15 * @class tinymce.ui.FormatControls |
|
16 */ |
|
17 define("tinymce/ui/FormatControls", [ |
|
18 "tinymce/ui/Control", |
|
19 "tinymce/ui/Widget", |
|
20 "tinymce/ui/FloatPanel", |
|
21 "tinymce/util/Tools", |
|
22 "tinymce/EditorManager", |
|
23 "tinymce/Env" |
|
24 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) { |
|
25 var each = Tools.each; |
|
26 |
|
27 EditorManager.on('AddEditor', function(e) { |
|
28 if (e.editor.rtl) { |
|
29 Control.rtl = true; |
|
30 } |
|
31 |
|
32 registerControls(e.editor); |
|
33 }); |
|
34 |
|
35 Control.translate = function(text) { |
|
36 return EditorManager.translate(text); |
|
37 }; |
|
38 |
|
39 Widget.tooltips = !Env.iOS; |
|
40 |
|
41 function registerControls(editor) { |
|
42 var formatMenu; |
|
43 |
|
44 function createListBoxChangeHandler(items, formatName) { |
|
45 return function() { |
|
46 var self = this; |
|
47 |
|
48 editor.on('nodeChange', function(e) { |
|
49 var formatter = editor.formatter; |
|
50 var value = null; |
|
51 |
|
52 each(e.parents, function(node) { |
|
53 each(items, function(item) { |
|
54 if (formatName) { |
|
55 if (formatter.matchNode(node, formatName, {value: item.value})) { |
|
56 value = item.value; |
|
57 } |
|
58 } else { |
|
59 if (formatter.matchNode(node, item.value)) { |
|
60 value = item.value; |
|
61 } |
|
62 } |
|
63 |
|
64 if (value) { |
|
65 return false; |
|
66 } |
|
67 }); |
|
68 |
|
69 if (value) { |
|
70 return false; |
|
71 } |
|
72 }); |
|
73 |
|
74 self.value(value); |
|
75 }); |
|
76 }; |
|
77 } |
|
78 |
|
79 function createFormats(formats) { |
|
80 formats = formats.replace(/;$/, '').split(';'); |
|
81 |
|
82 var i = formats.length; |
|
83 while (i--) { |
|
84 formats[i] = formats[i].split('='); |
|
85 } |
|
86 |
|
87 return formats; |
|
88 } |
|
89 |
|
90 function createFormatMenu() { |
|
91 var count = 0, newFormats = []; |
|
92 |
|
93 var defaultStyleFormats = [ |
|
94 {title: 'Headings', items: [ |
|
95 {title: 'Heading 1', format: 'h1'}, |
|
96 {title: 'Heading 2', format: 'h2'}, |
|
97 {title: 'Heading 3', format: 'h3'}, |
|
98 {title: 'Heading 4', format: 'h4'}, |
|
99 {title: 'Heading 5', format: 'h5'}, |
|
100 {title: 'Heading 6', format: 'h6'} |
|
101 ]}, |
|
102 |
|
103 {title: 'Inline', items: [ |
|
104 {title: 'Bold', icon: 'bold', format: 'bold'}, |
|
105 {title: 'Italic', icon: 'italic', format: 'italic'}, |
|
106 {title: 'Underline', icon: 'underline', format: 'underline'}, |
|
107 {title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'}, |
|
108 {title: 'Superscript', icon: 'superscript', format: 'superscript'}, |
|
109 {title: 'Subscript', icon: 'subscript', format: 'subscript'}, |
|
110 {title: 'Code', icon: 'code', format: 'code'} |
|
111 ]}, |
|
112 |
|
113 {title: 'Blocks', items: [ |
|
114 {title: 'Paragraph', format: 'p'}, |
|
115 {title: 'Blockquote', format: 'blockquote'}, |
|
116 {title: 'Div', format: 'div'}, |
|
117 {title: 'Pre', format: 'pre'} |
|
118 ]}, |
|
119 |
|
120 {title: 'Alignment', items: [ |
|
121 {title: 'Left', icon: 'alignleft', format: 'alignleft'}, |
|
122 {title: 'Center', icon: 'aligncenter', format: 'aligncenter'}, |
|
123 {title: 'Right', icon: 'alignright', format: 'alignright'}, |
|
124 {title: 'Justify', icon: 'alignjustify', format: 'alignjustify'} |
|
125 ]} |
|
126 ]; |
|
127 |
|
128 function createMenu(formats) { |
|
129 var menu = []; |
|
130 |
|
131 if (!formats) { |
|
132 return; |
|
133 } |
|
134 |
|
135 each(formats, function(format) { |
|
136 var menuItem = { |
|
137 text: format.title, |
|
138 icon: format.icon |
|
139 }; |
|
140 |
|
141 if (format.items) { |
|
142 menuItem.menu = createMenu(format.items); |
|
143 } else { |
|
144 var formatName = format.format || "custom" + count++; |
|
145 |
|
146 if (!format.format) { |
|
147 format.name = formatName; |
|
148 newFormats.push(format); |
|
149 } |
|
150 |
|
151 menuItem.format = formatName; |
|
152 menuItem.cmd = format.cmd; |
|
153 } |
|
154 |
|
155 menu.push(menuItem); |
|
156 }); |
|
157 |
|
158 return menu; |
|
159 } |
|
160 |
|
161 function createStylesMenu() { |
|
162 var menu; |
|
163 |
|
164 if (editor.settings.style_formats_merge) { |
|
165 if (editor.settings.style_formats) { |
|
166 menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats)); |
|
167 } else { |
|
168 menu = createMenu(defaultStyleFormats); |
|
169 } |
|
170 } else { |
|
171 menu = createMenu(editor.settings.style_formats || defaultStyleFormats); |
|
172 } |
|
173 |
|
174 return menu; |
|
175 } |
|
176 |
|
177 editor.on('init', function() { |
|
178 each(newFormats, function(format) { |
|
179 editor.formatter.register(format.name, format); |
|
180 }); |
|
181 }); |
|
182 |
|
183 return { |
|
184 type: 'menu', |
|
185 items: createStylesMenu(), |
|
186 onPostRender: function(e) { |
|
187 editor.fire('renderFormatsMenu', {control: e.control}); |
|
188 }, |
|
189 itemDefaults: { |
|
190 preview: true, |
|
191 |
|
192 textStyle: function() { |
|
193 if (this.settings.format) { |
|
194 return editor.formatter.getCssText(this.settings.format); |
|
195 } |
|
196 }, |
|
197 |
|
198 onPostRender: function() { |
|
199 var self = this; |
|
200 |
|
201 self.parent().on('show', function() { |
|
202 var formatName, command; |
|
203 |
|
204 formatName = self.settings.format; |
|
205 if (formatName) { |
|
206 self.disabled(!editor.formatter.canApply(formatName)); |
|
207 self.active(editor.formatter.match(formatName)); |
|
208 } |
|
209 |
|
210 command = self.settings.cmd; |
|
211 if (command) { |
|
212 self.active(editor.queryCommandState(command)); |
|
213 } |
|
214 }); |
|
215 }, |
|
216 |
|
217 onclick: function() { |
|
218 if (this.settings.format) { |
|
219 toggleFormat(this.settings.format); |
|
220 } |
|
221 |
|
222 if (this.settings.cmd) { |
|
223 editor.execCommand(this.settings.cmd); |
|
224 } |
|
225 } |
|
226 } |
|
227 }; |
|
228 } |
|
229 |
|
230 formatMenu = createFormatMenu(); |
|
231 |
|
232 // Simple format controls <control/format>:<UI text> |
|
233 each({ |
|
234 bold: 'Bold', |
|
235 italic: 'Italic', |
|
236 underline: 'Underline', |
|
237 strikethrough: 'Strikethrough', |
|
238 subscript: 'Subscript', |
|
239 superscript: 'Superscript' |
|
240 }, function(text, name) { |
|
241 editor.addButton(name, { |
|
242 tooltip: text, |
|
243 onPostRender: function() { |
|
244 var self = this; |
|
245 |
|
246 // TODO: Fix this |
|
247 if (editor.formatter) { |
|
248 editor.formatter.formatChanged(name, function(state) { |
|
249 self.active(state); |
|
250 }); |
|
251 } else { |
|
252 editor.on('init', function() { |
|
253 editor.formatter.formatChanged(name, function(state) { |
|
254 self.active(state); |
|
255 }); |
|
256 }); |
|
257 } |
|
258 }, |
|
259 onclick: function() { |
|
260 toggleFormat(name); |
|
261 } |
|
262 }); |
|
263 }); |
|
264 |
|
265 // Simple command controls <control>:[<UI text>,<Command>] |
|
266 each({ |
|
267 outdent: ['Decrease indent', 'Outdent'], |
|
268 indent: ['Increase indent', 'Indent'], |
|
269 cut: ['Cut', 'Cut'], |
|
270 copy: ['Copy', 'Copy'], |
|
271 paste: ['Paste', 'Paste'], |
|
272 help: ['Help', 'mceHelp'], |
|
273 selectall: ['Select all', 'SelectAll'], |
|
274 removeformat: ['Clear formatting', 'RemoveFormat'], |
|
275 visualaid: ['Visual aids', 'mceToggleVisualAid'], |
|
276 newdocument: ['New document', 'mceNewDocument'] |
|
277 }, function(item, name) { |
|
278 editor.addButton(name, { |
|
279 tooltip: item[0], |
|
280 cmd: item[1] |
|
281 }); |
|
282 }); |
|
283 |
|
284 // Simple command controls with format state |
|
285 each({ |
|
286 blockquote: ['Blockquote', 'mceBlockQuote'], |
|
287 numlist: ['Numbered list', 'InsertOrderedList'], |
|
288 bullist: ['Bullet list', 'InsertUnorderedList'], |
|
289 subscript: ['Subscript', 'Subscript'], |
|
290 superscript: ['Superscript', 'Superscript'], |
|
291 alignleft: ['Align left', 'JustifyLeft'], |
|
292 aligncenter: ['Align center', 'JustifyCenter'], |
|
293 alignright: ['Align right', 'JustifyRight'], |
|
294 alignjustify: ['Justify', 'JustifyFull'] |
|
295 }, function(item, name) { |
|
296 editor.addButton(name, { |
|
297 tooltip: item[0], |
|
298 cmd: item[1], |
|
299 onPostRender: function() { |
|
300 var self = this; |
|
301 |
|
302 // TODO: Fix this |
|
303 if (editor.formatter) { |
|
304 editor.formatter.formatChanged(name, function(state) { |
|
305 self.active(state); |
|
306 }); |
|
307 } else { |
|
308 editor.on('init', function() { |
|
309 editor.formatter.formatChanged(name, function(state) { |
|
310 self.active(state); |
|
311 }); |
|
312 }); |
|
313 } |
|
314 } |
|
315 }); |
|
316 }); |
|
317 |
|
318 function toggleUndoRedoState(type) { |
|
319 return function() { |
|
320 var self = this; |
|
321 |
|
322 type = type == 'redo' ? 'hasRedo' : 'hasUndo'; |
|
323 |
|
324 function checkState() { |
|
325 return editor.undoManager ? editor.undoManager[type]() : false; |
|
326 } |
|
327 |
|
328 self.disabled(!checkState()); |
|
329 editor.on('Undo Redo AddUndo TypingUndo ClearUndos', function() { |
|
330 self.disabled(!checkState()); |
|
331 }); |
|
332 }; |
|
333 } |
|
334 |
|
335 function toggleVisualAidState() { |
|
336 var self = this; |
|
337 |
|
338 editor.on('VisualAid', function(e) { |
|
339 self.active(e.hasVisual); |
|
340 }); |
|
341 |
|
342 self.active(editor.hasVisual); |
|
343 } |
|
344 |
|
345 editor.addButton('undo', { |
|
346 tooltip: 'Undo', |
|
347 onPostRender: toggleUndoRedoState('undo'), |
|
348 cmd: 'undo' |
|
349 }); |
|
350 |
|
351 editor.addButton('redo', { |
|
352 tooltip: 'Redo', |
|
353 onPostRender: toggleUndoRedoState('redo'), |
|
354 cmd: 'redo' |
|
355 }); |
|
356 |
|
357 editor.addMenuItem('newdocument', { |
|
358 text: 'New document', |
|
359 icon: 'newdocument', |
|
360 cmd: 'mceNewDocument' |
|
361 }); |
|
362 |
|
363 editor.addMenuItem('undo', { |
|
364 text: 'Undo', |
|
365 icon: 'undo', |
|
366 shortcut: 'Meta+Z', |
|
367 onPostRender: toggleUndoRedoState('undo'), |
|
368 cmd: 'undo' |
|
369 }); |
|
370 |
|
371 editor.addMenuItem('redo', { |
|
372 text: 'Redo', |
|
373 icon: 'redo', |
|
374 shortcut: 'Meta+Y', |
|
375 onPostRender: toggleUndoRedoState('redo'), |
|
376 cmd: 'redo' |
|
377 }); |
|
378 |
|
379 editor.addMenuItem('visualaid', { |
|
380 text: 'Visual aids', |
|
381 selectable: true, |
|
382 onPostRender: toggleVisualAidState, |
|
383 cmd: 'mceToggleVisualAid' |
|
384 }); |
|
385 |
|
386 each({ |
|
387 cut: ['Cut', 'Cut', 'Meta+X'], |
|
388 copy: ['Copy', 'Copy', 'Meta+C'], |
|
389 paste: ['Paste', 'Paste', 'Meta+V'], |
|
390 selectall: ['Select all', 'SelectAll', 'Meta+A'], |
|
391 bold: ['Bold', 'Bold', 'Meta+B'], |
|
392 italic: ['Italic', 'Italic', 'Meta+I'], |
|
393 underline: ['Underline', 'Underline'], |
|
394 strikethrough: ['Strikethrough', 'Strikethrough'], |
|
395 subscript: ['Subscript', 'Subscript'], |
|
396 superscript: ['Superscript', 'Superscript'], |
|
397 removeformat: ['Clear formatting', 'RemoveFormat'] |
|
398 }, function(item, name) { |
|
399 editor.addMenuItem(name, { |
|
400 text: item[0], |
|
401 icon: name, |
|
402 shortcut: item[2], |
|
403 cmd: item[1] |
|
404 }); |
|
405 }); |
|
406 |
|
407 editor.on('mousedown', function() { |
|
408 FloatPanel.hideAll(); |
|
409 }); |
|
410 |
|
411 function toggleFormat(fmt) { |
|
412 if (fmt.control) { |
|
413 fmt = fmt.control.value(); |
|
414 } |
|
415 |
|
416 if (fmt) { |
|
417 editor.execCommand('mceToggleFormat', false, fmt); |
|
418 } |
|
419 } |
|
420 |
|
421 editor.addButton('styleselect', { |
|
422 type: 'menubutton', |
|
423 text: 'Formats', |
|
424 menu: formatMenu |
|
425 }); |
|
426 |
|
427 editor.addButton('formatselect', function() { |
|
428 var items = [], blocks = createFormats(editor.settings.block_formats || |
|
429 'Paragraph=p;' + |
|
430 'Heading 1=h1;' + |
|
431 'Heading 2=h2;' + |
|
432 'Heading 3=h3;' + |
|
433 'Heading 4=h4;' + |
|
434 'Heading 5=h5;' + |
|
435 'Heading 6=h6;' + |
|
436 'Preformatted=pre' |
|
437 ); |
|
438 |
|
439 each(blocks, function(block) { |
|
440 items.push({ |
|
441 text: block[0], |
|
442 value: block[1], |
|
443 textStyle: function() { |
|
444 return editor.formatter.getCssText(block[1]); |
|
445 } |
|
446 }); |
|
447 }); |
|
448 |
|
449 return { |
|
450 type: 'listbox', |
|
451 text: blocks[0][0], |
|
452 values: items, |
|
453 fixedWidth: true, |
|
454 onselect: toggleFormat, |
|
455 onPostRender: createListBoxChangeHandler(items) |
|
456 }; |
|
457 }); |
|
458 |
|
459 editor.addButton('fontselect', function() { |
|
460 var defaultFontsFormats = |
|
461 'Andale Mono=andale mono,monospace;' + |
|
462 'Arial=arial,helvetica,sans-serif;' + |
|
463 'Arial Black=arial black,sans-serif;' + |
|
464 'Book Antiqua=book antiqua,palatino,serif;' + |
|
465 'Comic Sans MS=comic sans ms,sans-serif;' + |
|
466 'Courier New=courier new,courier,monospace;' + |
|
467 'Georgia=georgia,palatino,serif;' + |
|
468 'Helvetica=helvetica,arial,sans-serif;' + |
|
469 'Impact=impact,sans-serif;' + |
|
470 'Symbol=symbol;' + |
|
471 'Tahoma=tahoma,arial,helvetica,sans-serif;' + |
|
472 'Terminal=terminal,monaco,monospace;' + |
|
473 'Times New Roman=times new roman,times,serif;' + |
|
474 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + |
|
475 'Verdana=verdana,geneva,sans-serif;' + |
|
476 'Webdings=webdings;' + |
|
477 'Wingdings=wingdings,zapf dingbats'; |
|
478 |
|
479 var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats); |
|
480 |
|
481 each(fonts, function(font) { |
|
482 items.push({ |
|
483 text: {raw: font[0]}, |
|
484 value: font[1], |
|
485 textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : '' |
|
486 }); |
|
487 }); |
|
488 |
|
489 return { |
|
490 type: 'listbox', |
|
491 text: 'Font Family', |
|
492 tooltip: 'Font Family', |
|
493 values: items, |
|
494 fixedWidth: true, |
|
495 onPostRender: createListBoxChangeHandler(items, 'fontname'), |
|
496 onselect: function(e) { |
|
497 if (e.control.settings.value) { |
|
498 editor.execCommand('FontName', false, e.control.settings.value); |
|
499 } |
|
500 } |
|
501 }; |
|
502 }); |
|
503 |
|
504 editor.addButton('fontsizeselect', function() { |
|
505 var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt'; |
|
506 var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats; |
|
507 |
|
508 each(fontsize_formats.split(' '), function(item) { |
|
509 var text = item, value = item; |
|
510 // Allow text=value font sizes. |
|
511 var values = item.split('='); |
|
512 if (values.length > 1) { |
|
513 text = values[0]; |
|
514 value = values[1]; |
|
515 } |
|
516 items.push({text: text, value: value}); |
|
517 }); |
|
518 |
|
519 return { |
|
520 type: 'listbox', |
|
521 text: 'Font Sizes', |
|
522 tooltip: 'Font Sizes', |
|
523 values: items, |
|
524 fixedWidth: true, |
|
525 onPostRender: createListBoxChangeHandler(items, 'fontsize'), |
|
526 onclick: function(e) { |
|
527 if (e.control.settings.value) { |
|
528 editor.execCommand('FontSize', false, e.control.settings.value); |
|
529 } |
|
530 } |
|
531 }; |
|
532 }); |
|
533 |
|
534 editor.addMenuItem('formats', { |
|
535 text: 'Formats', |
|
536 menu: formatMenu |
|
537 }); |
|
538 } |
|
539 }); |