serialize.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. ///import core
  2. ///commands 定制过滤规则
  3. ///commandsName Serialize
  4. ///commandsTitle 定制过滤规则
  5. UE.plugins['serialize'] = function () {
  6. var ie = browser.ie,
  7. version = browser.version;
  8. function ptToPx(value){
  9. return /pt/.test(value) ? value.replace( /([\d.]+)pt/g, function( str ) {
  10. return Math.round(parseFloat(str) * 96 / 72) + "px";
  11. } ) : value;
  12. }
  13. var me = this, autoClearEmptyNode = me.options.autoClearEmptyNode,
  14. EMPTY_TAG = dtd.$empty,
  15. parseHTML = function () {
  16. //干掉<a> 后便变得空格,保留</a> 这样的空格
  17. var RE_PART = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  18. RE_ATTR = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,
  19. EMPTY_ATTR = {checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1},
  20. CDATA_TAG = {script:1,style: 1},
  21. NEED_PARENT_TAG = {
  22. "li": { "$": 'ul', "ul": 1, "ol": 1 },
  23. "dd": { "$": "dl", "dl": 1 },
  24. "dt": { "$": "dl", "dl": 1 },
  25. "option": { "$": "select", "select": 1 },
  26. "td": { "$": "tr", "tr": 1 },
  27. "th": { "$": "tr", "tr": 1 },
  28. "tr": { "$": "tbody", "tbody": 1, "thead": 1, "tfoot": 1, "table": 1 },
  29. "tbody": { "$": "table", 'table':1,"colgroup": 1 },
  30. "thead": { "$": "table", "table": 1 },
  31. "tfoot": { "$": "table", "table": 1 },
  32. "col": { "$": "colgroup","colgroup":1 }
  33. };
  34. var NEED_CHILD_TAG = {
  35. "table": "td", "tbody": "td", "thead": "td", "tfoot": "td", "tr": "td",
  36. "colgroup": "col",
  37. "ul": "li", "ol": "li",
  38. "dl": "dd",
  39. "select": "option"
  40. };
  41. function parse( html, callbacks ) {
  42. var match,
  43. nextIndex = 0,
  44. tagName,
  45. cdata;
  46. RE_PART.exec( "" );
  47. while ( (match = RE_PART.exec( html )) ) {
  48. var tagIndex = match.index;
  49. if ( tagIndex > nextIndex ) {
  50. var text = html.slice( nextIndex, tagIndex );
  51. if ( cdata ) {
  52. cdata.push( text );
  53. } else {
  54. callbacks.onText( text );
  55. }
  56. }
  57. nextIndex = RE_PART.lastIndex;
  58. if ( (tagName = match[1]) ) {
  59. tagName = tagName.toLowerCase();
  60. if ( cdata && tagName == cdata._tag_name ) {
  61. callbacks.onCDATA( cdata.join( '' ) );
  62. cdata = null;
  63. }
  64. if ( !cdata ) {
  65. callbacks.onTagClose( tagName );
  66. continue;
  67. }
  68. }
  69. if ( cdata ) {
  70. cdata.push( match[0] );
  71. continue;
  72. }
  73. if ( (tagName = match[3]) ) {
  74. if ( /="/.test( tagName ) ) {
  75. continue;
  76. }
  77. tagName = tagName.toLowerCase();
  78. var attrPart = match[4],
  79. attrMatch,
  80. attrMap = {},
  81. selfClosing = attrPart && attrPart.slice( -1 ) == '/';
  82. if ( attrPart ) {
  83. RE_ATTR.exec( "" );
  84. while ( (attrMatch = RE_ATTR.exec( attrPart )) ) {
  85. var attrName = attrMatch[1].toLowerCase(),
  86. attrValue = attrMatch[2] || attrMatch[3] || attrMatch[4] || '';
  87. if ( !attrValue && EMPTY_ATTR[attrName] ) {
  88. attrValue = attrName;
  89. }
  90. if ( attrName == 'style' ) {
  91. if ( ie && version <= 6 ) {
  92. attrValue = attrValue.replace( /(?!;)\s*([\w-]+):/g, function ( m, p1 ) {
  93. return p1.toLowerCase() + ':';
  94. } );
  95. }
  96. }
  97. //没有值的属性不添加
  98. if ( attrValue ) {
  99. attrMap[attrName] = attrValue.replace( /:\s*/g, ':' )
  100. }
  101. }
  102. }
  103. callbacks.onTagOpen( tagName, attrMap, selfClosing );
  104. if ( !cdata && CDATA_TAG[tagName] ) {
  105. cdata = [];
  106. cdata._tag_name = tagName;
  107. }
  108. continue;
  109. }
  110. if ( (tagName = match[2]) ) {
  111. callbacks.onComment( tagName );
  112. }
  113. }
  114. if ( html.length > nextIndex ) {
  115. callbacks.onText( html.slice( nextIndex, html.length ) );
  116. }
  117. }
  118. return function ( html, forceDtd ) {
  119. var fragment = {
  120. type: 'fragment',
  121. parent: null,
  122. children: []
  123. };
  124. var currentNode = fragment;
  125. function addChild( node ) {
  126. node.parent = currentNode;
  127. currentNode.children.push( node );
  128. }
  129. function addElement( element, open ) {
  130. var node = element;
  131. // 遇到结构化标签的时候
  132. if ( NEED_PARENT_TAG[node.tag] ) {
  133. // 考虑这种情况的时候, 结束之前的标签
  134. // e.g. <table><tr><td>12312`<tr>`4566
  135. while ( NEED_PARENT_TAG[currentNode.tag] && NEED_PARENT_TAG[currentNode.tag][node.tag] ) {
  136. currentNode = currentNode.parent;
  137. }
  138. // 如果前一个标签和这个标签是同一级, 结束之前的标签
  139. // e.g. <ul><li>123<li>
  140. if ( currentNode.tag == node.tag ) {
  141. currentNode = currentNode.parent;
  142. }
  143. // 向上补齐父标签
  144. while ( NEED_PARENT_TAG[node.tag] ) {
  145. if ( NEED_PARENT_TAG[node.tag][currentNode.tag] ) break;
  146. node = node.parent = {
  147. type: 'element',
  148. tag: NEED_PARENT_TAG[node.tag]['$'],
  149. attributes: {},
  150. children: [node]
  151. };
  152. }
  153. }
  154. if ( forceDtd ) {
  155. // 如果遇到这个标签不能放在前一个标签内部,则结束前一个标签,span单独处理
  156. while ( dtd[node.tag] && !(currentNode.tag == 'span' ? utils.extend( dtd['strong'], {'a':1,'A':1} ) : (dtd[currentNode.tag] || dtd['div']))[node.tag] ) {
  157. if ( tagEnd( currentNode ) ) continue;
  158. if ( !currentNode.parent ) break;
  159. currentNode = currentNode.parent;
  160. }
  161. }
  162. node.parent = currentNode;
  163. currentNode.children.push( node );
  164. if ( open ) {
  165. currentNode = element;
  166. }
  167. if ( element.attributes.style ) {
  168. element.attributes.style = element.attributes.style.toLowerCase();
  169. }
  170. return element;
  171. }
  172. // 结束一个标签的时候,需要判断一下它是否缺少子标签
  173. // e.g. <table></table>
  174. function tagEnd( node ) {
  175. var needTag;
  176. if ( !node.children.length && (needTag = NEED_CHILD_TAG[node.tag]) ) {
  177. addElement( {
  178. type: 'element',
  179. tag: needTag,
  180. attributes: {},
  181. children: []
  182. }, true );
  183. return true;
  184. }
  185. return false;
  186. }
  187. parse( html, {
  188. onText: function ( text ) {
  189. while ( !(dtd[currentNode.tag] || dtd['div'])['#'] ) {
  190. //节点之间的空白不能当作节点处理
  191. // if(/^[ \t\r\n]+$/.test( text )){
  192. // return;
  193. // }
  194. if ( tagEnd( currentNode ) ) continue;
  195. currentNode = currentNode.parent;
  196. }
  197. //if(/^[ \t\n\r]*/.test(text))
  198. addChild( {
  199. type: 'text',
  200. data: text
  201. } );
  202. },
  203. onComment: function ( text ) {
  204. addChild( {
  205. type: 'comment',
  206. data: text
  207. } );
  208. },
  209. onCDATA: function ( text ) {
  210. while ( !(dtd[currentNode.tag] || dtd['div'])['#'] ) {
  211. if ( tagEnd( currentNode ) ) continue;
  212. currentNode = currentNode.parent;
  213. }
  214. addChild( {
  215. type: 'cdata',
  216. data: text
  217. } );
  218. },
  219. onTagOpen: function ( tag, attrs, closed ) {
  220. closed = closed || EMPTY_TAG[tag] ;
  221. addElement( {
  222. type: 'element',
  223. tag: tag,
  224. attributes: attrs,
  225. closed: closed,
  226. children: []
  227. }, !closed );
  228. },
  229. onTagClose: function ( tag ) {
  230. var node = currentNode;
  231. // 向上找匹配的标签, 这里不考虑dtd的情况是因为tagOpen的时候已经处理过了, 这里不会遇到
  232. while ( node && tag != node.tag ) {
  233. node = node.parent;
  234. }
  235. if ( node ) {
  236. // 关闭中间的标签
  237. for ( var tnode = currentNode; tnode !== node.parent; tnode = tnode.parent ) {
  238. tagEnd( tnode );
  239. }
  240. //去掉空白的inline节点
  241. //分页,锚点保留
  242. //|| dtd.$removeEmptyBlock[node.tag])
  243. // if ( !node.children.length && dtd.$removeEmpty[node.tag] && !node.attributes.anchorname && node.attributes['class'] != 'pagebreak' && node.tag != 'a') {
  244. //
  245. // node.parent.children.pop();
  246. // }
  247. currentNode = node.parent;
  248. } else {
  249. // 如果没有找到开始标签, 则创建新标签
  250. // eg. </div> => <div></div>
  251. //针对视屏网站embed会给结束符,这里特殊处理一下
  252. if ( !(dtd.$removeEmpty[tag] || dtd.$removeEmptyBlock[tag] || tag == 'embed') ) {
  253. node = {
  254. type: 'element',
  255. tag: tag,
  256. attributes: {},
  257. children: []
  258. };
  259. addElement( node, true );
  260. tagEnd( node );
  261. currentNode = node.parent;
  262. }
  263. }
  264. }
  265. } );
  266. // 处理这种情况, 只有开始标签没有结束标签的情况, 需要关闭开始标签
  267. // eg. <table>
  268. while ( currentNode !== fragment ) {
  269. tagEnd( currentNode );
  270. currentNode = currentNode.parent;
  271. }
  272. return fragment;
  273. };
  274. }();
  275. var unhtml1 = function () {
  276. var map = { '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
  277. function rep( m ) {
  278. return map[m];
  279. }
  280. return function ( str ) {
  281. str = str + '';
  282. return str ? str.replace( /[<>"']/g, rep ) : '';
  283. };
  284. }();
  285. var toHTML = function () {
  286. function printChildren( node, pasteplain ) {
  287. var children = node.children;
  288. var buff = [];
  289. for ( var i = 0,ci; ci = children[i]; i++ ) {
  290. buff.push( toHTML( ci, pasteplain ) );
  291. }
  292. return buff.join( '' );
  293. }
  294. function printAttrs( attrs ) {
  295. var buff = [];
  296. for ( var k in attrs ) {
  297. var value = attrs[k];
  298. if(k == 'style'){
  299. //pt==>px
  300. value = ptToPx(value);
  301. //color rgb ==> hex
  302. if(/rgba?\s*\([^)]*\)/.test(value)){
  303. value = value.replace( /rgba?\s*\(([^)]*)\)/g, function( str ) {
  304. return utils.fixColor('color',str);
  305. } )
  306. }
  307. //过滤掉所有的white-space,在纯文本编辑器里粘贴过来的内容,到chrome中会带有span和white-space属性,导致出现不能折行的情况
  308. //所以在这里去掉这个属性
  309. attrs[k] = utils.optCss(value.replace(/windowtext/g,'#000'))
  310. .replace(/white-space[^;]+;/g,'');
  311. }
  312. buff.push( k + '="' + unhtml1( attrs[k] ) + '"' );
  313. }
  314. return buff.join( ' ' )
  315. }
  316. function printData( node, notTrans ) {
  317. //trace:1399 输入html代码时空格转换成为&nbsp;
  318. //node.data.replace(/&nbsp;/g,' ') 针对pre中的空格和出现的&nbsp;把他们在得到的html代码中都转换成为空格,为了在源码模式下显示为空格而不是&nbsp;
  319. return notTrans ? node.data.replace(/&nbsp;/g,' ') : unhtml1( node.data ).replace(/ /g,'&nbsp;');
  320. }
  321. //纯文本模式下标签转换
  322. var transHtml = {
  323. 'div':'p',
  324. 'li':'p',
  325. 'tr':'p',
  326. 'br':'br',
  327. 'p':'p'//trace:1398 碰到p标签自己要加上p,否则transHtml[tag]是undefined
  328. };
  329. function printElement( node, pasteplain ) {
  330. if ( node.type == 'element' && !node.children.length && (dtd.$removeEmpty[node.tag]) && node.tag != 'a' && utils.isEmptyObject(node.attributes) && autoClearEmptyNode) {// 锚点保留
  331. return html;
  332. }
  333. var tag = node.tag;
  334. if ( pasteplain && tag == 'td' ) {
  335. if ( !html ) html = '';
  336. html += printChildren( node, pasteplain ) + '&nbsp;&nbsp;&nbsp;';
  337. } else {
  338. var attrs = printAttrs( node.attributes );
  339. var html = '<' + (pasteplain && transHtml[tag] ? transHtml[tag] : tag) + (attrs ? ' ' + attrs : '') + (EMPTY_TAG[tag] ? ' />' : '>');
  340. if ( !EMPTY_TAG[tag] ) {
  341. //trace:1627 ,2070
  342. //p标签为空,将不占位这里占位符不起作用,用&nbsp;或者br
  343. if( tag == 'p' && !node.children.length){
  344. html += browser.ie ? '&nbsp;' : '<br/>';
  345. }
  346. html += printChildren( node, pasteplain );
  347. html += '</' + (pasteplain && transHtml[tag] ? transHtml[tag] : tag) + '>';
  348. }
  349. }
  350. return html;
  351. }
  352. return function ( node, pasteplain ) {
  353. if ( node.type == 'fragment' ) {
  354. return printChildren( node, pasteplain );
  355. } else if ( node.type == 'element' ) {
  356. return printElement( node, pasteplain );
  357. } else if ( node.type == 'text' || node.type == 'cdata' ) {
  358. return printData( node, dtd.$notTransContent[node.parent.tag] );
  359. } else if ( node.type == 'comment' ) {
  360. return '<!--' + node.data + '-->';
  361. }
  362. return '';
  363. };
  364. }();
  365. //过滤word
  366. var transformWordHtml = function () {
  367. function isWordDocument( strValue ) {
  368. var re = new RegExp( /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<v:)/ig );
  369. return re.test( strValue );
  370. }
  371. function ensureUnits( v ) {
  372. v = v.replace( /([\d.]+)([\w]+)?/g, function ( m, p1, p2 ) {
  373. return (Math.round( parseFloat( p1 ) ) || 1) + (p2 || 'px');
  374. } );
  375. return v;
  376. }
  377. function filterPasteWord( str ) {
  378. str = str.replace( /<!--\s*EndFragment\s*-->[\s\S]*$/, '' )
  379. //remove link break
  380. .replace( /^(\r\n|\n|\r)|(\r\n|\n|\r)$/ig, "" )
  381. //remove &nbsp; entities at the start of contents
  382. .replace( /^\s*(&nbsp;)+/ig, "" )
  383. //remove &nbsp; entities at the end of contents
  384. .replace( /(&nbsp;|<br[^>]*>)+\s*$/ig, "" )
  385. // Word comments like conditional comments etc
  386. .replace( /<!--[\s\S]*?-->/ig, "" )
  387. //转换图片
  388. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
  389. try{
  390. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  391. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  392. src = str.match(/src=\s*"([^"]*)"/i)[1];
  393. return '<img width="'+ptToPx(width)+'" height="'+ptToPx(height)+'" src="' + src + '" />'
  394. } catch(e){
  395. return '';
  396. }
  397. })
  398. //去掉多余的属性
  399. .replace( /v:\w+=["']?[^'"]+["']?/g, '' )
  400. // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, MS Office namespaced tags, and a few other tags
  401. .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
  402. //convert word headers to strong
  403. .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
  404. //remove lang attribute
  405. .replace( /(lang)\s*=\s*([\'\"]?)[\w-]+\2/ig, "" )
  406. //清除多余的font不能匹配&nbsp;有可能是空格
  407. .replace( /<font[^>]*>\s*<\/font>/gi, '' )
  408. //清除多余的class
  409. .replace( /class\s*=\s*["']?(?:(?:MsoTableGrid)|(?:MsoListParagraph)|(?:MsoNormal(Table)?))\s*["']?/gi, '' );
  410. // Examine all styles: delete junk, transform some, and keep the rest
  411. //修复了原有的问题, 比如style='fontsize:"宋体"'原来的匹配失效了
  412. str = str.replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
  413. var n = [],
  414. i = 0,
  415. s = style.replace( /^\s+|\s+$/, '' ).replace( /&quot;/gi, "'" ).split( /;\s*/g );
  416. // Examine each style definition within the tag's style attribute
  417. for ( var i = 0; i < s.length; i++ ) {
  418. var v = s[i];
  419. var name, value,
  420. parts = v.split( ":" );
  421. if ( parts.length == 2 ) {
  422. name = parts[0].toLowerCase();
  423. value = parts[1].toLowerCase();
  424. // Translate certain MS Office styles into their CSS equivalents
  425. switch ( name ) {
  426. case "mso-padding-alt":
  427. case "mso-padding-top-alt":
  428. case "mso-padding-right-alt":
  429. case "mso-padding-bottom-alt":
  430. case "mso-padding-left-alt":
  431. case "mso-margin-alt":
  432. case "mso-margin-top-alt":
  433. case "mso-margin-right-alt":
  434. case "mso-margin-bottom-alt":
  435. case "mso-margin-left-alt":
  436. //ie下会出现挤到一起的情况
  437. // case "mso-table-layout-alt":
  438. case "mso-height":
  439. case "mso-width":
  440. case "mso-vertical-align-alt":
  441. //trace:1819 ff下会解析出padding在table上
  442. if(!/<table/.test(tag))
  443. n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + ensureUnits( value );
  444. continue;
  445. case "horiz-align":
  446. n[i] = "text-align:" + value;
  447. continue;
  448. case "vert-align":
  449. n[i] = "vertical-align:" + value;
  450. continue;
  451. case "font-color":
  452. case "mso-foreground":
  453. n[i] = "color:" + value;
  454. continue;
  455. case "mso-background":
  456. case "mso-highlight":
  457. n[i] = "background:" + value;
  458. continue;
  459. case "mso-default-height":
  460. n[i] = "min-height:" + ensureUnits( value );
  461. continue;
  462. case "mso-default-width":
  463. n[i] = "min-width:" + ensureUnits( value );
  464. continue;
  465. case "mso-padding-between-alt":
  466. n[i] = "border-collapse:separate;border-spacing:" + ensureUnits( value );
  467. continue;
  468. case "text-line-through":
  469. if ( (value == "single") || (value == "double") ) {
  470. n[i] = "text-decoration:line-through";
  471. }
  472. continue;
  473. //trace:1870
  474. // //word里边的字体统一干掉
  475. // case 'font-family':
  476. // continue;
  477. case "mso-zero-height":
  478. if ( value == "yes" ) {
  479. n[i] = "display:none";
  480. }
  481. continue;
  482. case 'margin':
  483. if ( !/[1-9]/.test( parts[1] ) ) {
  484. continue;
  485. }
  486. }
  487. if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name ) ) {
  488. // if ( !/mso\-list/.test( name ) )
  489. continue;
  490. }
  491. if(/text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)){
  492. continue;
  493. }
  494. n[i] = name + ":" + parts[1]; // Lower-case name, but keep value case
  495. }
  496. }
  497. // If style attribute contained any valid styles the re-write it; otherwise delete style attribute.
  498. if ( i > 0 ) {
  499. return tag + ' style="' + n.join( ';' ) + '"';
  500. } else {
  501. return tag;
  502. }
  503. } );
  504. str = str.replace( /([ ]+)<\/span>/ig, function ( m, p ) {
  505. return new Array( p.length + 1 ).join( '&nbsp;' ) + '</span>';
  506. } );
  507. return str;
  508. }
  509. return function ( html ) {
  510. //过了word,才能转p->li
  511. first = null;
  512. parentTag = '',liStyle = '',firstTag = '';
  513. if ( isWordDocument( html ) ) {
  514. html = filterPasteWord( html );
  515. }
  516. return html.replace( />[ \t\r\n]*</g, '><' );
  517. };
  518. }();
  519. var NODE_NAME_MAP = {
  520. 'text': '#text',
  521. 'comment': '#comment',
  522. 'cdata': '#cdata-section',
  523. 'fragment': '#document-fragment'
  524. };
  525. // function _likeLi( node ) {
  526. // var a;
  527. // if ( node && node.tag == 'p' ) {
  528. // //office 2011下有效
  529. // if ( node.attributes['class'] == 'MsoListParagraph' || /mso-list/.test( node.attributes.style ) ) {
  530. // a = 1;
  531. // } else {
  532. // var firstChild = node.children[0];
  533. // if ( firstChild && firstChild.tag == 'span' && /Wingdings/i.test( firstChild.attributes.style ) ) {
  534. // a = 1;
  535. // }
  536. // }
  537. // }
  538. // return a;
  539. // }
  540. //为p==>li 做个标志
  541. var first,
  542. // orderStyle = {
  543. // 'decimal' : /\d+/,
  544. // 'lower-roman': /^m{0,4}(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$/,
  545. // 'upper-roman': /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/,
  546. // 'lower-alpha' : /^\(?[a-z]+\)?$/,
  547. // 'upper-alpha': /^\(?[A-Z]+\)?$/
  548. // },
  549. // unorderStyle = { 'disc' : /^[l\u00B7\u2002]/, 'circle' : /^[\u006F\u00D8]/,'square' : /^[\u006E\u25C6]/},
  550. parentTag = '',liStyle = '',firstTag;
  551. //写入编辑器时,调用,进行转换操作
  552. function transNode( node, word_img_flag ) {
  553. var sizeMap = [0, 10, 12, 16, 18, 24, 32, 48],
  554. attr,
  555. indexOf = utils.indexOf;
  556. switch ( node.tag ) {
  557. case 'script':
  558. node.tag = 'div';
  559. node.attributes._ue_div_script = 1;
  560. node.attributes._ue_script_data = node.children[0] ? encodeURIComponent(node.children[0].data) : '';
  561. node.children = [];
  562. break;
  563. case 'img':
  564. //todo base64暂时去掉,后边做远程图片上传后,干掉这个
  565. if(node.attributes.src && /^data:/.test(node.attributes.src)){
  566. return {
  567. type : 'fragment',
  568. children:[]
  569. }
  570. }
  571. if ( node.attributes.src && /^(?:file)/.test( node.attributes.src ) ) {
  572. if ( !/(gif|bmp|png|jpg|jpeg)$/.test( node.attributes.src ) ) {
  573. return {
  574. type : 'fragment',
  575. children:[]
  576. }
  577. }
  578. node.attributes.word_img = node.attributes.src;
  579. node.attributes.src = me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif';
  580. var flag = parseInt(node.attributes.width)<128||parseInt(node.attributes.height)<43;
  581. node.attributes.style="background:url(" + me.options.UEDITOR_HOME_URL +"themes/default/images/"+(flag?"word.gif":"localimage.png")+") no-repeat center center;border:1px solid #ddd";
  582. //node.attributes.style = 'width:395px;height:173px;';
  583. word_img_flag && (word_img_flag.flag = 1);
  584. }
  585. if(browser.ie && browser.version < 7 )
  586. node.attributes.orgSrc = node.attributes.src;
  587. node.attributes.data_ue_src = node.attributes.data_ue_src || node.attributes.src;
  588. break;
  589. case 'li':
  590. var child = node.children[0];
  591. if ( !child || child.type != 'element' || child.tag != 'p' && dtd.p[child.tag] ) {
  592. var tmpPNode = {
  593. type: 'element',
  594. tag: 'p',
  595. attributes: {},
  596. parent : node
  597. };
  598. tmpPNode.children = child ? node.children :[
  599. browser.ie ? {
  600. type:'text',
  601. data:domUtils.fillChar,
  602. parent : tmpPNode
  603. }:
  604. {
  605. type : 'element',
  606. tag : 'br',
  607. attributes:{},
  608. closed: true,
  609. children: [],
  610. parent : tmpPNode
  611. }
  612. ];
  613. node.children = [tmpPNode];
  614. }
  615. break;
  616. case 'table':
  617. case 'td':
  618. optStyle( node );
  619. break;
  620. case 'a'://锚点,a==>img
  621. if ( node.attributes['anchorname'] ) {
  622. node.tag = 'img';
  623. node.attributes = {
  624. 'class' : 'anchorclass',
  625. 'anchorname':node.attributes['name']
  626. };
  627. node.closed = 1;
  628. }
  629. node.attributes.href && (node.attributes.data_ue_src = node.attributes.href);
  630. break;
  631. case 'b':
  632. node.tag = node.name = 'strong';
  633. break;
  634. case 'i':
  635. node.tag = node.name = 'em';
  636. break;
  637. case 'u':
  638. node.tag = node.name = 'span';
  639. node.attributes.style = (node.attributes.style || '') + ';text-decoration:underline;';
  640. break;
  641. case 's':
  642. case 'del':
  643. node.tag = node.name = 'span';
  644. node.attributes.style = (node.attributes.style || '') + ';text-decoration:line-through;';
  645. if ( node.children.length == 1 ) {
  646. child = node.children[0];
  647. if ( child.tag == node.tag ) {
  648. node.attributes.style += ";" + child.attributes.style;
  649. node.children = child.children;
  650. }
  651. }
  652. break;
  653. case 'span':
  654. // if ( /mso-list/.test( node.attributes.style ) ) {
  655. //
  656. //
  657. // //判断了两次就不在判断了
  658. // if ( firstTag != 'end' ) {
  659. //
  660. // var ci = node.children[0],p;
  661. // while ( ci.type == 'element' ) {
  662. // ci = ci.children[0];
  663. // }
  664. // for ( p in unorderStyle ) {
  665. // if ( unorderStyle[p].test( ci.data ) ) {
  666. //
  667. // // ci.data = ci.data.replace(unorderStyle[p],'');
  668. // parentTag = 'ul';
  669. // liStyle = p;
  670. // break;
  671. // }
  672. // }
  673. //
  674. //
  675. // if ( !parentTag ) {
  676. // for ( p in orderStyle ) {
  677. // if ( orderStyle[p].test( ci.data.replace( /\.$/, '' ) ) ) {
  678. // // ci.data = ci.data.replace(orderStyle[p],'');
  679. // parentTag = 'ol';
  680. // liStyle = p;
  681. // break;
  682. // }
  683. // }
  684. // }
  685. // if ( firstTag ) {
  686. // if ( ci.data == firstTag ) {
  687. // if ( parentTag != 'ul' ) {
  688. // liStyle = '';
  689. // }
  690. // parentTag = 'ul'
  691. // } else {
  692. // if ( parentTag != 'ol' ) {
  693. // liStyle = '';
  694. // }
  695. // parentTag = 'ol'
  696. // }
  697. // firstTag = 'end'
  698. // } else {
  699. // firstTag = ci.data
  700. // }
  701. // if ( parentTag ) {
  702. // var tmpNode = node;
  703. // while ( tmpNode && tmpNode.tag != 'ul' && tmpNode.tag != 'ol' ) {
  704. // tmpNode = tmpNode.parent;
  705. // }
  706. // if(tmpNode ){
  707. // tmpNode.tag = parentTag;
  708. // tmpNode.attributes.style = 'list-style-type:' + liStyle;
  709. // }
  710. //
  711. //
  712. //
  713. // }
  714. //
  715. // }
  716. //
  717. // node = {
  718. // type : 'fragment',
  719. // children : []
  720. // };
  721. // break;
  722. //
  723. //
  724. // }
  725. var style = node.attributes.style;
  726. if ( style ) {
  727. if ( !node.attributes.style || browser.webkit && style == "white-space:nowrap;") {
  728. delete node.attributes.style;
  729. }
  730. }
  731. //针对ff3.6span的样式不能正确继承的修复
  732. if(browser.gecko && browser.version <= 10902 && node.parent){
  733. var parent = node.parent;
  734. if(parent.tag == 'span' && parent.attributes && parent.attributes.style){
  735. node.attributes.style = parent.attributes.style + ';' + node.attributes.style;
  736. }
  737. }
  738. if ( utils.isEmptyObject( node.attributes ) && autoClearEmptyNode) {
  739. node.type = 'fragment'
  740. }
  741. break;
  742. case 'font':
  743. node.tag = node.name = 'span';
  744. attr = node.attributes;
  745. node.attributes = {
  746. 'style': (attr.size ? 'font-size:' + (sizeMap[attr.size] || 12) + 'px' : '')
  747. + ';' + (attr.color ? 'color:'+ attr.color : '')
  748. + ';' + (attr.face ? 'font-family:'+ attr.face : '')
  749. + ';' + (attr.style||'')
  750. };
  751. while(node.parent.tag == node.tag && node.parent.children.length == 1){
  752. node.attributes.style && (node.parent.attributes.style ? (node.parent.attributes.style += ";" + node.attributes.style) : (node.parent.attributes.style = node.attributes.style));
  753. node.parent.children = node.children;
  754. node = node.parent;
  755. }
  756. break;
  757. case 'p':
  758. if ( node.attributes.align ) {
  759. node.attributes.style = (node.attributes.style || '') + ';text-align:' +
  760. node.attributes.align + ';';
  761. delete node.attributes.align;
  762. }
  763. // if ( _likeLi( node ) ) {
  764. //
  765. // if ( !first ) {
  766. //
  767. // var ulNode = {
  768. // type: 'element',
  769. // tag: 'ul',
  770. // attributes: {},
  771. // children: []
  772. // },
  773. // index = indexOf( node.parent.children, node );
  774. // node.parent.children[index] = ulNode;
  775. // ulNode.parent = node.parent;
  776. // ulNode.children[0] = node;
  777. // node.parent = ulNode;
  778. //
  779. // while ( 1 ) {
  780. // node = ulNode.parent.children[index + 1];
  781. // if ( _likeLi( node ) ) {
  782. // ulNode.children[ulNode.children.length] = node;
  783. // node.parent = ulNode;
  784. // ulNode.parent.children.splice( index + 1, 1 );
  785. //
  786. // } else {
  787. // break;
  788. // }
  789. // }
  790. //
  791. // return ulNode;
  792. // }
  793. // node.tag = node.name = 'li';
  794. // //为chrome能找到标号做的处理
  795. // if ( browser.webkit ) {
  796. // var span = node.children[0];
  797. //
  798. // while ( span && span.type == 'element' ) {
  799. // span = span.children[0]
  800. // }
  801. // span && (span.parent.attributes.style = (span.parent.attributes.style || '') + ';mso-list:10');
  802. // }
  803. //
  804. //
  805. // delete node.attributes['class'];
  806. // delete node.attributes.style;
  807. //
  808. //
  809. // }
  810. }
  811. return node;
  812. }
  813. function optStyle( node ) {
  814. if ( ie && node.attributes.style ) {
  815. var style = node.attributes.style;
  816. node.attributes.style = style.replace(/;\s*/g,';');
  817. node.attributes.style = node.attributes.style.replace( /^\s*|\s*$/, '' )
  818. }
  819. }
  820. //getContent调用转换
  821. function transOutNode( node ) {
  822. switch ( node.tag ) {
  823. case 'div' :
  824. if(node.attributes._ue_div_script){
  825. node.tag = 'script';
  826. node.children = [{type:'cdata',data:decodeURIComponent(node.attributes._ue_script_data)||'',parent:node}];
  827. delete node.attributes._ue_div_script;
  828. delete node.attributes._ue_script_data;
  829. break;
  830. }
  831. case 'table':
  832. !node.attributes.style && delete node.attributes.style;
  833. if ( ie && node.attributes.style ) {
  834. optStyle( node );
  835. }
  836. if(node.attributes['class'] == 'noBorderTable'){
  837. delete node.attributes['class'];
  838. }
  839. break;
  840. case 'td':
  841. case 'th':
  842. if ( /display\s*:\s*none/i.test( node.attributes.style ) ) {
  843. return {
  844. type: 'fragment',
  845. children: []
  846. };
  847. }
  848. if ( ie && !node.children.length ) {
  849. var txtNode = {
  850. type: 'text',
  851. data:domUtils.fillChar,
  852. parent : node
  853. };
  854. node.children[0] = txtNode;
  855. }
  856. if ( ie && node.attributes.style ) {
  857. optStyle( node );
  858. }
  859. if(node.attributes['class'] == 'selectTdClass'){
  860. delete node.attributes['class']
  861. }
  862. break;
  863. case 'img'://锚点,img==>a
  864. if ( node.attributes.anchorname ) {
  865. node.tag = 'a';
  866. node.attributes = {
  867. name : node.attributes.anchorname,
  868. anchorname : 1
  869. };
  870. node.closed = null;
  871. }else{
  872. if(node.attributes.data_ue_src){
  873. node.attributes.src = node.attributes.data_ue_src;
  874. delete node.attributes.data_ue_src;
  875. }
  876. }
  877. break;
  878. case 'a':
  879. if(node.attributes.data_ue_src){
  880. node.attributes.href = node.attributes.data_ue_src;
  881. delete node.attributes.data_ue_src;
  882. }
  883. }
  884. return node;
  885. }
  886. function childrenAccept( node, visit, ctx ) {
  887. if ( !node.children || !node.children.length ) {
  888. return node;
  889. }
  890. var children = node.children;
  891. for ( var i = 0; i < children.length; i++ ) {
  892. var newNode = visit( children[i], ctx );
  893. if ( newNode.type == 'fragment' ) {
  894. var args = [i, 1];
  895. args.push.apply( args, newNode.children );
  896. children.splice.apply( children, args );
  897. //节点为空的就干掉,不然后边的补全操作会添加多余的节点
  898. if ( !children.length ) {
  899. node = {
  900. type: 'fragment',
  901. children: []
  902. }
  903. }
  904. i --;
  905. } else {
  906. children[i] = newNode;
  907. }
  908. }
  909. return node;
  910. }
  911. function Serialize( rules ) {
  912. this.rules = rules;
  913. }
  914. Serialize.prototype = {
  915. // NOTE: selector目前只支持tagName
  916. rules: null,
  917. // NOTE: node必须是fragment
  918. filter: function ( node, rules, modify ) {
  919. rules = rules || this.rules;
  920. var whiteList = rules && rules.whiteList;
  921. var blackList = rules && rules.blackList;
  922. function visitNode( node, parent ) {
  923. node.name = node.type == 'element' ?
  924. node.tag : NODE_NAME_MAP[node.type];
  925. if ( parent == null ) {
  926. return childrenAccept( node, visitNode, node );
  927. }
  928. if ( blackList && blackList[node.name] ) {
  929. modify && (modify.flag = 1);
  930. return {
  931. type: 'fragment',
  932. children: []
  933. };
  934. }
  935. if ( whiteList ) {
  936. if ( node.type == 'element' ) {
  937. if ( parent.type == 'fragment' ? whiteList[node.name] : whiteList[node.name] && whiteList[parent.name][node.name] ) {
  938. var props;
  939. if ( (props = whiteList[node.name].$) ) {
  940. var oldAttrs = node.attributes;
  941. var newAttrs = {};
  942. for ( var k in props ) {
  943. if ( oldAttrs[k] ) {
  944. newAttrs[k] = oldAttrs[k];
  945. }
  946. }
  947. node.attributes = newAttrs;
  948. }
  949. } else {
  950. modify && (modify.flag = 1);
  951. node.type = 'fragment';
  952. // NOTE: 这里算是一个hack
  953. node.name = parent.name;
  954. }
  955. } else {
  956. // NOTE: 文本默认允许
  957. }
  958. }
  959. if ( blackList || whiteList ) {
  960. childrenAccept( node, visitNode, node );
  961. }
  962. return node;
  963. }
  964. return visitNode( node, null );
  965. },
  966. transformInput: function ( node, word_img_flag ) {
  967. function visitNode( node ) {
  968. node = transNode( node, word_img_flag );
  969. // if ( node.tag == 'ol' || node.tag == 'ul' ) {
  970. // first = 1;
  971. // }
  972. node = childrenAccept( node, visitNode, node );
  973. // if ( node.tag == 'ol' || node.tag == 'ul' ) {
  974. // first = 0;
  975. // parentTag = '',liStyle = '',firstTag = '';
  976. // }
  977. if ( me.options.pageBreakTag && node.type == 'text' && node.data.replace( /\s/g, '' ) == me.options.pageBreakTag ) {
  978. node.type = 'element';
  979. node.name = node.tag = 'hr';
  980. delete node.data;
  981. node.attributes = {
  982. 'class' : 'pagebreak',
  983. noshade:"noshade",
  984. size:"5",
  985. 'unselectable' : 'on',
  986. 'style' : 'moz-user-select:none;-khtml-user-select: none;'
  987. };
  988. node.children = [];
  989. }
  990. //去掉多余的空格和换行
  991. if(node.type == 'text' && !dtd.$notTransContent[node.parent.tag]){
  992. node.data = node.data.replace(/[\r\t\n]*/g,'')//.replace(/[ ]*$/g,'')
  993. }
  994. return node;
  995. }
  996. return visitNode( node );
  997. },
  998. transformOutput: function ( node ) {
  999. function visitNode( node ) {
  1000. if ( node.tag == 'hr' && node.attributes['class'] == 'pagebreak' ) {
  1001. delete node.tag;
  1002. node.type = 'text';
  1003. node.data = me.options.pageBreakTag;
  1004. delete node.children;
  1005. }
  1006. node = transOutNode( node );
  1007. if ( node.tag == 'ol' || node.tag == 'ul' ) {
  1008. first = 1;
  1009. }
  1010. node = childrenAccept( node, visitNode, node );
  1011. if ( node.tag == 'ol' || node.tag == 'ul' ) {
  1012. first = 0;
  1013. }
  1014. return node;
  1015. }
  1016. return visitNode( node );
  1017. },
  1018. toHTML: toHTML,
  1019. parseHTML: parseHTML,
  1020. word: transformWordHtml
  1021. };
  1022. me.serialize = new Serialize( me.options.serialize || {});
  1023. UE.serialize = new Serialize( {} );
  1024. };