source.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. ///import core
  2. ///import plugins/serialize.js
  3. ///import plugins/undo.js
  4. ///commands 查看源码
  5. ///commandsName Source
  6. ///commandsTitle 查看源码
  7. (function (){
  8. function SourceFormater(config){
  9. config = config || {};
  10. this.indentChar = config.indentChar || ' ';
  11. this.breakChar = config.breakChar || '\n';
  12. this.selfClosingEnd = config.selfClosingEnd || ' />';
  13. }
  14. var unhtml1 = function (){
  15. var map = { '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
  16. function rep( m ){ return map[m]; }
  17. return function ( str ) {
  18. str = str + '';
  19. return str ? str.replace( /[<>"']/g, rep ) : '';
  20. };
  21. }();
  22. var inline = utils.extend({a:1,A:1},dtd.$inline,true);
  23. function printAttrs(attrs){
  24. var buff = [];
  25. for (var k in attrs) {
  26. buff.push(k + '="' + unhtml1(attrs[k]) + '"');
  27. }
  28. return buff.join(' ');
  29. }
  30. SourceFormater.prototype = {
  31. format: function (html){
  32. var node = UE.serialize.parseHTML(html);
  33. this.buff = [];
  34. this.indents = '';
  35. this.indenting = 1;
  36. this.visitNode(node);
  37. return this.buff.join('');
  38. },
  39. visitNode: function (node){
  40. if (node.type == 'fragment') {
  41. this.visitChildren(node.children);
  42. } else if (node.type == 'element') {
  43. var selfClosing = dtd.$empty[node.tag];
  44. this.visitTag(node.tag, node.attributes, selfClosing);
  45. this.visitChildren(node.children);
  46. if (!selfClosing) {
  47. this.visitEndTag(node.tag);
  48. }
  49. } else if (node.type == 'comment') {
  50. this.visitComment(node.data);
  51. } else {
  52. this.visitText(node.data,dtd.$notTransContent[node.parent.tag]);
  53. }
  54. },
  55. visitChildren: function (children){
  56. for (var i=0; i<children.length; i++) {
  57. this.visitNode(children[i]);
  58. }
  59. },
  60. visitTag: function (tag, attrs, selfClosing){
  61. if (this.indenting) {
  62. this.indent();
  63. } else if (!inline[tag]) { // todo: 去掉a, 因为dtd的inline里面没有a
  64. this.newline();
  65. this.indent();
  66. }
  67. this.buff.push('<', tag);
  68. var attrPart = printAttrs(attrs);
  69. if (attrPart) {
  70. this.buff.push(' ', attrPart);
  71. }
  72. if (selfClosing) {
  73. this.buff.push(this.selfClosingEnd);
  74. if (tag == 'br') {
  75. this.newline();
  76. }
  77. } else {
  78. this.buff.push('>');
  79. this.indents += this.indentChar;
  80. }
  81. if (!inline[tag]) {
  82. this.newline();
  83. }
  84. },
  85. indent: function (){
  86. this.buff.push(this.indents);
  87. this.indenting = 0;
  88. },
  89. newline: function (){
  90. this.buff.push(this.breakChar);
  91. this.indenting = 1;
  92. },
  93. visitEndTag: function (tag){
  94. this.indents = this.indents.slice(0, -this.indentChar.length);
  95. if (this.indenting) {
  96. this.indent();
  97. } else if (!inline[tag]) {
  98. this.newline();
  99. this.indent();
  100. }
  101. this.buff.push('</', tag, '>');
  102. },
  103. visitText: function (text,notTrans){
  104. if (this.indenting) {
  105. this.indent();
  106. }
  107. // if(!notTrans){
  108. // text = text.replace(/&nbsp;/g, ' ').replace(/[ ][ ]+/g, function (m){
  109. // return new Array(m.length + 1).join('&nbsp;');
  110. // }).replace(/(?:^ )|(?: $)/g, '&nbsp;');
  111. // }
  112. text = text.replace(/&nbsp;/g, ' ')
  113. this.buff.push(text);
  114. },
  115. visitComment: function (text){
  116. if (this.indenting) {
  117. this.indent();
  118. }
  119. this.buff.push('<!--', text, '-->');
  120. }
  121. };
  122. var sourceEditors = {
  123. textarea: function (editor, holder){
  124. var textarea = holder.ownerDocument.createElement('textarea');
  125. textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;';
  126. // todo: IE下只有onresize属性可用... 很纠结
  127. if (browser.ie && browser.version < 8) {
  128. textarea.style.width = holder.offsetWidth + 'px';
  129. textarea.style.height = holder.offsetHeight + 'px';
  130. holder.onresize = function (){
  131. textarea.style.width = holder.offsetWidth + 'px';
  132. textarea.style.height = holder.offsetHeight + 'px';
  133. };
  134. }
  135. holder.appendChild(textarea);
  136. return {
  137. setContent: function (content){
  138. textarea.value = content;
  139. },
  140. getContent: function (){
  141. return textarea.value;
  142. },
  143. select: function (){
  144. var range;
  145. if (browser.ie) {
  146. range = textarea.createTextRange();
  147. range.collapse(true);
  148. range.select();
  149. } else {
  150. //todo: chrome下无法设置焦点
  151. textarea.setSelectionRange(0, 0);
  152. textarea.focus();
  153. }
  154. },
  155. dispose: function (){
  156. holder.removeChild(textarea);
  157. // todo
  158. holder.onresize = null;
  159. textarea = null;
  160. holder = null;
  161. }
  162. };
  163. },
  164. codemirror: function (editor, holder){
  165. var options = {
  166. mode: "text/html",
  167. tabMode: "indent",
  168. lineNumbers: true,
  169. lineWrapping:true
  170. };
  171. var codeEditor = window.CodeMirror(holder, options);
  172. var dom = codeEditor.getWrapperElement();
  173. dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
  174. codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;';
  175. codeEditor.refresh();
  176. return {
  177. setContent: function (content){
  178. codeEditor.setValue(content);
  179. },
  180. getContent: function (){
  181. return codeEditor.getValue();
  182. },
  183. select: function (){
  184. codeEditor.focus();
  185. },
  186. dispose: function (){
  187. holder.removeChild(dom);
  188. dom = null;
  189. codeEditor = null;
  190. }
  191. };
  192. }
  193. };
  194. UE.plugins['source'] = function (){
  195. var me = this;
  196. var opt = this.options;
  197. var formatter = new SourceFormater(opt.source);
  198. var sourceMode = false;
  199. var sourceEditor;
  200. opt.sourceEditor = opt.sourceEditor || 'codemirror';
  201. function createSourceEditor(holder){
  202. return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder);
  203. }
  204. var bakCssText;
  205. me.commands['source'] = {
  206. execCommand: function (){
  207. sourceMode = !sourceMode;
  208. if (sourceMode) {
  209. me.undoManger && me.undoManger.save();
  210. this.currentSelectedArr && domUtils.clearSelectedArr(this.currentSelectedArr);
  211. if(browser.gecko)
  212. me.body.contentEditable = false;
  213. bakCssText = me.iframe.style.cssText;
  214. me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;';
  215. var content = formatter.format(me.hasContents() ? me.getContent() : '');
  216. sourceEditor = createSourceEditor(me.iframe.parentNode);
  217. sourceEditor.setContent(content);
  218. setTimeout(function (){
  219. sourceEditor.select();
  220. });
  221. } else {
  222. me.iframe.style.cssText = bakCssText;
  223. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  224. cont = cont.replace(/>[\n\r\t]+([ ]{4})+/g,'>').replace(/[\n\r\t]+([ ]{4})+</g,'<').replace(/>[\n\r\t]+</g,'><');
  225. me.setContent(cont);
  226. sourceEditor.dispose();
  227. sourceEditor = null;
  228. setTimeout(function(){
  229. var first = me.body.firstChild;
  230. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  231. if(!first){
  232. me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
  233. first = me.body.firstChild;
  234. }
  235. //要在ifm为显示时ff才能取到selection,否则报错
  236. me.undoManger && me.undoManger.save();
  237. while(first && first.firstChild){
  238. first = first.firstChild;
  239. }
  240. var range = me.selection.getRange();
  241. if(first.nodeType == 3 || dtd.$empty[first.tagName]){
  242. range.setStartBefore(first)
  243. }else{
  244. range.setStart(first,0);
  245. }
  246. if(browser.gecko){
  247. var input = document.createElement('input');
  248. input.style.cssText = 'position:absolute;left:0;top:-32768px';
  249. document.body.appendChild(input);
  250. me.body.contentEditable = false;
  251. setTimeout(function(){
  252. domUtils.setViewportOffset(input, { left: -32768, top: 0 });
  253. input.focus();
  254. setTimeout(function(){
  255. me.body.contentEditable = true;
  256. range.setCursor(false,true);
  257. domUtils.remove(input)
  258. })
  259. })
  260. }else{
  261. range.setCursor(false,true);
  262. }
  263. })
  264. }
  265. this.fireEvent('sourcemodechanged', sourceMode);
  266. },
  267. queryCommandState: function (){
  268. return sourceMode|0;
  269. }
  270. };
  271. var oldQueryCommandState = me.queryCommandState;
  272. me.queryCommandState = function (cmdName){
  273. cmdName = cmdName.toLowerCase();
  274. if (sourceMode) {
  275. return cmdName == 'source' ? 1 : -1;
  276. }
  277. return oldQueryCommandState.apply(this, arguments);
  278. };
  279. //解决在源码模式下getContent不能得到最新的内容问题
  280. var oldGetContent = me.getContent;
  281. me.getContent = function (){
  282. if(sourceMode && sourceEditor ){
  283. var html = sourceEditor.getContent();
  284. if (this.serialize) {
  285. var node = this.serialize.parseHTML(html);
  286. node = this.serialize.filter(node);
  287. html = this.serialize.toHTML(node);
  288. }
  289. return html;
  290. }else{
  291. return oldGetContent.apply(this, arguments)
  292. }
  293. };
  294. if(opt.sourceEditor == "codemirror"){
  295. me.addListener("ready",function(){
  296. utils.loadFile(document,{
  297. src : opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror2.15/codemirror.js",
  298. tag : "script",
  299. type : "text/javascript",
  300. defer : "defer"
  301. });
  302. utils.loadFile(document,{
  303. tag : "link",
  304. rel : "stylesheet",
  305. type : "text/css",
  306. href : opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror2.15/codemirror.css"
  307. });
  308. });
  309. }
  310. };
  311. })();