/branches/versions/aipo702b/design/htmlmock/javascript/dijit/Editor.xd.js

http://aipo.googlecode.com/ · JavaScript · 389 lines · 321 code · 18 blank · 50 comment · 50 complexity · 54ac7f7bc8fae6e5cdd863a052b20a8e MD5 · raw file

  1. dojo._xdResourceLoaded({
  2. depends: [["provide", "dijit.Editor"],
  3. ["require", "dijit._editor.RichText"],
  4. ["require", "dijit.Toolbar"],
  5. ["require", "dijit._editor._Plugin"],
  6. ["require", "dijit._Container"],
  7. ["require", "dojo.i18n"],
  8. ["requireLocalization", "dijit._editor", "commands", null, "ROOT,cs,de,es,fr,hu,it,ja,ko,pl,pt,ru,zh,zh-tw", "ROOT,cs,de,es,fr,hu,it,ja,ko,pl,pt,ru,zh,zh-tw"]],
  9. defineResource: function(dojo){if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10. dojo._hasResource["dijit.Editor"] = true;
  11. dojo.provide("dijit.Editor");
  12. dojo.require("dijit._editor.RichText");
  13. dojo.require("dijit.Toolbar");
  14. dojo.require("dijit._editor._Plugin");
  15. dojo.require("dijit._Container");
  16. dojo.require("dojo.i18n");
  17. ;
  18. dojo.declare(
  19. "dijit.Editor",
  20. dijit._editor.RichText,
  21. {
  22. // summary: A rich-text Editing widget
  23. // plugins: Array
  24. // a list of plugin names (as strings) or instances (as objects)
  25. // for this widget.
  26. plugins: null,
  27. // extraPlugins: Array
  28. // a list of extra plugin names which will be appended to plugins array
  29. extraPlugins: null,
  30. constructor: function(){
  31. this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
  32. "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull"/*"createLink"*/];
  33. this._plugins=[];
  34. this._editInterval = this.editActionInterval * 1000;
  35. },
  36. postCreate: function(){
  37. //for custom undo/redo
  38. if(this.customUndo){
  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);
  44. }
  45. if(dojo.isArray(this.extraPlugins)){
  46. this.plugins=this.plugins.concat(this.extraPlugins);
  47. }
  48. // try{
  49. dijit.Editor.superclass.postCreate.apply(this, arguments);
  50. this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
  51. if(!this.toolbar){
  52. // if we haven't been assigned a toolbar, create one
  53. var toolbarNode = dojo.doc.createElement("div");
  54. dojo.place(toolbarNode, this.editingArea, "before");
  55. this.toolbar = new dijit.Toolbar({}, toolbarNode);
  56. }
  57. dojo.forEach(this.plugins, this.addPlugin, this);
  58. this.onNormalizedDisplayChanged(); //update toolbar button status
  59. // }catch(e){ console.debug(e); }
  60. },
  61. destroy: function(){
  62. dojo.forEach(this._plugins, function(p){
  63. if(p.destroy){
  64. p.destroy();
  65. }
  66. });
  67. this._plugins=[];
  68. this.toolbar.destroy(); delete this.toolbar;
  69. this.inherited('destroy',arguments);
  70. },
  71. addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
  72. // summary:
  73. // takes a plugin name as a string or a plugin instance and
  74. // adds it to the toolbar and associates it with this editor
  75. // instance. The resulting plugin is added to the Editor's
  76. // plugins array. If index is passed, it's placed in the plugins
  77. // array at that index. No big magic, but a nice helper for
  78. // passing in plugin names via markup.
  79. // plugin: String, args object or plugin instance. Required.
  80. // args: This object will be passed to the plugin constructor.
  81. // index:
  82. // Integer, optional. Used when creating an instance from
  83. // something already in this.plugins. Ensures that the new
  84. // instance is assigned to this.plugins at that index.
  85. var args=dojo.isString(plugin)?{name:plugin}:plugin;
  86. if(!args.setEditor){
  87. var o={"args":args,"plugin":null,"editor":this};
  88. dojo.publish("dijit.Editor.getPlugin",[o]);
  89. if(!o.plugin){
  90. var pc = dojo.getObject(args.name);
  91. if(pc){
  92. o.plugin=new pc(args);
  93. }
  94. }
  95. if(!o.plugin){
  96. console.debug('Cannot find plugin',plugin);
  97. return;
  98. }
  99. plugin=o.plugin;
  100. }
  101. if(arguments.length > 1){
  102. this._plugins[index] = plugin;
  103. }else{
  104. this._plugins.push(plugin);
  105. }
  106. plugin.setEditor(this);
  107. if(dojo.isFunction(plugin.setToolbar)){
  108. plugin.setToolbar(this.toolbar);
  109. }
  110. },
  111. /* beginning of custom undo/redo support */
  112. // customUndo: Boolean
  113. // Whether we shall use custom undo/redo support instead of the native
  114. // browser support. By default, we only enable customUndo for IE, as it
  115. // has broken native undo/redo support. Note: the implementation does
  116. // support other browsers which have W3C DOM2 Range API.
  117. customUndo: dojo.isIE,
  118. // editActionInterval: Integer
  119. // When using customUndo, not every keystroke will be saved as a step.
  120. // Instead typing (including delete) will be grouped together: after
  121. // a user stop typing for editActionInterval seconds, a step will be
  122. // saved; if a user resume typing within editActionInterval seconds,
  123. // the timeout will be restarted. By default, editActionInterval is 3
  124. // seconds.
  125. editActionInterval: 3,
  126. beginEditing: function(cmd){
  127. if(!this._inEditing){
  128. this._inEditing=true;
  129. this._beginEditing(cmd);
  130. }
  131. if(this.editActionInterval>0){
  132. if(this._editTimer){
  133. clearTimeout(this._editTimer);
  134. }
  135. this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
  136. }
  137. },
  138. _steps:[],
  139. _undoedSteps:[],
  140. execCommand: function(cmd){
  141. if(this.customUndo && (cmd=='undo' || cmd=='redo')){
  142. return this[cmd]();
  143. }else{
  144. try{
  145. if(this.customUndo){
  146. this.endEditing();
  147. this._beginEditing();
  148. }
  149. var r = this.inherited('execCommand',arguments);
  150. if(this.customUndo){
  151. this._endEditing();
  152. }
  153. return r;
  154. }catch(e){
  155. if(dojo.isMoz && /copy|cut|paste/.test(cmd)){
  156. // Warn user of platform limitation. Cannot programmatically access keyboard. See ticket #4136
  157. var sub = dojo.string.substitute,
  158. accel = {cut:'X', copy:'C', paste:'V'},
  159. isMac = navigator.userAgent.indexOf("Macintosh") != -1;
  160. alert(sub(this.commands.systemShortcutFF,
  161. [this.commands[cmd], sub(this.commands[isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
  162. }
  163. return false;
  164. }
  165. }
  166. },
  167. queryCommandEnabled: function(cmd){
  168. if(this.customUndo && (cmd=='undo' || cmd=='redo')){
  169. return cmd=='undo'?(this._steps.length>1):(this._undoedSteps.length>0);
  170. }else{
  171. return this.inherited('queryCommandEnabled',arguments);
  172. }
  173. },
  174. _changeToStep: function(from,to){
  175. this.setValue(to.text);
  176. var b=to.bookmark;
  177. if(!b){ return; }
  178. if(dojo.isIE){
  179. if(dojo.isArray(b)){//IE CONTROL
  180. var tmp=[];
  181. dojo.forEach(b,function(n){
  182. tmp.push(dijit.range.getNode(n,this.editNode));
  183. },this);
  184. b=tmp;
  185. }
  186. }else{//w3c range
  187. var r=dijit.range.create();
  188. r.setStart(dijit.range.getNode(b.startContainer,this.editNode),b.startOffset);
  189. r.setEnd(dijit.range.getNode(b.endContainer,this.editNode),b.endOffset);
  190. b=r;
  191. }
  192. dojo.withGlobal(this.window,'moveToBookmark',dijit,[b]);
  193. },
  194. undo: function(){
  195. // console.log('undo');
  196. this.endEditing(true);
  197. var s=this._steps.pop();
  198. if(this._steps.length>0){
  199. this.focus();
  200. this._changeToStep(s,this._steps[this._steps.length-1]);
  201. this._undoedSteps.push(s);
  202. this.onDisplayChanged();
  203. return true;
  204. }
  205. return false;
  206. },
  207. redo: function(){
  208. // console.log('redo');
  209. this.endEditing(true);
  210. var s=this._undoedSteps.pop();
  211. if(s && this._steps.length>0){
  212. this.focus();
  213. this._changeToStep(this._steps[this._steps.length-1],s);
  214. this._steps.push(s);
  215. this.onDisplayChanged();
  216. return true;
  217. }
  218. return false;
  219. },
  220. endEditing: function(ignore_caret){
  221. if(this._editTimer){
  222. clearTimeout(this._editTimer);
  223. }
  224. if(this._inEditing){
  225. this._endEditing(ignore_caret);
  226. this._inEditing=false;
  227. }
  228. },
  229. _getBookmark: function(){
  230. var b=dojo.withGlobal(this.window,dijit.getBookmark);
  231. if(dojo.isIE){
  232. if(dojo.isArray(b)){//CONTROL
  233. var tmp=[];
  234. dojo.forEach(b,function(n){
  235. tmp.push(dijit.range.getIndex(n,this.editNode).o);
  236. },this);
  237. b=tmp;
  238. }
  239. }else{//w3c range
  240. var tmp=dijit.range.getIndex(b.startContainer,this.editNode).o
  241. b={startContainer:tmp,
  242. startOffset:b.startOffset,
  243. endContainer:b.endContainer===b.startContainer?tmp:dijit.range.getIndex(b.endContainer,this.editNode).o,
  244. endOffset:b.endOffset};
  245. }
  246. return b;
  247. },
  248. _beginEditing: function(cmd){
  249. if(this._steps.length===0){
  250. this._steps.push({'text':this.savedContent,'bookmark':this._getBookmark()});
  251. }
  252. },
  253. _endEditing: function(ignore_caret){
  254. var v=this.getValue(true);
  255. this._undoedSteps=[];//clear undoed steps
  256. this._steps.push({'text':v,'bookmark':this._getBookmark()});
  257. },
  258. onKeyDown: function(e){
  259. if(!this.customUndo){
  260. this.inherited('onKeyDown',arguments);
  261. return;
  262. }
  263. var k=e.keyCode,ks=dojo.keys;
  264. if(e.ctrlKey){
  265. if(k===90||k===122){ //z
  266. dojo.stopEvent(e);
  267. this.undo();
  268. return;
  269. }else if(k===89||k===121){ //y
  270. dojo.stopEvent(e);
  271. this.redo();
  272. return;
  273. }
  274. }
  275. this.inherited('onKeyDown',arguments);
  276. switch(k){
  277. case ks.ENTER:
  278. this.beginEditing();
  279. break;
  280. case ks.BACKSPACE:
  281. case ks.DELETE:
  282. this.beginEditing();
  283. break;
  284. case 88: //x
  285. case 86: //v
  286. if(e.ctrlKey && !e.altKey && !e.metaKey){
  287. this.endEditing();//end current typing step if any
  288. if(e.keyCode == 88){
  289. this.beginEditing('cut');
  290. //use timeout to trigger after the cut is complete
  291. setTimeout(dojo.hitch(this, this.endEditing), 1);
  292. }else{
  293. this.beginEditing('paste');
  294. //use timeout to trigger after the paste is complete
  295. setTimeout(dojo.hitch(this, this.endEditing), 1);
  296. }
  297. break;
  298. }
  299. //pass through
  300. default:
  301. if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
  302. this.beginEditing();
  303. break;
  304. }
  305. //pass through
  306. case ks.ALT:
  307. this.endEditing();
  308. break;
  309. case ks.UP_ARROW:
  310. case ks.DOWN_ARROW:
  311. case ks.LEFT_ARROW:
  312. case ks.RIGHT_ARROW:
  313. case ks.HOME:
  314. case ks.END:
  315. case ks.PAGE_UP:
  316. case ks.PAGE_DOWN:
  317. this.endEditing(true);
  318. break;
  319. //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
  320. case ks.CTRL:
  321. case ks.SHIFT:
  322. case ks.TAB:
  323. break;
  324. }
  325. },
  326. _onBlur: function(){
  327. this.inherited('_onBlur',arguments);
  328. this.endEditing(true);
  329. },
  330. onClick: function(){
  331. this.endEditing(true);
  332. this.inherited('onClick',arguments);
  333. }
  334. /* end of custom undo/redo support */
  335. }
  336. );
  337. /* the following code is to registered a handler to get default plugins */
  338. dojo.subscribe("dijit.Editor.getPlugin",null,function(o){
  339. if(o.plugin){ return; }
  340. var args=o.args, p;
  341. var _p = dijit._editor._Plugin;
  342. var name=args.name;
  343. switch(name){
  344. case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
  345. case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
  346. case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
  347. case "selectAll": case "removeFormat":
  348. p = new _p({ command: name });
  349. break;
  350. case "bold": case "italic": case "underline": case "strikethrough":
  351. case "subscript": case "superscript":
  352. p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
  353. break;
  354. case "|":
  355. p = new _p({ button: new dijit.ToolbarSeparator() });
  356. break;
  357. case "createLink":
  358. // dojo['require']('dijit._editor.plugins.LinkDialog');
  359. p = new dijit._editor.plugins.LinkDialog({ command: name });
  360. break;
  361. case "foreColor": case "hiliteColor":
  362. p = new dijit._editor.plugins.TextColor({ command: name });
  363. break;
  364. case "fontName": case "fontSize": case "formatBlock":
  365. p = new dijit._editor.plugins.FontChoice({ command: name });
  366. }
  367. // console.log('name',name,p);
  368. o.plugin=p;
  369. });
  370. }
  371. }});