/** * Editor.Events.js * * Copyright, Moxiecode Systems AB * Released under LGPL License. * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ (function(tinymce) { var each = tinymce.each; /** * Creates all event dispatcher instances for the editor instance and also adds * passthoughs for legacy callback handlers. */ tinymce.Editor.prototype.setupEvents = function() { var self = this, settings = self.settings; // Add events to the editor each([ /** * Fires before the initialization of the editor. * * @event onPreInit * @param {tinymce.Editor} sender Editor instance. * @see #onInit * @example * // Adds an observer to the onPreInit event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onPreInit.add(function(ed) { * console.debug('PreInit: ' + ed.id); * }); * } * }); */ 'onPreInit', /** * Fires before the initialization of the editor. * * @event onBeforeRenderUI * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onBeforeRenderUI event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onBeforeRenderUI.add(function(ed, cm) { * console.debug('Before render: ' + ed.id); * }); * } * }); */ 'onBeforeRenderUI', /** * Fires after the rendering has completed. * * @event onPostRender * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onPostRender event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onPostRender.add(function(ed, cm) { * console.debug('After render: ' + ed.id); * }); * } * }); */ 'onPostRender', /** * Fires when the onload event on the body occurs. * * @event onLoad * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onLoad event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onLoad.add(function(ed, cm) { * console.debug('Document loaded: ' + ed.id); * }); * } * }); */ 'onLoad', /** * Fires after the initialization of the editor is done. * * @event onInit * @param {tinymce.Editor} sender Editor instance. * @see #onPreInit * @example * // Adds an observer to the onInit event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onInit.add(function(ed) { * console.debug('Editor is done: ' + ed.id); * }); * } * }); */ 'onInit', /** * Fires when the editor instance is removed from page. * * @event onRemove * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onRemove event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onRemove.add(function(ed) { * console.debug('Editor was removed: ' + ed.id); * }); * } * }); */ 'onRemove', /** * Fires when the editor is activated. * * @event onActivate * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onActivate event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onActivate.add(function(ed) { * console.debug('Editor was activated: ' + ed.id); * }); * } * }); */ 'onActivate', /** * Fires when the editor is deactivated. * * @event onDeactivate * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onDeactivate event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onDeactivate.add(function(ed) { * console.debug('Editor was deactivated: ' + ed.id); * }); * } * }); */ 'onDeactivate', /** * Fires when something in the body of the editor is clicked. * * @event onClick * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onClick event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onClick.add(function(ed, e) { * console.debug('Editor was clicked: ' + e.target.nodeName); * }); * } * }); */ 'onClick', /** * Fires when a registered event is intercepted. * * @event onEvent * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onEvent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onEvent.add(function(ed, e) { * console.debug('Editor event occurred: ' + e.target.nodeName); * }); * } * }); */ 'onEvent', /** * Fires when a mouseup event is intercepted inside the editor. * * @event onMouseUp * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onMouseUp event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onMouseUp.add(function(ed, e) { * console.debug('Mouse up event: ' + e.target.nodeName); * }); * } * }); */ 'onMouseUp', /** * Fires when a mousedown event is intercepted inside the editor. * * @event onMouseDown * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onMouseDown event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onMouseDown.add(function(ed, e) { * console.debug('Mouse down event: ' + e.target.nodeName); * }); * } * }); */ 'onMouseDown', /** * Fires when a dblclick event is intercepted inside the editor. * * @event onDblClick * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onDblClick event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onDblClick.add(function(ed, e) { * console.debug('Double click event: ' + e.target.nodeName); * }); * } * }); */ 'onDblClick', /** * Fires when a keydown event is intercepted inside the editor. * * @event onKeyDown * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onKeyDown event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onKeyDown.add(function(ed, e) { * console.debug('Key down event: ' + e.keyCode); * }); * } * }); */ 'onKeyDown', /** * Fires when a keydown event is intercepted inside the editor. * * @event onKeyUp * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onKeyUp event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onKeyUp.add(function(ed, e) { * console.debug('Key up event: ' + e.keyCode); * }); * } * }); */ 'onKeyUp', /** * Fires when a keypress event is intercepted inside the editor. * * @event onKeyPress * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onKeyPress event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onKeyPress.add(function(ed, e) { * console.debug('Key press event: ' + e.keyCode); * }); * } * }); */ 'onKeyPress', /** * Fires when a contextmenu event is intercepted inside the editor. * * @event onContextMenu * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onContextMenu event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onContextMenu.add(function(ed, e) { * console.debug('Context menu event:' + e.target); * }); * } * }); */ 'onContextMenu', /** * Fires when a form submit event is intercepted. * * @event onSubmit * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onSubmit event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onSubmit.add(function(ed, e) { * console.debug('Form submit:' + e.target); * }); * } * }); */ 'onSubmit', /** * Fires when a form reset event is intercepted. * * @event onReset * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onReset event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onReset.add(function(ed, e) { * console.debug('Form reset:' + e.target); * }); * } * }); */ 'onReset', /** * Fires when a paste event is intercepted inside the editor. * * @event onPaste * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onPaste event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onPaste.add(function(ed, e) { * console.debug('Pasted plain text'); * }); * } * }); */ 'onPaste', /** * Fires when the Serializer does a preProcess on the contents. * * @event onPreProcess * @param {tinymce.Editor} sender Editor instance. * @param {Object} obj PreProcess object. * @option {Node} node DOM node for the item being serialized. * @option {String} format The specified output format normally "html". * @option {Boolean} get Is true if the process is on a getContent operation. * @option {Boolean} set Is true if the process is on a setContent operation. * @option {Boolean} cleanup Is true if the process is on a cleanup operation. * @example * // Adds an observer to the onPreProcess event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onPreProcess.add(function(ed, o) { * // Add a class to each paragraph in the editor * ed.dom.addClass(ed.dom.select('p', o.node), 'myclass'); * }); * } * }); */ 'onPreProcess', /** * Fires when the Serializer does a postProcess on the contents. * * @event onPostProcess * @param {tinymce.Editor} sender Editor instance. * @param {Object} obj PreProcess object. * @example * // Adds an observer to the onPostProcess event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onPostProcess.add(function(ed, o) { * // Remove all paragraphs and replace with BR * o.content = o.content.replace(/]+>|

/g, ''); * o.content = o.content.replace(/<\/p>/g, '
'); * }); * } * }); */ 'onPostProcess', /** * Fires before new contents is added to the editor. Using for example setContent. * * @event onBeforeSetContent * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onBeforeSetContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onBeforeSetContent.add(function(ed, o) { * // Replaces all a characters with b characters * o.content = o.content.replace(/a/g, 'b'); * }); * } * }); */ 'onBeforeSetContent', /** * Fires before contents is extracted from the editor using for example getContent. * * @event onBeforeGetContent * @param {tinymce.Editor} sender Editor instance. * @param {Event} evt W3C DOM Event instance. * @example * // Adds an observer to the onBeforeGetContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onBeforeGetContent.add(function(ed, o) { * console.debug('Before get content.'); * }); * } * }); */ 'onBeforeGetContent', /** * Fires after the contents has been added to the editor using for example onSetContent. * * @event onSetContent * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onSetContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onSetContent.add(function(ed, o) { * // Replaces all a characters with b characters * o.content = o.content.replace(/a/g, 'b'); * }); * } * }); */ 'onSetContent', /** * Fires after the contents has been extracted from the editor using for example getContent. * * @event onGetContent * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onGetContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onGetContent.add(function(ed, o) { * // Replace all a characters with b * o.content = o.content.replace(/a/g, 'b'); * }); * } * }); */ 'onGetContent', /** * Fires when the editor gets loaded with contents for example when the load method is executed. * * @event onLoadContent * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onLoadContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onLoadContent.add(function(ed, o) { * // Output the element name * console.debug(o.element.nodeName); * }); * } * }); */ 'onLoadContent', /** * Fires when the editor contents gets saved for example when the save method is executed. * * @event onSaveContent * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onSaveContent event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onSaveContent.add(function(ed, o) { * // Output the element name * console.debug(o.element.nodeName); * }); * } * }); */ 'onSaveContent', /** * Fires when the user changes node location using the mouse or keyboard. * * @event onNodeChange * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onNodeChange event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onNodeChange.add(function(ed, cm, e) { * // Activates the link button when the caret is placed in a anchor element * if (e.nodeName == 'A') * cm.setActive('link', true); * }); * } * }); */ 'onNodeChange', /** * Fires when a new undo level is added to the editor. * * @event onChange * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onChange event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onChange.add(function(ed, l) { * console.debug('Editor contents was modified. Contents: ' + l.content); * }); * } * }); */ 'onChange', /** * Fires before a command gets executed for example "Bold". * * @event onBeforeExecCommand * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onBeforeExecCommand event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onBeforeExecCommand.add(function(ed, cmd, ui, val) { * console.debug('Command is to be executed: ' + cmd); * }); * } * }); */ 'onBeforeExecCommand', /** * Fires after a command is executed for example "Bold". * * @event onExecCommand * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onExecCommand event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onExecCommand.add(function(ed, cmd, ui, val) { * console.debug('Command was executed: ' + cmd); * }); * } * }); */ 'onExecCommand', /** * Fires when the contents is undo:ed. * * @event onUndo * @param {tinymce.Editor} sender Editor instance. * @param {Object} level Undo level object. * @ example * // Adds an observer to the onUndo event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onUndo.add(function(ed, level) { * console.debug('Undo was performed: ' + level.content); * }); * } * }); */ 'onUndo', /** * Fires when the contents is redo:ed. * * @event onRedo * @param {tinymce.Editor} sender Editor instance. * @param {Object} level Undo level object. * @example * // Adds an observer to the onRedo event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onRedo.add(function(ed, level) { * console.debug('Redo was performed: ' +level.content); * }); * } * }); */ 'onRedo', /** * Fires when visual aids is enabled/disabled. * * @event onVisualAid * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onVisualAid event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onVisualAid.add(function(ed, e, s) { * console.debug('onVisualAid event: ' + ed.id + ", State: " + s); * }); * } * }); */ 'onVisualAid', /** * Fires when the progress throbber is shown above the editor. * * @event onSetProgressState * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onSetProgressState event using tinyMCE.init * tinyMCE.init({ * ... * setup : function(ed) { * ed.onSetProgressState.add(function(ed, b) { * if (b) * console.debug('SHOW!'); * else * console.debug('HIDE!'); * }); * } * }); */ 'onSetProgressState', /** * Fires after an attribute is set using setAttrib. * * @event onSetAttrib * @param {tinymce.Editor} sender Editor instance. * @example * // Adds an observer to the onSetAttrib event using tinyMCE.init *tinyMCE.init({ * ... * setup : function(ed) { * ed.onSetAttrib.add(function(ed, node, attribute, attributeValue) { * console.log('onSetAttrib tag'); * }); * } * }); */ 'onSetAttrib' ], function(name) { self[name] = new tinymce.util.Dispatcher(self); }); // Handle legacy cleanup_callback option if (settings.cleanup_callback) { self.onBeforeSetContent.add(function(ed, o) { o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); }); self.onPreProcess.add(function(ed, o) { if (o.set) ed.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o); if (o.get) ed.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o); }); self.onPostProcess.add(function(ed, o) { if (o.set) o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); if (o.get) o.content = ed.execCallback('cleanup_callback', 'get_from_editor', o.content, o); }); } // Handle legacy save_callback option if (settings.save_callback) { self.onGetContent.add(function(ed, o) { if (o.save) o.content = ed.execCallback('save_callback', ed.id, o.content, ed.getBody()); }); } // Handle legacy handle_event_callback option if (settings.handle_event_callback) { self.onEvent.add(function(ed, e, o) { if (self.execCallback('handle_event_callback', e, ed, o) === false) { e.preventDefault(); e.stopPropagation(); } }); } // Handle legacy handle_node_change_callback option if (settings.handle_node_change_callback) { self.onNodeChange.add(function(ed, cm, n) { ed.execCallback('handle_node_change_callback', ed.id, n, -1, -1, true, ed.selection.isCollapsed()); }); } // Handle legacy save_callback option if (settings.save_callback) { self.onSaveContent.add(function(ed, o) { var h = ed.execCallback('save_callback', ed.id, o.content, ed.getBody()); if (h) o.content = h; }); } // Handle legacy onchange_callback option if (settings.onchange_callback) { self.onChange.add(function(ed, l) { ed.execCallback('onchange_callback', ed, l); }); } }; /** * Binds native DOM events and sends these out to the dispatchers. */ tinymce.Editor.prototype.bindNativeEvents = function() { // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset var self = this, i, settings = self.settings, dom = self.dom, nativeToDispatcherMap; nativeToDispatcherMap = { mouseup : 'onMouseUp', mousedown : 'onMouseDown', click : 'onClick', keyup : 'onKeyUp', keydown : 'onKeyDown', keypress : 'onKeyPress', submit : 'onSubmit', reset : 'onReset', contextmenu : 'onContextMenu', dblclick : 'onDblClick', paste : 'onPaste' // Doesn't work in all browsers yet }; // Handler that takes a native event and sends it out to a dispatcher like onKeyDown function eventHandler(evt, args) { var type = evt.type; // Don't fire events when it's removed if (self.removed) return; // Sends the native event out to a global dispatcher then to the specific event dispatcher if (self.onEvent.dispatch(self, evt, args) !== false) { self[nativeToDispatcherMap[evt.fakeType || evt.type]].dispatch(self, evt, args); } }; // Opera doesn't support focus event for contentEditable elements so we need to fake it function doOperaFocus(e) { self.focus(true); }; function nodeChanged(ed, e) { // Normalize selection for example a|a becomes a|a except for Ctrl+A since it selects everything if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) { self.selection.normalize(); } self.nodeChanged(); } // Add DOM events each(nativeToDispatcherMap, function(dispatcherName, nativeName) { var root = settings.content_editable ? self.getBody() : self.getDoc(); switch (nativeName) { case 'contextmenu': dom.bind(root, nativeName, eventHandler); break; case 'paste': dom.bind(self.getBody(), nativeName, eventHandler); break; case 'submit': case 'reset': dom.bind(self.getElement().form || tinymce.DOM.getParent(self.id, 'form'), nativeName, eventHandler); break; default: dom.bind(root, nativeName, eventHandler); } }); // Set the editor as active when focused dom.bind(settings.content_editable ? self.getBody() : (tinymce.isGecko ? self.getDoc() : self.getWin()), 'focus', function(e) { self.focus(true); }); if (settings.content_editable && tinymce.isOpera) { dom.bind(self.getBody(), 'click', doOperaFocus); dom.bind(self.getBody(), 'keydown', doOperaFocus); } // Add node change handler self.onMouseUp.add(nodeChanged); self.onKeyUp.add(function(ed, e) { var keyCode = e.keyCode; if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey) nodeChanged(ed, e); }); // Add reset handler self.onReset.add(function() { self.setContent(self.startContent, {format : 'raw'}); }); // Add shortcuts function handleShortcut(e, execute) { if (e.altKey || e.ctrlKey || e.metaKey) { each(self.shortcuts, function(shortcut) { var ctrlState = tinymce.isMac ? e.metaKey : e.ctrlKey; if (shortcut.ctrl != ctrlState || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) return; if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) { e.preventDefault(); if (execute) { shortcut.func.call(shortcut.scope); } return true; } }); } }; self.onKeyUp.add(function(ed, e) { handleShortcut(e); }); self.onKeyPress.add(function(ed, e) { handleShortcut(e); }); self.onKeyDown.add(function(ed, e) { handleShortcut(e, true); }); if (tinymce.isOpera) { self.onClick.add(function(ed, e) { e.preventDefault(); }); } }; })(tinymce);