]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/themes/modern/theme.js
WordPress 4.3
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / themes / modern / theme.js
1 /**
2  * theme.js
3  *
4  * Released under LGPL License.
5  * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
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,
15                 each = tinymce.each, DOM = tinymce.DOM, Rect = tinymce.ui.Rect, FloatPanel = tinymce.ui.FloatPanel;
16
17         // Default menus
18         var defaultMenus = {
19                 file: {title: 'File', items: 'newdocument'},
20                 edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall'},
21                 insert: {title: 'Insert', items: '|'},
22                 view: {title: 'View', items: 'visualaid |'},
23                 format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
24                 table: {title: 'Table'},
25                 tools: {title: 'Tools'}
26         };
27
28         var defaultToolbar = "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | " +
29                 "bullist numlist outdent indent | link image";
30
31         function createToolbar(items) {
32                 var toolbarItems = [], buttonGroup;
33
34                 if (!items) {
35                         return;
36                 }
37
38                 each(items.split(/[ ,]/), function(item) {
39                         var itemName;
40
41                         function bindSelectorChanged() {
42                                 var selection = editor.selection;
43
44                                 if (itemName == "bullist") {
45                                         selection.selectorChanged('ul > li', function(state, args) {
46                                                 var nodeName, i = args.parents.length;
47
48                                                 while (i--) {
49                                                         nodeName = args.parents[i].nodeName;
50                                                         if (nodeName == "OL" || nodeName == "UL") {
51                                                                 break;
52                                                         }
53                                                 }
54
55                                                 item.active(state && nodeName == "UL");
56                                         });
57                                 }
58
59                                 if (itemName == "numlist") {
60                                         selection.selectorChanged('ol > li', function(state, args) {
61                                                 var nodeName, i = args.parents.length;
62
63                                                 while (i--) {
64                                                         nodeName = args.parents[i].nodeName;
65                                                         if (nodeName == "OL" || nodeName == "UL") {
66                                                                 break;
67                                                         }
68                                                 }
69
70                                                 item.active(state && nodeName == "OL");
71                                         });
72                                 }
73
74                                 if (item.settings.stateSelector) {
75                                         selection.selectorChanged(item.settings.stateSelector, function(state) {
76                                                 item.active(state);
77                                         }, true);
78                                 }
79
80                                 if (item.settings.disabledStateSelector) {
81                                         selection.selectorChanged(item.settings.disabledStateSelector, function(state) {
82                                                 item.disabled(state);
83                                         });
84                                 }
85                         }
86
87                         if (item == "|") {
88                                 buttonGroup = null;
89                         } else {
90                                 if (Factory.has(item)) {
91                                         item = {type: item};
92
93                                         if (settings.toolbar_items_size) {
94                                                 item.size = settings.toolbar_items_size;
95                                         }
96
97                                         toolbarItems.push(item);
98                                         buttonGroup = null;
99                                 } else {
100                                         if (!buttonGroup) {
101                                                 buttonGroup = {type: 'buttongroup', items: []};
102                                                 toolbarItems.push(buttonGroup);
103                                         }
104
105                                         if (editor.buttons[item]) {
106                                                 // TODO: Move control creation to some UI class
107                                                 itemName = item;
108                                                 item = editor.buttons[itemName];
109
110                                                 if (typeof item == "function") {
111                                                         item = item();
112                                                 }
113
114                                                 item.type = item.type || 'button';
115
116                                                 if (settings.toolbar_items_size) {
117                                                         item.size = settings.toolbar_items_size;
118                                                 }
119
120                                                 item = Factory.create(item);
121                                                 buttonGroup.items.push(item);
122
123                                                 if (editor.initialized) {
124                                                         bindSelectorChanged();
125                                                 } else {
126                                                         editor.on('init', bindSelectorChanged);
127                                                 }
128                                         }
129                                 }
130                         }
131                 });
132
133                 return {
134                         type: 'toolbar',
135                         layout: 'flow',
136                         items: toolbarItems
137                 };
138         }
139
140         /**
141          * Creates the toolbars from config and returns a toolbar array.
142          *
143          * @return {Array} Array with toolbars.
144          */
145         function createToolbars() {
146                 var toolbars = [];
147
148                 function addToolbar(items) {
149                         if (items) {
150                                 toolbars.push(createToolbar(items));
151                                 return true;
152                         }
153                 }
154
155                 // Convert toolbar array to multiple options
156                 if (tinymce.isArray(settings.toolbar)) {
157                         // Empty toolbar array is the same as a disabled toolbar
158                         if (settings.toolbar.length === 0) {
159                                 return;
160                         }
161
162                         tinymce.each(settings.toolbar, function(toolbar, i) {
163                                 settings["toolbar" + (i + 1)] = toolbar;
164                         });
165
166                         delete settings.toolbar;
167                 }
168
169                 // Generate toolbar<n>
170                 for (var i = 1; i < 10; i++) {
171                         if (!addToolbar(settings["toolbar" + i])) {
172                                 break;
173                         }
174                 }
175
176                 // Generate toolbar or default toolbar unless it's disabled
177                 if (!toolbars.length && settings.toolbar !== false) {
178                         addToolbar(settings.toolbar || defaultToolbar);
179                 }
180
181                 if (toolbars.length) {
182                         return {
183                                 type: 'panel',
184                                 layout: 'stack',
185                                 classes: "toolbar-grp",
186                                 ariaRoot: true,
187                                 ariaRemember: true,
188                                 items: toolbars
189                         };
190                 }
191         }
192
193         /**
194          * Creates the menu buttons based on config.
195          *
196          * @return {Array} Menu buttons array.
197          */
198         function createMenuButtons() {
199                 var name, menuButtons = [];
200
201                 function createMenuItem(name) {
202                         var menuItem;
203
204                         if (name == '|') {
205                                 return {text: '|'};
206                         }
207
208                         menuItem = editor.menuItems[name];
209
210                         return menuItem;
211                 }
212
213                 function createMenu(context) {
214                         var menuButton, menu, menuItems, isUserDefined, removedMenuItems;
215
216                         removedMenuItems = tinymce.makeMap((settings.removed_menuitems || '').split(/[ ,]/));
217
218                         // User defined menu
219                         if (settings.menu) {
220                                 menu = settings.menu[context];
221                                 isUserDefined = true;
222                         } else {
223                                 menu = defaultMenus[context];
224                         }
225
226                         if (menu) {
227                                 menuButton = {text: menu.title};
228                                 menuItems = [];
229
230                                 // Default/user defined items
231                                 each((menu.items || '').split(/[ ,]/), function(item) {
232                                         var menuItem = createMenuItem(item);
233
234                                         if (menuItem && !removedMenuItems[item]) {
235                                                 menuItems.push(createMenuItem(item));
236                                         }
237                                 });
238
239                                 // Added though context
240                                 if (!isUserDefined) {
241                                         each(editor.menuItems, function(menuItem) {
242                                                 if (menuItem.context == context) {
243                                                         if (menuItem.separator == 'before') {
244                                                                 menuItems.push({text: '|'});
245                                                         }
246
247                                                         if (menuItem.prependToContext) {
248                                                                 menuItems.unshift(menuItem);
249                                                         } else {
250                                                                 menuItems.push(menuItem);
251                                                         }
252
253                                                         if (menuItem.separator == 'after') {
254                                                                 menuItems.push({text: '|'});
255                                                         }
256                                                 }
257                                         });
258                                 }
259
260                                 for (var i = 0; i < menuItems.length; i++) {
261                                         if (menuItems[i].text == '|') {
262                                                 if (i === 0 || i == menuItems.length - 1) {
263                                                         menuItems.splice(i, 1);
264                                                 }
265                                         }
266                                 }
267
268                                 menuButton.menu = menuItems;
269
270                                 if (!menuButton.menu.length) {
271                                         return null;
272                                 }
273                         }
274
275                         return menuButton;
276                 }
277
278                 var defaultMenuBar = [];
279                 if (settings.menu) {
280                         for (name in settings.menu) {
281                                 defaultMenuBar.push(name);
282                         }
283                 } else {
284                         for (name in defaultMenus) {
285                                 defaultMenuBar.push(name);
286                         }
287                 }
288
289                 var enabledMenuNames = typeof settings.menubar == "string" ? settings.menubar.split(/[ ,]/) : defaultMenuBar;
290                 for (var i = 0; i < enabledMenuNames.length; i++) {
291                         var menu = enabledMenuNames[i];
292                         menu = createMenu(menu);
293
294                         if (menu) {
295                                 menuButtons.push(menu);
296                         }
297                 }
298
299                 return menuButtons;
300         }
301
302         /**
303          * Adds accessibility shortcut keys to panel.
304          *
305          * @param {tinymce.ui.Panel} panel Panel to add focus to.
306          */
307         function addAccessibilityKeys(panel) {
308                 function focus(type) {
309                         var item = panel.find(type)[0];
310
311                         if (item) {
312                                 item.focus(true);
313                         }
314                 }
315
316                 editor.shortcuts.add('Alt+F9', '', function() {
317                         focus('menubar');
318                 });
319
320                 editor.shortcuts.add('Alt+F10', '', function() {
321                         focus('toolbar');
322                 });
323
324                 editor.shortcuts.add('Alt+F11', '', function() {
325                         focus('elementpath');
326                 });
327
328                 panel.on('cancel', function() {
329                         editor.focus();
330                 });
331         }
332
333         /**
334          * Resizes the editor to the specified width, height.
335          */
336         function resizeTo(width, height) {
337                 var containerElm, iframeElm, containerSize, iframeSize;
338
339                 function getSize(elm) {
340                         return {
341                                 width: elm.clientWidth,
342                                 height: elm.clientHeight
343                         };
344                 }
345
346                 containerElm = editor.getContainer();
347                 iframeElm = editor.getContentAreaContainer().firstChild;
348                 containerSize = getSize(containerElm);
349                 iframeSize = getSize(iframeElm);
350
351                 if (width !== null) {
352                         width = Math.max(settings.min_width || 100, width);
353                         width = Math.min(settings.max_width || 0xFFFF, width);
354
355                         DOM.setStyle(containerElm, 'width', width + (containerSize.width - iframeSize.width));
356                         DOM.setStyle(iframeElm, 'width', width);
357                 }
358
359                 height = Math.max(settings.min_height || 100, height);
360                 height = Math.min(settings.max_height || 0xFFFF, height);
361                 DOM.setStyle(iframeElm, 'height', height);
362
363                 editor.fire('ResizeEditor');
364         }
365
366         function resizeBy(dw, dh) {
367                 var elm = editor.getContentAreaContainer();
368                 self.resizeTo(elm.clientWidth + dw, elm.clientHeight + dh);
369         }
370
371         /**
372          * Handles contextual toolbars.
373          */
374         function addContextualToolbars() {
375                 var scrollContainer;
376
377                 function getContextToolbars() {
378                         return editor.contextToolbars || [];
379                 }
380
381                 function getElementRect(elm) {
382                         var pos, targetRect, root;
383
384                         pos = tinymce.DOM.getPos(editor.getContentAreaContainer());
385                         targetRect = editor.dom.getRect(elm);
386                         root = editor.dom.getRoot();
387
388                         // Adjust targetPos for scrolling in the editor
389                         if (root.nodeName == 'BODY') {
390                                 targetRect.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
391                                 targetRect.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
392                         }
393
394                         targetRect.x += pos.x;
395                         targetRect.y += pos.y;
396
397                         return targetRect;
398                 }
399
400                 function hideAllFloatingPanels() {
401                         each(editor.contextToolbars, function(toolbar) {
402                                 if (toolbar.panel) {
403                                         toolbar.panel.hide();
404                                 }
405                         });
406                 }
407
408                 function reposition(match) {
409                         var relPos, panelRect, elementRect, contentAreaRect, panel, relRect, testPositions;
410
411                         if (editor.removed) {
412                                 return;
413                         }
414
415                         if (!match || !match.toolbar.panel) {
416                                 hideAllFloatingPanels();
417                                 return;
418                         }
419
420                         testPositions = [
421                                 'tc-bc', 'bc-tc',
422                                 'tl-bl', 'bl-tl',
423                                 'tr-br', 'br-tr'
424                         ];
425
426                         panel = match.toolbar.panel;
427                         panel.show();
428
429                         elementRect = getElementRect(match.element);
430                         panelRect = tinymce.DOM.getRect(panel.getEl());
431                         contentAreaRect = tinymce.DOM.getRect(editor.getContentAreaContainer() || editor.getBody());
432
433                         if (!editor.inline) {
434                                 contentAreaRect.w = editor.getDoc().documentElement.offsetWidth;
435                         }
436
437                         // Inflate the elementRect so it doesn't get placed above resize handles
438                         if (editor.selection.controlSelection.isResizable(match.element)) {
439                                 elementRect = Rect.inflate(elementRect, 0, 7);
440                         }
441
442                         relPos = Rect.findBestRelativePosition(panelRect, elementRect, contentAreaRect, testPositions);
443
444                         if (relPos) {
445                                 each(testPositions.concat('inside'), function(pos) {
446                                         panel.classes.toggle('tinymce-inline-' + pos, pos == relPos);
447                                 });
448
449                                 relRect = Rect.relativePosition(panelRect, elementRect, relPos);
450                                 panel.moveTo(relRect.x, relRect.y);
451                         } else {
452                                 each(testPositions, function(pos) {
453                                         panel.classes.toggle('tinymce-inline-' + pos, false);
454                                 });
455
456                                 panel.classes.toggle('tinymce-inline-inside', true);
457
458                                 elementRect = Rect.intersect(contentAreaRect, elementRect);
459
460                                 if (elementRect) {
461                                         relPos = Rect.findBestRelativePosition(panelRect, elementRect, contentAreaRect, [
462                                                 'tc-tc', 'tl-tl', 'tr-tr'
463                                         ]);
464
465                                         if (relPos) {
466                                                 relRect = Rect.relativePosition(panelRect, elementRect, relPos);
467                                                 panel.moveTo(relRect.x, relRect.y);
468                                         } else {
469                                                 panel.moveTo(elementRect.x, elementRect.y);
470                                         }
471                                 } else {
472                                         panel.hide();
473                                 }
474                         }
475
476                         //drawRect(contentAreaRect, 'blue');
477                         //drawRect(elementRect, 'red');
478                         //drawRect(panelRect, 'green');
479                 }
480
481                 function repositionHandler() {
482                         function execute() {
483                                 if (editor.selection) {
484                                         reposition(findFrontMostMatch(editor.selection.getNode()));
485                                 }
486                         }
487
488                         if (window.requestAnimationFrame) {
489                                 window.requestAnimationFrame(execute);
490                         } else {
491                                 execute();
492                         }
493                 }
494
495                 function bindScrollEvent() {
496                         if (!scrollContainer) {
497                                 scrollContainer = editor.selection.getScrollContainer() || editor.getWin();
498                                 tinymce.$(scrollContainer).on('scroll', repositionHandler);
499
500                                 editor.on('remove', function() {
501                                         tinymce.$(scrollContainer).off('scroll');
502                                 });
503                         }
504                 }
505
506                 function showContextToolbar(match) {
507                         var panel;
508
509                         if (match.toolbar.panel) {
510                                 match.toolbar.panel.show();
511                                 reposition(match);
512                                 return;
513                         }
514
515                         bindScrollEvent();
516
517                         panel = Factory.create({
518                                 type: 'floatpanel',
519                                 role: 'application',
520                                 classes: 'tinymce tinymce-inline',
521                                 layout: 'flex',
522                                 direction: 'column',
523                                 align: 'stretch',
524                                 autohide: false,
525                                 autofix: true,
526                                 fixed: true,
527                                 border: 1,
528                                 items: createToolbar(match.toolbar.items)
529                         });
530
531                         match.toolbar.panel = panel;
532                         panel.renderTo(document.body).reflow();
533                         reposition(match);
534                 }
535
536                 function hideAllContextToolbars() {
537                         tinymce.each(getContextToolbars(), function(toolbar) {
538                                 if (toolbar.panel) {
539                                         toolbar.panel.hide();
540                                 }
541                         });
542                 }
543
544                 function findFrontMostMatch(targetElm) {
545                         var i, y, parentsAndSelf, toolbars = getContextToolbars();
546
547                         parentsAndSelf = editor.$(targetElm).parents().add(targetElm);
548                         for (i = parentsAndSelf.length - 1; i >= 0; i--) {
549                                 for (y = toolbars.length - 1; y >= 0; y--) {
550                                         if (toolbars[y].predicate(parentsAndSelf[i])) {
551                                                 return {
552                                                         toolbar: toolbars[y],
553                                                         element: parentsAndSelf[i]
554                                                 };
555                                         }
556                                 }
557                         }
558
559                         return null;
560                 }
561
562                 editor.on('click keyup blur', function() {
563                         // Needs to be delayed to avoid Chrome img focus out bug
564                         window.setTimeout(function() {
565                                 var match;
566
567                                 if (editor.removed) {
568                                         return;
569                                 }
570
571                                 match = findFrontMostMatch(editor.selection.getNode());
572                                 if (match) {
573                                         showContextToolbar(match);
574                                 } else {
575                                         hideAllContextToolbars();
576                                 }
577                         }, 0);
578                 });
579
580                 editor.on('ObjectResizeStart', function() {
581                         var match = findFrontMostMatch(editor.selection.getNode());
582
583                         if (match && match.toolbar.panel) {
584                                 match.toolbar.panel.hide();
585                         }
586                 });
587
588                 editor.on('nodeChange ResizeEditor ResizeWindow', repositionHandler);
589
590                 editor.on('remove', function() {
591                         tinymce.each(getContextToolbars(), function(toolbar) {
592                                 if (toolbar.panel) {
593                                         toolbar.panel.remove();
594                                 }
595                         });
596
597                         editor.contextToolbars = {};
598                 });
599         }
600
601         /**
602          * Renders the inline editor UI.
603          *
604          * @return {Object} Name/value object with theme data.
605          */
606         function renderInlineUI(args) {
607                 var panel, inlineToolbarContainer;
608
609                 if (settings.fixed_toolbar_container) {
610                         inlineToolbarContainer = DOM.select(settings.fixed_toolbar_container)[0];
611                 }
612
613                 function reposition() {
614                         if (panel && panel.moveRel && panel.visible() && !panel._fixed) {
615                                 // TODO: This is kind of ugly and doesn't handle multiple scrollable elements
616                                 var scrollContainer = editor.selection.getScrollContainer(), body = editor.getBody();
617                                 var deltaX = 0, deltaY = 0;
618
619                                 if (scrollContainer) {
620                                         var bodyPos = DOM.getPos(body), scrollContainerPos = DOM.getPos(scrollContainer);
621
622                                         deltaX = Math.max(0, scrollContainerPos.x - bodyPos.x);
623                                         deltaY = Math.max(0, scrollContainerPos.y - bodyPos.y);
624                                 }
625
626                                 panel.fixed(false).moveRel(body, editor.rtl ? ['tr-br', 'br-tr'] : ['tl-bl', 'bl-tl', 'tr-br']).moveBy(deltaX, deltaY);
627                         }
628                 }
629
630                 function show() {
631                         if (panel) {
632                                 panel.show();
633                                 reposition();
634                                 DOM.addClass(editor.getBody(), 'mce-edit-focus');
635                         }
636                 }
637
638                 function hide() {
639                         if (panel) {
640                                 // We require two events as the inline float panel based toolbar does not have autohide=true
641                                 panel.hide();
642
643                                 // All other autohidden float panels will be closed below.
644                                 FloatPanel.hideAll();
645
646                                 DOM.removeClass(editor.getBody(), 'mce-edit-focus');
647                         }
648                 }
649
650                 function render() {
651                         if (panel) {
652                                 if (!panel.visible()) {
653                                         show();
654                                 }
655
656                                 return;
657                         }
658
659                         // Render a plain panel inside the inlineToolbarContainer if it's defined
660                         panel = self.panel = Factory.create({
661                                 type: inlineToolbarContainer ? 'panel' : 'floatpanel',
662                                 role: 'application',
663                                 classes: 'tinymce tinymce-inline',
664                                 layout: 'flex',
665                                 direction: 'column',
666                                 align: 'stretch',
667                                 autohide: false,
668                                 autofix: true,
669                                 fixed: !!inlineToolbarContainer,
670                                 border: 1,
671                                 items: [
672                                         settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()},
673                                         createToolbars()
674                                 ]
675                         });
676
677                         // Add statusbar
678                         /*if (settings.statusbar !== false) {
679                                 panel.add({type: 'panel', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', items: [
680                                         {type: 'elementpath'}
681                                 ]});
682                         }*/
683
684                         editor.fire('BeforeRenderUI');
685                         panel.renderTo(inlineToolbarContainer || document.body).reflow();
686
687                         addAccessibilityKeys(panel);
688                         show();
689                         addContextualToolbars();
690
691                         editor.on('nodeChange', reposition);
692                         editor.on('activate', show);
693                         editor.on('deactivate', hide);
694
695                         editor.nodeChanged();
696                 }
697
698                 settings.content_editable = true;
699
700                 editor.on('focus', function() {
701                         // Render only when the CSS file has been loaded
702                         if (args.skinUiCss) {
703                                 tinymce.DOM.styleSheetLoader.load(args.skinUiCss, render, render);
704                         } else {
705                                 render();
706                         }
707                 });
708
709                 editor.on('blur hide', hide);
710
711                 // Remove the panel when the editor is removed
712                 editor.on('remove', function() {
713                         if (panel) {
714                                 panel.remove();
715                                 panel = null;
716                         }
717                 });
718
719                 // Preload skin css
720                 if (args.skinUiCss) {
721                         tinymce.DOM.styleSheetLoader.load(args.skinUiCss);
722                 }
723
724                 return {};
725         }
726
727         /**
728          * Renders the iframe editor UI.
729          *
730          * @param {Object} args Details about target element etc.
731          * @return {Object} Name/value object with theme data.
732          */
733         function renderIframeUI(args) {
734                 var panel, resizeHandleCtrl, startSize;
735
736                 if (args.skinUiCss) {
737                         tinymce.DOM.loadCSS(args.skinUiCss);
738                 }
739
740                 // Basic UI layout
741                 panel = self.panel = Factory.create({
742                         type: 'panel',
743                         role: 'application',
744                         classes: 'tinymce',
745                         style: 'visibility: hidden',
746                         layout: 'stack',
747                         border: 1,
748                         items: [
749                                 settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()},
750                                 createToolbars(),
751                                 {type: 'panel', name: 'iframe', layout: 'stack', classes: 'edit-area', html: '', border: '1 0 0 0'}
752                         ]
753                 });
754
755                 if (settings.resize !== false) {
756                         resizeHandleCtrl = {
757                                 type: 'resizehandle',
758                                 direction: settings.resize,
759
760                                 onResizeStart: function() {
761                                         var elm = editor.getContentAreaContainer().firstChild;
762
763                                         startSize = {
764                                                 width: elm.clientWidth,
765                                                 height: elm.clientHeight
766                                         };
767                                 },
768
769                                 onResize: function(e) {
770                                         if (settings.resize == 'both') {
771                                                 resizeTo(startSize.width + e.deltaX, startSize.height + e.deltaY);
772                                         } else {
773                                                 resizeTo(null, startSize.height + e.deltaY);
774                                         }
775                                 }
776                         };
777                 }
778
779                 // Add statusbar if needed
780                 if (settings.statusbar !== false) {
781                         panel.add({type: 'panel', name: 'statusbar', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', ariaRoot: true, items: [
782                                 {type: 'elementpath'},
783                                 resizeHandleCtrl
784                         ]});
785                 }
786
787                 if (settings.readonly) {
788                         panel.find('*').disabled(true);
789                 }
790
791                 editor.fire('BeforeRenderUI');
792                 panel.renderBefore(args.targetNode).reflow();
793
794                 if (settings.width) {
795                         tinymce.DOM.setStyle(panel.getEl(), 'width', settings.width);
796                 }
797
798                 // Remove the panel when the editor is removed
799                 editor.on('remove', function() {
800                         panel.remove();
801                         panel = null;
802                 });
803
804                 // Add accesibility shortcuts
805                 addAccessibilityKeys(panel);
806                 addContextualToolbars();
807
808                 return {
809                         iframeContainer: panel.find('#iframe')[0].getEl(),
810                         editorContainer: panel.getEl()
811                 };
812         }
813
814         /**
815          * Renders the UI for the theme. This gets called by the editor.
816          *
817          * @param {Object} args Details about target element etc.
818          * @return {Object} Theme UI data items.
819          */
820         self.renderUI = function(args) {
821                 var skin = settings.skin !== false ? settings.skin || 'lightgray' : false;
822
823                 if (skin) {
824                         var skinUrl = settings.skin_url;
825
826                         if (skinUrl) {
827                                 skinUrl = editor.documentBaseURI.toAbsolute(skinUrl);
828                         } else {
829                                 skinUrl = tinymce.baseURL + '/skins/' + skin;
830                         }
831
832                         // Load special skin for IE7
833                         // TODO: Remove this when we drop IE7 support
834                         if (tinymce.Env.documentMode <= 7) {
835                                 args.skinUiCss = skinUrl + '/skin.ie7.min.css';
836                         } else {
837                                 args.skinUiCss = skinUrl + '/skin.min.css';
838                         }
839
840                         // Load content.min.css or content.inline.min.css
841                         editor.contentCSS.push(skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css');
842                 }
843
844                 // Handle editor setProgressState change
845                 editor.on('ProgressState', function(e) {
846                         self.throbber = self.throbber || new tinymce.ui.Throbber(self.panel.getEl('body'));
847
848                         if (e.state) {
849                                 self.throbber.show(e.time);
850                         } else {
851                                 self.throbber.hide();
852                         }
853                 });
854
855                 if (settings.inline) {
856                         return renderInlineUI(args);
857                 }
858
859                 return renderIframeUI(args);
860         };
861
862         self.resizeTo = resizeTo;
863         self.resizeBy = resizeBy;
864 });