78355d806148f5ec02dbc79c5b93756453d89d5b.svn-base 12 KB

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