]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/themes/modern/theme.js
WordPress 3.9
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / themes / modern / theme.js
1 /**
2  * theme.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 /*global tinymce:true */
12
13 tinymce.ThemeManager.add('modern', function(editor) {
14         var self = this, settings = editor.settings, Factory = tinymce.ui.Factory, each = tinymce.each, DOM = tinymce.DOM;
15
16         // Default menus
17         var defaultMenus = {
18                 file: {title: 'File', items: 'newdocument'},
19                 edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall'},
20                 insert: {title: 'Insert', items: '|'},
21                 view: {title: 'View', items: 'visualaid |'},
22                 format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
23                 table: {title: 'Table'},
24                 tools: {title: 'Tools'}
25         };
26
27         var defaultToolbar = "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | " +
28                 "bullist numlist outdent indent | link image";
29
30         /**
31          * Creates the toolbars from config and returns a toolbar array.
32          *
33          * @return {Array} Array with toolbars.
34          */
35         function createToolbars() {
36                 var toolbars = [];
37
38                 function addToolbar(items) {
39                         var toolbarItems = [], buttonGroup;
40
41                         if (!items) {
42                                 return;
43                         }
44
45                         each(items.split(/[ ,]/), function(item) {
46                                 var itemName;
47
48                                 function bindSelectorChanged() {
49                                         var selection = editor.selection;
50
51                                         if (itemName == "bullist") {
52                                                 selection.selectorChanged('ul > li', function(state, args) {
53                                                         var nodeName, i = args.parents.length;
54
55                                                         while (i--) {
56                                                                 nodeName = args.parents[i].nodeName;
57                                                                 if (nodeName == "OL" || nodeName == "UL") {
58                                                                         break;
59                                                                 }
60                                                         }
61
62                                                         item.active(state && nodeName == "UL");
63                                                 });
64                                         }
65
66                                         if (itemName == "numlist") {
67                                                 selection.selectorChanged('ol > li', function(state, args) {
68                                                         var nodeName, i = args.parents.length;
69
70                                                         while (i--) {
71                                                                 nodeName = args.parents[i].nodeName;
72                                                                 if (nodeName == "OL" || nodeName == "UL") {
73                                                                         break;
74                                                                 }
75                                                         }
76
77                                                         item.active(state && nodeName == "OL");
78                                                 });
79                                         }
80
81                                         if (item.settings.stateSelector) {
82                                                 selection.selectorChanged(item.settings.stateSelector, function(state) {
83                                                         item.active(state);
84                                                 }, true);
85                                         }
86
87                                         if (item.settings.disabledStateSelector) {
88                                                 selection.selectorChanged(item.settings.disabledStateSelector, function(state) {
89                                                         item.disabled(state);
90                                                 });
91                                         }
92                                 }
93
94                                 if (item == "|") {
95                                         buttonGroup = null;
96                                 } else {
97                                         if (Factory.has(item)) {
98                                                 item = {type: item};
99
100                                                 if (settings.toolbar_items_size) {
101                                                         item.size = settings.toolbar_items_size;
102                                                 }
103
104                                                 toolbarItems.push(item);
105                                                 buttonGroup = null;
106                                         } else {
107                                                 if (!buttonGroup) {
108                                                         buttonGroup = {type: 'buttongroup', items: []};
109                                                         toolbarItems.push(buttonGroup);
110                                                 }
111
112                                                 if (editor.buttons[item]) {
113                                                         // TODO: Move control creation to some UI class
114                                                         itemName = item;
115                                                         item = editor.buttons[itemName];
116
117                                                         if (typeof(item) == "function") {
118                                                                 item = item();
119                                                         }
120
121                                                         item.type = item.type || 'button';
122
123                                                         if (settings.toolbar_items_size) {
124                                                                 item.size = settings.toolbar_items_size;
125                                                         }
126
127                                                         item = Factory.create(item);
128                                                         buttonGroup.items.push(item);
129
130                                                         if (editor.initialized) {
131                                                                 bindSelectorChanged();
132                                                         } else {
133                                                                 editor.on('init', bindSelectorChanged);
134                                                         }
135                                                 }
136                                         }
137                                 }
138                         });
139
140                         toolbars.push({type: 'toolbar', layout: 'flow', items: toolbarItems});
141
142                         return true;
143                 }
144
145                 // Convert toolbar array to multiple options
146                 if (tinymce.isArray(settings.toolbar)) {
147                         // Empty toolbar array is the same as a disabled toolbar
148                         if (settings.toolbar.length === 0) {
149                                 return;
150                         }
151
152                         tinymce.each(settings.toolbar, function(toolbar, i) {
153                                 settings["toolbar" + (i + 1)] = toolbar;
154                         });
155
156                         delete settings.toolbar;
157                 }
158
159                 // Generate toolbar<n>
160                 for (var i = 1; i < 10; i++) {
161                         if (!addToolbar(settings["toolbar" + i])) {
162                                 break;
163                         }
164                 }
165
166                 // Generate toolbar or default toolbar unless it's disabled
167                 if (!toolbars.length && settings.toolbar !== false) {
168                         addToolbar(settings.toolbar || defaultToolbar);
169                 }
170
171                 if (toolbars.length) {
172                         return {
173                                 type: 'panel',
174                                 layout: 'stack',
175                                 classes: "toolbar-grp",
176                                 ariaRoot: true,
177                                 ariaRemember: true,
178                                 items: toolbars
179                         };
180                 }
181         }
182
183         /**
184          * Creates the menu buttons based on config.
185          *
186          * @return {Array} Menu buttons array.
187          */
188         function createMenuButtons() {
189                 var name, menuButtons = [];
190
191                 function createMenuItem(name) {
192                         var menuItem;
193
194                         if (name == '|') {
195                                 return {text: '|'};
196                         }
197
198                         menuItem = editor.menuItems[name];
199
200                         return menuItem;
201                 }
202
203                 function createMenu(context) {
204                         var menuButton, menu, menuItems, isUserDefined, removedMenuItems;
205
206                         removedMenuItems = tinymce.makeMap((settings.removed_menuitems || '').split(/[ ,]/));
207
208                         // User defined menu
209                         if (settings.menu) {
210                                 menu = settings.menu[context];
211                                 isUserDefined = true;
212                         } else {
213                                 menu = defaultMenus[context];
214                         }
215
216                         if (menu) {
217                                 menuButton = {text: menu.title};
218                                 menuItems = [];
219
220                                 // Default/user defined items
221                                 each((menu.items || '').split(/[ ,]/), function(item) {
222                                         var menuItem = createMenuItem(item);
223
224                                         if (menuItem && !removedMenuItems[item]) {
225                                                 menuItems.push(createMenuItem(item));
226                                         }
227                                 });
228
229                                 // Added though context
230                                 if (!isUserDefined) {
231                                         each(editor.menuItems, function(menuItem) {
232                                                 if (menuItem.context == context) {
233                                                         if (menuItem.separator == 'before') {
234                                                                 menuItems.push({text: '|'});
235                                                         }
236
237                                                         if (menuItem.prependToContext) {
238                                                                 menuItems.unshift(menuItem);
239                                                         } else {
240                                                                 menuItems.push(menuItem);
241                                                         }
242
243                                                         if (menuItem.separator == 'after') {
244                                                                 menuItems.push({text: '|'});
245                                                         }
246                                                 }
247                                         });
248                                 }
249
250                                 for (var i = 0; i < menuItems.length; i++) {
251                                         if (menuItems[i].text == '|') {
252                                                 if (i === 0 || i == menuItems.length - 1) {
253                                                         menuItems.splice(i, 1);
254                                                 }
255                                         }
256                                 }
257
258                                 menuButton.menu = menuItems;
259
260                                 if (!menuButton.menu.length) {
261                                         return null;
262                                 }
263                         }
264
265                         return menuButton;
266                 }
267
268                 var defaultMenuBar = [];
269                 if (settings.menu) {
270                         for (name in settings.menu) {
271                                 defaultMenuBar.push(name);
272                         }
273                 } else {
274                         for (name in defaultMenus) {
275                                 defaultMenuBar.push(name);
276                         }
277                 }
278
279                 var enabledMenuNames = typeof(settings.menubar) == "string" ? settings.menubar.split(/[ ,]/) : defaultMenuBar;
280                 for (var i = 0; i < enabledMenuNames.length; i++) {
281                         var menu = enabledMenuNames[i];
282                         menu = createMenu(menu);
283
284                         if (menu) {
285                                 menuButtons.push(menu);
286                         }
287                 }
288
289                 return menuButtons;
290         }
291
292         /**
293          * Adds accessibility shortcut keys to panel.
294          *
295          * @param {tinymce.ui.Panel} panel Panel to add focus to.
296          */
297         function addAccessibilityKeys(panel) {
298                 function focus(type) {
299                         var item = panel.find(type)[0];
300
301                         if (item) {
302                                 item.focus(true);
303                         }
304                 }
305
306                 editor.shortcuts.add('Alt+F9', '', function() {
307                         focus('menubar');
308                 });
309
310                 editor.shortcuts.add('Alt+F10', '', function() {
311                         focus('toolbar');
312                 });
313
314                 editor.shortcuts.add('Alt+F11', '', function() {
315                         focus('elementpath');
316                 });
317
318                 panel.on('cancel', function() {
319                         editor.focus();
320                 });
321         }
322
323         /**
324          * Resizes the editor to the specified width, height.
325          */
326         function resizeTo(width, height) {
327                 var containerElm, iframeElm, containerSize, iframeSize;
328
329                 function getSize(elm) {
330                         return {
331                                 width: elm.clientWidth,
332                                 height: elm.clientHeight
333                         };
334                 }
335
336                 containerElm = editor.getContainer();
337                 iframeElm = editor.getContentAreaContainer().firstChild;
338                 containerSize = getSize(containerElm);
339                 iframeSize = getSize(iframeElm);
340
341                 if (width !== null) {
342                         width = Math.max(settings.min_width || 100, width);
343                         width = Math.min(settings.max_width || 0xFFFF, width);
344
345                         DOM.css(containerElm, 'width', width + (containerSize.width - iframeSize.width));
346                         DOM.css(iframeElm, 'width', width);
347                 }
348
349                 height = Math.max(settings.min_height || 100, height);
350                 height = Math.min(settings.max_height || 0xFFFF, height);
351                 DOM.css(iframeElm, 'height', height);
352
353                 editor.fire('ResizeEditor');
354         }
355
356         function resizeBy(dw, dh) {
357                 var elm = editor.getContentAreaContainer();
358                 self.resizeTo(elm.clientWidth + dw, elm.clientHeight + dh);
359         }
360
361         /**
362          * Renders the inline editor UI.
363          *
364          * @return {Object} Name/value object with theme data.
365          */
366         function renderInlineUI(args) {
367                 var panel, inlineToolbarContainer;
368
369                 if (settings.fixed_toolbar_container) {
370                         inlineToolbarContainer = DOM.select(settings.fixed_toolbar_container)[0];
371                 }
372
373                 function reposition() {
374                         if (panel && panel.moveRel && panel.visible() && !panel._fixed) {
375                                 // TODO: This is kind of ugly and doesn't handle multiple scrollable elements
376                                 var scrollContainer = editor.selection.getScrollContainer(), body = editor.getBody();
377                                 var deltaX = 0, deltaY = 0;
378
379                                 if (scrollContainer) {
380                                         var bodyPos = DOM.getPos(body), scrollContainerPos = DOM.getPos(scrollContainer);
381
382                                         deltaX = Math.max(0, scrollContainerPos.x - bodyPos.x);
383                                         deltaY = Math.max(0, scrollContainerPos.y - bodyPos.y);
384                                 }
385
386                                 panel.fixed(false).moveRel(body, editor.rtl ? ['tr-br', 'br-tr'] : ['tl-bl', 'bl-tl']).moveBy(deltaX, deltaY);
387                         }
388                 }
389
390                 function show() {
391                         if (panel) {
392                                 panel.show();
393                                 reposition();
394                                 DOM.addClass(editor.getBody(), 'mce-edit-focus');
395                         }
396                 }
397
398                 function hide() {
399                         if (panel) {
400                                 panel.hide();
401                                 DOM.removeClass(editor.getBody(), 'mce-edit-focus');
402                         }
403                 }
404
405                 function render() {
406                         if (panel) {
407                                 if (!panel.visible()) {
408                                         show();
409                                 }
410
411                                 return;
412                         }
413
414                         // Render a plain panel inside the inlineToolbarContainer if it's defined
415                         panel = self.panel = Factory.create({
416                                 type: inlineToolbarContainer ? 'panel' : 'floatpanel',
417                                 role: 'application',
418                                 classes: 'tinymce tinymce-inline',
419                                 layout: 'flex',
420                                 direction: 'column',
421                                 align: 'stretch',
422                                 autohide: false,
423                                 autofix: true,
424                                 fixed: !!inlineToolbarContainer,
425                                 border: 1,
426                                 items: [
427                                         settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()},
428                                         createToolbars()
429                                 ]
430                         });
431
432                         // Add statusbar
433                         /*if (settings.statusbar !== false) {
434                                 panel.add({type: 'panel', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', items: [
435                                         {type: 'elementpath'}
436                                 ]});
437                         }*/
438
439                         editor.fire('BeforeRenderUI');
440                         panel.renderTo(inlineToolbarContainer || document.body).reflow();
441
442                         addAccessibilityKeys(panel);
443                         show();
444
445                         editor.on('nodeChange', reposition);
446                         editor.on('activate', show);
447                         editor.on('deactivate', hide);
448
449                         editor.nodeChanged();
450                 }
451
452                 settings.content_editable = true;
453
454                 editor.on('focus', function() {
455                         // Render only when the CSS file has been loaded
456                         if (args.skinUiCss) {
457                                 tinymce.DOM.styleSheetLoader.load(args.skinUiCss, render, render);
458                         } else {
459                                 render();
460                         }
461                 });
462
463                 editor.on('blur', hide);
464
465                 // Remove the panel when the editor is removed
466                 editor.on('remove', function() {
467                         if (panel) {
468                                 panel.remove();
469                                 panel = null;
470                         }
471                 });
472
473                 // Preload skin css
474                 if (args.skinUiCss) {
475                         tinymce.DOM.styleSheetLoader.load(args.skinUiCss);
476                 }
477
478                 return {};
479         }
480
481         /**
482          * Renders the iframe editor UI.
483          *
484          * @param {Object} args Details about target element etc.
485          * @return {Object} Name/value object with theme data.
486          */
487         function renderIframeUI(args) {
488                 var panel, resizeHandleCtrl, startSize;
489
490                 if (args.skinUiCss) {
491                         tinymce.DOM.loadCSS(args.skinUiCss);
492                 }
493
494                 // Basic UI layout
495                 panel = self.panel = Factory.create({
496                         type: 'panel',
497                         role: 'application',
498                         classes: 'tinymce',
499                         style: 'visibility: hidden',
500                         layout: 'stack',
501                         border: 1,
502                         items: [
503                                 settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()},
504                                 createToolbars(),
505                                 {type: 'panel', name: 'iframe', layout: 'stack', classes: 'edit-area', html: '', border: '1 0 0 0'}
506                         ]
507                 });
508
509                 if (settings.resize !== false) {
510                         resizeHandleCtrl = {
511                                 type: 'resizehandle',
512                                 direction: settings.resize,
513
514                                 onResizeStart: function() {
515                                         var elm = editor.getContentAreaContainer().firstChild;
516
517                                         startSize = {
518                                                 width: elm.clientWidth,
519                                                 height: elm.clientHeight
520                                         };
521                                 },
522
523                                 onResize: function(e) {
524                                         if (settings.resize == 'both') {
525                                                 resizeTo(startSize.width + e.deltaX, startSize.height + e.deltaY);
526                                         } else {
527                                                 resizeTo(null, startSize.height + e.deltaY);
528                                         }
529                                 }
530                         };
531                 }
532
533                 // Add statusbar if needed
534                 if (settings.statusbar !== false) {
535                         panel.add({type: 'panel', name: 'statusbar', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', ariaRoot: true, items: [
536                                 {type: 'elementpath'},
537                                 resizeHandleCtrl
538                         ]});
539                 }
540
541                 if (settings.readonly) {
542                         panel.find('*').disabled(true);
543                 }
544
545                 editor.fire('BeforeRenderUI');
546                 panel.renderBefore(args.targetNode).reflow();
547
548                 if (settings.width) {
549                         tinymce.DOM.setStyle(panel.getEl(), 'width', settings.width);
550                 }
551
552                 // Remove the panel when the editor is removed
553                 editor.on('remove', function() {
554                         panel.remove();
555                         panel = null;
556                 });
557
558                 // Add accesibility shortkuts
559                 addAccessibilityKeys(panel);
560
561                 return {
562                         iframeContainer: panel.find('#iframe')[0].getEl(),
563                         editorContainer: panel.getEl()
564                 };
565         }
566
567         /**
568          * Renders the UI for the theme. This gets called by the editor.
569          *
570          * @param {Object} args Details about target element etc.
571          * @return {Object} Theme UI data items.
572          */
573         self.renderUI = function(args) {
574                 var skin = settings.skin !== false ? settings.skin || 'lightgray' : false;
575
576                 if (skin) {
577                         var skinUrl = settings.skin_url;
578
579                         if (skinUrl) {
580                                 skinUrl = editor.documentBaseURI.toAbsolute(skinUrl);
581                         } else {
582                                 skinUrl = tinymce.baseURL + '/skins/' + skin;
583                         }
584
585                         // Load special skin for IE7
586                         // TODO: Remove this when we drop IE7 support
587                         if (tinymce.Env.documentMode <= 7) {
588                                 args.skinUiCss = skinUrl + '/skin.ie7.min.css';
589                         } else {
590                                 args.skinUiCss = skinUrl + '/skin.min.css';
591                         }
592
593                         // Load content.min.css or content.inline.min.css
594                         editor.contentCSS.push(skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css');
595                 }
596
597                 // Handle editor setProgressState change
598                 editor.on('ProgressState', function(e) {
599                         self.throbber = self.throbber || new tinymce.ui.Throbber(self.panel.getEl('body'));
600
601                         if (e.state) {
602                                 self.throbber.show(e.time);
603                         } else {
604                                 self.throbber.hide();
605                         }
606                 });
607
608                 if (settings.inline) {
609                         return renderInlineUI(args);
610                 }
611
612                 return renderIframeUI(args);
613         };
614
615         self.resizeTo = resizeTo;
616         self.resizeBy = resizeBy;
617 });