374 lines
11 KiB
JavaScript
374 lines
11 KiB
JavaScript
|
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, "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");
|
||
|
|
||
|
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(){
|
||
|
if(!dojo.isArray(this.plugins)){
|
||
|
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{
|
||
|
this.inherited(arguments);
|
||
|
// 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
|
||
|
this.toolbar = new dijit.Toolbar({});
|
||
|
dojo.place(this.toolbar.domNode, this.editingArea, "before");
|
||
|
}
|
||
|
|
||
|
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 && p.destroy){
|
||
|
p.destroy();
|
||
|
}
|
||
|
});
|
||
|
this._plugins=[];
|
||
|
this.toolbar.destroy(); delete this.toolbar;
|
||
|
this.inherited(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._scopeName + ".Editor.getPlugin",[o]);
|
||
|
if(!o.plugin){
|
||
|
var pc = dojo.getObject(args.name);
|
||
|
if(pc){
|
||
|
o.plugin=new pc(args);
|
||
|
}
|
||
|
}
|
||
|
if(!o.plugin){
|
||
|
console.warn('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);
|
||
|
}
|
||
|
},
|
||
|
_moveToBookmark: function(b){
|
||
|
var bookmark=b;
|
||
|
if(dojo.isIE){
|
||
|
if(dojo.isArray(b)){//IE CONTROL
|
||
|
bookmark=[];
|
||
|
dojo.forEach(b,function(n){
|
||
|
bookmark.push(dijit.range.getNode(n,this.editNode));
|
||
|
},this);
|
||
|
}
|
||
|
}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);
|
||
|
bookmark=r;
|
||
|
}
|
||
|
dojo.withGlobal(this.window,'moveToBookmark',dijit,[bookmark]);
|
||
|
},
|
||
|
_changeToStep: function(from,to){
|
||
|
this.setValue(to.text);
|
||
|
var b=to.bookmark;
|
||
|
if(!b){ return; }
|
||
|
this._moveToBookmark(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);
|
||
|
var tmp=[];
|
||
|
if(dojo.isIE){
|
||
|
if(dojo.isArray(b)){//CONTROL
|
||
|
dojo.forEach(b,function(n){
|
||
|
tmp.push(dijit.range.getIndex(n,this.editNode).o);
|
||
|
},this);
|
||
|
b=tmp;
|
||
|
}
|
||
|
}else{//w3c range
|
||
|
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 && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
|
||
|
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:
|
||
|
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._scopeName + ".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":
|
||
|
case "insertHorizontalRule":
|
||
|
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() });
|
||
|
}
|
||
|
// console.log('name',name,p);
|
||
|
o.plugin=p;
|
||
|
});
|
||
|
|
||
|
}
|