+ /**
+ * Experimental: create a floating toolbar.
+ * This functionality will change in the next releases. Not recommended for use by plugins.
+ */
+ editor.on( 'preinit', function() {
+ var Factory = tinymce.ui.Factory,
+ settings = editor.settings,
+ activeToolbar,
+ currentSelection,
+ timeout,
+ container = editor.getContainer(),
+ wpAdminbar = document.getElementById( 'wpadminbar' ),
+ mceIframe = document.getElementById( editor.id + '_ifr' ),
+ mceToolbar,
+ mceStatusbar,
+ wpStatusbar;
+
+ if ( container ) {
+ mceToolbar = tinymce.$( '.mce-toolbar-grp', container )[0];
+ mceStatusbar = tinymce.$( '.mce-statusbar', container )[0];
+ }
+
+ if ( editor.id === 'content' ) {
+ wpStatusbar = document.getElementById( 'post-status-info' );
+ }
+
+ function create( buttons, bottom ) {
+ var toolbar,
+ toolbarItems = [],
+ buttonGroup;
+
+ each( buttons, function( item ) {
+ var itemName;
+
+ function bindSelectorChanged() {
+ var selection = editor.selection;
+
+ if ( itemName === 'bullist' ) {
+ selection.selectorChanged( 'ul > li', function( state, args ) {
+ var i = args.parents.length,
+ nodeName;
+
+ while ( i-- ) {
+ nodeName = args.parents[ i ].nodeName;
+
+ if ( nodeName === 'OL' || nodeName == 'UL' ) {
+ break;
+ }
+ }
+
+ item.active( state && nodeName === 'UL' );
+ } );
+ }
+
+ if ( itemName === 'numlist' ) {
+ selection.selectorChanged( 'ol > li', function( state, args ) {
+ var i = args.parents.length,
+ nodeName;
+
+ while ( i-- ) {
+ nodeName = args.parents[ i ].nodeName;
+
+ if ( nodeName === 'OL' || nodeName === 'UL' ) {
+ break;
+ }
+ }
+
+ item.active( state && nodeName === 'OL' );
+ } );
+ }
+
+ if ( item.settings.stateSelector ) {
+ selection.selectorChanged( item.settings.stateSelector, function( state ) {
+ item.active( state );
+ }, true );
+ }
+
+ if ( item.settings.disabledStateSelector ) {
+ selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
+ item.disabled( state );
+ } );
+ }
+ }
+
+ if ( item === '|' ) {
+ buttonGroup = null;
+ } else {
+ if ( Factory.has( item ) ) {
+ item = {
+ type: item
+ };
+
+ if ( settings.toolbar_items_size ) {
+ item.size = settings.toolbar_items_size;
+ }
+
+ toolbarItems.push( item );
+
+ buttonGroup = null;
+ } else {
+ if ( ! buttonGroup ) {
+ buttonGroup = {
+ type: 'buttongroup',
+ items: []
+ };
+
+ toolbarItems.push( buttonGroup );
+ }
+
+ if ( editor.buttons[ item ] ) {
+ itemName = item;
+ item = editor.buttons[ itemName ];
+
+ if ( typeof item === 'function' ) {
+ item = item();
+ }
+
+ item.type = item.type || 'button';
+
+ if ( settings.toolbar_items_size ) {
+ item.size = settings.toolbar_items_size;
+ }
+
+ item = Factory.create( item );
+
+ buttonGroup.items.push( item );
+
+ if ( editor.initialized ) {
+ bindSelectorChanged();
+ } else {
+ editor.on( 'init', bindSelectorChanged );
+ }
+ }
+ }
+ }
+ } );
+
+ toolbar = Factory.create( {
+ type: 'panel',
+ layout: 'stack',
+ classes: 'toolbar-grp inline-toolbar-grp',
+ ariaRoot: true,
+ ariaRemember: true,
+ items: [ {
+ type: 'toolbar',
+ layout: 'flow',
+ items: toolbarItems
+ } ]
+ } );
+
+ toolbar.bottom = bottom;
+
+ function reposition() {
+ if ( ! currentSelection ) {
+ return this;
+ }
+
+ var scrollX = window.pageXOffset || document.documentElement.scrollLeft,
+ scrollY = window.pageYOffset || document.documentElement.scrollTop,
+ windowWidth = window.innerWidth,
+ windowHeight = window.innerHeight,
+ iframeRect = mceIframe ? mceIframe.getBoundingClientRect() : {
+ top: 0,
+ right: windowWidth,
+ bottom: windowHeight,
+ left: 0,
+ width: windowWidth,
+ height: windowHeight
+ },
+ toolbar = this.getEl(),
+ toolbarWidth = toolbar.offsetWidth,
+ toolbarHeight = toolbar.offsetHeight,
+ selection = currentSelection.getBoundingClientRect(),
+ selectionMiddle = ( selection.left + selection.right ) / 2,
+ buffer = 5,
+ margin = 8,
+ spaceNeeded = toolbarHeight + margin + buffer,
+ wpAdminbarBottom = wpAdminbar ? wpAdminbar.getBoundingClientRect().bottom : 0,
+ mceToolbarBottom = mceToolbar ? mceToolbar.getBoundingClientRect().bottom : 0,
+ mceStatusbarTop = mceStatusbar ? windowHeight - mceStatusbar.getBoundingClientRect().top : 0,
+ wpStatusbarTop = wpStatusbar ? windowHeight - wpStatusbar.getBoundingClientRect().top : 0,
+ blockedTop = Math.max( 0, wpAdminbarBottom, mceToolbarBottom, iframeRect.top ),
+ blockedBottom = Math.max( 0, mceStatusbarTop, wpStatusbarTop, windowHeight - iframeRect.bottom ),
+ spaceTop = selection.top + iframeRect.top - blockedTop,
+ spaceBottom = windowHeight - iframeRect.top - selection.bottom - blockedBottom,
+ editorHeight = windowHeight - blockedTop - blockedBottom,
+ className = '',
+ iosOffsetTop = 0,
+ iosOffsetBottom = 0,
+ top, left;
+
+ if ( spaceTop >= editorHeight || spaceBottom >= editorHeight ) {
+ this.scrolling = true;
+ this.hide();
+ this.scrolling = false;
+ return this;
+ }
+
+ // Add offset in iOS to move the menu over the image, out of the way of the default iOS menu.
+ if ( tinymce.Env.iOS && currentSelection.nodeName === 'IMG' ) {
+ iosOffsetTop = 54;
+ iosOffsetBottom = 46;
+ }
+
+ if ( this.bottom ) {
+ if ( spaceBottom >= spaceNeeded ) {
+ className = ' mce-arrow-up';
+ top = selection.bottom + iframeRect.top + scrollY - iosOffsetBottom;
+ } else if ( spaceTop >= spaceNeeded ) {
+ className = ' mce-arrow-down';
+ top = selection.top + iframeRect.top + scrollY - toolbarHeight - margin + iosOffsetTop;
+ }
+ } else {
+ if ( spaceTop >= spaceNeeded ) {
+ className = ' mce-arrow-down';
+ top = selection.top + iframeRect.top + scrollY - toolbarHeight - margin + iosOffsetTop;
+ } else if ( spaceBottom >= spaceNeeded && editorHeight / 2 > selection.bottom + iframeRect.top - blockedTop ) {
+ className = ' mce-arrow-up';
+ top = selection.bottom + iframeRect.top + scrollY - iosOffsetBottom;
+ }
+ }
+
+ if ( typeof top === 'undefined' ) {
+ top = scrollY + blockedTop + buffer + iosOffsetBottom;
+ }
+
+ left = selectionMiddle - toolbarWidth / 2 + iframeRect.left + scrollX;
+
+ if ( selection.left < 0 || selection.right > iframeRect.width ) {
+ left = iframeRect.left + scrollX + ( iframeRect.width - toolbarWidth ) / 2;
+ } else if ( toolbarWidth >= windowWidth ) {
+ className += ' mce-arrow-full';
+ left = 0;
+ } else if ( ( left < 0 && selection.left + toolbarWidth > windowWidth ) || ( left + toolbarWidth > windowWidth && selection.right - toolbarWidth < 0 ) ) {
+ left = ( windowWidth - toolbarWidth ) / 2;
+ } else if ( left < iframeRect.left + scrollX ) {
+ className += ' mce-arrow-left';
+ left = selection.left + iframeRect.left + scrollX;
+ } else if ( left + toolbarWidth > iframeRect.width + iframeRect.left + scrollX ) {
+ className += ' mce-arrow-right';
+ left = selection.right - toolbarWidth + iframeRect.left + scrollX;
+ }
+
+ // No up/down arrows on the menu over images in iOS.
+ if ( tinymce.Env.iOS && currentSelection.nodeName === 'IMG' ) {
+ className = className.replace( / ?mce-arrow-(up|down)/g, '' );
+ }
+
+ toolbar.className = toolbar.className.replace( / ?mce-arrow-[\w]+/g, '' ) + className;
+
+ DOM.setStyles( toolbar, {
+ 'left': left,
+ 'top': top
+ } );
+
+ return this;
+ }
+
+ toolbar.on( 'show', function() {
+ this.reposition();
+ } );
+
+ toolbar.on( 'keydown', function( event ) {
+ if ( event.keyCode === 27 ) {
+ this.hide();
+ editor.focus();
+ }
+ } );
+
+ editor.on( 'remove', function() {
+ toolbar.remove();
+ } );
+
+ toolbar.reposition = reposition;
+ toolbar.hide().renderTo( document.body );
+
+ return toolbar;