X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/3e7fab96d7874067884348df10bbdcdefa4a89ad..8ab4a4532479e8db471032b51042ec8c4716d091:/wp-includes/js/tinymce/tiny_mce_popup.js diff --git a/wp-includes/js/tinymce/tiny_mce_popup.js b/wp-includes/js/tinymce/tiny_mce_popup.js index acfca0a7..733f4397 100644 --- a/wp-includes/js/tinymce/tiny_mce_popup.js +++ b/wp-includes/js/tinymce/tiny_mce_popup.js @@ -1,294 +1,542 @@ -// Some global instances, this will be filled later -var tinyMCE = null, tinyMCELang = null; +/** + * Popup.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +var tinymce, tinyMCE; + +/** + * TinyMCE popup/dialog helper class. This gives you easy access to the + * parent editor instance and a bunch of other things. It's higly recommended + * that you load this script into your dialogs. + * + * @static + * @class tinyMCEPopup + */ +var tinyMCEPopup = { + /** + * Initializes the popup this will be called automatically. + * + * @method init + */ + init: function() { + var self = this, parentWin, settings, uiWindow; + + // Find window & API + parentWin = self.getWin(); + tinymce = tinyMCE = parentWin.tinymce; + self.editor = tinymce.EditorManager.activeEditor; + self.params = self.editor.windowManager.getParams(); + + uiWindow = self.editor.windowManager.windows[self.editor.windowManager.windows.length - 1]; + self.features = uiWindow.features; + self.uiWindow = uiWindow; + + settings = self.editor.settings; + + // Setup popup CSS path(s) + if (settings.popup_css !== false) { + if (settings.popup_css) { + settings.popup_css = self.editor.documentBaseURI.toAbsolute(settings.popup_css); + } else { + settings.popup_css = self.editor.baseURI.toAbsolute("plugins/compat3x/css/dialog.css"); + } + } -function TinyMCE_Popup() { -}; + if (settings.popup_css_add) { + settings.popup_css += ',' + self.editor.documentBaseURI.toAbsolute(settings.popup_css_add); + } -TinyMCE_Popup.prototype = { - findWin : function(w) { - var c; + // Setup local DOM + self.dom = self.editor.windowManager.createInstance('tinymce.dom.DOMUtils', document, { + ownEvents: true, + proxy: tinyMCEPopup._eventProxy + }); - // Check parents - c = w; - while (c && (c = c.parent) != null) { - if (typeof(c.tinyMCE) != "undefined") - return c; - } + self.dom.bind(window, 'ready', self._onDOMLoaded, self); - // Check openers - c = w; - while (c && (c = c.opener) != null) { - if (typeof(c.tinyMCE) != "undefined") - return c; + // Enables you to skip loading the default css + if (self.features.popup_css !== false) { + self.dom.loadCSS(self.features.popup_css || self.editor.settings.popup_css); } - // Try top - if (typeof(top.tinyMCE) != "undefined") - return top; + // Setup on init listeners + self.listeners = []; + + /** + * Fires when the popup is initialized. + * + * @event onInit + * @param {tinymce.Editor} editor Editor instance. + * @example + * // Alerts the selected contents when the dialog is loaded + * tinyMCEPopup.onInit.add(function(ed) { + * alert(ed.selection.getContent()); + * }); + * + * // Executes the init method on page load in some object using the SomeObject scope + * tinyMCEPopup.onInit.add(SomeObject.init, SomeObject); + */ + self.onInit = { + add: function(func, scope) { + self.listeners.push({func : func, scope : scope}); + } + }; - return null; + self.isWindow = !self.getWindowArg('mce_inline'); + self.id = self.getWindowArg('mce_window_id'); }, - init : function() { - var win = window.opener ? window.opener : window.dialogArguments, c; - var inst, re, title, divElm; + /** + * Returns the reference to the parent window that opened the dialog. + * + * @method getWin + * @return {Window} Reference to the parent window that opened the dialog. + */ + getWin: function() { + // Added frameElement check to fix bug: #2817583 + return (!window.frameElement && window.dialogArguments) || opener || parent || top; + }, - if (!win) - win = this.findWin(window); + /** + * Returns a window argument/parameter by name. + * + * @method getWindowArg + * @param {String} name Name of the window argument to retrive. + * @param {String} defaultValue Optional default value to return. + * @return {String} Argument value or default value if it wasn't found. + */ + getWindowArg : function(name, defaultValue) { + var value = this.params[name]; + + return tinymce.is(value) ? value : defaultValue; + }, - if (!win) { - alert("tinyMCE object reference not found from popup."); - return; - } + /** + * Returns a editor parameter/config option value. + * + * @method getParam + * @param {String} name Name of the editor config option to retrive. + * @param {String} defaultValue Optional default value to return. + * @return {String} Parameter value or default value if it wasn't found. + */ + getParam : function(name, defaultValue) { + return this.editor.getParam(name, defaultValue); + }, - window.opener = win; - this.windowOpener = win; - this.onLoadEval = ""; + /** + * Returns a language item by key. + * + * @method getLang + * @param {String} name Language item like mydialog.something. + * @param {String} defaultValue Optional default value to return. + * @return {String} Language value for the item like "my string" or the default value if it wasn't found. + */ + getLang : function(name, defaultValue) { + return this.editor.getLang(name, defaultValue); + }, - // Setup parent references - tinyMCE = win.tinyMCE; - tinyMCELang = win.tinyMCELang; + /** + * Executed a command on editor that opened the dialog/popup. + * + * @method execCommand + * @param {String} cmd Command to execute. + * @param {Boolean} ui Optional boolean value if the UI for the command should be presented or not. + * @param {Object} val Optional value to pass with the comman like an URL. + * @param {Object} a Optional arguments object. + */ + execCommand : function(cmd, ui, val, args) { + args = args || {}; + args.skip_focus = 1; - inst = tinyMCE.selectedInstance; - this.isWindow = tinyMCE.getWindowArg('mce_inside_iframe', false) == false; - this.storeSelection = (tinyMCE.isRealIE) && !this.isWindow && tinyMCE.getWindowArg('mce_store_selection', true); + this.restoreSelection(); + return this.editor.execCommand(cmd, ui, val, args); + }, - if (this.isWindow) - window.focus(); + /** + * Resizes the dialog to the inner size of the window. This is needed since various browsers + * have different border sizes on windows. + * + * @method resizeToInnerSize + */ + resizeToInnerSize : function() { + /*var self = this; + + // Detach it to workaround a Chrome specific bug + // https://sourceforge.net/tracker/?func=detail&atid=635682&aid=2926339&group_id=103281 + setTimeout(function() { + var vp = self.dom.getViewPort(window); + + self.editor.windowManager.resizeBy( + self.getWindowArg('mce_width') - vp.w, + self.getWindowArg('mce_height') - vp.h, + self.id || window + ); + }, 10);*/ + }, - // Store selection - if (this.storeSelection) - inst.selectionBookmark = inst.selection.getBookmark(true); + /** + * Will executed the specified string when the page has been loaded. This function + * was added for compatibility with the 2.x branch. + * + * @method executeOnLoad + * @param {String} evil String to evalutate on init. + */ + executeOnLoad : function(evil) { + this.onInit.add(function() { + eval(evil); + }); + }, - // Setup dir - if (tinyMCELang.lang_dir) - document.dir = tinyMCELang.lang_dir; + /** + * Stores the current editor selection for later restoration. This can be useful since some browsers + * looses it's selection if a control element is selected/focused inside the dialogs. + * + * @method storeSelection + */ + storeSelection : function() { + this.editor.windowManager.bookmark = tinyMCEPopup.editor.selection.getBookmark(1); + }, - // Setup title - re = new RegExp('{|\\\$|}', 'g'); - title = document.title.replace(re, ""); - if (typeof(tinyMCELang[title]) != "undefined") { - divElm = document.createElement("div"); - divElm.innerHTML = tinyMCELang[title]; - document.title = divElm.innerHTML; + /** + * Restores any stored selection. This can be useful since some browsers + * looses it's selection if a control element is selected/focused inside the dialogs. + * + * @method restoreSelection + */ + restoreSelection : function() { + var self = tinyMCEPopup; - if (typeof(tinyMCE.setWindowTitle) != 'undefined') - tinyMCE.setWindowTitle(window, divElm.innerHTML); + if (!self.isWindow && tinymce.isIE) { + self.editor.selection.moveToBookmark(self.editor.windowManager.bookmark); } + }, - // Output Popup CSS class - document.write(''); + /** + * Loads a specific dialog language pack. If you pass in plugin_url as a argument + * when you open the window it will load the /langs/_dlg.js lang pack file. + * + * @method requireLangPack + */ + requireLangPack : function() { + var self = this, url = self.getWindowArg('plugin_url') || self.getWindowArg('theme_url'), settings = self.editor.settings, lang; + + if (settings.language !== false) { + lang = settings.language || "en"; + } - if (tinyMCE.getParam("popups_css_add")) { - c = tinyMCE.getParam("popups_css_add"); + if (url && lang && self.features.translate_i18n !== false && settings.language_load !== false) { + url += '/langs/' + lang + '_dlg.js'; - // Is relative - if (c.indexOf('://') == -1 && c.charAt(0) != '/') - c = tinyMCE.documentBasePath + "/" + c; + if (!tinymce.ScriptLoader.isDone(url)) { + document.write(''); + tinymce.ScriptLoader.markDone(url); + } + } + }, - document.write(''); + /** + * Executes a color picker on the specified element id. When the user + * then selects a color it will be set as the value of the specified element. + * + * @method pickColor + * @param {DOMEvent} e DOM event object. + * @param {string} element_id Element id to be filled with the color value from the picker. + */ + pickColor : function(e, element_id) { + var el = document.getElementById(element_id), colorPickerCallback = this.editor.settings.color_picker_callback; + if (colorPickerCallback) { + colorPickerCallback.call( + this.editor, + function (value) { + el.value = value; + try { + el.onchange(); + } catch (ex) { + // Try fire event, ignore errors + } + }, + el.value + ); } + }, - tinyMCE.addEvent(window, "load", this.onLoad); + /** + * Opens a filebrowser/imagebrowser this will set the output value from + * the browser as a value on the specified element. + * + * @method openBrowser + * @param {string} element_id Id of the element to set value in. + * @param {string} type Type of browser to open image/file/flash. + * @param {string} option Option name to get the file_broswer_callback function name from. + */ + openBrowser : function(element_id, type) { + tinyMCEPopup.restoreSelection(); + this.editor.execCallback('file_browser_callback', element_id, document.getElementById(element_id).value, type, window); }, - onLoad : function() { - var dir, i, elms, body = document.body; + /** + * Creates a confirm dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method confirm + * @param {String} t Title for the new confirm dialog. + * @param {function} cb Callback function to be executed after the user has selected ok or cancel. + * @param {Object} s Optional scope to execute the callback in. + */ + confirm : function(t, cb, s) { + this.editor.windowManager.confirm(t, cb, s, window); + }, - if (tinyMCE.getWindowArg('mce_replacevariables', true)) - body.innerHTML = tinyMCE.applyTemplate(body.innerHTML, tinyMCE.windowArgs); + /** + * Creates a alert dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method alert + * @param {String} tx Title for the new alert dialog. + * @param {function} cb Callback function to be executed after the user has selected ok. + * @param {Object} s Optional scope to execute the callback in. + */ + alert : function(tx, cb, s) { + this.editor.windowManager.alert(tx, cb, s, window); + }, - dir = tinyMCE.selectedInstance.settings.directionality; - if (dir == "rtl" && document.forms && document.forms.length > 0) { - elms = document.forms[0].elements; - for (i=0; i]+)/gi, ' $1="$2"'); + } - // Create wrapper - wrapper = doc.createElement("div"); - wrapper.id = 'mcBodyWrapper'; - wrapper.style.display = 'none'; - wrapper.style.margin = '0'; + document.dir = t.editor.getParam('directionality',''); - // Wrap body elements - nodes = doc.body.childNodes; - for (i=nodes.length-1; i>=0; i--) { - if (wrapper.hasChildNodes()) - wrapper.insertBefore(nodes[i].cloneNode(true), wrapper.firstChild); - else - wrapper.appendChild(nodes[i].cloneNode(true)); + if ((nv = t.editor.translate(h)) && nv != h) { + document.body.innerHTML = nv; + } - nodes[i].parentNode.removeChild(nodes[i]); + if ((nv = t.editor.translate(ti)) && nv != ti) { + document.title = ti = nv; } + } - // Add wrapper - doc.body.appendChild(wrapper); - - // Create iframe - iframe = document.createElement("iframe"); - iframe.id = "mcWinIframe"; - iframe.src = document.location.href.toLowerCase().indexOf('https') == -1 ? "about:blank" : tinyMCE.settings.default_document; - iframe.width = "100%"; - iframe.height = "100%"; - iframe.style.margin = '0'; - - // Add iframe - doc.body.appendChild(iframe); - - // Measure iframe - iframe = document.getElementById('mcWinIframe'); - dx = tinyMCE.getWindowArg('mce_width') - iframe.clientWidth; - dy = tinyMCE.getWindowArg('mce_height') - iframe.clientHeight; - - // Resize window - // tinyMCE.debug(tinyMCE.getWindowArg('mce_width') + "," + tinyMCE.getWindowArg('mce_height') + " - " + dx + "," + dy); - window.resizeBy(dx, dy); - - // Hide iframe and show wrapper - body.style.margin = oldMargin; - iframe.style.display = 'none'; - wrapper.style.display = 'block'; + if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) { + t.dom.addClass(document.body, 'forceColors'); } - }, - resizeToContent : function() { - var isMSIE = (navigator.appName == "Microsoft Internet Explorer"); - var isOpera = (navigator.userAgent.indexOf("Opera") != -1); - var elm, width, height, x, y, dx, dy; + document.body.style.display = ''; - if (isOpera) - return; + // Restore selection in IE when focus is placed on a non textarea or input element of the type text + if (tinymce.Env.ie) { + if (tinymce.Env.ie < 11) { + document.attachEvent('onmouseup', tinyMCEPopup._restoreSelection); - if (isMSIE) { - try { window.resizeTo(10, 10); } catch (e) {} + // Add base target element for it since it would fail with modal dialogs + t.dom.add(t.dom.select('head')[0], 'base', {target: '_self'}); + } else { + document.addEventListener('mouseup', tinyMCEPopup._restoreSelection, false); + } + } - elm = document.body; - width = elm.offsetWidth; - height = elm.offsetHeight; - dx = (elm.scrollWidth - width) + 4; - dy = elm.scrollHeight - height; + t.restoreSelection(); + t.resizeToInnerSize(); - try { window.resizeBy(dx, dy); } catch (e) {} + // Set inline title + if (!t.isWindow) { + t.editor.windowManager.setTitle(window, ti); } else { - window.scrollBy(1000, 1000); - if (window.scrollX > 0 || window.scrollY > 0) { - window.resizeBy(window.innerWidth * 2, window.innerHeight * 2); - window.sizeToContent(); - window.scrollTo(0, 0); - x = parseInt(screen.width / 2.0) - (window.outerWidth / 2.0); - y = parseInt(screen.height / 2.0) - (window.outerHeight / 2.0); - window.moveTo(x, y); - } + window.focus(); } - }, - getWindowArg : function(name, default_value) { - return tinyMCE.getWindowArg(name, default_value); - }, + if (!tinymce.isIE && !t.isWindow) { + t.dom.bind(document, 'focus', function() { + t.editor.windowManager.focus(t.id); + }); + } - restoreSelection : function() { - var inst; + // Patch for accessibility + tinymce.each(t.dom.select('select'), function(e) { + e.onkeydown = tinyMCEPopup._accessHandler; + }); - if (this.storeSelection) { - inst = tinyMCE.selectedInstance; + // Call onInit + // Init must be called before focus so the selection won't get lost by the focus call + tinymce.each(t.listeners, function(o) { + o.func.call(o.scope, t.editor); + }); - inst.getWin().focus(); + // Move focus to window + if (t.getWindowArg('mce_auto_focus', true)) { + window.focus(); - if (inst.selectionBookmark) - inst.selection.moveToBookmark(inst.selectionBookmark); + // Focus element with mceFocus class + tinymce.each(document.forms, function(f) { + tinymce.each(f.elements, function(e) { + if (t.dom.hasClass(e, 'mceFocus') && !e.disabled) { + e.focus(); + return false; // Break loop + } + }); + }); + } + + document.onkeyup = tinyMCEPopup._closeWinKeyHandler; + + if ('textContent' in document) { + t.uiWindow.getEl('head').firstChild.textContent = document.title; + } else { + t.uiWindow.getEl('head').firstChild.innerText = document.title; } }, - execCommand : function(command, user_interface, value) { - var inst = tinyMCE.selectedInstance; + _accessHandler : function(e) { + e = e || window.event; - this.restoreSelection(); - inst.execCommand(command, user_interface, value); + if (e.keyCode == 13 || e.keyCode == 32) { + var elm = e.target || e.srcElement; - // Store selection - if (this.storeSelection) - inst.selectionBookmark = inst.selection.getBookmark(true); - }, + if (elm.onchange) { + elm.onchange(); + } - close : function() { - tinyMCE.closeWindow(window); + return tinymce.dom.Event.cancel(e); + } }, - pickColor : function(e, element_id) { - tinyMCE.selectedInstance.execCommand('mceColorPicker', true, { - element_id : element_id, - document : document, - window : window, - store_selection : false - }); + _closeWinKeyHandler : function(e) { + e = e || window.event; + + if (e.keyCode == 27) { + tinyMCEPopup.close(); + } }, - openBrowser : function(element_id, type, option) { - var cb = tinyMCE.getParam(option, tinyMCE.getParam("file_browser_callback")); - var url = document.getElementById(element_id).value; + _eventProxy: function(id) { + return function(evt) { + tinyMCEPopup.dom.events.callNativeHandler(id, evt); + }; + } +}; - tinyMCE.setWindowArg("window", window); - tinyMCE.setWindowArg("document", document); +tinyMCEPopup.init(); - // Call to external callback - if (eval('typeof(tinyMCEPopup.windowOpener.' + cb + ')') == "undefined") - alert("Callback function: " + cb + " could not be found."); - else - eval("tinyMCEPopup.windowOpener." + cb + "(element_id, url, type, window);"); - }, +tinymce.util.Dispatcher = function(scope) { + this.scope = scope || this; + this.listeners = []; - importClass : function(c) { - var n; + this.add = function(callback, scope) { + this.listeners.push({cb : callback, scope : scope || this.scope}); - window[c] = function() {}; + return callback; + }; - for (n in window.opener[c].prototype) - window[c].prototype[n] = window.opener[c].prototype[n]; + this.addToTop = function(callback, scope) { + var self = this, listener = {cb : callback, scope : scope || self.scope}; - window[c].constructor = window.opener[c].constructor; - } + // Create new listeners if addToTop is executed in a dispatch loop + if (self.inDispatch) { + self.listeners = [listener].concat(self.listeners); + } else { + self.listeners.unshift(listener); + } + return callback; }; -// Setup global instance -var tinyMCEPopup = new TinyMCE_Popup(); + this.remove = function(callback) { + var listeners = this.listeners, output = null; -tinyMCEPopup.init(); + tinymce.each(listeners, function(listener, i) { + if (callback == listener.cb) { + output = listener; + listeners.splice(i, 1); + return false; + } + }); + + return output; + }; + + this.dispatch = function() { + var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener; + + self.inDispatch = true; + + // Needs to be a real loop since the listener count might change while looping + // And this is also more efficient + for (i = 0; i < listeners.length; i++) { + listener = listeners[i]; + returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]); + + if (returnValue === false) { + break; + } + } + + self.inDispatch = false; + + return returnValue; + }; +};