Spaces:
Sleeping
Sleeping
/** | |
* tinymce_mce_popup.js | |
* | |
* Released under LGPL License. | |
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved | |
* | |
* 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"); | |
} | |
} | |
if (settings.popup_css_add) { | |
settings.popup_css += ',' + self.editor.documentBaseURI.toAbsolute(settings.popup_css_add); | |
} | |
// Setup local DOM | |
self.dom = self.editor.windowManager.createInstance('tinymce.dom.DOMUtils', document, { | |
ownEvents: true, | |
proxy: tinyMCEPopup._eventProxy | |
}); | |
self.dom.bind(window, 'ready', self._onDOMLoaded, self); | |
// 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); | |
} | |
// 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 }); | |
} | |
}; | |
self.isWindow = !self.getWindowArg('mce_inline'); | |
self.id = self.getWindowArg('mce_window_id'); | |
}, | |
/** | |
* 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; | |
}, | |
/** | |
* Returns a window argument/parameter by name. | |
* | |
* @method getWindowArg | |
* @param {String} name Name of the window argument to retrieve. | |
* @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; | |
}, | |
/** | |
* Returns a editor parameter/config option value. | |
* | |
* @method getParam | |
* @param {String} name Name of the editor config option to retrieve. | |
* @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); | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* 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; | |
this.restoreSelection(); | |
return this.editor.execCommand(cmd, ui, val, args); | |
}, | |
/** | |
* 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);*/ | |
}, | |
/** | |
* 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); | |
}); | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* 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 (!self.isWindow && tinymce.isIE) { | |
self.editor.selection.moveToBookmark(self.editor.windowManager.bookmark); | |
} | |
}, | |
/** | |
* Loads a specific dialog language pack. If you pass in plugin_url as a argument | |
* when you open the window it will load the <plugin url>/langs/<code>_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 (url && lang && self.features.translate_i18n !== false && settings.language_load !== false) { | |
url += '/langs/' + lang + '_dlg.js'; | |
if (!tinymce.ScriptLoader.isDone(url)) { | |
document.write('<script type="text/javascript" src="' + url + '"></script>'); | |
tinymce.ScriptLoader.markDone(url); | |
} | |
} | |
}, | |
/** | |
* 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 | |
); | |
} | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* Closes the current window. | |
* | |
* @method close | |
*/ | |
close: function () { | |
var t = this; | |
// To avoid domain relaxing issue in Opera | |
function close() { | |
t.editor.windowManager.close(window); | |
tinymce = tinyMCE = t.editor = t.params = t.dom = t.dom.doc = null; // Cleanup | |
} | |
if (tinymce.isOpera) { | |
t.getWin().setTimeout(close, 0); | |
} else { | |
close(); | |
} | |
}, | |
// Internal functions | |
_restoreSelection: function () { | |
var e = window.event.srcElement; | |
if (e.nodeName == 'INPUT' && (e.type == 'submit' || e.type == 'button')) { | |
tinyMCEPopup.restoreSelection(); | |
} | |
}, | |
/* _restoreSelection : function() { | |
var e = window.event.srcElement; | |
// If user focus a non text input or textarea | |
if ((e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA') || e.type != 'text') | |
tinyMCEPopup.restoreSelection(); | |
},*/ | |
_onDOMLoaded: function () { | |
var t = tinyMCEPopup, ti = document.title, h, nv; | |
// Translate page | |
if (t.features.translate_i18n !== false) { | |
var map = { | |
"update": "Ok", | |
"insert": "Ok", | |
"cancel": "Cancel", | |
"not_set": "--", | |
"class_name": "Class name", | |
"browse": "Browse" | |
}; | |
var langCode = (tinymce.settings ? tinymce.settings : t.editor.settings).language || 'en'; | |
for (var key in map) { | |
tinymce.i18n.data[langCode + "." + key] = tinymce.i18n.translate(map[key]); | |
} | |
h = document.body.innerHTML; | |
// Replace a=x with a="x" in IE | |
if (tinymce.isIE) { | |
h = h.replace(/ (value|title|alt)=([^"][^\s>]+)/gi, ' $1="$2"'); | |
} | |
document.dir = t.editor.getParam('directionality', ''); | |
if ((nv = t.editor.translate(h)) && nv != h) { | |
document.body.innerHTML = nv; | |
} | |
if ((nv = t.editor.translate(ti)) && nv != ti) { | |
document.title = ti = nv; | |
} | |
} | |
if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) { | |
t.dom.addClass(document.body, 'forceColors'); | |
} | |
document.body.style.display = ''; | |
// 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); | |
// 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); | |
} | |
} | |
t.restoreSelection(); | |
t.resizeToInnerSize(); | |
// Set inline title | |
if (!t.isWindow) { | |
t.editor.windowManager.setTitle(window, ti); | |
} else { | |
window.focus(); | |
} | |
if (!tinymce.isIE && !t.isWindow) { | |
t.dom.bind(document, 'focus', function () { | |
t.editor.windowManager.focus(t.id); | |
}); | |
} | |
// Patch for accessibility | |
tinymce.each(t.dom.select('select'), function (e) { | |
e.onkeydown = tinyMCEPopup._accessHandler; | |
}); | |
// 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); | |
}); | |
// Move focus to window | |
if (t.getWindowArg('mce_auto_focus', true)) { | |
window.focus(); | |
// 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; | |
} | |
}, | |
_accessHandler: function (e) { | |
e = e || window.event; | |
if (e.keyCode == 13 || e.keyCode == 32) { | |
var elm = e.target || e.srcElement; | |
if (elm.onchange) { | |
elm.onchange(); | |
} | |
return tinymce.dom.Event.cancel(e); | |
} | |
}, | |
_closeWinKeyHandler: function (e) { | |
e = e || window.event; | |
if (e.keyCode == 27) { | |
tinyMCEPopup.close(); | |
} | |
}, | |
_eventProxy: function (id) { | |
return function (evt) { | |
tinyMCEPopup.dom.events.callNativeHandler(id, evt); | |
}; | |
} | |
}; | |
tinyMCEPopup.init(); | |
tinymce.util.Dispatcher = function (scope) { | |
this.scope = scope || this; | |
this.listeners = []; | |
this.add = function (callback, scope) { | |
this.listeners.push({ cb: callback, scope: scope || this.scope }); | |
return callback; | |
}; | |
this.addToTop = function (callback, scope) { | |
var self = this, listener = { cb: callback, scope: scope || self.scope }; | |
// 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; | |
}; | |
this.remove = function (callback) { | |
var listeners = this.listeners, output = null; | |
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; | |
}; | |
}; | |