1 if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit.Editor"] = true;
3 dojo.provide("dijit.Editor");
4 dojo.require("dijit._editor.RichText");
5 dojo.require("dijit.Toolbar");
6 dojo.require("dijit._editor._Plugin");
7 dojo.require("dijit._Container");
8 dojo.require("dojo.i18n");
9 dojo.requireLocalization("dijit._editor", "commands", null, "zh,pt,da,tr,ru,de,sv,ja,he,fi,nb,el,ar,pt-pt,cs,fr,es,ko,nl,zh-tw,pl,it,hu,ROOT");
13 dijit._editor.RichText,
15 // summary: A rich-text Editing widget
18 // a list of plugin names (as strings) or instances (as objects)
22 // extraPlugins: Array
23 // a list of extra plugin names which will be appended to plugins array
26 constructor: function(){
27 if(!dojo.isArray(this.plugins)){
28 this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
29 "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull"/*"createLink"*/];
33 this._editInterval = this.editActionInterval * 1000;
36 postCreate: function(){
37 //for custom undo/redo
39 dojo['require']("dijit._editor.range");
40 this._steps=this._steps.slice(0);
41 this._undoedSteps=this._undoedSteps.slice(0);
42 // this.addKeyHandler('z',this.KEY_CTRL,this.undo);
43 // this.addKeyHandler('y',this.KEY_CTRL,this.redo);
45 if(dojo.isArray(this.extraPlugins)){
46 this.plugins=this.plugins.concat(this.extraPlugins);
50 this.inherited(arguments);
51 // dijit.Editor.superclass.postCreate.apply(this, arguments);
53 this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
56 // if we haven't been assigned a toolbar, create one
57 this.toolbar = new dijit.Toolbar({});
58 dojo.place(this.toolbar.domNode, this.editingArea, "before");
61 dojo.forEach(this.plugins, this.addPlugin, this);
62 this.onNormalizedDisplayChanged(); //update toolbar button status
63 // }catch(e){ console.debug(e); }
66 dojo.forEach(this._plugins, function(p){
72 this.toolbar.destroy(); delete this.toolbar;
73 this.inherited(arguments);
75 addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
77 // takes a plugin name as a string or a plugin instance and
78 // adds it to the toolbar and associates it with this editor
79 // instance. The resulting plugin is added to the Editor's
80 // plugins array. If index is passed, it's placed in the plugins
81 // array at that index. No big magic, but a nice helper for
82 // passing in plugin names via markup.
83 // plugin: String, args object or plugin instance. Required.
84 // args: This object will be passed to the plugin constructor.
86 // Integer, optional. Used when creating an instance from
87 // something already in this.plugins. Ensures that the new
88 // instance is assigned to this.plugins at that index.
89 var args=dojo.isString(plugin)?{name:plugin}:plugin;
91 var o={"args":args,"plugin":null,"editor":this};
92 dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]);
94 var pc = dojo.getObject(args.name);
96 o.plugin=new pc(args);
100 console.warn('Cannot find plugin',plugin);
105 if(arguments.length > 1){
106 this._plugins[index] = plugin;
108 this._plugins.push(plugin);
110 plugin.setEditor(this);
111 if(dojo.isFunction(plugin.setToolbar)){
112 plugin.setToolbar(this.toolbar);
115 /* beginning of custom undo/redo support */
117 // customUndo: Boolean
118 // Whether we shall use custom undo/redo support instead of the native
119 // browser support. By default, we only enable customUndo for IE, as it
120 // has broken native undo/redo support. Note: the implementation does
121 // support other browsers which have W3C DOM2 Range API.
122 customUndo: dojo.isIE,
124 // editActionInterval: Integer
125 // When using customUndo, not every keystroke will be saved as a step.
126 // Instead typing (including delete) will be grouped together: after
127 // a user stop typing for editActionInterval seconds, a step will be
128 // saved; if a user resume typing within editActionInterval seconds,
129 // the timeout will be restarted. By default, editActionInterval is 3
131 editActionInterval: 3,
132 beginEditing: function(cmd){
133 if(!this._inEditing){
134 this._inEditing=true;
135 this._beginEditing(cmd);
137 if(this.editActionInterval>0){
139 clearTimeout(this._editTimer);
141 this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
146 execCommand: function(cmd){
147 if(this.customUndo && (cmd=='undo' || cmd=='redo')){
153 this._beginEditing();
155 var r = this.inherited('execCommand',arguments);
161 if(dojo.isMoz && /copy|cut|paste/.test(cmd)){
162 // Warn user of platform limitation. Cannot programmatically access keyboard. See ticket #4136
163 var sub = dojo.string.substitute,
164 accel = {cut:'X', copy:'C', paste:'V'},
165 isMac = navigator.userAgent.indexOf("Macintosh") != -1;
166 alert(sub(this.commands.systemShortcutFF,
167 [this.commands[cmd], sub(this.commands[isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
173 queryCommandEnabled: function(cmd){
174 if(this.customUndo && (cmd=='undo' || cmd=='redo')){
175 return cmd=='undo'?(this._steps.length>1):(this._undoedSteps.length>0);
177 return this.inherited('queryCommandEnabled',arguments);
180 _moveToBookmark: function(b){
183 if(dojo.isArray(b)){//IE CONTROL
185 dojo.forEach(b,function(n){
186 bookmark.push(dijit.range.getNode(n,this.editNode));
190 var r=dijit.range.create();
191 r.setStart(dijit.range.getNode(b.startContainer,this.editNode),b.startOffset);
192 r.setEnd(dijit.range.getNode(b.endContainer,this.editNode),b.endOffset);
195 dojo.withGlobal(this.window,'moveToBookmark',dijit,[bookmark]);
197 _changeToStep: function(from,to){
198 this.setValue(to.text);
201 this._moveToBookmark(b);
204 // console.log('undo');
205 this.endEditing(true);
206 var s=this._steps.pop();
207 if(this._steps.length>0){
209 this._changeToStep(s,this._steps[this._steps.length-1]);
210 this._undoedSteps.push(s);
211 this.onDisplayChanged();
217 // console.log('redo');
218 this.endEditing(true);
219 var s=this._undoedSteps.pop();
220 if(s && this._steps.length>0){
222 this._changeToStep(this._steps[this._steps.length-1],s);
224 this.onDisplayChanged();
229 endEditing: function(ignore_caret){
231 clearTimeout(this._editTimer);
234 this._endEditing(ignore_caret);
235 this._inEditing=false;
238 _getBookmark: function(){
239 var b=dojo.withGlobal(this.window,dijit.getBookmark);
242 if(dojo.isArray(b)){//CONTROL
243 dojo.forEach(b,function(n){
244 tmp.push(dijit.range.getIndex(n,this.editNode).o);
249 tmp=dijit.range.getIndex(b.startContainer,this.editNode).o;
250 b={startContainer:tmp,
251 startOffset:b.startOffset,
252 endContainer:b.endContainer===b.startContainer?tmp:dijit.range.getIndex(b.endContainer,this.editNode).o,
253 endOffset:b.endOffset};
257 _beginEditing: function(cmd){
258 if(this._steps.length===0){
259 this._steps.push({'text':this.savedContent,'bookmark':this._getBookmark()});
262 _endEditing: function(ignore_caret){
263 var v=this.getValue(true);
265 this._undoedSteps=[];//clear undoed steps
266 this._steps.push({text: v, bookmark: this._getBookmark()});
268 onKeyDown: function(e){
269 if(!this.customUndo){
270 this.inherited('onKeyDown',arguments);
273 var k = e.keyCode, ks = dojo.keys;
274 if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
275 if(k == 90 || k == 122){ //z
279 }else if(k == 89 || k == 121){ //y
285 this.inherited('onKeyDown',arguments);
295 if(e.ctrlKey && !e.altKey && !e.metaKey){
296 this.endEditing();//end current typing step if any
298 this.beginEditing('cut');
299 //use timeout to trigger after the cut is complete
300 setTimeout(dojo.hitch(this, this.endEditing), 1);
302 this.beginEditing('paste');
303 //use timeout to trigger after the paste is complete
304 setTimeout(dojo.hitch(this, this.endEditing), 1);
310 if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
326 this.endEditing(true);
328 //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
336 this.inherited('_onBlur',arguments);
337 this.endEditing(true);
340 this.endEditing(true);
341 this.inherited('onClick',arguments);
343 /* end of custom undo/redo support */
347 /* the following code is to registered a handler to get default plugins */
348 dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
349 if(o.plugin){ return; }
350 var args = o.args, p;
351 var _p = dijit._editor._Plugin;
352 var name = args.name;
354 case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
355 case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
356 case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
357 case "selectAll": case "removeFormat":
358 case "insertHorizontalRule":
359 p = new _p({ command: name });
362 case "bold": case "italic": case "underline": case "strikethrough":
363 case "subscript": case "superscript":
364 p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
367 p = new _p({ button: new dijit.ToolbarSeparator() });
369 // console.log('name',name,p);