PasteFromWord.js 7.5 KB

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