if (!dojo._hasResource["dijit.Editor"]) { // _hasResource checks added by // build. Do not use _hasResource // directly in your code. dojo._hasResource["dijit.Editor"] = true; dojo.provide("dijit.Editor"); dojo.require("dijit._editor.RichText"); dojo.require("dijit.Toolbar"); dojo.require("dijit._editor._Plugin"); dojo.require("dijit._Container"); dojo.require("dojo.i18n"); dojo.requireLocalization("dijit._editor", "commands", null, "ko,zh,ja,zh-tw,ru,it,hu,fr,pt,pl,es,ROOT,de,cs"); dojo.declare("dijit.Editor", dijit._editor.RichText, { // summary: A rich-text Editing widget // plugins: Array // a list of plugin names (as strings) or instances (as objects) // for this widget. plugins : null, // extraPlugins: Array // a list of extra plugin names which will be appended to plugins array extraPlugins : null, constructor : function() { this.plugins = ["undo", "redo", "|", "cut", "copy", "paste", "|", "bold", "italic", "underline", "strikethrough", "|", "insertOrderedList", "insertUnorderedList", "indent", "outdent", "|", "justifyLeft", "justifyRight", "justifyCenter", "justifyFull"/* "createLink" */]; this._plugins = []; this._editInterval = this.editActionInterval * 1000; }, postCreate : function() { // for custom undo/redo if (this.customUndo) { dojo['require']("dijit._editor.range"); this._steps = this._steps.slice(0); this._undoedSteps = this._undoedSteps.slice(0); // this.addKeyHandler('z',this.KEY_CTRL,this.undo); // this.addKeyHandler('y',this.KEY_CTRL,this.redo); } if (dojo.isArray(this.extraPlugins)) { this.plugins = this.plugins.concat(this.extraPlugins); } // try{ dijit.Editor.superclass.postCreate.apply(this, arguments); this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang); if (!this.toolbar) { // if we haven't been assigned a toolbar, create one var toolbarNode = dojo.doc.createElement("div"); dojo.place(toolbarNode, this.editingArea, "before"); this.toolbar = new dijit.Toolbar({}, toolbarNode); } dojo.forEach(this.plugins, this.addPlugin, this); this.onNormalizedDisplayChanged(); // update toolbar button status // }catch(e){ console.debug(e); } }, destroy : function() { dojo.forEach(this._plugins, function(p) { if (p.destroy) { p.destroy(); } }); this._plugins = []; this.toolbar.destroy(); delete this.toolbar; this.inherited('destroy', arguments); }, addPlugin : function(/* String||Object */plugin, /* Integer? */index) { // summary: // takes a plugin name as a string or a plugin instance and // adds it to the toolbar and associates it with this editor // instance. The resulting plugin is added to the Editor's // plugins array. If index is passed, it's placed in the plugins // array at that index. No big magic, but a nice helper for // passing in plugin names via markup. // plugin: String, args object or plugin instance. Required. // args: This object will be passed to the plugin constructor. // index: // Integer, optional. Used when creating an instance from // something already in this.plugins. Ensures that the new // instance is assigned to this.plugins at that index. var args = dojo.isString(plugin) ? { name : plugin } : plugin; if (!args.setEditor) { var o = { "args" : args, "plugin" : null, "editor" : this }; dojo.publish("dijit.Editor.getPlugin", [o]); if (!o.plugin) { var pc = dojo.getObject(args.name); if (pc) { o.plugin = new pc(args); } } if (!o.plugin) { console.debug('Cannot find plugin', plugin); return; } plugin = o.plugin; } if (arguments.length > 1) { this._plugins[index] = plugin; } else { this._plugins.push(plugin); } plugin.setEditor(this); if (dojo.isFunction(plugin.setToolbar)) { plugin.setToolbar(this.toolbar); } }, /* beginning of custom undo/redo support */ // customUndo: Boolean // Whether we shall use custom undo/redo support instead of the native // browser support. By default, we only enable customUndo for IE, as it // has broken native undo/redo support. Note: the implementation does // support other browsers which have W3C DOM2 Range API. customUndo : dojo.isIE, // editActionInterval: Integer // When using customUndo, not every keystroke will be saved as a step. // Instead typing (including delete) will be grouped together: after // a user stop typing for editActionInterval seconds, a step will be // saved; if a user resume typing within editActionInterval seconds, // the timeout will be restarted. By default, editActionInterval is 3 // seconds. editActionInterval : 3, beginEditing : function(cmd) { if (!this._inEditing) { this._inEditing = true; this._beginEditing(cmd); } if (this.editActionInterval > 0) { if (this._editTimer) { clearTimeout(this._editTimer); } this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval); } }, _steps : [], _undoedSteps : [], execCommand : function(cmd) { if (this.customUndo && (cmd == 'undo' || cmd == 'redo')) { return this[cmd](); } else { try { if (this.customUndo) { this.endEditing(); this._beginEditing(); } var r = this.inherited('execCommand', arguments); if (this.customUndo) { this._endEditing(); } return r; } catch (e) { if (dojo.isMoz && /copy|cut|paste/.test(cmd)) { // Warn user of platform limitation. Cannot // programmatically access keyboard. See ticket #4136 var sub = dojo.string.substitute, accel = { cut : 'X', copy : 'C', paste : 'V' }, isMac = navigator.userAgent.indexOf("Macintosh") != -1; alert(sub(this.commands.systemShortcutFF, [ this.commands[cmd], sub( this.commands[isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])])); } return false; } } }, queryCommandEnabled : function(cmd) { if (this.customUndo && (cmd == 'undo' || cmd == 'redo')) { return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0); } else { return this.inherited('queryCommandEnabled', arguments); } }, _changeToStep : function(from, to) { this.setValue(to.text); var b = to.bookmark; if (!b) { return; } if (dojo.isIE) { if (dojo.isArray(b)) {// IE CONTROL var tmp = []; dojo.forEach(b, function(n) { tmp.push(dijit.range.getNode(n, this.editNode)); }, this); b = tmp; } } else {// w3c range var r = dijit.range.create(); r.setStart( dijit.range.getNode(b.startContainer, this.editNode), b.startOffset); r.setEnd(dijit.range.getNode(b.endContainer, this.editNode), b.endOffset); b = r; } dojo.withGlobal(this.window, 'moveToBookmark', dijit, [b]); }, undo : function() { // console.log('undo'); this.endEditing(true); var s = this._steps.pop(); if (this._steps.length > 0) { this.focus(); this._changeToStep(s, this._steps[this._steps.length - 1]); this._undoedSteps.push(s); this.onDisplayChanged(); return true; } return false; }, redo : function() { // console.log('redo'); this.endEditing(true); var s = this._undoedSteps.pop(); if (s && this._steps.length > 0) { this.focus(); this._changeToStep(this._steps[this._steps.length - 1], s); this._steps.push(s); this.onDisplayChanged(); return true; } return false; }, endEditing : function(ignore_caret) { if (this._editTimer) { clearTimeout(this._editTimer); } if (this._inEditing) { this._endEditing(ignore_caret); this._inEditing = false; } }, _getBookmark : function() { var b = dojo.withGlobal(this.window, dijit.getBookmark); if (dojo.isIE) { if (dojo.isArray(b)) {// CONTROL var tmp = []; dojo.forEach(b, function(n) { tmp .push(dijit.range.getIndex(n, this.editNode).o); }, this); b = tmp; } } else {// w3c range var tmp = dijit.range.getIndex(b.startContainer, this.editNode).o b = { startContainer : tmp, startOffset : b.startOffset, endContainer : b.endContainer === b.startContainer ? tmp : dijit.range.getIndex(b.endContainer, this.editNode).o, endOffset : b.endOffset }; } return b; }, _beginEditing : function(cmd) { if (this._steps.length === 0) { this._steps.push({ 'text' : this.savedContent, 'bookmark' : this._getBookmark() }); } }, _endEditing : function(ignore_caret) { var v = this.getValue(true); this._undoedSteps = [];// clear undoed steps this._steps.push({ 'text' : v, 'bookmark' : this._getBookmark() }); }, onKeyDown : function(e) { if (!this.customUndo) { this.inherited('onKeyDown', arguments); return; } var k = e.keyCode, ks = dojo.keys; if (e.ctrlKey) { if (k === 90 || k === 122) { // z dojo.stopEvent(e); this.undo(); return; } else if (k === 89 || k === 121) { // y dojo.stopEvent(e); this.redo(); return; } } this.inherited('onKeyDown', arguments); switch (k) { case ks.ENTER : this.beginEditing(); break; case ks.BACKSPACE : case ks.DELETE : this.beginEditing(); break; case 88 : // x case 86 : // v if (e.ctrlKey && !e.altKey && !e.metaKey) { this.endEditing();// end current typing step if any if (e.keyCode == 88) { this.beginEditing('cut'); // use timeout to trigger after the cut is complete setTimeout(dojo.hitch(this, this.endEditing), 1); } else { this.beginEditing('paste'); // use timeout to trigger after the paste is // complete setTimeout(dojo.hitch(this, this.endEditing), 1); } break; } // pass through default : if (!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode < dojo.keys.F1 || e.keyCode > dojo.keys.F15)) { this.beginEditing(); break; } // pass through case ks.ALT : this.endEditing(); break; case ks.UP_ARROW : case ks.DOWN_ARROW : case ks.LEFT_ARROW : case ks.RIGHT_ARROW : case ks.HOME : case ks.END : case ks.PAGE_UP : case ks.PAGE_DOWN : this.endEditing(true); break; // maybe ctrl+backspace/delete, so don't endEditing when ctrl is // pressed case ks.CTRL : case ks.SHIFT : case ks.TAB : break; } }, _onBlur : function() { this.inherited('_onBlur', arguments); this.endEditing(true); }, onClick : function() { this.endEditing(true); this.inherited('onClick', arguments); } /* end of custom undo/redo support */ }); /* the following code is to registered a handler to get default plugins */ dojo.subscribe("dijit.Editor.getPlugin", null, function(o) { if (o.plugin) { return; } var args = o.args, p; var _p = dijit._editor._Plugin; var name = args.name; switch (name) { case "undo" : case "redo" : case "cut" : case "copy" : case "paste" : case "insertOrderedList" : case "insertUnorderedList" : case "indent" : case "outdent" : case "justifyCenter" : case "justifyFull" : case "justifyLeft" : case "justifyRight" : case "delete" : case "selectAll" : case "removeFormat" : p = new _p({ command : name }); break; case "bold" : case "italic" : case "underline" : case "strikethrough" : case "subscript" : case "superscript" : p = new _p({ buttonClass : dijit.form.ToggleButton, command : name }); break; case "|" : p = new _p({ button : new dijit.ToolbarSeparator() }); break; case "createLink" : // dojo['require']('dijit._editor.plugins.LinkDialog'); p = new dijit._editor.plugins.LinkDialog({ command : name }); break; case "foreColor" : case "hiliteColor" : p = new dijit._editor.plugins.TextColor({ command : name }); break; case "fontName" : case "fontSize" : case "formatBlock" : p = new dijit._editor.plugins.FontChoice({ command : name }); } // console.log('name',name,p); o.plugin = p; }); }