4 * Copyright, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://www.tinymce.com/license
8 * Contributing: http://www.tinymce.com/contributing
12 * Internal class containing all TinyMCE specific control types such as
13 * format listboxes, fontlist boxes, toolbar buttons etc.
15 * @class tinymce.ui.FormatControls
17 define("tinymce/ui/FormatControls", [
20 "tinymce/ui/FloatPanel",
22 "tinymce/EditorManager",
24 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
25 var each = Tools.each;
27 EditorManager.on('AddEditor', function(e) {
28 registerControls(e.editor);
31 Control.translate = function(text) {
32 return EditorManager.translate(text);
35 Widget.tooltips = !Env.iOS;
37 function registerControls(editor) {
40 // Generates a preview for a format
41 function getPreviewCss(format) {
42 var name, previewElm, dom = editor.dom;
43 var previewCss = '', parentFontSize, previewStyles;
45 previewStyles = editor.settings.preview_styles;
48 if (previewStyles === false) {
54 previewStyles = 'font-family font-size font-weight text-decoration ' +
55 'text-transform color background-color border border-radius';
58 // Removes any variables since these can't be previewed
59 function removeVars(val) {
60 return val.replace(/%(\w+)/g, '');
63 // Create block/inline element to use for preview
64 format = editor.formatter.get(format);
70 name = format.block || format.inline || 'span';
71 previewElm = dom.create(name);
73 // Add format styles to preview element
74 each(format.styles, function(value, name) {
75 value = removeVars(value);
78 dom.setStyle(previewElm, name, value);
82 // Add attributes to preview element
83 each(format.attributes, function(value, name) {
84 value = removeVars(value);
87 dom.setAttrib(previewElm, name, value);
91 // Add classes to preview element
92 each(format.classes, function(value) {
93 value = removeVars(value);
95 if (!dom.hasClass(previewElm, value)) {
96 dom.addClass(previewElm, value);
100 editor.fire('PreviewFormats');
102 // Add the previewElm outside the visual area
103 dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
104 editor.getBody().appendChild(previewElm);
106 // Get parent container font size so we can compute px values out of em/% for older IE:s
107 parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
108 parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
110 each(previewStyles.split(' '), function(name) {
111 var value = dom.getStyle(previewElm, name, true);
113 // If background is transparent then check if the body has a background color we can use
114 if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
115 value = dom.getStyle(editor.getBody(), name, true);
117 // Ignore white since it's the default color, not the nicest fix
118 // TODO: Fix this by detecting runtime style
119 if (dom.toHex(value).toLowerCase() == '#ffffff') {
124 if (name == 'color') {
125 // Ignore black since it's the default color, not the nicest fix
126 // TODO: Fix this by detecting runtime style
127 if (dom.toHex(value).toLowerCase() == '#000000') {
132 // Old IE won't calculate the font size so we need to do that manually
133 if (name == 'font-size') {
134 if (/em|%$/.test(value)) {
135 if (parentFontSize === 0) {
139 // Convert font size from em/% to px
140 value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
141 value = (value * parentFontSize) + 'px';
145 if (name == "border" && value) {
146 previewCss += 'padding:0 2px;';
149 previewCss += name + ':' + value + ';';
152 editor.fire('AfterPreviewFormats');
154 //previewCss += 'line-height:normal';
156 dom.remove(previewElm);
161 function createListBoxChangeHandler(items, formatName) {
165 editor.on('nodeChange', function(e) {
166 var formatter = editor.formatter;
169 each(e.parents, function(node) {
170 each(items, function(item) {
172 if (formatter.matchNode(node, formatName, {value: item.value})) {
176 if (formatter.matchNode(node, item.value)) {
196 function createFormats(formats) {
197 formats = formats.split(';');
199 var i = formats.length;
201 formats[i] = formats[i].split('=');
207 function createFormatMenu() {
208 var count = 0, newFormats = [];
210 var defaultStyleFormats = [
211 {title: 'Headers', items: [
212 {title: 'Header 1', format: 'h1'},
213 {title: 'Header 2', format: 'h2'},
214 {title: 'Header 3', format: 'h3'},
215 {title: 'Header 4', format: 'h4'},
216 {title: 'Header 5', format: 'h5'},
217 {title: 'Header 6', format: 'h6'}
220 {title: 'Inline', items: [
221 {title: 'Bold', icon: 'bold', format: 'bold'},
222 {title: 'Italic', icon: 'italic', format: 'italic'},
223 {title: 'Underline', icon: 'underline', format: 'underline'},
224 {title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
225 {title: 'Superscript', icon: 'superscript', format: 'superscript'},
226 {title: 'Subscript', icon: 'subscript', format: 'subscript'},
227 {title: 'Code', icon: 'code', format: 'code'}
230 {title: 'Blocks', items: [
231 {title: 'Paragraph', format: 'p'},
232 {title: 'Blockquote', format: 'blockquote'},
233 {title: 'Div', format: 'div'},
234 {title: 'Pre', format: 'pre'}
237 {title: 'Alignment', items: [
238 {title: 'Left', icon: 'alignleft', format: 'alignleft'},
239 {title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
240 {title: 'Right', icon: 'alignright', format: 'alignright'},
241 {title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
245 function createMenu(formats) {
252 each(formats, function(format) {
259 menuItem.menu = createMenu(format.items);
261 var formatName = format.format || "custom" + count++;
263 if (!format.format) {
264 format.name = formatName;
265 newFormats.push(format);
268 menuItem.format = formatName;
277 editor.on('init', function() {
278 each(newFormats, function(format) {
279 editor.formatter.register(format.name, format);
283 var menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
288 onPostRender: function(e) {
289 editor.fire('renderFormatsMenu', {control: e.control});
294 textStyle: function() {
295 if (this.settings.format) {
296 return getPreviewCss(this.settings.format);
300 onPostRender: function() {
301 var self = this, formatName = this.settings.format;
304 self.parent().on('show', function() {
305 self.disabled(!editor.formatter.canApply(formatName));
306 self.active(editor.formatter.match(formatName));
311 onclick: function() {
312 if (this.settings.format) {
313 toggleFormat(this.settings.format);
322 formatMenu = createFormatMenu();
324 // Simple format controls <control/format>:<UI text>
328 underline: 'Underline',
329 strikethrough: 'Strikethrough',
330 subscript: 'Subscript',
331 superscript: 'Superscript'
332 }, function(text, name) {
333 editor.addButton(name, {
335 onPostRender: function() {
339 if (editor.formatter) {
340 editor.formatter.formatChanged(name, function(state) {
344 editor.on('init', function() {
345 editor.formatter.formatChanged(name, function(state) {
351 onclick: function() {
357 // Simple command controls <control>:[<UI text>,<Command>]
359 outdent: ['Decrease indent', 'Outdent'],
360 indent: ['Increase indent', 'Indent'],
362 copy: ['Copy', 'Copy'],
363 paste: ['Paste', 'Paste'],
364 help: ['Help', 'mceHelp'],
365 selectall: ['Select all', 'SelectAll'],
366 hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
367 removeformat: ['Clear formatting', 'RemoveFormat'],
368 visualaid: ['Visual aids', 'mceToggleVisualAid'],
369 newdocument: ['New document', 'mceNewDocument']
370 }, function(item, name) {
371 editor.addButton(name, {
377 // Simple command controls with format state
379 blockquote: ['Toggle blockquote', 'mceBlockQuote'],
380 numlist: ['Numbered list', 'InsertOrderedList'],
381 bullist: ['Bullet list', 'InsertUnorderedList'],
382 subscript: ['Subscript', 'Subscript'],
383 superscript: ['Superscript', 'Superscript'],
384 alignleft: ['Align left', 'JustifyLeft'],
385 aligncenter: ['Align center', 'JustifyCenter'],
386 alignright: ['Align right', 'JustifyRight'],
387 alignjustify: ['Justify', 'JustifyFull']
388 }, function(item, name) {
389 editor.addButton(name, {
392 onPostRender: function() {
396 if (editor.formatter) {
397 editor.formatter.formatChanged(name, function(state) {
401 editor.on('init', function() {
402 editor.formatter.formatChanged(name, function(state) {
412 return editor.undoManager ? editor.undoManager.hasUndo() : false;
416 return editor.undoManager ? editor.undoManager.hasRedo() : false;
419 function toggleUndoState() {
422 self.disabled(!hasUndo());
423 editor.on('Undo Redo AddUndo TypingUndo', function() {
424 self.disabled(!hasUndo());
428 function toggleRedoState() {
431 self.disabled(!hasRedo());
432 editor.on('Undo Redo AddUndo TypingUndo', function() {
433 self.disabled(!hasRedo());
437 function toggleVisualAidState() {
440 editor.on('VisualAid', function(e) {
441 self.active(e.hasVisual);
444 self.active(editor.hasVisual);
447 editor.addButton('undo', {
449 onPostRender: toggleUndoState,
453 editor.addButton('redo', {
455 onPostRender: toggleRedoState,
459 editor.addMenuItem('newdocument', {
460 text: 'New document',
463 cmd: 'mceNewDocument'
466 editor.addMenuItem('undo', {
470 onPostRender: toggleUndoState,
474 editor.addMenuItem('redo', {
478 onPostRender: toggleRedoState,
482 editor.addMenuItem('visualaid', {
485 onPostRender: toggleVisualAidState,
486 cmd: 'mceToggleVisualAid'
490 cut: ['Cut', 'Cut', 'Ctrl+X'],
491 copy: ['Copy', 'Copy', 'Ctrl+C'],
492 paste: ['Paste', 'Paste', 'Ctrl+V'],
493 selectall: ['Select all', 'SelectAll', 'Ctrl+A'],
494 bold: ['Bold', 'Bold', 'Ctrl+B'],
495 italic: ['Italic', 'Italic', 'Ctrl+I'],
496 underline: ['Underline', 'Underline'],
497 strikethrough: ['Strikethrough', 'Strikethrough'],
498 subscript: ['Subscript', 'Subscript'],
499 superscript: ['Superscript', 'Superscript'],
500 removeformat: ['Clear formatting', 'RemoveFormat']
501 }, function(item, name) {
502 editor.addMenuItem(name, {
510 editor.on('mousedown', function() {
511 FloatPanel.hideAll();
514 function toggleFormat(fmt) {
516 fmt = fmt.control.value();
520 editor.execCommand('mceToggleFormat', false, fmt);
524 editor.addButton('styleselect', {
530 editor.addButton('formatselect', function() {
531 var items = [], blocks = createFormats(editor.settings.block_formats ||
543 each(blocks, function(block) {
547 textStyle: function() {
548 return getPreviewCss(block[1]);
555 text: {raw: blocks[0][0]},
558 onselect: toggleFormat,
559 onPostRender: createListBoxChangeHandler(items)
563 editor.addButton('fontselect', function() {
564 var defaultFontsFormats =
565 'Andale Mono=andale mono,times;' +
566 'Arial=arial,helvetica,sans-serif;' +
567 'Arial Black=arial black,avant garde;' +
568 'Book Antiqua=book antiqua,palatino;' +
569 'Comic Sans MS=comic sans ms,sans-serif;' +
570 'Courier New=courier new,courier;' +
571 'Georgia=georgia,palatino;' +
572 'Helvetica=helvetica;' +
573 'Impact=impact,chicago;' +
575 'Tahoma=tahoma,arial,helvetica,sans-serif;' +
576 'Terminal=terminal,monaco;' +
577 'Times New Roman=times new roman,times;' +
578 'Trebuchet MS=trebuchet ms,geneva;' +
579 'Verdana=verdana,geneva;' +
580 'Webdings=webdings;' +
581 'Wingdings=wingdings,zapf dingbats';
583 var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
585 each(fonts, function(font) {
587 text: {raw: font[0]},
589 textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
596 tooltip: 'Font Family',
599 onPostRender: createListBoxChangeHandler(items, 'fontname'),
600 onselect: function(e) {
601 if (e.control.settings.value) {
602 editor.execCommand('FontName', false, e.control.settings.value);
608 editor.addButton('fontsizeselect', function() {
609 var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
610 var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
612 each(fontsize_formats.split(' '), function(item) {
613 items.push({text: item, value: item});
619 tooltip: 'Font Sizes',
622 onPostRender: createListBoxChangeHandler(items, 'fontsize'),
623 onclick: function(e) {
624 if (e.control.settings.value) {
625 editor.execCommand('FontSize', false, e.control.settings.value);
631 editor.addMenuItem('formats', {