xheditor.js 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923
  1. (function($){
  2. if($.xheditor)return false;//防止JS重复加载
  3. $.fn.xheditor=function(options)
  4. {
  5. var arrSuccess=[];
  6. this.each(function(){
  7. if(!$.nodeName(this,'TEXTAREA'))return;
  8. if(options===false)//卸载
  9. {
  10. if(this.xheditor)
  11. {
  12. this.xheditor.remove();
  13. this.xheditor=null;
  14. }
  15. }
  16. else//初始化
  17. {
  18. if(!this.xheditor)
  19. {
  20. var tOptions=/({.*})/.exec($(this).attr('class'));
  21. if(tOptions)
  22. {
  23. try{tOptions=eval('('+tOptions[1]+')');}catch(ex){};
  24. options=$.extend({},tOptions,options );
  25. }
  26. var editor=new $.xheditor(this,options);
  27. if(editor.init())
  28. {
  29. this.xheditor=editor;
  30. arrSuccess.push(editor);
  31. }
  32. else editor=null;
  33. }
  34. else arrSuccess.push(this.xheditor);
  35. }
  36. });
  37. if(arrSuccess.length===0)arrSuccess=false;
  38. if(arrSuccess.length===1)arrSuccess=arrSuccess[0];
  39. return arrSuccess;
  40. }
  41. var xCount=0,browerVer=$.browser.version,isIE=$.browser.msie,isMozilla=$.browser.mozilla,isSafari=$.browser.safari,isOpera=$.browser.opera,bShowPanel=false,bClickCancel=true,bShowModal=false,bCheckEscInit=false;
  42. var _jPanel,_jShadow,_jCntLine,_jPanelButton;
  43. var jModal,jModalShadow,layerShadow,jOverlay,jHideSelect,onModalRemove;
  44. var editorRoot;
  45. $('script[src*=xheditor]').each(function(){
  46. var s=this.src;
  47. if(s.match(/xheditor[^\/]*\.js/i)){editorRoot=s.replace(/[\?#].*$/, '').replace(/(^|[\/\\])[^\/]*$/, '$1');return false;}
  48. });
  49. var specialKeys={ 27: 'esc', 9: 'tab', 32:'space', 13: 'enter', 8:'backspace', 145: 'scroll',
  50. 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',
  51. 35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down',
  52. 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8',
  53. 120:'f9', 121:'f10', 122:'f11', 123:'f12' };
  54. var itemColors=['#FFFFFF','#CCCCCC','#C0C0C0','#999999','#666666','#333333','#000000','#FFCCCC','#FF6666','#FF0000','#CC0000','#990000','#660000','#330000','#FFCC99','#FF9966','#FF9900','#FF6600','#CC6600','#993300','#663300','#FFFF99','#FFFF66','#FFCC66','#FFCC33','#CC9933','#996633','#663333','#FFFFCC','#FFFF33','#FFFF00','#FFCC00','#999900','#666600','#333300','#99FF99','#66FF99','#33FF33','#33CC00','#009900','#006600','#003300','#99FFFF','#33FFFF','#66CCCC','#00CCCC','#339999','#336666','#003333','#CCFFFF','#66FFFF','#33CCFF','#3366FF','#3333FF','#000099','#000066','#CCCCFF','#9999FF','#6666CC','#6633FF','#6600CC','#333399','#330099','#FFCCFF','#FF99FF','#CC66CC','#CC33CC','#993399','#663366','#330033'];
  55. var arrBlocktag=[{n:'p',t:'普通段落'},{n:'h1',t:'标题1'},{n:'h2',t:'标题2'},{n:'h3',t:'标题3'},{n:'h4',t:'标题4'},{n:'h5',t:'标题5'},{n:'h6',t:'标题6'},{n:'pre',t:'已编排格式'},{n:'address',t:'地址'}];
  56. var arrFontname=[{n:'宋体',c:'SimSun'},{n:'仿宋体',c:'FangSong_GB2312'},{n:'黑体',c:'SimHei'},{n:'楷体',c:'KaiTi_GB2312'},{n:'微软雅黑',c:'Microsoft YaHei'},{n:'Arial'},{n:'Arial Narrow'},{n:'Arial Black'},{n:'Comic Sans MS'},{n:'Courier New'},{n:'System'},{n:'Times New Roman'},{n:'Tahoma'},{n:'Verdana'}];
  57. var arrFontsize=[{n:'xx-small',wkn:'x-small',s:'8pt',t:'极小'},{n:'x-small',wkn:'small',s:'10pt',t:'特小'},{n:'small',wkn:'medium',s:'12pt',t:'小'},{n:'medium',wkn:'large',s:'14pt',t:'中'},{n:'large',wkn:'x-large',s:'18pt',t:'大'},{n:'x-large',wkn:'xx-large',s:'24pt',t:'特大'},{n:'xx-large',wkn:'-webkit-xxx-large',s:'36pt',t:'极大'}];
  58. var menuAlign=[{s:'左对齐',v:'justifyleft'},{s:'居中',v:'justifycenter'},{s:'右对齐',v:'justifyright'},{s:'两端对齐',v:'justifyfull'}],menuList=[{s:'数字列表',v:'insertOrderedList'},{s:'符号列表',v:'insertUnorderedList'}];
  59. var htmlPastetext='<div>使用键盘快捷键(Ctrl+V)把内容粘贴到方框里,按 确定</div><div><textarea id="xhePastetextValue" wrap="soft" spellcheck="false" style="width:300px;height:100px;" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  60. var htmlLink='<div>链接地址: <input type="text" id="xheLinkUrl" value="http://" class="xheText" /></div><div>打开方式: <select id="xheLinkTarget"><option selected="selected" value="">默认</option><option value="_blank">新窗口</option><option value="_self">当前窗口</option><option value="_parent">父窗口</option></select></div><div style="display:none">链接文字: <input type="text" id="xheLinkText" value="" class="xheText" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  61. var htmlImg='<div>图片文件: <input type="text" id="xheImgUrl" value="http://" class="xheText" /></div><div>替换文本: <input type="text" id="xheImgAlt" /></div><div>对齐方式: <select id="xheImgAlign"><option selected="selected" value="">默认</option><option value="left">左对齐</option><option value="right">右对齐</option><option value="top">顶端</option><option value="middle">居中</option><option value="baseline">基线</option><option value="bottom">底边</option></select></div><div>宽度高度: <input type="text" id="xheImgWidth" style="width:40px;" /> x <input type="text" id="xheImgHeight" style="width:40px;" /></div><div>边框大小: <input type="text" id="xheImgBorder" style="width:40px;" /></div><div>水平间距: <input type="text" id="xheImgHspace" style="width:40px;" /> 垂直间距: <input type="text" id="xheImgVspace" style="width:40px;" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  62. var htmlFlash='<div>动画文件: <input type="text" id="xheFlashUrl" value="http://" class="xheText" /></div><div>宽度高度: <input type="text" id="xheFlashWidth" style="width:40px;" value="480" /> x <input type="text" id="xheFlashHeight" style="width:40px;" value="400" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  63. var htmlMedia='<div>媒体文件: <input type="text" id="xheMediaUrl" value="http://" class="xheText" /></div><div>宽度高度: <input type="text" id="xheMediaWidth" style="width:40px;" value="480" /> x <input type="text" id="xheMediaHeight" style="width:40px;" value="400" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  64. var htmlTable='<div>行数列数: <input type="text" id="xheTableRows" style="width:40px;" value="3" /> x <input type="text" id="xheTableColumns" style="width:40px;" value="2" /></div><div>标题单元: <select id="xheTableHeaders"><option selected="selected" value="">无</option><option value="row">第一行</option><option value="col">第一列</option><option value="both">第一行和第一列</option></select></div><div>宽度高度: <input type="text" id="xheTableWidth" style="width:40px;" value="200" /> x <input type="text" id="xheTableHeight" style="width:40px;" value="" /></div><div>边框大小: <input type="text" id="xheTableBorder" style="width:40px;" value="1" /></div><div>表格间距: <input type="text" id="xheTableCellSpacing" style="width:40px;" value="1" /> 表格填充: <input type="text" id="xheTableCellPadding" style="width:40px;" value="1" /></div><div>对齐方式: <select id="xheTableAlign"><option selected="selected" value="">默认</option><option value="left">左对齐</option><option value="center">居中</option><option value="right">右对齐</option></select></div><div>表格标题: <input type="text" id="xheTableCaption" /></div><div style="text-align:right;"><input type="button" id="xheSave" value="确定" /></div>';
  65. var htmlAbout='<div style="font:12px Arial;width:245px;word-wrap:break-word;word-break:break-all;"><p><span style="font-size:20px;color:#1997DF;">xhEditor</span><br />v1.1.5 (build 110301)</p><p>xhEditor是基于jQuery开发的跨平台轻量XHTML编辑器,基于<a href="http://www.gnu.org/licenses/lgpl.html" target="_blank">LGPL</a>开源协议发布。</p><p>Copyright © <a href="http://xheditor.com/" target="_blank">xhEditor.com</a>. All rights reserved.</p></div>';
  66. var itemEmots={'default':{name:'默认',width:24,height:24,line:7,list:{'smile':'微笑','tongue':'吐舌头','titter':'偷笑','laugh':'大笑','sad':'难过','wronged':'委屈','fastcry':'快哭了','cry':'哭','wail':'大哭','mad':'生气','knock':'敲打','curse':'骂人','crazy':'抓狂','angry':'发火','ohmy':'惊讶','awkward':'尴尬','panic':'惊恐','shy':'害羞','cute':'可怜','envy':'羡慕','proud':'得意','struggle':'奋斗','quiet':'安静','shutup':'闭嘴','doubt':'疑问','despise':'鄙视','sleep':'睡觉','bye':'再见'}}};
  67. var arrTools={Cut:{t:'剪切 (Ctrl+X)'},Copy:{t:'复制 (Ctrl+C)'},Paste:{t:'粘贴 (Ctrl+V)'},Pastetext:{t:'粘贴文本',h:isIE?0:1},Blocktag:{t:'段落标签',h:1},Fontface:{t:'字体',h:1},FontSize:{t:'字体大小',h:1},Bold:{t:'加粗 (Ctrl+B)',s:'Ctrl+B'},Italic:{t:'斜体 (Ctrl+I)',s:'Ctrl+I'},Underline:{t:'下划线 (Ctrl+U)',s:'Ctrl+U'},Strikethrough:{t:'删除线'},FontColor:{t:'字体颜色',h:1},BackColor:{t:'背景颜色',h:1},SelectAll:{t:'全选 (Ctrl+A)'},Removeformat:{t:'删除文字格式'},Align:{t:'对齐',h:1},List:{t:'列表',h:1},Outdent:{t:'减少缩进 (Shift+Tab)',s:'Shift+Tab'},Indent:{t:'增加缩进 (Tab)',s:'Tab'},Link:{t:'超链接 (Ctrl+K)',s:'Ctrl+K',h:1},Unlink:{t:'取消超链接'},Img:{t:'图片',h:1},Flash:{t:'Flash动画',h:1},Media:{t:'多媒体文件',h:1},Emot:{t:'表情',s:'ctrl+e',h:1},Table:{t:'表格',h:1},Source:{t:'源代码'},Preview:{t:'预览'},Print:{t:'打印 (Ctrl+P)',s:'Ctrl+P'},Fullscreen:{t:'全屏编辑 (Esc)',s:'Esc'}};
  68. var toolsThemes={
  69. mini:'Bold,Italic,Underline,Strikethrough,|,Align,List,|,Link,Img',
  70. simple:'Blocktag,Fontface,FontSize,Bold,Italic,Underline,Strikethrough,FontColor,BackColor,|,Align,List,Outdent,Indent,|,Link,Img,Emot',
  71. full:'Cut,Copy,Paste,Pastetext,|,Blocktag,Fontface,FontSize,Bold,Italic,Underline,Strikethrough,FontColor,BackColor,SelectAll,Removeformat,|,Align,List,Outdent,Indent,|,Link,Unlink,Img,Flash,Media,Emot,Table,|,Source,Preview,Print,Fullscreen'};
  72. toolsThemes.mfull=toolsThemes.full.replace(/\|(,Align)/i,'/$1');
  73. var arrDbClick={'a':'Link','img':'Img','embed':'Embed'},uploadInputname='filedata';
  74. var arrEntities={'<':'&lt;','>':'&gt;','"':'&quot;','®':'&reg;','©':'&copy;'};//实体
  75. var regEntities=/[<>"®©]/g;
  76. $.xheditor=function(textarea,options)
  77. {
  78. var defaults={skin:'default',tools:'full',clickCancelDialog:true,linkTag:false,internalScript:false,inlineScript:false,internalStyle:true,inlineStyle:true,showBlocktag:false,forcePtag:true,upLinkExt:"zip,rar,txt",upImgExt:"jpg,jpeg,gif,png",upFlashExt:"swf",upMediaExt:"wmv,avi,wma,mp3,mid",modalWidth:350,modalHeight:220,modalTitle:true,defLinkText:'点击打开链接',layerShadow:3,emotMark:false,upBtnText:'上传',cleanPaste:2,hoverExecDelay:100,html5Upload:true,upMultiple:99};
  79. var _this=this,_text=textarea,_jText=$(_text),_jForm=_jText.closest('form'),_jTools,_jArea,_win,_jWin,_doc,_jDoc;
  80. var bookmark;
  81. var bInit=false,bSource=false,bFullscreen=false,bCleanPaste=false,outerScroll,bShowBlocktag=false,sLayoutStyle='',ev=null,timer,bDisableHoverExec=false,bQuickHoverExec=false;
  82. var lastPoint=null,lastAngle=null;//鼠标悬停显示
  83. var editorHeight=0;
  84. var settings=_this.settings=$.extend({},defaults,options );
  85. var plugins=settings.plugins,strPlugins=[];
  86. if(plugins)
  87. {
  88. arrTools=$.extend({},arrTools,plugins);
  89. $.each(plugins,function(n){strPlugins.push(n);});
  90. strPlugins=strPlugins.join(',');
  91. }
  92. if(settings.tools.match(/^\s*(m?full|simple|mini)\s*$/i))
  93. {
  94. var toolsTheme=toolsThemes[$.trim(settings.tools)];
  95. settings.tools=(settings.tools.match(/m?full/i)&&plugins)?toolsTheme.replace('Table','Table,'+strPlugins):toolsTheme;//插件接在full的Table后面
  96. }
  97. if(!settings.tools.match(/(^|,)\s*About\s*(,|$)/i))settings.tools+=',About';
  98. settings.tools=settings.tools.split(',');
  99. if(settings.editorRoot)editorRoot=settings.editorRoot;
  100. editorRoot=getLocalUrl(editorRoot,'abs');
  101. if(settings.urlBase)settings.urlBase=getLocalUrl(settings.urlBase,'abs');
  102. //基本控件名
  103. var idCSS='xheCSS_'+settings.skin,idContainer='xhe'+xCount+'_container',idTools='xhe'+xCount+'_Tool',idIframeArea='xhe'+xCount+'_iframearea',idIframe='xhe'+xCount+'_iframe',idFixFFCursor='xhe'+xCount+'_fixffcursor';
  104. var headHTML='',bodyClass='',skinPath=editorRoot+'xheditor_skin/'+settings.skin+'/',arrEmots=itemEmots,urlType=settings.urlType,urlBase=settings.urlBase,emotPath=settings.emotPath,emotPath=emotPath?emotPath:editorRoot+'xheditor_emot/',selEmotGroup='';
  105. arrEmots=$.extend({},arrEmots,settings.emots);
  106. emotPath=getLocalUrl(emotPath,'rel',urlBase?urlBase:null);//返回最短表情路径
  107. bShowBlocktag=settings.showBlocktag;
  108. if(bShowBlocktag)bodyClass+=' showBlocktag';
  109. var arrShortCuts=[];
  110. this.init=function()
  111. {
  112. //加载样式表
  113. if($('#'+idCSS).length===0)$('head').append('<link id="'+idCSS+'" rel="stylesheet" type="text/css" href="'+skinPath+'ui.css" />');
  114. //初始化编辑器
  115. var cw = settings.width || _text.style.width || _jText.outerWidth();
  116. editorHeight = settings.height || _text.style.height || _jText.outerHeight();
  117. if(is(editorHeight,'string'))editorHeight=editorHeight.replace(/[^\d]+/g,'');
  118. if(cw<=0||editorHeight<=0)//禁止对隐藏区域里的textarea初始化编辑器
  119. {
  120. alert('当前textarea处于隐藏状态,请将之显示后再初始化xhEditor,或者直接设置textarea的width和height样式');
  121. return false;
  122. }
  123. if(/^[0-9\.]+$/i.test(''+cw))cw+='px';
  124. //编辑器CSS背景
  125. var editorBackground=settings.background || _text.style.background;
  126. //工具栏内容初始化
  127. var arrToolsHtml=['<span class="xheGStart"/>'],tool,cn,regSeparator=/\||\//i;
  128. $.each(settings.tools,function(i,n)
  129. {
  130. if(n.match(regSeparator))arrToolsHtml.push('<span class="xheGEnd"/>');
  131. if(n==='|')arrToolsHtml.push('<span class="xheSeparator"/>');
  132. else if(n==='/')arrToolsHtml.push('<br />');
  133. else
  134. {
  135. tool=arrTools[n];
  136. if(!tool)return;
  137. if(tool.c)cn=tool.c;
  138. else cn='xheIcon xheBtn'+n;
  139. arrToolsHtml.push('<span><a href="javascript:void(0);" title="'+tool.t+'" name="'+n+'" class="xheButton xheEnabled" tabindex="-1"><span class="'+cn+'" unselectable="on" /></a></span>');
  140. if(tool.s)_this.addShortcuts(tool.s,n);
  141. }
  142. if(n.match(regSeparator))arrToolsHtml.push('<span class="xheGStart"/>');
  143. });
  144. arrToolsHtml.push('<span class="xheGEnd"/><br />');
  145. _jText.after($('<input type="text" id="'+idFixFFCursor+'" style="position:absolute;display:none;" /><span id="'+idContainer+'" class="xhe_'+settings.skin+'" style="display:none"><table cellspacing="0" cellpadding="0" class="xheLayout" style="width:'+cw+';height:'+editorHeight+'px;"><tbody><tr><td id="'+idTools+'" class="xheTool" unselectable="on" style="height:1px;"></td></tr><tr><td id="'+idIframeArea+'" class="xheIframeArea"><iframe frameborder="0" id="'+idIframe+'" src="javascript:;" style="width:100%;"></iframe></td></tr></tbody></table></span>'));
  146. _jTools=$('#'+idTools);_jArea=$('#'+idIframeArea);
  147. headHTML='<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><link rel="stylesheet" href="'+skinPath+'iframe.css"/>';
  148. var loadCSS=settings.loadCSS;
  149. if(loadCSS)
  150. {
  151. if(is(loadCSS,'array'))for(var i in loadCSS)headHTML+='<link rel="stylesheet" href="'+loadCSS[i]+'"/>';
  152. else
  153. {
  154. if(loadCSS.match(/\s*<style(\s+[^>]*?)?>[\s\S]+?<\/style>\s*/i))headHTML+=loadCSS;
  155. else headHTML+='<link rel="stylesheet" href="'+loadCSS+'"/>';
  156. }
  157. }
  158. var iframeHTML='<html><head>'+headHTML;
  159. if(editorBackground)iframeHTML+='<style>body{background:'+editorBackground+';}</style>';
  160. iframeHTML+='</head><body spellcheck="false" class="editMode'+bodyClass+'"></body></html>';
  161. _this.win=_win=$('#'+idIframe)[0].contentWindow;
  162. _jWin=$(_win);
  163. try{
  164. this.doc=_doc = _win.document;_jDoc=$(_doc);
  165. _doc.open();
  166. _doc.write(iframeHTML);
  167. _doc.close();
  168. if(isIE)_doc.body.contentEditable='true';
  169. else _doc.designMode = 'On';
  170. }catch(e){}
  171. setTimeout(setOpts,300);
  172. _this.setSource();
  173. _win.setInterval=null;//针对jquery 1.3无法操作iframe window问题的hack
  174. //添加工具栏
  175. _jTools.append(arrToolsHtml.join('')).bind('mousedown contextmenu',returnFalse).click(function(event)
  176. {
  177. var jButton=$(event.target).closest('a');
  178. if(jButton.is('.xheEnabled'))
  179. {
  180. ev=event;
  181. _this.exec(jButton.attr('name'));
  182. }
  183. return false;
  184. });
  185. _jTools.find('.xheButton').hover(function(event){//鼠标悬停执行
  186. var jButton=$(this),delay=settings.hoverExecDelay;
  187. var tAngle=lastAngle;lastAngle=null;
  188. if(delay===-1||bDisableHoverExec||!jButton.is('.xheEnabled'))return false;
  189. if(tAngle&&tAngle>10)//检测误操作
  190. {
  191. bDisableHoverExec=true;
  192. setTimeout(function(){bDisableHoverExec=false;},100);
  193. return false;
  194. }
  195. var cmd=jButton.attr('name'),bHover=arrTools[cmd].h===1;
  196. if(!bHover)
  197. {
  198. _this.hidePanel();//移到非悬停按钮上隐藏面板
  199. return false;
  200. }
  201. if(bQuickHoverExec)delay=0;
  202. if(delay>=0)timer=setTimeout(function(){
  203. ev=event;
  204. lastPoint={x:ev.clientX,y:ev.clientY};
  205. _this.exec(cmd);
  206. },delay);
  207. },function(event){lastPoint=null;if(timer)clearTimeout(timer);}).mousemove(function(event){
  208. if(lastPoint)
  209. {
  210. var diff={x:event.clientX-lastPoint.x,y:event.clientY-lastPoint.y};
  211. if(Math.abs(diff.x)>1||Math.abs(diff.y)>1)
  212. {
  213. if(diff.x>0&&diff.y>0)
  214. {
  215. var tAngle=Math.round(Math.atan(diff.y/diff.x)/0.017453293);
  216. if(lastAngle)lastAngle=(lastAngle+tAngle)/2
  217. else lastAngle=tAngle;
  218. }
  219. else lastAngle=null;
  220. lastPoint={x:event.clientX,y:event.clientY};
  221. }
  222. }
  223. });
  224. //初始化面板
  225. _jPanel=$('#xhePanel');
  226. _jShadow=$('#xheShadow');
  227. _jCntLine=$('#xheCntLine');
  228. if(_jPanel.length===0)
  229. {
  230. _jPanel=$('<div id="xhePanel"></div>').mousedown(function(ev){ev.stopPropagation()});
  231. _jShadow=$('<div id="xheShadow"></div>');
  232. _jCntLine=$('<div id="xheCntLine"></div>');
  233. setTimeout(function(){
  234. $(document.body).append(_jPanel).append(_jShadow).append(_jCntLine);
  235. },10);
  236. }
  237. //切换显示区域
  238. $('#'+idContainer).show();
  239. _jText.hide();
  240. _jArea.css('height',editorHeight-_jTools.outerHeight());
  241. //setTimeout(function(){_jArea.css('height',editorHeight-_jTools.outerHeight());},100);
  242. //绑定内核事件
  243. _jText.focus(_this.focus);
  244. _jForm.submit(saveResult).bind('reset', loadReset);
  245. $(window).bind('unload beforeunload',saveResult).bind('resize',fixFullHeight);
  246. $(document).mousedown(clickCancelPanel);
  247. if(!bCheckEscInit){$(document).keydown(checkEsc);bCheckEscInit=true;}
  248. _jWin.focus(function(){if(settings.focus)settings.focus();}).blur(function(){if(settings.blur)settings.blur();});
  249. if(isSafari)_jWin.click(fixAppleSel);
  250. _jDoc.mousedown(clickCancelPanel).keydown(checkShortcuts).keypress(forcePtag).dblclick(checkDblClick).bind('mousedown click',function(ev){_jText.trigger(ev.type);});
  251. if(isIE)
  252. {
  253. //IE控件上Backspace会导致页面后退
  254. _jDoc.keydown(function(ev){var rng=_this.getRng();if(ev.which===8&&rng.item){$(rng.item(0)).remove();return false;}});
  255. //修正IE拖动img大小不更新width和height属性值的问题
  256. function fixResize(ev)
  257. {
  258. var jImg=$(ev.target),v;
  259. if(v=jImg.css('width'))jImg.css('width','').attr('width',v.replace(/[^0-9%]+/g, ''));
  260. if(v=jImg.css('height'))jImg.css('height','').attr('height',v.replace(/[^0-9%]+/g, ''));
  261. }
  262. _jDoc.bind('controlselect',function(ev){
  263. ev=ev.target;if(!$.nodeName(ev,'IMG'))return;
  264. $(ev).unbind('resizeend',fixResize).bind('resizeend',fixResize);
  265. });
  266. }
  267. var jBody=$(_doc.documentElement);
  268. jBody.bind('paste',cleanPaste);
  269. if(settings.disableContextmenu)jBody.bind('contextmenu',returnFalse);
  270. //HTML5编辑区域直接拖放上传
  271. if(settings.html5Upload)jBody.bind('dragenter dragover',function(ev){var types;if((types=ev.originalEvent.dataTransfer.types)&&$.inArray('Files', types)!==-1)return false;}).bind('drop',function(ev){
  272. var dataTransfer=ev.originalEvent.dataTransfer,fileList;
  273. if(dataTransfer&&(fileList=dataTransfer.files)&&fileList.length>0){
  274. var i,cmd,arrCmd=['Link','Img','Flash','Media'],arrExt=[],strExt;
  275. for(i in arrCmd){
  276. cmd=arrCmd[i];
  277. if(settings['up'+cmd+'Url']&&settings['up'+cmd+'Url'].match(/^[^!].*/i))arrExt.push(cmd+':,'+settings['up'+cmd+'Ext']);//允许上传
  278. }
  279. if(arrExt.length===0)return false;//禁止上传
  280. else strExt=arrExt.join(',');
  281. function getCmd(fileList){
  282. var match,fileExt,cmd;
  283. for(i=0;i<fileList.length;i++){
  284. fileExt=fileList[i].fileName.replace(/.+\./,'');
  285. if(match=strExt.match(new RegExp('(\\w+):[^:]*,'+fileExt+'(?:,|$)','i'))){
  286. if(!cmd)cmd=match[1];
  287. else if(cmd!==match[1])return 2;
  288. }
  289. else return 1;
  290. }
  291. return cmd;
  292. }
  293. cmd=getCmd(fileList);
  294. if(cmd===1)alert('上传文件的扩展名必需为:'+strExt.replace(/\w+:,/g,''));
  295. else if(cmd===2)alert('每次只能拖放上传同一类型文件');
  296. else if(cmd){
  297. _this.startUpload(fileList,settings['up'+cmd+'Url'],'*',function(arrMsg){
  298. var arrUrl=[],msg,onUpload=settings.onUpload;
  299. if(onUpload)onUpload(arrMsg);//用户上传回调
  300. for(i in arrMsg){
  301. msg=arrMsg[i];
  302. url=is(msg,'string')?msg:msg.url;
  303. if(url.substr(0,1)==='!')url=url.substr(1);
  304. arrUrl.push(url);
  305. }
  306. _this.exec(cmd);
  307. $('#xhe'+cmd+'Url').val(arrUrl.join(' '));
  308. $('#xheSave').click();
  309. });
  310. }
  311. return false;
  312. }
  313. });
  314. //添加用户快捷键
  315. var shortcuts=settings.shortcuts;
  316. if(shortcuts)$.each(shortcuts,function(key,func){_this.addShortcuts(key,func);});
  317. xCount++;
  318. bInit=true;
  319. if(settings.fullscreen)_this.toggleFullscreen();
  320. else if(settings.sourceMode)setTimeout(_this.toggleSource,20);
  321. return true;
  322. }
  323. this.remove=function()
  324. {
  325. _this.hidePanel();
  326. saveResult();//卸载前同步最新内容到textarea
  327. //取消绑定事件
  328. _jText.unbind('focus',_this.focus);
  329. _jForm.unbind('submit',saveResult).unbind('reset', loadReset);
  330. $(window).unbind('unload beforeunload',saveResult).unbind('resize',fixFullHeight);
  331. $(document).unbind('mousedown',clickCancelPanel);
  332. $('#'+idContainer).remove();
  333. $('#'+idFixFFCursor).remove();
  334. _jText.show();
  335. bInit=false;
  336. }
  337. this.saveBookmark=function(){
  338. if(!bSource){
  339. var rng=_this.getRng();
  340. rng=rng.cloneRange?rng.cloneRange():rng;
  341. bookmark={'top':_jWin.scrollTop(),'rng':rng};
  342. }
  343. }
  344. this.loadBookmark=function()
  345. {
  346. if(bSource||!bookmark)return;
  347. _this.focus();
  348. var rng=bookmark.rng;
  349. if(isIE)rng.select();
  350. else
  351. {
  352. var sel=_this.getSel();
  353. sel.removeAllRanges();
  354. sel.addRange(rng);
  355. }
  356. _jWin.scrollTop(bookmark.top);
  357. bookmark=null;
  358. }
  359. this.focus=function()
  360. {
  361. if(!bSource)_jWin.focus();
  362. else $('#sourceCode',_doc).focus();
  363. if(isIE){
  364. var rng=_this.getRng();
  365. if(rng.parentElement&&rng.parentElement().ownerDocument!==_doc)_this.setCursorFirst();//修正IE初始焦点问题
  366. }
  367. return false;
  368. }
  369. this.setCursorFirst=function(firstBlock)
  370. {
  371. _win.scrollTo(0,0);
  372. var rng=_this.getRng(true),_body=_doc.body,firstNode=_body,firstTag;
  373. if(firstBlock&&firstNode.firstChild&&(firstTag=firstNode.firstChild.tagName)&&firstTag.match(/^p|div|h[1-6]$/i))firstNode=_body.firstChild;
  374. isIE?rng.moveToElementText(firstNode):rng.setStart(firstNode,0);
  375. rng.collapse(true);
  376. if(isIE)rng.select();
  377. else{var sel=_this.getSel();sel.removeAllRanges();sel.addRange(rng);}
  378. }
  379. this.getSel=function()
  380. {
  381. return _win.getSelection ? _win.getSelection() : _doc.selection;
  382. }
  383. this.getRng=function(bNew)
  384. {
  385. var sel,rng;
  386. try{
  387. if(!bNew){
  388. sel=_this.getSel();
  389. rng = sel.rangeCount > 0 ? sel.getRangeAt(0) : sel.createRange?sel.createRange():null;
  390. }
  391. if(!rng)rng = _doc.createRange?_doc.createRange():_doc.body.createTextRange();
  392. }catch (ex){}
  393. return rng;
  394. }
  395. this.getParent=function(tag)
  396. {
  397. var rng=_this.getRng(),p;
  398. if(!isIE)
  399. {
  400. p = rng.commonAncestorContainer;
  401. if(!rng.collapsed)if(rng.startContainer === rng.endContainer&&rng.startOffset - rng.endOffset < 2&&rng.startContainer.hasChildNodes())p = rng.startContainer.childNodes[rng.startOffset];
  402. }
  403. else p=rng.item?rng.item(0):rng.parentElement();
  404. tag=tag?tag:'*';p=$(p);
  405. if(!p.is(tag))p=$(p).closest(tag);
  406. return p;
  407. }
  408. this.getSelect=function(format)
  409. {
  410. var sel=_this.getSel(),rng=_this.getRng(),isCollapsed=true;
  411. if (!rng || rng.item)isCollapsed=false
  412. else isCollapsed=!sel || rng.boundingWidth === 0 || rng.collapsed;
  413. if(format==='text')return isCollapsed ? '' : (rng.text || (sel.toString ? sel.toString() : ''));
  414. var sHtml;
  415. if(rng.cloneContents)
  416. {
  417. var tmp=$('<div></div>'),c;
  418. c = rng.cloneContents();
  419. if(c)tmp.append(c);
  420. sHtml=tmp.html();
  421. }
  422. else if(is(rng.item))sHtml=rng.item(0).outerHTML;
  423. else if(is(rng.htmlText))sHtml=rng.htmlText;
  424. else sHtml=rng.toString();
  425. if(isCollapsed)sHtml='';
  426. sHtml=_this.processHTML(sHtml,'read');
  427. sHtml=_this.cleanHTML(sHtml);
  428. sHtml=_this.formatXHTML(sHtml);
  429. return sHtml;
  430. }
  431. this.pasteHTML=function(sHtml,bStart)
  432. {
  433. if(bSource)return false;
  434. _this.focus();
  435. sHtml=_this.processHTML(sHtml,'write');
  436. var sel=_this.getSel(),rng=_this.getRng();
  437. if(bStart!==undefined)//非覆盖式插入
  438. {
  439. if(rng.item)
  440. {
  441. var item=rng.item(0);
  442. rng=_this.getRng(true);
  443. rng.moveToElementText(item);
  444. rng.select();
  445. }
  446. rng.collapse(bStart);
  447. }
  448. sHtml+='<'+(isIE?'img':'span')+' id="_xhe_temp" width="0" height="0" />';
  449. if(rng.insertNode)
  450. {
  451. rng.deleteContents();
  452. rng.insertNode(rng.createContextualFragment(sHtml));
  453. }
  454. else
  455. {
  456. if(sel.type.toLowerCase()==='control'){sel.clear();rng=_this.getRng();};
  457. rng.pasteHTML(sHtml);
  458. }
  459. var jTemp=$('#_xhe_temp',_doc),temp=jTemp[0];
  460. if(isIE)
  461. {
  462. rng.moveToElementText(temp);
  463. rng.select();
  464. }
  465. else
  466. {
  467. rng.selectNode(temp);
  468. sel.removeAllRanges();
  469. sel.addRange(rng);
  470. }
  471. jTemp.remove();
  472. }
  473. this.pasteText=function(text,bStart)
  474. {
  475. if(!text)text='';
  476. text=_this.domEncode(text);
  477. text = text.replace(/\r?\n/g, '<br />');
  478. _this.pasteHTML(text,bStart);
  479. }
  480. this.appendHTML=function(sHtml)
  481. {
  482. if(bSource)return false;
  483. _this.focus();
  484. sHtml=_this.processHTML(sHtml,'write');
  485. $(_doc.body).append(sHtml);
  486. }
  487. this.domEncode=function(text)
  488. {
  489. return text.replace(regEntities,function(c){return arrEntities[c];});
  490. }
  491. this.setSource=function(sHtml)
  492. {
  493. bookmark=null;
  494. if(typeof sHtml!=='string'&&sHtml!=='')sHtml=_text.value;
  495. if(bSource)$('#sourceCode',_doc).val(sHtml);
  496. else
  497. {
  498. if(settings.beforeSetSource)sHtml=settings.beforeSetSource(sHtml);
  499. sHtml=_this.cleanHTML(sHtml);
  500. sHtml=_this.formatXHTML(sHtml);
  501. sHtml=_this.processHTML(sHtml,'write');
  502. if(isIE){//修正IE会删除可视内容前的script,style,<!--
  503. _doc.body.innerHTML='<img id="_xhe_temp" width="0" height="0" />'+sHtml+'\n';//修正IE会删除&符号后面代码的问题
  504. $('#_xhe_temp',_doc).remove();
  505. }
  506. else _doc.body.innerHTML=sHtml;
  507. }
  508. }
  509. this.processHTML=function(sHtml,mode)
  510. {
  511. var appleClass=' class="Apple-style-span"';
  512. if(mode==='write')
  513. {//write
  514. sHtml=sHtml.replace(/(<(\/?)(\w+))((?:\s+[\w-:]+\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))*)\s*((\/?)>)/g,function(all,left,end1,tag,attr,right,end2){
  515. tag=tag.toLowerCase();
  516. //编辑状态统一转为strike
  517. if(isMozilla){
  518. if(tag==='strong')tag='b';
  519. else if(tag==='em')tag='i';
  520. }
  521. else if(isSafari){
  522. if(tag==='strong'){tag='span';if(!end1)attr+=appleClass+' style="font-weight: bold;"';}
  523. else if(tag==='em'){tag='span';if(!end1)attr+=appleClass+' style="font-style: italic;"';}
  524. else if(tag==='u'){tag='span';if(!end1)attr+=appleClass+' style="text-decoration: underline;"';}
  525. else if(tag==='strike'){tag='span';if(!end1)attr+=appleClass+' style="text-decoration: line-through;"';}
  526. }
  527. var emot,tableBorder;
  528. if(tag==='del')tag='strike';
  529. else if(tag==='img'){
  530. //恢复emot
  531. attr=attr.replace(/\s+emot\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/i,function(all,v){
  532. emot=v.match(/^(["']?)(.*)\1/)[2];
  533. emot=emot.split(',');
  534. if(!emot[1]){emot[1]=emot[0];emot[0]=''}
  535. if(emot[0]==='default')emot[0]='';
  536. return settings.emotMark?all:'';
  537. });
  538. }
  539. else if(tag==='a'&&end2)right='></a>';
  540. else if(tag==='table'&&!end1){
  541. var tb=attr.match(/\s+border\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/i);
  542. if(!tb||tb[1].match(/^(["']?)\s*0\s*\1$/))tableBorder='xhe-border';
  543. }
  544. var bAppleClass;
  545. attr=attr.replace(/\s+([\w-:]+)\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/g,function(all,n,v){
  546. n=n.toLowerCase();
  547. v=v.match(/^(["']?)(.*)\1/)[2];
  548. aft='';//尾部增加属性
  549. if(isIE&&n.match(/^(disabled|checked|readonly|selected)$/)&&v.match(/^(false|0)$/i))return '';
  550. //恢复emot
  551. if(tag==='img'&&emot&&n==='src')return '';
  552. //保存属性值:src,href
  553. if(n.match(/^(src|href)$/)){
  554. aft=' _xhe_'+n+'="'+v+'"';
  555. if(urlBase)v=getLocalUrl(v,'abs',urlBase);
  556. }
  557. //显示table边框
  558. if(tableBorder&&n==='class'){
  559. v+=' '+tableBorder;
  560. tableBorder='';
  561. }
  562. //处理Safari style值
  563. if(isSafari&&n==='style'){
  564. v=v.replace(/(^|;)\s*(font-size)\s*:\s*([a-z-]+)\s*(;|$)/i,function(all,left,n,v,right){
  565. var t,s;
  566. for(var i=0;i<arrFontsize.length;i++)
  567. {
  568. t=arrFontsize[i];
  569. if(v===t.n){s=t.wkn;break;}
  570. }
  571. return left+n+':'+s+right;
  572. });
  573. if(tag==='span'&&v.match(/(^|;)\s*(font-family|font-size|color|background-color)\s*:\s*[^;]+\s*(;|$)/i))bAppleClass=true;
  574. }
  575. return ' '+n+'="'+v+'"'+aft;
  576. });
  577. //恢复emot
  578. if(emot)attr+=' src="'+emotPath+(emot[0]?emot[0]:'default')+'/'+emot[1]+'.gif"';
  579. if(bAppleClass)attr+=appleClass;
  580. if(tableBorder)attr+=' class="'+tableBorder+'"';
  581. return '<'+end1+tag+attr+right;
  582. });
  583. if(isIE)sHtml = sHtml.replace(/&apos;/ig, '&#39;');
  584. if(!isSafari)
  585. {
  586. //style转font
  587. function style2font(all,tag,style,content)
  588. {
  589. var attrs='',f,s1,s2,c;
  590. f=style.match(/font-family\s*:\s*([^;"]+)/i);
  591. if(f)attrs+=' face="'+f[1]+'"';
  592. s1=style.match(/font-size\s*:\s*([^;"]+)/i);
  593. if(s1)
  594. {
  595. s1=s1[1].toLowerCase();
  596. for(var j=0;j<arrFontsize.length;j++)if(s1===arrFontsize[j].n||s1===arrFontsize[j].s){s2=j+1;break;}
  597. if(s2)
  598. {
  599. attrs+=' size="'+s2+'"';
  600. style=style.replace(/(^|;)(\s*font-size\s*:\s*[^;"]+;?)+/ig,'$1');
  601. }
  602. }
  603. c=style.match(/(?:^|[\s;])color\s*:\s*([^;"]+)/i);
  604. if(c)
  605. {
  606. var rgb;
  607. if(rgb=c[1].match(/\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i)){c[1]='#';for(var i=1;i<=3;i++)c[1]+=(rgb[i]-0).toString(16);}
  608. c[1]=c[1].replace(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i,'#$1$1$2$2$3$3');
  609. attrs+=' color="'+c[1]+'"';
  610. }
  611. style=style.replace(/(^|;)(\s*(font-family|color)\s*:\s*[^;"]+;?)+/ig,'$1');
  612. if(attrs!=='')
  613. {
  614. if(style)attrs+=' style="'+style+'"';
  615. return '<font'+attrs+'>'+content+"</font>";
  616. }
  617. else return all;
  618. }
  619. sHtml = sHtml.replace(/<(span)(?:\s+[^>]*?)?\s+style\s*=\s*"((?:[^"]*?;)*\s*(?:font-family|font-size|color)\s*:[^"]*)"(?: [^>]*)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?<\/\1>)*?)<\/\1>/ig,style2font);//第3层
  620. sHtml = sHtml.replace(/<(span)(?:\s+[^>]*?)?\s+style\s*=\s*"((?:[^"]*?;)*\s*(?:font-family|font-size|color)\s*:[^"]*)"(?: [^>]*)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?)<\/\1>/ig,style2font);//第2层
  621. sHtml = sHtml.replace(/<(span)(?:\s+[^>]*?)?\s+style\s*=\s*"((?:[^"]*?;)*\s*(?:font-family|font-size|color)\s*:[^"]*)"(?: [^>]*)?>(((?!<\1(\s+[^>]*?)?>)[\s\S])*?)<\/\1>/ig,style2font);//最里层
  622. }
  623. }
  624. else
  625. {//read
  626. if(isSafari)
  627. {
  628. //转换apple的style为strong,em等
  629. var arrAppleSpan=[{r:/font-weight:\sbold/ig,t:'strong'},{r:/font-style:\sitalic/ig,t:'em'},{r:/text-decoration:\sunderline/ig,t:'u'},{r:/text-decoration:\sline-through/ig,t:'strike'}];
  630. function replaceAppleSpan(all,tag,attr1,attr2,content)
  631. {
  632. var attr=attr1+attr2,newTag='';
  633. if(!attr)return content;
  634. for(var i=0;i<arrAppleSpan.length;i++)
  635. {
  636. if(attr.match(arrAppleSpan[i].r))
  637. {
  638. newTag=arrAppleSpan[i].t;
  639. break;
  640. }
  641. }
  642. if(newTag)return '<'+newTag+'>'+content+'</'+newTag+'>';
  643. else return all;
  644. }
  645. for(var i=0;i<2;i++){
  646. sHtml = sHtml.replace(/<(span)(\s+[^>]*?)?\s+class\s*=\s*"Apple-style-span"(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?<\/\1>)*?)<\/\1>/ig,replaceAppleSpan);//第3层
  647. sHtml = sHtml.replace(/<(span)(\s+[^>]*?)?\s+class\s*=\s*"Apple-style-span"(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?)<\/\1>/ig,replaceAppleSpan);//第2层
  648. sHtml = sHtml.replace(/<(span)(\s+[^>]*?)?\s+class\s*=\s*"Apple-style-span"(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S])*?)<\/\1>/ig,replaceAppleSpan);//最里层
  649. }
  650. }
  651. sHtml=sHtml.replace(/(<(\w+))((?:\s+[\w-:]+\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))*)\s*(\/?>)/g,function(all,left,tag,attr,right){
  652. tag=tag.toLowerCase();
  653. //恢复属性值src,href
  654. var saveValue;
  655. attr=attr.replace(/\s+_xhe_(?:src|href)\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/i,function(all,v){saveValue=v.match(/^(["']?)(.*)\1/)[2];return '';});
  656. if(saveValue&&urlType)saveValue=getLocalUrl(saveValue,urlType,urlBase);
  657. attr=attr.replace(/\s+([\w-:]+)\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/g,function(all,n,v){
  658. n=n.toLowerCase();
  659. v=v.match(/^(["']?)(.*)\1/)[2];
  660. if(n==='class'){//清理class属性
  661. if(v.match(/^["']?(apple|webkit)/i))return '';
  662. if(tag==='table'){
  663. v=v.replace(/\s?xhe-border/ig,'');
  664. if(v==='')return '';
  665. }
  666. }
  667. else if(n.match(/^((_xhe_|_moz_|_webkit_)|jquery\d+)/i))return '';//清理临时属性
  668. else if(saveValue&&n.match(/^(src|href)$/i))return ' '+n+'="'+saveValue+'"';//恢复属性值src,href
  669. else if(isSafari&&n==='style'){//转换webkit的font-size
  670. v=v.replace(/(^|;)\s*(font-size)\s*:\s*([a-z-]+)\s*(;|$)/i,function(all,left,n,v,right){
  671. var t,s;
  672. for(var i=0;i<arrFontsize.length;i++)
  673. {
  674. t=arrFontsize[i];
  675. if(v===t.wkn){s=t.n;break;}
  676. }
  677. return left+n+':'+s+right;
  678. });
  679. }
  680. return ' '+n+'="'+v+'"';
  681. });
  682. //img强制加alt
  683. if(tag==='img'&&!attr.match(/\s+alt\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/i))attr+=' alt=""';
  684. return left+attr+right;
  685. });
  686. sHtml=sHtml.replace(/^\s*(?:<(p|div)(?:\s+[^>]*?)?>)?\s*(<span(?:\s+[^>]*?)?>\s*<\/span>|<br(?:\s+[^>]*?)?>|&nbsp;)*\s*(?:<\/\1>)?\s*$/i, '');//修正浏览器在空内容情况下多出来的代码
  687. }
  688. //写和读innerHTML前pre中<br>转\r\n
  689. sHtml=sHtml.replace(/(<pre(?:\s+[^>]*?)?>)([\s\S]+?)(<\/pre>)/gi,function(all,left,code,right){
  690. return left+code.replace(/<br\s*\/?>/ig,'\r\n')+right;
  691. });
  692. return sHtml;
  693. }
  694. this.getSource=function(bFormat)
  695. {
  696. var sHtml,beforeGetSource=settings.beforeGetSource;
  697. if(bSource)
  698. {
  699. sHtml=$('#sourceCode',_doc).val();
  700. if(!beforeGetSource)sHtml=_this.formatXHTML(sHtml,false);
  701. }
  702. else
  703. {
  704. sHtml=_this.processHTML(_doc.body.innerHTML,'read');
  705. sHtml=_this.cleanHTML(sHtml);
  706. sHtml=_this.formatXHTML(sHtml,bFormat);
  707. if(beforeGetSource)sHtml=beforeGetSource(sHtml);
  708. }
  709. _text.value=sHtml;
  710. return sHtml;
  711. }
  712. this.cleanWord=function(sHtml)
  713. {
  714. var cleanPaste=settings.cleanPaste;
  715. if(cleanPaste>0&&cleanPaste<3&&sHtml.match(/mso(-|normal)|WordDocument|<table\s+[^>]*?x:str/i))
  716. {
  717. //区块标签清理
  718. sHtml = sHtml.replace(/<!--[\s\S]*?-->|<!(--)?\[[\s\S]+?\](--)?>|<style(\s+[^>]*?)?>[\s\S]*?<\/style>/ig, '');
  719. sHtml = sHtml.replace(/\r?\n/ig, '');
  720. sHtml=sHtml.replace(/(<(\/?)([\w-:]+))((?:\s+[\w-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))?)*)\s*(\/?>)/g,function(all,left,end,tag,attr,right){
  721. tag=tag.toLowerCase();
  722. if((tag.match(/^(link|img)$/)&&attr.match(/file:\/\//i))||tag.match(/:/)||(tag==='span'&&cleanPaste===2))return '';
  723. if(!end){
  724. attr=attr.replace(/\s([\w-:]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))?/ig,function(all,n,v){
  725. n=n.toLowerCase();
  726. if(n.match(/:/))return '';
  727. else if(n.match(/^(class|lang|language|span)$/))return '';
  728. else if(tag==='td'&&(n==='height'||(n==='width'&&!attr.match(/\scolspan="\d+"/i))))return '';
  729. else if(n==='style'){
  730. if(cleanPaste===2)return '';
  731. v=$.trim(v.replace(/\s*(mso-[^:]+:.+?|margin\s*:\s*0cm 0cm 0pt\s*|(text-align|font-variant|line-height)\s*:\s*.+?)(;|$)\s*/ig,''));
  732. return v?' '+n+'="'+v+'"':'';
  733. }
  734. return all;
  735. })
  736. if((tag==='a'&&!attr.match(/\s+href\s*=/)))return '';
  737. }
  738. return left+attr+right;
  739. });
  740. for(var i=0;i<3;i++)sHtml = sHtml.replace( /<([^\s>]+)(\s+[^>]*)?>\s*<\/\1>/g,'');//空内容的标签
  741. }
  742. return sHtml;
  743. }
  744. this.cleanHTML=function(sHtml)
  745. {
  746. sHtml = sHtml.replace(/<!?\/?(DOCTYPE|html|body|meta)(\s+[^>]*?)?>/ig, '');
  747. var arrHeadSave;sHtml = sHtml.replace(/<head(?:\s+[^>]*?)?>([\s\S]*?)<\/head>/i, function(all,content){arrHeadSave=content.match(/<(script|style)(\s+[^>]*?)?>[\s\S]*?<\/\1>/ig);return '';});
  748. if(arrHeadSave)sHtml=arrHeadSave.join('')+sHtml;
  749. sHtml = sHtml.replace(/<\??xml(:\w+)?(\s+[^>]*?)?>([\s\S]*?<\/xml>)?/ig, '');
  750. if(!settings.internalScript)sHtml = sHtml.replace(/<script(\s+[^>]*?)?>[\s\S]*?<\/script>/ig, '');
  751. if(!settings.internalStyle)sHtml = sHtml.replace(/<style(\s+[^>]*?)?>[\s\S]*?<\/style>/ig, '');
  752. if(!settings.linkTag||!settings.inlineScript||!settings.inlineStyle)sHtml=sHtml.replace(/(<(\w+))((?:\s+[\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))*)\s*(\/?>)/ig,function(all,left,tag,attr,right){
  753. if(!settings.linkTag&&tag.toLowerCase()==='link')return '';
  754. if(!settings.inlineScript)attr=attr.replace(/\s+on(?:click|dblclick|mouse(down|up|move|over|out|enter|leave|wheel)|key(down|press|up)|change|select|submit|reset|blur|focus|load|unload)\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/ig,'');
  755. if(!settings.inlineStyle)attr=attr.replace(/\s+(style|class)\s*=\s*("[^"]*"|'[^']*'|[^>\s]+)/ig,'');
  756. return left+attr+right;
  757. });
  758. sHtml=sHtml.replace(/<\/(strong|b|u|strike|em|i)>((?:\s|<br\/?>|&nbsp;)*?)<\1(\s+[^>]*?)?>/ig,'$2');//连续相同标签
  759. return sHtml;
  760. }
  761. this.formatXHTML=function(sHtml,bFormat){
  762. var emptyTags = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param");//HTML 4.01
  763. var blockTags = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,h1,h2,h3,h4,h5,h6,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");//HTML 4.01
  764. var inlineTags = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");//HTML 4.01
  765. var closeSelfTags = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
  766. var fillAttrsTags = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
  767. var cdataTags = makeMap("script,style");
  768. var tagReplac={'b':'strong','i':'em','s':'del','strike':'del'};
  769. var regTag=/<(?:\/([\w:]+)|!--([^>]*?)--|([\w:]+)((?:\s+[\w-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))?)*)\s*(\/?))>/g;
  770. var regAttr = /\s+([\w-:]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s]+)))?/g;
  771. var results=[],stack=[];
  772. stack.last = function(){return this[ this.length - 1 ];};
  773. var match,tagIndex,nextIndex=0,tagName,tagCDATA,arrCDATA,text;
  774. var lvl=-1,lastTag='body',lastTagStart,stopFormat=false;
  775. while(match=regTag.exec(sHtml)){
  776. tagIndex = match.index;
  777. if(tagIndex>nextIndex){//保存前面的文本或者CDATA
  778. text=sHtml.substring(nextIndex,tagIndex);
  779. if(tagCDATA)arrCDATA.push(text);
  780. else onText(text);
  781. }
  782. nextIndex = regTag.lastIndex;
  783. if(tagName=match[1]){//结束标签
  784. tagName=processTag(tagName);
  785. if(tagCDATA&&tagName===tagCDATA){//结束标签前输出CDATA
  786. onCDATA(arrCDATA.join(''));
  787. tagCDATA=null;
  788. arrCDATA=null;
  789. }
  790. if(!tagCDATA){
  791. onEndTag(tagName);
  792. continue;
  793. }
  794. }
  795. if(tagCDATA)arrCDATA.push(match[0]);
  796. else{
  797. if(tagName=match[3]){//开始标签
  798. tagName=processTag(tagName);
  799. onStartTag(tagName,match[4],match[5]);
  800. if(cdataTags[tagName]){
  801. tagCDATA=tagName;
  802. arrCDATA=[];
  803. }
  804. }
  805. else if(match[2])onComment(match[0]);//注释标签
  806. }
  807. }
  808. if(sHtml.length>nextIndex)onText(sHtml.substring(nextIndex,sHtml.length ));//结尾文本
  809. onEndTag();//封闭未结束的标签
  810. sHtml=results.join('');
  811. results=null;
  812. function makeMap(str)
  813. {
  814. var obj = {}, items = str.split(",");
  815. for ( var i = 0; i < items.length; i++ )obj[ items[i] ] = true;
  816. return obj;
  817. }
  818. function processTag(tagName)
  819. {
  820. tagName=tagName.toLowerCase();
  821. var tag=tagReplac[tagName];
  822. return tag?tag:tagName;
  823. }
  824. function onStartTag(tagName,rest,unary)
  825. {
  826. if(blockTags[tagName])while(stack.last()&&inlineTags[stack.last()])onEndTag(stack.last());//块标签
  827. if(closeSelfTags[tagName]&&stack.last()===tagName)onEndTag(tagName);//自封闭标签
  828. unary = emptyTags[ tagName ] || !!unary;
  829. if (!unary)stack.push(tagName);
  830. var all=Array();
  831. all.push('<' + tagName);
  832. rest.replace(regAttr, function(match, name)
  833. {
  834. name=name.toLowerCase();
  835. var value = arguments[2] ? arguments[2] :
  836. arguments[3] ? arguments[3] :
  837. arguments[4] ? arguments[4] :
  838. fillAttrsTags[name] ? name : "";
  839. all.push(' '+name+'="'+value+'"');
  840. });
  841. all.push((unary ? " /" : "") + ">");
  842. addHtmlFrag(all.join(''),tagName,true);
  843. if(tagName==='pre')stopFormat=true;
  844. }
  845. function onEndTag(tagName)
  846. {
  847. if(!tagName)var pos=0;//清空栈
  848. else for(var pos=stack.length-1;pos>=0;pos--)if(stack[pos]===tagName)break;//向上寻找匹配的开始标签
  849. if(pos>=0)
  850. {
  851. for(var i=stack.length-1;i>=pos;i--)addHtmlFrag("</" + stack[i] + ">",stack[i]);
  852. stack.length=pos;
  853. }
  854. if(tagName==='pre'){
  855. stopFormat=false;
  856. lvl--;
  857. }
  858. }
  859. function onText(text){
  860. addHtmlFrag(_this.domEncode(text));
  861. }
  862. function onCDATA(text){
  863. results.push(text);
  864. }
  865. function onComment(text){
  866. results.push(text);
  867. }
  868. function addHtmlFrag(html,tagName,bStart)
  869. {
  870. if(!stopFormat)html=html.replace(/(\t*\r?\n\t*)+/g,'');//清理换行符和相邻的制表符
  871. if(!stopFormat&&bFormat===true)
  872. {
  873. if(html.match(/^\s*$/))return;//不格式化空内容的标签
  874. var bBlock=blockTags[tagName],tag=bBlock?tagName:'';
  875. if(bBlock)
  876. {
  877. if(bStart)lvl++;//块开始
  878. if(lastTag==='')lvl--;//补文本结束
  879. }
  880. else if(lastTag)lvl++;//文本开始
  881. if(tag!==lastTag||bBlock)addIndent();
  882. results.push(html);
  883. if(tagName==='br')addIndent();//回车强制换行
  884. if(bBlock&&(emptyTags[tagName]||!bStart))lvl--;//块结束
  885. lastTag=bBlock?tagName:'';lastTagStart=bStart;
  886. }
  887. else results.push(html);
  888. }
  889. function addIndent(){results.push('\r\n');if(lvl>0){var tabs=lvl;while(tabs--)results.push("\t");}}
  890. //font转style
  891. function font2style(all,tag,attrs,content)
  892. {
  893. if(!attrs)return content;
  894. var styles='',f,s,c,style;
  895. f=attrs.match(/ face\s*=\s*"\s*([^"]+)\s*"/i);
  896. if(f)styles+='font-family:'+f[1]+';';
  897. s=attrs.match(/ size\s*=\s*"\s*(\d+)\s*"/i);
  898. if(s)styles+='font-size:'+arrFontsize[(s[1]>7?7:(s[1]<1?1:s[1]))-1].n+';';
  899. c=attrs.match(/ color\s*=\s*"\s*([^"]+)\s*"/i);
  900. if(c)styles+='color:'+c[1]+';';
  901. style=attrs.match(/ style\s*=\s*"\s*([^"]+)\s*"/i);
  902. if(style)styles+=style[1];
  903. if(styles)content='<span style="'+styles+'">'+content+'</span>';
  904. return content;
  905. }
  906. sHtml = sHtml.replace(/<(font)(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?<\/\1>)*?)<\/\1>/ig,font2style);//第3层
  907. sHtml = sHtml.replace(/<(font)(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S]|<\1(\s+[^>]*?)?>((?!<\1(\s+[^>]*?)?>)[\s\S])*?<\/\1>)*?)<\/\1>/ig,font2style);//第2层
  908. sHtml = sHtml.replace(/<(font)(\s+[^>]*?)?>(((?!<\1(\s+[^>]*?)?>)[\s\S])*?)<\/\1>/ig,font2style);//最里层
  909. sHtml = sHtml.replace(/^(\s*\r?\n)+|(\s*\r?\n)+$/g,'');//清理首尾换行
  910. return sHtml;
  911. }
  912. this.toggleShowBlocktag=function(state)
  913. {
  914. if(bShowBlocktag===state)return;
  915. bShowBlocktag=!bShowBlocktag;
  916. var _jBody=$(_doc.body);
  917. if(bShowBlocktag)
  918. {
  919. bodyClass+=' showBlocktag';
  920. _jBody.addClass('showBlocktag');
  921. }
  922. else
  923. {
  924. bodyClass=bodyClass.replace(' showBlocktag','');
  925. _jBody.removeClass('showBlocktag');
  926. }
  927. }
  928. this.toggleSource=function(state)
  929. {
  930. if(bSource===state)return;
  931. _jTools.find('[name=Source]').toggleClass('xheEnabled').toggleClass('xheActive');
  932. var _body=_doc.body,jBody=$(_body),sHtml;
  933. var sourceCode,cursorMark='<span id="_xhe_cursor"></span>',cursorPos=0;
  934. if(!bSource)
  935. {//转为源代码模式
  936. _this.pasteHTML(cursorMark,true);//标记当前位置
  937. sHtml=_this.getSource(true);
  938. cursorPos=sHtml.indexOf(cursorMark);
  939. if(!isOpera)cursorPos=sHtml.substring(0,cursorPos).replace(/\r/g,'').length;//修正非opera光标定位点
  940. sHtml=sHtml.replace(cursorMark,'');
  941. if(isIE)_body.contentEditable='false';
  942. else _doc.designMode = 'Off';
  943. jBody.attr('scroll','no').attr('class','sourceMode').html('<textarea id="sourceCode" wrap="soft" spellcheck="false" height="100%" />');
  944. sourceCode=$('#sourceCode',jBody).blur(_this.getSource)[0];
  945. }
  946. else
  947. {//转为编辑模式
  948. sHtml=_this.getSource();
  949. jBody.html('').removeAttr('scroll').attr('class','editMode'+bodyClass);
  950. if(isIE)_body.contentEditable='true';
  951. else _doc.designMode = 'On';
  952. if(isMozilla)
  953. {
  954. _this._exec("inserthtml","-");//修正firefox源代码切换回来无法删除文字的问题
  955. $('#'+idFixFFCursor).show().focus().hide();//临时修正Firefox 3.6光标丢失问题
  956. }
  957. }
  958. bSource=!bSource;
  959. _this.setSource(sHtml);
  960. _this.focus();
  961. if(bSource)//光标定位源码
  962. {
  963. if(sourceCode.setSelectionRange)sourceCode.setSelectionRange(cursorPos, cursorPos);
  964. else
  965. {
  966. var rng = sourceCode.createTextRange();
  967. rng.move("character",cursorPos);
  968. rng.select();
  969. }
  970. }
  971. else _this.setCursorFirst(true);//定位最前面
  972. _jTools.find('[name=Source],[name=Preview]').toggleClass('xheEnabled');
  973. _jTools.find('.xheButton').not('[name=Source],[name=Fullscreen],[name=About]').toggleClass('xheEnabled');
  974. setTimeout(setOpts,300);
  975. }
  976. this.showPreview=function()
  977. {
  978. var beforeSetSource=settings.beforeSetSource,sContent=_this.getSource();
  979. if(beforeSetSource)sContent=beforeSetSource(sContent);
  980. var sHTML='<html><head>'+headHTML+'<title>预览</title>'+(urlBase?'<base href="'+urlBase+'"/>':'')+'</head><body>' + sContent + '</body></html>';
  981. var screen=window.screen,oWindow=window.open('', 'xhePreview', 'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width='+Math.round(screen.width*0.9)+',height='+Math.round(screen.height*0.8)+',left='+Math.round(screen.width*0.05)),oDoc=oWindow.document;
  982. oDoc.open();
  983. oDoc.write(sHTML);
  984. oDoc.close();
  985. oWindow.focus();
  986. }
  987. this.toggleFullscreen=function(state)
  988. {
  989. if(bFullscreen===state)return;
  990. var jLayout=$('#'+idContainer).find('.xheLayout'),jContainer=$('#'+idContainer),browserVer=jQuery.browser.version,isIE67=(isIE&&(browserVer==6||browserVer==7));
  991. if(bFullscreen)
  992. {//取消全屏
  993. if(isIE67)_jText.after(jContainer);
  994. jLayout.attr('style',sLayoutStyle);
  995. _jArea.height(editorHeight-_jTools.outerHeight());
  996. $(window).scrollTop(outerScroll);
  997. setTimeout(function(){$(window).scrollTop(outerScroll);},10);//Firefox需要延迟设置
  998. }
  999. else
  1000. {//显示全屏
  1001. if(isIE67)$('body').append(jContainer);
  1002. outerScroll=$(window).scrollTop();
  1003. sLayoutStyle=jLayout.attr('style');
  1004. jLayout.removeAttr('style');
  1005. _jArea.height('100%');
  1006. setTimeout(fixFullHeight,100);
  1007. }
  1008. if(isMozilla)//临时修正Firefox 3.6源代码光标丢失问题
  1009. {
  1010. $('#'+idFixFFCursor).show().focus().hide();
  1011. setTimeout(_this.focus,1);
  1012. }
  1013. else if(isIE67)_this.setCursorFirst();
  1014. bFullscreen=!bFullscreen;
  1015. jContainer.toggleClass('xhe_Fullscreen');
  1016. $('html').toggleClass('xhe_Fullfix');
  1017. _jTools.find('[name=Fullscreen]').toggleClass('xheActive');
  1018. setTimeout(setOpts,300);
  1019. }
  1020. this.showMenu=function(menuitems,callback)
  1021. {
  1022. var jMenu=$('<div class="xheMenu"></div>'),arrItem=[];
  1023. $.each(menuitems,function(n,v){arrItem.push('<a href="javascript:void(0);" title="'+(v.t?v.t:v.s)+'" v="'+v.v+'">'+v.s+'</a>');});
  1024. jMenu.append(arrItem.join(''));
  1025. jMenu.click(function(ev){callback($(ev.target).closest('a').attr('v'));_this.hidePanel();return false;}).mousedown(returnFalse);
  1026. _this.showPanel(jMenu);
  1027. }
  1028. this.showColor=function(callback)
  1029. {
  1030. var jColor=$('<div class="xheColor"></div>'),arrItem=[],count=0;
  1031. $.each(itemColors,function(n,v)
  1032. {
  1033. if(count%7===0)arrItem.push((count>0?'</div>':'')+'<div>');
  1034. arrItem.push('<a href="javascript:void(0);" xhev="'+v+'" title="'+v+'" style="background:'+v+'"></a>');
  1035. count++;
  1036. });
  1037. arrItem.push('</div>');
  1038. jColor.append(arrItem.join(''));
  1039. jColor.click(function(ev){ev=ev.target;if(!$.nodeName(ev,'A'))return;callback($(ev).attr('xhev'));_this.hidePanel();return false;}).mousedown(returnFalse);
  1040. _this.showPanel(jColor);
  1041. }
  1042. this.showPastetext=function()
  1043. {
  1044. var jPastetext=$(htmlPastetext),jValue=$('#xhePastetextValue',jPastetext),jSave=$('#xheSave',jPastetext);
  1045. jSave.click(function(){
  1046. _this.loadBookmark();
  1047. var sValue=jValue.val();
  1048. if(sValue)_this.pasteText(sValue);
  1049. _this.hidePanel();
  1050. return false;
  1051. });
  1052. _this.showDialog(jPastetext);
  1053. }
  1054. this.showLink=function()
  1055. {
  1056. var jLink=$(htmlLink),jParent=_this.getParent('a'),jText=$('#xheLinkText',jLink),jUrl=$('#xheLinkUrl',jLink),jTarget=$('#xheLinkTarget',jLink),jSave=$('#xheSave',jLink),selHtml=_this.getSelect();
  1057. if(jParent.length===1)
  1058. {
  1059. jUrl.val(xheAttr(jParent,'href'));
  1060. jTarget.attr('value',jParent.attr('target'));
  1061. }
  1062. else if(selHtml==='')jText.val(settings.defLinkText).closest('div').show();
  1063. if(settings.upLinkUrl)_this.uploadInit(jUrl,settings.upLinkUrl,settings.upLinkExt);
  1064. jSave.click(function(){
  1065. var url=jUrl.val();
  1066. _this.loadBookmark();
  1067. if(url===''||jParent.length===0)_this._exec('unlink');
  1068. if(url!==''&&url!=='http://')
  1069. {
  1070. var aUrl=url.split(' '),sTarget=jTarget.val(),sText=jText.val();
  1071. if(aUrl.length>1)
  1072. {//批量插入
  1073. _this._exec('unlink');//批量前删除当前链接并重新获取选择内容
  1074. selHtml=_this.getSelect();
  1075. var sTemplate='<a href="xhe_tmpurl"',sLink,arrLink=[];
  1076. if(sTarget!=='')sTemplate+=' target="'+sTarget+'"';
  1077. sTemplate+='>xhe_tmptext</a>';
  1078. sText=(selHtml!==''?selHtml:(sText?sText:url));
  1079. for(var i in aUrl)
  1080. {
  1081. url=aUrl[i];
  1082. if(url!=='')
  1083. {
  1084. url=url.split('||');
  1085. sLink=sTemplate;
  1086. sLink=sLink.replace('xhe_tmpurl',url[0]);
  1087. sLink=sLink.replace('xhe_tmptext',url[1]?url[1]:sText);
  1088. arrLink.push(sLink);
  1089. }
  1090. }
  1091. _this.pasteHTML(arrLink.join('&nbsp;'));
  1092. }
  1093. else
  1094. {//单url模式
  1095. url=aUrl[0].split('||');
  1096. if(!sText)sText=url[0];
  1097. sText=url[1]?url[1]:(selHtml!=='')?'':sText?sText:url[0];
  1098. if(jParent.length===0)
  1099. {
  1100. if(sText)_this.pasteHTML('<a href="#xhe_tmpurl">'+sText+'</a>');
  1101. else _this._exec('createlink','#xhe_tmpurl');
  1102. jParent=$('a[href$="#xhe_tmpurl"]',_doc);
  1103. }
  1104. else if(sText&&!isSafari)jParent.text(sText);//safari改写文本会导致光标丢失
  1105. xheAttr(jParent,'href',url[0]);
  1106. if(sTarget!=='')jParent.attr('target',sTarget);
  1107. else jParent.removeAttr('target');
  1108. }
  1109. }
  1110. _this.hidePanel();
  1111. return false;
  1112. });
  1113. _this.showDialog(jLink);
  1114. }
  1115. this.showImg=function()
  1116. {
  1117. var jImg=$(htmlImg),jParent=_this.getParent('img'),jUrl=$('#xheImgUrl',jImg),jAlt=$('#xheImgAlt',jImg),jAlign=$('#xheImgAlign',jImg),jWidth=$('#xheImgWidth',jImg),jHeight=$('#xheImgHeight',jImg),jBorder=$('#xheImgBorder',jImg),jVspace=$('#xheImgVspace',jImg),jHspace=$('#xheImgHspace',jImg),jSave=$('#xheSave',jImg);
  1118. if(jParent.length===1)
  1119. {
  1120. jUrl.val(xheAttr(jParent,'src'));
  1121. jAlt.val(jParent.attr('alt'));
  1122. jAlign.val(jParent.attr('align'));
  1123. jWidth.val(jParent.attr('width'));
  1124. jHeight.val(jParent.attr('height'));
  1125. jBorder.val(jParent.attr('border'));
  1126. var vspace=jParent.attr('vspace'),hspace=jParent.attr('hspace');
  1127. jVspace.val(vspace<=0?'':vspace);
  1128. jHspace.val(hspace<=0?'':hspace);
  1129. }
  1130. if(settings.upImgUrl)_this.uploadInit(jUrl,settings.upImgUrl,settings.upImgExt);
  1131. jSave.click(function(){
  1132. _this.loadBookmark();
  1133. var url=jUrl.val();
  1134. if(url!==''&&url!=='http://')
  1135. {
  1136. var aUrl=url.split(' '),sAlt=jAlt.val(),sAlign=jAlign.val(),sWidth=jWidth.val(),sHeight=jHeight.val(),sBorder=jBorder.val(),sVspace=jVspace.val(),sHspace=jHspace.val();;
  1137. if(aUrl.length>1)
  1138. {//批量插入
  1139. var sTemplate='<img src="xhe_tmpurl"',sImg,arrImg=[];
  1140. if(sAlt!=='')sTemplate+=' alt="'+sAlt+'"';
  1141. if(sAlign!=='')sTemplate+=' align="'+sAlign+'"';
  1142. if(sWidth!=='')sTemplate+=' width="'+sWidth+'"';
  1143. if(sHeight!=='')sTemplate+=' height="'+sHeight+'"';
  1144. if(sBorder!=='')sTemplate+=' border="'+sBorder+'"';
  1145. if(sVspace!=='')sTemplate+=' vspace="'+sVspace+'"';
  1146. if(sHspace!=='')sTemplate+=' hspace="'+sHspace+'"';
  1147. sTemplate+=' />';
  1148. for(var i in aUrl)
  1149. {
  1150. url=aUrl[i];
  1151. if(url!=='')
  1152. {
  1153. url=url.split('||');
  1154. sImg=sTemplate;
  1155. sImg=sImg.replace('xhe_tmpurl',url[0]);
  1156. if(url[1])sImg='<a href="'+url[1]+'" target="_blank">'+sImg+'</a>'
  1157. arrImg.push(sImg);
  1158. }
  1159. }
  1160. _this.pasteHTML(arrImg.join('&nbsp;'));
  1161. }
  1162. else if(aUrl.length===1)
  1163. {//单URL模式
  1164. url=aUrl[0];
  1165. if(url!=='')
  1166. {
  1167. url=url.split('||');
  1168. if(jParent.length===0)
  1169. {
  1170. _this.pasteHTML('<img src="'+url[0]+'#xhe_tmpurl" />');
  1171. jParent=$('img[src$="#xhe_tmpurl"]',_doc);
  1172. }
  1173. xheAttr(jParent,'src',url[0])
  1174. if(sAlt!=='')jParent.attr('alt',sAlt);
  1175. if(sAlign!=='')jParent.attr('align',sAlign);
  1176. else jParent.removeAttr('align');
  1177. if(sWidth!=='')jParent.attr('width',sWidth);
  1178. else jParent.removeAttr('width');
  1179. if(sHeight!=='')jParent.attr('height',sHeight);
  1180. else jParent.removeAttr('height');
  1181. if(sBorder!=='')jParent.attr('border',sBorder);
  1182. else jParent.removeAttr('border');
  1183. if(sVspace!=='')jParent.attr('vspace',sVspace);
  1184. else jParent.removeAttr('vspace');
  1185. if(sHspace!=='')jParent.attr('hspace',sHspace);
  1186. else jParent.removeAttr('hspace');
  1187. if(url[1])
  1188. {
  1189. var jLink=jParent.parent('a');
  1190. if(jLink.length===0)
  1191. {
  1192. jParent.wrap('<a></a>');
  1193. jLink=jParent.parent('a');
  1194. }
  1195. xheAttr(jLink,'href',url[1]);
  1196. jLink.attr('target','_blank');
  1197. }
  1198. }
  1199. }
  1200. }
  1201. else if(jParent.length===1)jParent.remove();
  1202. _this.hidePanel();
  1203. return false;
  1204. });
  1205. _this.showDialog(jImg);
  1206. }
  1207. this.showEmbed=function(sType,sHtml,sMime,sClsID,sBaseAttrs,sUploadUrl,sUploadExt)
  1208. {
  1209. var jEmbed=$(sHtml),jParent=_this.getParent('embed[type="'+sMime+'"],embed[classid="'+sClsID+'"]'),jUrl=$('#xhe'+sType+'Url',jEmbed),jWidth=$('#xhe'+sType+'Width',jEmbed),jHeight=$('#xhe'+sType+'Height',jEmbed),jSave=$('#xheSave',jEmbed);
  1210. if(sUploadUrl)_this.uploadInit(jUrl,sUploadUrl,sUploadExt);
  1211. _this.showDialog(jEmbed);
  1212. if(jParent.length===1)
  1213. {
  1214. jUrl.val(xheAttr(jParent,'src'));
  1215. jWidth.val(jParent.attr('width'));
  1216. jHeight.val(jParent.attr('height'));
  1217. }
  1218. jSave.click(function(){
  1219. _this.loadBookmark();
  1220. var url=jUrl.val();
  1221. if(url!==''&&url!=='http://')
  1222. {
  1223. var w=jWidth.val(),h=jHeight.val(),reg=/^\d+%?$/;
  1224. if(!reg.test(w))w=412;if(!reg.test(h))h=300;
  1225. var sBaseCode='<embed type="'+sMime+'" classid="'+sClsID+'" src="xhe_tmpurl"'+sBaseAttrs;
  1226. var aUrl=url.split(' ');
  1227. if(aUrl.length>1)
  1228. {//批量插入
  1229. var sTemplate=sBaseCode+'',sEmbed,arrEmbed=[];
  1230. sTemplate+=' width="xhe_width" height="xhe_height" />';
  1231. for(var i in aUrl)
  1232. {
  1233. url=aUrl[i].split('||');
  1234. sEmbed=sTemplate;
  1235. sEmbed=sEmbed.replace('xhe_tmpurl',url[0])
  1236. sEmbed=sEmbed.replace('xhe_width',url[1]?url[1]:w)
  1237. sEmbed=sEmbed.replace('xhe_height',url[2]?url[2]:h)
  1238. if(url!=='')arrEmbed.push(sEmbed);
  1239. }
  1240. _this.pasteHTML(arrEmbed.join('&nbsp;'));
  1241. }
  1242. else if(aUrl.length===1)
  1243. {//单URL模式
  1244. url=aUrl[0].split('||');
  1245. if(jParent.length===0)
  1246. {
  1247. _this.pasteHTML(sBaseCode.replace('xhe_tmpurl',url[0]+'#xhe_tmpurl')+' />');
  1248. jParent=$('embed[src$="#xhe_tmpurl"]',_doc);
  1249. }
  1250. xheAttr(jParent,'src',url[0]);
  1251. jParent.attr('width',url[1]?url[1]:w);
  1252. jParent.attr('height',url[2]?url[2]:h);
  1253. }
  1254. }
  1255. else if(jParent.length===1)jParent.remove();
  1256. _this.hidePanel();
  1257. return false;
  1258. });
  1259. }
  1260. this.showEmot=function(group)
  1261. {
  1262. var jEmot=$('<div class="xheEmot"></div>');
  1263. group=group?group:(selEmotGroup?selEmotGroup:'default');
  1264. var arrEmot=arrEmots[group];
  1265. var sEmotPath=emotPath+group+'/',n=0,arrList=[],jList='';
  1266. var ew=arrEmot.width,eh=arrEmot.height,line=arrEmot.line,count=arrEmot.count,list=arrEmot.list;
  1267. if(count)
  1268. {
  1269. for(var i=1;i<=count;i++)
  1270. {
  1271. n++;
  1272. arrList.push('<a href="javascript:void(0);" style="background-image:url('+sEmotPath+i+'.gif);" emot="'+group+','+i+'" xhev="">&nbsp;</a>');
  1273. if(n%line===0)arrList.push('<br />');
  1274. }
  1275. }
  1276. else
  1277. {
  1278. $.each(list,function(id,title)
  1279. {
  1280. n++;
  1281. arrList.push('<a href="javascript:void(0);" style="background-image:url('+sEmotPath+id+'.gif);" emot="'+group+','+id+'" title="'+title+'" xhev="'+title+'">&nbsp;</a>');
  1282. if(n%line===0)arrList.push('<br />');
  1283. });
  1284. }
  1285. var w=line*(ew+12),h=Math.ceil(n/line)*(eh+12),mh=w*0.75;
  1286. if(h<=mh)mh='';
  1287. jList=$('<style>'+(mh?'.xheEmot div{width:'+(w+20)+'px;height:'+mh+'px;}':'')+'.xheEmot div a{width:'+ew+'px;height:'+eh+'px;}</style><div>'+arrList.join('')+'</div>').click(function(ev){ev=ev.target;var jA=$(ev);if(!$.nodeName(ev,'A'))return;_this.pasteHTML('<img emot="'+jA.attr('emot')+'" alt="'+jA.attr('xhev')+'">');_this.hidePanel();return false;}).mousedown(returnFalse);
  1288. jEmot.append(jList);
  1289. var gcount=0,arrGroup=['<ul>'],jGroup;//表情分类
  1290. $.each(arrEmots,function(g,v){
  1291. gcount++;
  1292. arrGroup.push('<li'+(group===g?' class="cur"':'')+'><a href="javascript:void(0);" group="'+g+'">'+v.name+'</a></li>');
  1293. });
  1294. if(gcount>1)
  1295. {
  1296. arrGroup.push('</ul><br style="clear:both;" />');
  1297. jGroup=$(arrGroup.join('')).click(function(ev){selEmotGroup=$(ev.target).attr('group');_this.exec('Emot');return false;}).mousedown(returnFalse);
  1298. jEmot.append(jGroup);
  1299. }
  1300. _this.showPanel(jEmot);
  1301. }
  1302. this.showTable=function()
  1303. {
  1304. var jTable=$(htmlTable),jRows=$('#xheTableRows',jTable),jColumns=$('#xheTableColumns',jTable),jHeaders=$('#xheTableHeaders',jTable),jWidth=$('#xheTableWidth',jTable),jHeight=$('#xheTableHeight',jTable),jBorder=$('#xheTableBorder',jTable),jCellSpacing=$('#xheTableCellSpacing',jTable),jCellPadding=$('#xheTableCellPadding',jTable),jAlign=$('#xheTableAlign',jTable),jCaption=$('#xheTableCaption',jTable),jSave=$('#xheSave',jTable);
  1305. jSave.click(function(){
  1306. _this.loadBookmark();
  1307. var sCaption=jCaption.val(),sBorder=jBorder.val(),sRows=jRows.val(),sCols=jColumns.val(),sHeaders=jHeaders.val(),sWidth=jWidth.val(),sHeight=jHeight.val(),sCellSpacing=jCellSpacing.val(),sCellPadding=jCellPadding.val(),sAlign=jAlign.val();
  1308. var i,j,htmlTable='<table'+(sBorder!==''?' border="'+sBorder+'"':'')+(sWidth!==''?' width="'+sWidth+'"':'')+(sHeight!==''?' width="'+sHeight+'"':'')+(sCellSpacing!==''?' cellspacing="'+sCellSpacing+'"':'')+(sCellPadding!==''?' cellpadding="'+sCellPadding+'"':'')+(sAlign!==''?' align="'+sAlign+'"':'')+'>';
  1309. if(sCaption!=='')htmlTable+='<caption>'+sCaption+'</caption>';
  1310. if(sHeaders==='row'||sHeaders==='both')
  1311. {
  1312. htmlTable+='<tr>';
  1313. for(i=0;i<sCols;i++)htmlTable+='<th scope="col">&nbsp;</th>';
  1314. htmlTable+='</tr>';
  1315. sRows--;
  1316. }
  1317. htmlTable+='<tbody>';
  1318. for(i=0;i<sRows;i++)
  1319. {
  1320. htmlTable+='<tr>';
  1321. for(j=0;j<sCols;j++)
  1322. {
  1323. if(j===0&&(sHeaders==='col'||sHeaders==='both'))htmlTable+='<th scope="row">&nbsp;</th>';
  1324. else htmlTable+='<td>&nbsp;</td>';
  1325. }
  1326. htmlTable+='</tr>';
  1327. }
  1328. htmlTable+='</tbody></table>';
  1329. _this.pasteHTML(htmlTable);
  1330. _this.hidePanel();
  1331. return false;
  1332. });
  1333. _this.showDialog(jTable);
  1334. }
  1335. this.showAbout=function()
  1336. {
  1337. var jAbout=$(htmlAbout);
  1338. _this.showDialog(jAbout);
  1339. }
  1340. this.addShortcuts=function(key,cmd)
  1341. {
  1342. key=key.toLowerCase();
  1343. if(arrShortCuts[key]===undefined)arrShortCuts[key]=Array();
  1344. arrShortCuts[key].push(cmd);
  1345. }
  1346. this.delShortcuts=function(key){delete arrShortCuts[key];}
  1347. this.uploadInit=function(jText,toUrl,upext)
  1348. {
  1349. var jUpload=$('<span class="xheUpload"><input type="text" style="visibility:hidden;" tabindex="-1" /><input type="button" value="'+settings.upBtnText+'" class="xheBtn" tabindex="-1" /></span>'),jUpBtn=$('.xheBtn',jUpload);
  1350. var bHtml5Upload=settings.html5Upload,upMultiple=bHtml5Upload?settings.upMultiple:1;
  1351. jText.after(jUpload);jUpBtn.before(jText);
  1352. toUrl=toUrl.replace(/{editorRoot}/ig,editorRoot);
  1353. if(toUrl.substr(0,1)==='!')//自定义上传管理页
  1354. {
  1355. jUpBtn.click(function(){
  1356. bShowPanel=false;//防止按钮面板被关闭
  1357. _this.showIframeModal('上传文件',toUrl.substr(1),setUploadMsg,null,null,function(){bShowPanel=true;});
  1358. });
  1359. }
  1360. else
  1361. {//系统默认ajax上传
  1362. jUpload.append('<input type="file"'+(upMultiple>1?' multiple=""':'')+' class="xheFile" size="13" name="'+uploadInputname+'" tabindex="-1" />');
  1363. var jFile=$('.xheFile',jUpload),arrMsg;
  1364. jFile.change(function(){arrMsg=[];_this.startUpload(jFile[0],toUrl,upext,setUploadMsg);});
  1365. setTimeout(function(){//拖放上传
  1366. jText.closest('.xheDialog').bind('dragenter dragover',returnFalse).bind('drop',function(ev){
  1367. var dataTransfer=ev.originalEvent.dataTransfer,fileList;
  1368. if(bHtml5Upload&&dataTransfer&&(fileList=dataTransfer.files)&&fileList.length>0)_this.startUpload(fileList,toUrl,upext,setUploadMsg);
  1369. return false;
  1370. });
  1371. },10);
  1372. }
  1373. function setUploadMsg(arrMsg)
  1374. {
  1375. if(is(arrMsg,'string'))arrMsg=[arrMsg];//允许单URL传递
  1376. var bImmediate=false,i,count=arrMsg.length,msg,url,arrUrl=[],onUpload=settings.onUpload;
  1377. if(onUpload)onUpload(arrMsg);//用户上传回调
  1378. for(i=0;i<count;i++)
  1379. {
  1380. msg=arrMsg[i];
  1381. url=is(msg,'string')?msg:msg.url;
  1382. if(url.substr(0,1)==='!'){bImmediate=true;url=url.substr(1);}
  1383. arrUrl.push(url);
  1384. }
  1385. jText.val(arrUrl.join(' '));
  1386. if(bImmediate)jText.closest('.xheDialog').find('#xheSave').click();
  1387. }
  1388. }
  1389. this.startUpload=function(fromFiles,toUrl,limitExt,onUploadComplete)
  1390. {
  1391. var arrMsg=[],bHtml5Upload=settings.html5Upload,upMultiple=bHtml5Upload?settings.upMultiple:1;
  1392. var upload,fileList,filename,jUploadTip=$('<div style="padding:22px 0;text-align:center;line-height:30px;">文件上传中,请稍候……<br /></div>'),sLoading='<img src="'+skinPath+'img/loading.gif">';
  1393. if(!bHtml5Upload||(fromFiles.nodeType&&!((fileList=fromFiles.files)&&fileList[0])))
  1394. {
  1395. if(!checkFileExt(fromFiles.value,limitExt))return;
  1396. jUploadTip.append(sLoading);
  1397. upload=new _this.html4Upload(fromFiles,toUrl,onUploadCallback);
  1398. }
  1399. else
  1400. {
  1401. if(!fileList)fileList=fromFiles;//拖放文件列表
  1402. var i,len=fileList.length;
  1403. if(len>upMultiple){alert('请不要一次上传超过'+upMultiple+'个文件');return;}
  1404. for(i=0;i<len;i++)if(!checkFileExt(fileList[i].fileName,limitExt))return;
  1405. var jProgress=$('<div class="xheProgress"><div><span>0%</span></div></div>');
  1406. jUploadTip.append(jProgress);
  1407. upload=new _this.html5Upload(uploadInputname,fileList,toUrl,onUploadCallback,function(ev){
  1408. if(ev.loaded>=0)
  1409. {
  1410. var sPercent=Math.round((ev.loaded * 100) / ev.total)+'%';
  1411. $('div',jProgress).css('width',sPercent);
  1412. $('span',jProgress).text(sPercent+' ( '+formatBytes(ev.loaded)+' / '+formatBytes(ev.total)+' )');
  1413. }
  1414. else jProgress.replaceWith(sLoading);//不支持进度
  1415. });
  1416. }
  1417. var panelState=bShowPanel;
  1418. if(panelState)bShowPanel=false;//防止面板被关闭
  1419. _this.showModal('文件上传中(Esc取消上传)',jUploadTip,320,150,function(){bShowPanel=panelState;upload.remove();});
  1420. upload.start();
  1421. function onUploadCallback(sText,bFinish)
  1422. {
  1423. var data=Object,bOK=false;
  1424. try{data=eval('('+sText+')');}catch(ex){};
  1425. if(data.err===undefined||data.msg===undefined)alert(toUrl+' 上传接口发生错误!\r\n\r\n返回的错误内容为: \r\n\r\n'+sText);
  1426. else
  1427. {
  1428. if(data.err)alert(data.err);
  1429. else
  1430. {
  1431. arrMsg.push(data.msg);
  1432. bOK=true;//继续下一个文件上传
  1433. }
  1434. }
  1435. if(!bOK||bFinish)_this.removeModal();
  1436. if(bFinish&&bOK)onUploadComplete(arrMsg);//全部上传完成
  1437. return bOK;
  1438. }
  1439. }
  1440. this.html4Upload=function(fromfile,toUrl,callback)
  1441. {
  1442. var uid = new Date().getTime(),idIO='jUploadFrame'+uid,_this=this;
  1443. var jIO=$('<iframe name="'+idIO+'" class="xheHideArea" />').appendTo('body');
  1444. var jForm=$('<form action="'+toUrl+'" target="'+idIO+'" method="post" enctype="multipart/form-data" class="xheHideArea"></form>').appendTo('body');
  1445. var jOldFile = $(fromfile),jNewFile = jOldFile.clone().attr('disabled','true');
  1446. jOldFile.before(jNewFile).appendTo(jForm);
  1447. this.remove=function()
  1448. {
  1449. if(_this!==null)
  1450. {
  1451. jNewFile.before(jOldFile).remove();
  1452. jIO.remove();jForm.remove();
  1453. _this=null;
  1454. }
  1455. }
  1456. this.onLoad=function(){callback($(jIO[0].contentWindow.document.body).text(),true);}
  1457. this.start=function(){jForm.submit();jIO.load(_this.onLoad);}
  1458. return this;
  1459. }
  1460. this.html5Upload=function(inputname,fromFiles,toUrl,callback,onProgress)
  1461. {
  1462. var xhr,i=0,count=fromFiles.length,allLoaded=0,allSize=0,_this=this;
  1463. for(var j=0;j<count;j++)allSize+=fromFiles[j].fileSize;
  1464. this.remove=function(){if(xhr){xhr.abort();xhr=null;}}
  1465. this.uploadNext=function(sText)
  1466. {
  1467. if(sText)//当前文件上传完成
  1468. {
  1469. allLoaded+=fromFiles[i-1].fileSize;
  1470. returnProgress(0);
  1471. }
  1472. if((!sText||(sText&&callback(sText,i===count)===true))&&i<count)postFile(fromFiles[i++],toUrl,_this.uploadNext,function(loaded){returnProgress(loaded);});
  1473. }
  1474. this.start=function(){_this.uploadNext();}
  1475. function postFile(fromfile,toUrl,callback,onProgress)
  1476. {
  1477. xhr = new XMLHttpRequest(),upload=xhr.upload;
  1478. xhr.onreadystatechange=function(){if(xhr.readyState===4)callback(xhr.responseText);};
  1479. if(upload)upload.onprogress=function(ev){onProgress(ev.loaded);};
  1480. else onProgress(-1);//不支持进度
  1481. xhr.open("POST", toUrl);
  1482. xhr.setRequestHeader('Content-Type', 'application/octet-stream');
  1483. xhr.setRequestHeader('Content-Disposition', 'attachment; name="'+inputname+'"; filename="'+fromfile.fileName+'"');
  1484. if(xhr.sendAsBinary)xhr.sendAsBinary(fromfile.getAsBinary());
  1485. else xhr.send(fromfile);
  1486. }
  1487. function returnProgress(loaded){if(onProgress)onProgress({'loaded':allLoaded+loaded,'total':allSize});}
  1488. }
  1489. this.showIframeModal=function(title,ifmurl,callback,w,h,onRemove)
  1490. {
  1491. var jContent=$('<iframe frameborder="0" src="'+ifmurl.replace(/{editorRoot}/ig,editorRoot)+'" style="width:100%;height:100%;display:none;" /><div class="xheModalIfmWait"></div>'),jIframe=$(jContent[0]),jWait=$(jContent[1]);
  1492. _this.showModal(title,jContent,w,h,onRemove);
  1493. jIframe.load(function(){
  1494. var modalWin=jIframe[0].contentWindow,jModalDoc=$(modalWin.document);
  1495. modalWin.callback=function(v){_this.removeModal();callback(v);};
  1496. modalWin.unloadme=_this.removeModal;
  1497. jModalDoc.keydown(_this.checkEsc);
  1498. jIframe.show();jWait.remove();
  1499. });
  1500. }
  1501. this.showModal=function(title,content,w,h,onRemove)
  1502. {
  1503. if(bShowModal)return false;//只能弹出一个模式窗口
  1504. layerShadow=settings.layerShadow;
  1505. w=w?w:settings.modalWidth;h=h?h:settings.modalHeight;
  1506. jModal=$('<div class="xheModal" style="width:'+(w-1)+'px;height:'+h+'px;margin-left:-'+Math.ceil(w/2)+'px;'+(isIE&&browerVer<=7.0?'':'margin-top:-'+Math.ceil(h/2)+'px')+'">'+(settings.modalTitle?'<div class="xheModalTitle"><span class="xheModalClose" title="关闭 (Esc)"></span>'+title+'</div>':'')+'<div class="xheModalContent"></div></div>').appendTo('body');
  1507. jOverlay=$('<div class="xheModalOverlay"></div>').appendTo('body');
  1508. if(layerShadow>0)jModalShadow=$('<div class="xheModalShadow" style="width:'+jModal.outerWidth()+'px;height:'+jModal.outerHeight()+'px;margin-left:-'+(Math.ceil(w/2)-layerShadow-2)+'px;'+(isIE&&browerVer<=7.0?'':'margin-top:-'+(Math.ceil(h/2)-layerShadow-2)+'px')+'"></div>').appendTo('body');
  1509. $('.xheModalContent',jModal).css('height',h-(settings.modalTitle?$('.xheModalTitle').outerHeight():0)).html(content);
  1510. if(isIE&&browerVer===6.0)jHideSelect=$('select:visible').css('visibility','hidden');//隐藏覆盖的select
  1511. $('.xheModalClose',jModal).click(_this.removeModal);
  1512. jOverlay.show();if(layerShadow>0)jModalShadow.show();jModal.show();
  1513. bShowModal=true;
  1514. onModalRemove=onRemove;
  1515. }
  1516. this.removeModal=function(){if(jHideSelect)jHideSelect.css('visibility','visible');jModal.html('').remove();if(layerShadow>0)jModalShadow.remove();jOverlay.remove();if(onModalRemove)onModalRemove();bShowModal=false;};
  1517. this.showDialog=function(content)
  1518. {
  1519. var jDialog=$('<div class="xheDialog"></div>'),jContent=$(content),jSave=$('#xheSave',jContent);
  1520. if(jSave.length===1)
  1521. {
  1522. jContent.find('input[type=text],select').keypress(function(ev){if(ev.which===13){jSave.click();return false;}});
  1523. jContent.find('textarea').keydown(function(ev){if(ev.ctrlKey&&ev.which===13){jSave.click();return false;}});
  1524. jSave.after(' <input type="button" id="xheCancel" value="取消" />');
  1525. $('#xheCancel',jContent).click(_this.hidePanel);
  1526. if(!settings.clickCancelDialog)
  1527. {
  1528. bClickCancel=false;//关闭点击隐藏
  1529. var jFixCancel=$('<div class="xheFixCancel"></div>').appendTo('body').mousedown(returnFalse);
  1530. var xy=_jArea.offset();
  1531. jFixCancel.css({'left':xy.left,'top':xy.top,width:_jArea.outerWidth(),height:_jArea.outerHeight()})
  1532. }
  1533. jDialog.mousedown(function(){bDisableHoverExec=true;})//点击对话框禁止悬停执行
  1534. }
  1535. jDialog.append(jContent);
  1536. _this.showPanel(jDialog);
  1537. if(!isIE)setTimeout(function(){jDialog.find('input[type=text],textarea').filter(':visible').filter(function(){return $(this).css('visibility')!=='hidden';}).eq(0).focus();},10);//定位首个可见输入表单项,延迟解决opera无法设置焦点
  1538. }
  1539. this.showPanel=function(content)
  1540. {
  1541. if(!ev.target)return false;
  1542. _jPanel.html('').append(content).css('left',-999).css('top',-999);
  1543. _jPanelButton=$(ev.target).closest('a').addClass('xheActive');
  1544. var xy=_jPanelButton.offset();
  1545. var x=xy.left,y=xy.top;y+=_jPanelButton.outerHeight()-1;
  1546. _jCntLine.css({'left':x+1,'top':y,'width':_jPanelButton.width()}).show();
  1547. if((x+_jPanel.outerWidth())>document.body.clientWidth)x-=(_jPanel.outerWidth()-_jPanelButton.outerWidth());//向左显示面板
  1548. var layerShadow=settings.layerShadow;
  1549. if(layerShadow>0)_jShadow.css({'left':x+layerShadow,'top':y+layerShadow,'width':_jPanel.outerWidth(),'height':_jPanel.outerHeight()}).show();
  1550. _jPanel.css({'left':x,'top':y}).show();
  1551. bQuickHoverExec=bShowPanel=true;
  1552. }
  1553. this.hidePanel=function(){if(bShowPanel){_jPanelButton.removeClass('xheActive');_jShadow.hide();_jCntLine.hide();_jPanel.hide();bShowPanel=false;if(!bClickCancel){$('.xheFixCancel').remove();bClickCancel=true;};bQuickHoverExec=bDisableHoverExec=false;lastAngle=null;}}
  1554. this.exec=function(cmd)
  1555. {
  1556. _this.focus();
  1557. _this.hidePanel();
  1558. _this.saveBookmark();
  1559. var tool=arrTools[cmd];
  1560. if(!tool)return false;//无效命令
  1561. if(ev===null)//非鼠标点击
  1562. {
  1563. ev={};
  1564. var btn=_jTools.find('.xheButton[name='+cmd+']');
  1565. if(btn.length===1)ev.target=btn;//设置当前事件焦点
  1566. }
  1567. if(tool.e)tool.e.call(_this)//插件事件
  1568. else//内置工具
  1569. {
  1570. cmd=cmd.toLowerCase();
  1571. switch(cmd)
  1572. {
  1573. case 'cut':
  1574. try{_doc.execCommand(cmd);if(!_doc.queryCommandSupported(cmd))throw 'Error';}
  1575. catch(ex){alert('您的浏览器安全设置不允许使用剪切操作,请使用键盘快捷键(Ctrl + X)来完成');};
  1576. break;
  1577. case 'copy':
  1578. try{_doc.execCommand(cmd);if(!_doc.queryCommandSupported(cmd))throw 'Error';}
  1579. catch(ex){alert('您的浏览器安全设置不允许使用复制操作,请使用键盘快捷键(Ctrl + C)来完成');}
  1580. break;
  1581. case 'paste':
  1582. try{_doc.execCommand(cmd);if(!_doc.queryCommandSupported(cmd))throw 'Error';}
  1583. catch(ex){alert('您的浏览器安全设置不允许使用粘贴操作,请使用键盘快捷键(Ctrl + V)来完成');}
  1584. break;
  1585. case 'pastetext':
  1586. if(window.clipboardData)_this.pasteText(window.clipboardData.getData('Text', true));
  1587. else _this.showPastetext();
  1588. break;
  1589. case 'blocktag':
  1590. var menuBlocktag=[];
  1591. $.each(arrBlocktag,function(n,v){menuBlocktag.push({s:'<'+v.n+'>'+v.t+'</'+v.n+'>',v:'<'+v.n+'>',t:v.t});});
  1592. _this.showMenu(menuBlocktag,function(v){_this._exec('formatblock',v);});
  1593. break;
  1594. case 'fontface':
  1595. var menuFontname=[];
  1596. $.each(arrFontname,function(n,v){v.c=v.c?v.c:v.n;menuFontname.push({s:'<span style="font-family:'+v.c+'">'+v.n+'</span>',v:v.c,t:v.n});});
  1597. _this.showMenu(menuFontname,function(v){_this._exec('fontname',v);});
  1598. break;
  1599. case 'fontsize':
  1600. var menuFontsize=[];
  1601. $.each(arrFontsize,function(n,v){menuFontsize.push({s:'<span style="font-size:'+v.s+';">'+v.t+'('+v.s+')</span>',v:n+1,t:v.t});});
  1602. _this.showMenu(menuFontsize,function(v){_this._exec('fontsize',v);});
  1603. break;
  1604. case 'fontcolor':
  1605. _this.showColor(function(v){_this._exec('forecolor',v);});
  1606. break;
  1607. case 'backcolor':
  1608. _this.showColor(function(v){if(isIE)_this._exec('backcolor',v);else{setCSS(true);_this._exec('hilitecolor',v);setCSS(false);}});
  1609. break;
  1610. case 'align':
  1611. _this.showMenu(menuAlign,function(v){_this._exec(v);});
  1612. break;
  1613. case 'list':
  1614. _this.showMenu(menuList,function(v){_this._exec(v);});
  1615. break;
  1616. case 'link':
  1617. _this.showLink();
  1618. break;
  1619. case 'img':
  1620. _this.showImg();
  1621. break;
  1622. case 'flash':
  1623. _this.showEmbed('Flash',htmlFlash,'application/x-shockwave-flash','clsid:d27cdb6e-ae6d-11cf-96b8-4445535400000',' wmode="opaque" quality="high" menu="false" play="true" loop="true" allowfullscreen="true"',settings.upFlashUrl,settings.upFlashExt);
  1624. break;
  1625. case 'media':
  1626. _this.showEmbed('Media',htmlMedia,'application/x-mplayer2','clsid:6bf52a52-394a-11d3-b153-00c04f79faa6',' enablecontextmenu="false" autostart="false"',settings.upMediaUrl,settings.upMediaExt);
  1627. break;
  1628. case 'emot':
  1629. _this.showEmot();
  1630. break;
  1631. case 'table':
  1632. _this.showTable();
  1633. break;
  1634. case 'source':
  1635. _this.toggleSource();
  1636. break;
  1637. case 'preview':
  1638. _this.showPreview();
  1639. break;
  1640. case 'print':
  1641. _win.print();
  1642. break;
  1643. case 'fullscreen':
  1644. _this.toggleFullscreen();
  1645. break;
  1646. case 'about':
  1647. _this.showAbout();
  1648. break;
  1649. default:
  1650. _this._exec(cmd);
  1651. break;
  1652. }
  1653. }
  1654. ev=null;
  1655. }
  1656. this._exec=function(cmd,param,noFocus)
  1657. {
  1658. if(!noFocus)_this.focus();
  1659. var state;
  1660. if(param!==undefined)state=_doc.execCommand(cmd,false,param);
  1661. else state=_doc.execCommand(cmd,false,null);
  1662. return state;
  1663. }
  1664. function checkDblClick(ev)
  1665. {
  1666. var target=ev.target,tool=arrDbClick[target.tagName.toLowerCase()];
  1667. if(tool)
  1668. {
  1669. if(tool==='Embed')//自动识别Flash和多媒体
  1670. {
  1671. var arrEmbed={'application/x-shockwave-flash':'Flash','application/x-mplayer2':'Media'};
  1672. tool=arrEmbed[target.type.toLowerCase()];
  1673. }
  1674. _this.exec(tool);
  1675. }
  1676. }
  1677. function checkEsc(ev)
  1678. {
  1679. if(ev.which===27)
  1680. {
  1681. if(bShowModal)_this.removeModal();
  1682. else if(bShowPanel)_this.hidePanel();
  1683. return false;
  1684. }
  1685. }
  1686. function loadReset(){setTimeout(_this.setSource,10);}
  1687. function saveResult(){_this.getSource();};
  1688. function cleanPaste(ev){
  1689. var cleanPaste=settings.cleanPaste;
  1690. if(cleanPaste===0||bSource||bCleanPaste)return true;
  1691. bCleanPaste=true;//解决IE右键粘贴重复产生paste的问题
  1692. _this.saveBookmark();
  1693. var jDiv=$('<div class="xhe-paste" style="top:'+_jWin.scrollTop()+'px;" />',_doc),div=jDiv[0],sel=_this.getSel(),rng=_this.getRng(true);
  1694. $(_doc.body).append(jDiv);
  1695. if(isIE){
  1696. rng.moveToElementText(div);
  1697. rng.execCommand('Paste');
  1698. ev.preventDefault();
  1699. }
  1700. else{
  1701. rng.selectNodeContents(div);
  1702. sel.removeAllRanges();
  1703. sel.addRange(rng);
  1704. }
  1705. setTimeout(function(){
  1706. var bText=(cleanPaste===3),sPaste;
  1707. if(bText)sPaste=jDiv.text();
  1708. else{
  1709. var jTDiv=$('.xhe-paste',jDiv),arrHtml=[];
  1710. if(jTDiv.length===0)jTDiv=jDiv;
  1711. jTDiv.each(function(i,n){arrHtml.push(n.innerHTML);});
  1712. sPaste=arrHtml.join('<br />');
  1713. }
  1714. jDiv.remove();
  1715. _this.loadBookmark();
  1716. if(bText)_this.pasteText(sPaste);
  1717. else{
  1718. sPaste=_this.cleanHTML(sPaste);
  1719. sPaste=_this.cleanWord(sPaste);
  1720. sPaste=_this.formatXHTML(sPaste);
  1721. _this.pasteHTML(sPaste);
  1722. }
  1723. bCleanPaste=false;
  1724. },0);
  1725. }
  1726. function setCSS(css)
  1727. {
  1728. try{_this._exec('styleWithCSS',css,true);}
  1729. catch(e)
  1730. {try{_this._exec('useCSS',!css,true);}catch(e){}}
  1731. }
  1732. function setOpts()
  1733. {
  1734. if(bInit&&!bSource)
  1735. {
  1736. setCSS(false);
  1737. try{_this._exec('enableObjectResizing',true,true);}catch(e){}
  1738. //try{_this._exec('enableInlineTableEditing',false,true);}catch(e){}
  1739. if(isIE)try{_this._exec('BackgroundImageCache',true,true);}catch(e){}
  1740. }
  1741. }
  1742. function forcePtag(ev)
  1743. {
  1744. if(bSource||ev.which!==13||ev.shiftKey||ev.ctrlKey||ev.altKey)return true;
  1745. var pNode=_this.getParent('p,h1,h2,h3,h4,h5,h6,pre,address,div,li');
  1746. if(pNode.is('li'))return true;
  1747. if(settings.forcePtag){if(pNode.length===0)_this._exec('formatblock','<p>');}
  1748. else
  1749. {
  1750. _this.pasteHTML('<br />');
  1751. if(isIE&&pNode.length>0&&_this.getRng().parentElement().childNodes.length===2)_this.pasteHTML('<br />');
  1752. return false;
  1753. }
  1754. }
  1755. function fixFullHeight()
  1756. {
  1757. if(!isMozilla&&!isSafari)
  1758. {
  1759. if(bFullscreen)_jArea.height('100%').css('height',_jArea.outerHeight()-_jTools.outerHeight());
  1760. if(isIE)_jTools.hide().show();
  1761. }
  1762. }
  1763. function fixAppleSel(e)
  1764. {
  1765. e=e.target;
  1766. if(e.tagName.match(/(img|embed)/i))
  1767. {
  1768. var sel=_this.getSel(),rng=_this.getRng(true);
  1769. rng.selectNode(e);
  1770. sel.removeAllRanges();
  1771. sel.addRange(rng);
  1772. }
  1773. }
  1774. function xheAttr(jObj,n,v)
  1775. {
  1776. if(!n)return false;
  1777. var kn='_xhe_'+n;
  1778. if(v)//设置属性
  1779. {
  1780. if(urlType)v=getLocalUrl(v,urlType,urlBase);
  1781. jObj.attr(n,urlBase?getLocalUrl(v,'abs',urlBase):v).removeAttr(kn).attr(kn,v);
  1782. }
  1783. return jObj.attr(kn)||jObj.attr(n);
  1784. }
  1785. function clickCancelPanel(){if(bClickCancel)_this.hidePanel();}
  1786. function checkShortcuts(event)
  1787. {
  1788. if(bSource)return true;
  1789. var code=event.which,special=specialKeys[code],sChar=special?special:String.fromCharCode(code).toLowerCase();
  1790. sKey='';
  1791. sKey+=event.ctrlKey?'ctrl+':'';sKey+=event.altKey?'alt+':'';sKey+=event.shiftKey?'shift+':'';sKey+=sChar;
  1792. var cmd=arrShortCuts[sKey],c;
  1793. for(c in cmd)
  1794. {
  1795. c=cmd[c];
  1796. if($.isFunction(c)){if(c.call(_this)===false)return false;}
  1797. else{_this.exec(c);return false;}//按钮独占快捷键
  1798. }
  1799. }
  1800. function is(o,t)
  1801. {
  1802. var n = typeof(o);
  1803. if (!t)return n != 'undefined';
  1804. if (t === 'array' && (o.hasOwnProperty && o instanceof Array))return true;
  1805. return n === t;
  1806. }
  1807. function getLocalUrl(url,urlType,urlBase)//绝对地址:abs,根地址:root,相对地址:rel
  1808. {
  1809. if(url.match(/^(file|mailto|ftp):/i))return url;
  1810. var baseUrl=urlBase?$('<a href="'+urlBase+'" />')[0]:location,protocol=baseUrl.protocol,host=baseUrl.host,hostname=baseUrl.hostname,port=baseUrl.port,path=baseUrl.pathname.replace(/\\/g,'/').replace(/[^\/]+$/i,'');
  1811. if(port==='')port='80';
  1812. if(path==='')path='/';
  1813. else if(path.charAt(0)!=='/')path='/'+path;//修正IE path
  1814. url=$.trim(url);
  1815. //删除域路径
  1816. if(urlType!=='abs')url=url.replace(new RegExp(protocol+'\\/\\/'+hostname.replace(/\./g,'\\.')+'(?::'+port+')'+(port==='80'?'?':'')+'(\/|$)','i'),'/');
  1817. //删除根路径
  1818. if(urlType==='rel')url=url.replace(new RegExp('^'+path.replace(/([\/\.\+\[\]\(\)])/g,'\\$1'),'i'),'');
  1819. //加上根路径
  1820. if(urlType!=='rel')
  1821. {
  1822. if(!url.match(/^(https?:\/\/|\/)/i))url=path+url;
  1823. if(url.charAt(0)==='/')//处理根路径中的..
  1824. {
  1825. var arrPath=[],arrFolder = url.split('/'),folder,i,l=arrFolder.length;
  1826. for(i=0;i<l;i++)
  1827. {
  1828. folder=arrFolder[i];
  1829. if(folder==='..')arrPath.pop();
  1830. else if(folder!==''&&folder!=='.')arrPath.push(folder);
  1831. }
  1832. if(arrFolder[l-1]==='')arrPath.push('');
  1833. url='/'+arrPath.join('/');
  1834. }
  1835. }
  1836. //加上域路径
  1837. if(urlType==='abs'&&!url.match(/^https?:\/\//i))url=protocol+'//'+host+url;
  1838. url=url.replace(/(https?:\/\/[^:\/?#]+):80(\/|$)/i,'$1$2');//省略80端口
  1839. return url;
  1840. }
  1841. function checkFileExt(filename,limitExt)
  1842. {
  1843. if(limitExt==='*'||filename.match(new RegExp('\.('+limitExt.replace(/,/g,'|')+')$','i')))return true;
  1844. else
  1845. {
  1846. alert('上传文件扩展名必需为: '+limitExt);
  1847. return false;
  1848. }
  1849. }
  1850. function formatBytes(bytes)
  1851. {
  1852. var s = ['Byte', 'KB', 'MB', 'GB', 'TB', 'PB'];
  1853. var e = Math.floor(Math.log(bytes)/Math.log(1024));
  1854. return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+s[e];
  1855. }
  1856. function returnFalse(){return false;}
  1857. }
  1858. $(function(){
  1859. $.fn.oldVal=$.fn.val;
  1860. $.fn.val=function(value)
  1861. {
  1862. var _this=this,editor;
  1863. if(value===undefined)if(_this[0]&&(editor=_this[0].xheditor))return editor.getSource();else return _this.oldVal();//读
  1864. return _this.each(function(){if(editor=this.xheditor)editor.setSource(value);else _this.oldVal(value);});//写
  1865. }
  1866. $('textarea').each(function(){
  1867. var self=$(this),xhClass=self.attr('class').match(/(?:^|\s)xheditor(?:\-(m?full|simple|mini))?(?:\s|$)/i);
  1868. if(xhClass)self.xheditor(xhClass[1]?{tools:xhClass[1]}:null);
  1869. });
  1870. });
  1871. })(jQuery);