PasteFromWord.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.editor.plugins.PasteFromWord"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.editor.plugins.PasteFromWord"] = true;
  8. dojo.provide("dojox.editor.plugins.PasteFromWord");
  9. dojo.require("dojo.string");
  10. dojo.require("dijit._editor._Plugin");
  11. dojo.require("dijit.form.Button");
  12. dojo.require("dijit.Dialog");
  13. dojo.require("dojo.i18n");
  14. dojo.require("dojox.html.format");
  15. dojo.requireLocalization("dojox.editor.plugins", "PasteFromWord", null, "ROOT,ar,bg,ca,cs,da,de,el,es,fi,fr,he,hr,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
  16. dojo.declare("dojox.editor.plugins.PasteFromWord",dijit._editor._Plugin,{
  17. // summary:
  18. // This plugin provides PasteFromWord cabability to the editor. When
  19. // clicked, a dialog opens with a spartan RichText instance to paste
  20. // word content into via the keyboard commands. The contents are
  21. // then filtered to remove word style classes and other meta-junk
  22. // that tends to cause issues.
  23. // iconClassPrefix: [const] String
  24. // The CSS class name for the button node is formed from `iconClassPrefix`
  25. // and `command`
  26. iconClassPrefix: "dijitAdditionalEditorIcon",
  27. // width: [public] String
  28. // The width to use for the rich text area in the copy/pate dialog, in px. Default is 400px.
  29. width: "400px",
  30. // height: [public] String
  31. // The height to use for the rich text area in the copy/pate dialog, in px. Default is 300px.
  32. height: "300px",
  33. _template: ["<div class='dijitPasteFromWordEmbeddedRTE'>",
  34. "<div style='width: ${width}; padding-top: 5px; padding-bottom: 5px;'>${instructions}</div>",
  35. "<div id='${uId}_rte' style='width: ${width}; height: ${height}'></div>",
  36. "<table style='width: ${width}' tabindex='-1'>",
  37. "<tbody>",
  38. "<tr>",
  39. "<td align='center'>",
  40. "<button type='button' dojoType='dijit.form.Button' id='${uId}_paste'>${paste}</button>",
  41. "&nbsp;",
  42. "<button type='button' dojoType='dijit.form.Button' id='${uId}_cancel'>${cancel}</button>",
  43. "</td>",
  44. "</tr>",
  45. "</tbody>",
  46. "</table>",
  47. "</div>"].join(""),
  48. // _filters: [private] Array
  49. // The filters is an array of regular expressions to try and strip out a lot
  50. // of style data MS Word likes to insert when pasting into a contentEditable.
  51. // Prettymuch all of it is junk and not good html. The hander is a place to put a function
  52. // for match handling. In most cases, it just handles it as empty string. But the option is
  53. // there for more complex handling.
  54. _filters: [
  55. // Meta tags, link tags, and prefixed tags
  56. {regexp: /(<meta\s*[^>]*\s*>)|(<\s*link\s* href="file:[^>]*\s*>)|(<\/?\s*\w+:[^>]*\s*>)/gi, handler: ""},
  57. // Style tags
  58. {regexp: /(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi, handler: ""},
  59. // MS class tags and comment tags.
  60. {regexp: /(class="Mso[^"]*")|(<!--(.|\s){1,}?-->)/gi, handler: ""},
  61. // blank p tags
  62. {regexp: /(<p[^>]*>\s*(\&nbsp;|\u00A0)*\s*<\/p[^>]*>)|(<p[^>]*>\s*<font[^>]*>\s*(\&nbsp;|\u00A0)*\s*<\/\s*font\s*>\s<\/p[^>]*>)/ig, handler: ""},
  63. // Strip out styles containing mso defs and margins, as likely added in IE and are not good to have as it mangles presentation.
  64. {regexp: /(style="[^"]*mso-[^;][^"]*")|(style="margin:\s*[^;"]*;")/gi, handler: ""},
  65. // Scripts (if any)
  66. {regexp: /(<\s*script[^>]*>((.|\s)*?)<\\?\/\s*script\s*>)|(<\s*script\b([^<>]|\s)*>?)|(<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>)/ig, handler: ""}
  67. ],
  68. _initButton: function(){
  69. // summary:
  70. // Over-ride for creation of the save button.
  71. var strings = dojo.i18n.getLocalization("dojox.editor.plugins", "PasteFromWord");
  72. this.button = new dijit.form.Button({
  73. label: strings["pasteFromWord"],
  74. showLabel: false,
  75. iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "PasteFromWord",
  76. tabIndex: "-1",
  77. onClick: dojo.hitch(this, "_openDialog")
  78. });
  79. this._uId = dijit.getUniqueId(this.editor.id);
  80. strings.uId = this._uId;
  81. strings.width = this.width || "400px";
  82. strings.height = this.height || "300px";
  83. this._dialog = new dijit.Dialog({title: strings["pasteFromWord"]}).placeAt(dojo.body());
  84. this._dialog.set("content", dojo.string.substitute(this._template, strings));
  85. // Make it translucent so we can fade in the window when the RTE is created.
  86. // the RTE has to be created 'visible, and this is a ncie trick to make the creation
  87. // 'pretty'.
  88. dojo.style(dojo.byId(this._uId + "_rte"), "opacity", 0.001);
  89. // Link up the action buttons to perform the insert or cleanup.
  90. this.connect(dijit.byId(this._uId + "_paste"), "onClick", "_paste");
  91. this.connect(dijit.byId(this._uId + "_cancel"), "onClick", "_cancel");
  92. this.connect(this._dialog, "onHide", "_clearDialog");
  93. },
  94. updateState: function(){
  95. // summary:
  96. // Over-ride for button state control for disabled to work.
  97. this.button.set("disabled", this.get("disabled"));
  98. },
  99. setEditor: function(editor){
  100. // summary:
  101. // Over-ride for the setting of the editor.
  102. // editor: Object
  103. // The editor to configure for this plugin to use.
  104. this.editor = editor;
  105. this._initButton();
  106. },
  107. _openDialog: function(){
  108. // summary:
  109. // Function to trigger opening the copy dialog.
  110. // tags:
  111. // private
  112. this._dialog.show();
  113. if(!this._rte){
  114. // RTE hasn't been created yet, so we need to create it now that the
  115. // dialog is showing up.
  116. setTimeout(dojo.hitch(this, function() {
  117. this._rte = new dijit._editor.RichText({height: this.height || "300px"}, this._uId + "_rte");
  118. this._rte.onLoadDeferred.addCallback(dojo.hitch(this, function() {
  119. dojo.animateProperty({
  120. node: this._rte.domNode, properties: { opacity: { start: 0.001, end: 1.0 } }
  121. }).play();
  122. }));
  123. }), 100);
  124. }
  125. },
  126. _paste: function(){
  127. // summary:
  128. // Function to handle setting the contents of the copy from dialog
  129. // into the editor.
  130. // tags:
  131. // private
  132. // Gather the content and try to format it a bit (makes regexp cleanup simpler).
  133. // It also normalizes tag names and styles, so regexps are the same across browsers.
  134. var content = dojox.html.format.prettyPrint(this._rte.get("value"));
  135. //Close up the dialog and clear old content.
  136. this._dialog.hide();
  137. // Apply all the filters to remove MS specific injected text.
  138. var i;
  139. for(i = 0; i < this._filters.length; i++){
  140. var filter = this._filters[i];
  141. content = content.replace(filter.regexp, filter.handler);
  142. }
  143. // Format it again to make sure it is reasonably formatted as
  144. // the regexp applies will have likely chewed up the formatting.
  145. content = dojox.html.format.prettyPrint(content);
  146. // Paste it in.
  147. this.editor.execCommand("inserthtml", content);
  148. },
  149. _cancel: function(){
  150. // summary:
  151. // Function to handle cancelling setting the contents of the
  152. // copy from dialog into the editor.
  153. // tags:
  154. // private
  155. this._dialog.hide();
  156. },
  157. _clearDialog: function(){
  158. // summary:
  159. // simple function to cleat the contents when hide is calledon dialog
  160. // copy from dialog into the editor.
  161. // tags:
  162. // private
  163. this._rte.set("value", "");
  164. },
  165. destroy: function(){
  166. // sunnary:
  167. // Cleanup function
  168. // tags:
  169. // public
  170. if(this._rte){
  171. this._rte.destroy();
  172. }
  173. if(this._dialog){
  174. this._dialog.destroyRecursive();
  175. }
  176. delete this._dialog;
  177. delete this._rte;
  178. this.inherited(arguments);
  179. }
  180. });
  181. // Register this plugin.
  182. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  183. if(o.plugin){ return; }
  184. var name = o.args.name.toLowerCase();
  185. if(name === "pastefromword"){
  186. o.plugin = new dojox.editor.plugins.PasteFromWord({
  187. width: ("width" in o.args)?o.args.width:"400px",
  188. height: ("height" in o.args)?o.args.width:"300px"
  189. });
  190. }
  191. });
  192. }