source.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /**
  2. * 源码编辑插件
  3. * @file
  4. * @since 1.2.6.1
  5. */
  6. (function() {
  7. var sourceEditors = {
  8. textarea: function(editor, holder) {
  9. var textarea = holder.ownerDocument.createElement("textarea");
  10. textarea.style.cssText =
  11. "position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;";
  12. // todo: IE下只有onresize属性可用... 很纠结
  13. if (browser.ie && browser.version < 8) {
  14. textarea.style.width = holder.offsetWidth + "px";
  15. textarea.style.height = holder.offsetHeight + "px";
  16. holder.onresize = function() {
  17. textarea.style.width = holder.offsetWidth + "px";
  18. textarea.style.height = holder.offsetHeight + "px";
  19. };
  20. }
  21. holder.appendChild(textarea);
  22. return {
  23. setContent: function(content) {
  24. textarea.value = content;
  25. },
  26. getContent: function() {
  27. return textarea.value;
  28. },
  29. select: function() {
  30. var range;
  31. if (browser.ie) {
  32. range = textarea.createTextRange();
  33. range.collapse(true);
  34. range.select();
  35. } else {
  36. //todo: chrome下无法设置焦点
  37. textarea.setSelectionRange(0, 0);
  38. textarea.focus();
  39. }
  40. },
  41. dispose: function() {
  42. holder.removeChild(textarea);
  43. // todo
  44. holder.onresize = null;
  45. textarea = null;
  46. holder = null;
  47. },
  48. focus: function (){
  49. textarea.focus();
  50. },
  51. blur: function (){
  52. textarea.blur();
  53. }
  54. };
  55. },
  56. codemirror: function(editor, holder) {
  57. var codeEditor = window.CodeMirror(holder, {
  58. mode: "text/html",
  59. tabMode: "indent",
  60. lineNumbers: true,
  61. lineWrapping: true
  62. });
  63. var dom = codeEditor.getWrapperElement();
  64. dom.style.cssText =
  65. 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
  66. codeEditor.getScrollerElement().style.cssText =
  67. "position:absolute;left:0;top:0;width:100%;height:100%;";
  68. codeEditor.refresh();
  69. return {
  70. getCodeMirror: function() {
  71. return codeEditor;
  72. },
  73. setContent: function(content) {
  74. codeEditor.setValue(content);
  75. },
  76. getContent: function() {
  77. return codeEditor.getValue();
  78. },
  79. select: function() {
  80. codeEditor.focus();
  81. },
  82. dispose: function() {
  83. holder.removeChild(dom);
  84. dom = null;
  85. codeEditor = null;
  86. },
  87. focus: function (){
  88. codeEditor.focus();
  89. },
  90. blur: function (){
  91. // codeEditor.blur();
  92. // since codemirror not support blur()
  93. codeEditor.setOption('readOnly', true);
  94. codeEditor.setOption('readOnly', false);
  95. }
  96. };
  97. }
  98. };
  99. UE.plugins["source"] = function() {
  100. var me = this;
  101. var opt = this.options;
  102. var sourceMode = false;
  103. var sourceEditor;
  104. var orgSetContent;
  105. var orgFocus;
  106. var orgBlur;
  107. opt.sourceEditor = browser.ie
  108. ? "textarea"
  109. : opt.sourceEditor || "codemirror";
  110. me.setOpt({
  111. sourceEditorFirst: false
  112. });
  113. function createSourceEditor(holder) {
  114. return sourceEditors[
  115. opt.sourceEditor == "codemirror" && window.CodeMirror
  116. ? "codemirror"
  117. : "textarea"
  118. ](me, holder);
  119. }
  120. var bakCssText;
  121. //解决在源码模式下getContent不能得到最新的内容问题
  122. var oldGetContent, bakAddress;
  123. /**
  124. * 切换源码模式和编辑模式
  125. * @command source
  126. * @method execCommand
  127. * @param { String } cmd 命令字符串
  128. * @example
  129. * ```javascript
  130. * editor.execCommand( 'source');
  131. * ```
  132. */
  133. /**
  134. * 查询当前编辑区域的状态是源码模式还是可视化模式
  135. * @command source
  136. * @method queryCommandState
  137. * @param { String } cmd 命令字符串
  138. * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
  139. * @example
  140. * ```javascript
  141. * editor.queryCommandState( 'source' );
  142. * ```
  143. */
  144. me.commands["source"] = {
  145. execCommand: function() {
  146. sourceMode = !sourceMode;
  147. if (sourceMode) {
  148. bakAddress = me.selection.getRange().createAddress(false, true);
  149. me.undoManger && me.undoManger.save(true);
  150. if (browser.gecko) {
  151. me.body.contentEditable = false;
  152. }
  153. bakCssText = me.iframe.style.cssText;
  154. me.iframe.style.cssText +=
  155. "position:absolute;left:-32768px;top:-32768px;";
  156. me.fireEvent("beforegetcontent");
  157. var root = UE.htmlparser(me.body.innerHTML);
  158. me.filterOutputRule(root);
  159. root.traversal(function(node) {
  160. if (node.type == "element") {
  161. switch (node.tagName) {
  162. case "td":
  163. case "th":
  164. case "caption":
  165. if (node.children && node.children.length == 1) {
  166. if (node.firstChild().tagName == "br") {
  167. node.removeChild(node.firstChild());
  168. }
  169. }
  170. break;
  171. case "pre":
  172. node.innerText(node.innerText().replace(/&nbsp;/g, " "));
  173. }
  174. }
  175. });
  176. me.fireEvent("aftergetcontent");
  177. var content = root.toHtml(true);
  178. sourceEditor = createSourceEditor(me.iframe.parentNode);
  179. sourceEditor.setContent(content);
  180. orgSetContent = me.setContent;
  181. me.setContent = function(html) {
  182. //这里暂时不触发事件,防止报错
  183. var root = UE.htmlparser(html);
  184. me.filterInputRule(root);
  185. html = root.toHtml();
  186. sourceEditor.setContent(html);
  187. };
  188. setTimeout(function() {
  189. sourceEditor.select();
  190. me.addListener("fullscreenchanged", function() {
  191. try {
  192. sourceEditor.getCodeMirror().refresh();
  193. } catch (e) {}
  194. });
  195. });
  196. //重置getContent,源码模式下取值也能是最新的数据
  197. oldGetContent = me.getContent;
  198. me.getContent = function() {
  199. return (
  200. sourceEditor.getContent() ||
  201. "<p>" + (browser.ie ? "" : "<br/>") + "</p>"
  202. );
  203. };
  204. orgFocus = me.focus;
  205. orgBlur = me.blur;
  206. me.focus = function(){
  207. sourceEditor.focus();
  208. };
  209. me.blur = function(){
  210. orgBlur.call(me);
  211. sourceEditor.blur();
  212. };
  213. } else {
  214. me.iframe.style.cssText = bakCssText;
  215. var cont =
  216. sourceEditor.getContent() ||
  217. "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
  218. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  219. cont = cont.replace(
  220. new RegExp("[\\r\\t\\n ]*</?(\\w+)\\s*(?:[^>]*)>", "g"),
  221. function(a, b) {
  222. if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
  223. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, "");
  224. }
  225. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, "");
  226. }
  227. );
  228. me.setContent = orgSetContent;
  229. me.setContent(cont);
  230. sourceEditor.dispose();
  231. sourceEditor = null;
  232. //还原getContent方法
  233. me.getContent = oldGetContent;
  234. me.focus = orgFocus;
  235. me.blur = orgBlur;
  236. var first = me.body.firstChild;
  237. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  238. if (!first) {
  239. me.body.innerHTML = "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
  240. first = me.body.firstChild;
  241. }
  242. //要在ifm为显示时ff才能取到selection,否则报错
  243. //这里不能比较位置了
  244. me.undoManger && me.undoManger.save(true);
  245. if (browser.gecko) {
  246. var input = document.createElement("input");
  247. input.style.cssText = "position:absolute;left:0;top:-32768px";
  248. document.body.appendChild(input);
  249. me.body.contentEditable = false;
  250. setTimeout(function() {
  251. domUtils.setViewportOffset(input, { left: -32768, top: 0 });
  252. input.focus();
  253. setTimeout(function() {
  254. me.body.contentEditable = true;
  255. me.selection.getRange().moveToAddress(bakAddress).select(true);
  256. domUtils.remove(input);
  257. });
  258. });
  259. } else {
  260. //ie下有可能报错,比如在代码顶头的情况
  261. try {
  262. me.selection.getRange().moveToAddress(bakAddress).select(true);
  263. } catch (e) {}
  264. }
  265. }
  266. this.fireEvent("sourcemodechanged", sourceMode);
  267. },
  268. queryCommandState: function() {
  269. return sourceMode | 0;
  270. },
  271. notNeedUndo: 1
  272. };
  273. var oldQueryCommandState = me.queryCommandState;
  274. me.queryCommandState = function(cmdName) {
  275. cmdName = cmdName.toLowerCase();
  276. if (sourceMode) {
  277. //源码模式下可以开启的命令
  278. return cmdName in
  279. {
  280. source: 1,
  281. fullscreen: 1
  282. }
  283. ? 1
  284. : -1;
  285. }
  286. return oldQueryCommandState.apply(this, arguments);
  287. };
  288. if (opt.sourceEditor == "codemirror") {
  289. me.addListener("ready", function() {
  290. utils.loadFile(
  291. document,
  292. {
  293. src:
  294. opt.codeMirrorJsUrl ||
  295. opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
  296. tag: "script",
  297. type: "text/javascript",
  298. defer: "defer"
  299. },
  300. function() {
  301. if (opt.sourceEditorFirst) {
  302. setTimeout(function() {
  303. me.execCommand("source");
  304. }, 0);
  305. }
  306. }
  307. );
  308. utils.loadFile(document, {
  309. tag: "link",
  310. rel: "stylesheet",
  311. type: "text/css",
  312. href:
  313. opt.codeMirrorCssUrl ||
  314. opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
  315. });
  316. });
  317. }
  318. };
  319. })();