diff options
author | Andrew Dolgov <[email protected]> | 2011-11-08 20:40:44 +0400 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-11-08 20:40:44 +0400 |
commit | 81bea17aefb26859f825b9293c7c99192874806e (patch) | |
tree | fb244408ca271affa2899adb634788802c9a89d8 /lib/dijit/_editor/RichText.js | |
parent | 870a70e109ac9e80a88047044530de53d0404ec7 (diff) |
upgrade Dojo to 1.6.1
Diffstat (limited to 'lib/dijit/_editor/RichText.js')
-rw-r--r-- | lib/dijit/_editor/RichText.js | 3112 |
1 files changed, 2081 insertions, 1031 deletions
diff --git a/lib/dijit/_editor/RichText.js b/lib/dijit/_editor/RichText.js index 696715986..978371557 100644 --- a/lib/dijit/_editor/RichText.js +++ b/lib/dijit/_editor/RichText.js @@ -1,1046 +1,2096 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit._editor.RichText"]){ -dojo._hasResource["dijit._editor.RichText"]=true; +if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._editor.RichText"] = true; dojo.provide("dijit._editor.RichText"); dojo.require("dijit._Widget"); dojo.require("dijit._CssStateMixin"); dojo.require("dijit._editor.selection"); dojo.require("dijit._editor.range"); dojo.require("dijit._editor.html"); -if(!dojo.config["useXDomain"]||dojo.config["allowXdRichTextSave"]){ -if(dojo._postLoad){ -(function(){ -var _1=dojo.doc.createElement("textarea"); -_1.id=dijit._scopeName+"._editor.RichText.savedContent"; -dojo.style(_1,{display:"none",position:"absolute",top:"-100px",height:"3px",width:"3px"}); -dojo.body().appendChild(_1); -})(); -}else{ -try{ -dojo.doc.write("<textarea id=\""+dijit._scopeName+"._editor.RichText.savedContent\" "+"style=\"display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;\"></textarea>"); -} -catch(e){ -} -} -} -dojo.declare("dijit._editor.RichText",[dijit._Widget,dijit._CssStateMixin],{constructor:function(_2){ -this.contentPreFilters=[]; -this.contentPostFilters=[]; -this.contentDomPreFilters=[]; -this.contentDomPostFilters=[]; -this.editingAreaStyleSheets=[]; -this.events=[].concat(this.events); -this._keyHandlers={}; -this.contentPreFilters.push(dojo.hitch(this,"_preFixUrlAttributes")); -if(dojo.isMoz){ -this.contentPreFilters.push(this._normalizeFontStyle); -this.contentPostFilters.push(this._removeMozBogus); -} -if(dojo.isWebKit){ -this.contentPreFilters.push(this._removeWebkitBogus); -this.contentPostFilters.push(this._removeWebkitBogus); -} -if(dojo.isIE){ -this.contentPostFilters.push(this._normalizeFontStyle); -} -if(_2&&dojo.isString(_2.value)){ -this.value=_2.value; -} -this.onLoadDeferred=new dojo.Deferred(); -},baseClass:"dijitEditor",inheritWidth:false,focusOnLoad:false,name:"",styleSheets:"",_content:"",height:"300px",minHeight:"1em",isClosed:true,isLoaded:false,_SEPARATOR:"@@**%%__RICHTEXTBOUNDRY__%%**@@",onLoadDeferred:null,isTabIndent:false,disableSpellCheck:false,postCreate:function(){ -if("textarea"==this.domNode.tagName.toLowerCase()){ -console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs."); -} -this.inherited(arguments); -dojo.publish(dijit._scopeName+"._editor.RichText::init",[this]); -this.open(); -this.setupDefaultShortcuts(); -},setupDefaultShortcuts:function(){ -var _3=dojo.hitch(this,function(_4,_5){ -return function(){ -return !this.execCommand(_4,_5); -}; -}); -var _6={b:_3("bold"),i:_3("italic"),u:_3("underline"),a:_3("selectall"),s:function(){ -this.save(true); -},m:function(){ -this.isTabIndent=!this.isTabIndent; -},"1":_3("formatblock","h1"),"2":_3("formatblock","h2"),"3":_3("formatblock","h3"),"4":_3("formatblock","h4"),"\\":_3("insertunorderedlist")}; -if(!dojo.isIE){ -_6.Z=_3("redo"); -} -for(var _7 in _6){ -this.addKeyHandler(_7,true,false,_6[_7]); -} -},events:["onKeyPress","onKeyDown","onKeyUp","onClick"],captureEvents:[],_editorCommandsLocalized:false,_localizeEditorCommands:function(){ -if(this._editorCommandsLocalized){ -return; -} -this._editorCommandsLocalized=true; -var _8=["div","p","pre","h1","h2","h3","h4","h5","h6","ol","ul","address"]; -var _9="",_a,i=0; -while((_a=_8[i++])){ -if(_a.charAt(1)!="l"){ -_9+="<"+_a+"><span>content</span></"+_a+"><br/>"; -}else{ -_9+="<"+_a+"><li>content</li></"+_a+"><br/>"; -} -} -var _b=dojo.doc.createElement("div"); -dojo.style(_b,{position:"absolute",top:"-2000px"}); -dojo.doc.body.appendChild(_b); -_b.innerHTML=_9; -var _c=_b.firstChild; -while(_c){ -dijit._editor.selection.selectElement(_c.firstChild); -dojo.withGlobal(this.window,"selectElement",dijit._editor.selection,[_c.firstChild]); -var _d=_c.tagName.toLowerCase(); -this._local2NativeFormatNames[_d]=document.queryCommandValue("formatblock"); -this._native2LocalFormatNames[this._local2NativeFormatNames[_d]]=_d; -_c=_c.nextSibling.nextSibling; -} -dojo.body().removeChild(_b); -},open:function(_e){ -if(!this.onLoadDeferred||this.onLoadDeferred.fired>=0){ -this.onLoadDeferred=new dojo.Deferred(); -} -if(!this.isClosed){ -this.close(); -} -dojo.publish(dijit._scopeName+"._editor.RichText::open",[this]); -this._content=""; -if(arguments.length==1&&_e.nodeName){ -this.domNode=_e; -} -var dn=this.domNode; -var _f; -if(dojo.isString(this.value)){ -_f=this.value; -delete this.value; -dn.innerHTML=""; -}else{ -if(dn.nodeName&&dn.nodeName.toLowerCase()=="textarea"){ -var ta=(this.textarea=dn); -this.name=ta.name; -_f=ta.value; -dn=this.domNode=dojo.doc.createElement("div"); -dn.setAttribute("widgetId",this.id); -ta.removeAttribute("widgetId"); -dn.cssText=ta.cssText; -dn.className+=" "+ta.className; -dojo.place(dn,ta,"before"); -var _10=dojo.hitch(this,function(){ -dojo.style(ta,{display:"block",position:"absolute",top:"-1000px"}); -if(dojo.isIE){ -var s=ta.style; -this.__overflow=s.overflow; -s.overflow="hidden"; -} -}); -if(dojo.isIE){ -setTimeout(_10,10); -}else{ -_10(); -} -if(ta.form){ -dojo.connect(ta.form,"onsubmit",this,function(){ -ta.value=this.getValue(); -}); -} -}else{ -_f=dijit._editor.getChildrenHtml(dn); -dn.innerHTML=""; -} -} -var _11=dojo.contentBox(dn); -this._oldHeight=_11.h; -this._oldWidth=_11.w; -this.savedContent=_f; -if(dn.nodeName&&dn.nodeName=="LI"){ -dn.innerHTML=" <br>"; -} -this.header=dn.ownerDocument.createElement("div"); -dn.appendChild(this.header); -this.editingArea=dn.ownerDocument.createElement("div"); -dn.appendChild(this.editingArea); -this.footer=dn.ownerDocument.createElement("div"); -dn.appendChild(this.footer); -if(this.name!==""&&(!dojo.config["useXDomain"]||dojo.config["allowXdRichTextSave"])){ -var _12=dojo.byId(dijit._scopeName+"._editor.RichText.savedContent"); -if(_12.value!==""){ -var _13=_12.value.split(this._SEPARATOR),i=0,dat; -while((dat=_13[i++])){ -var _14=dat.split(":"); -if(_14[0]==this.name){ -_f=_14[1]; -_13.splice(i,1); -break; -} -} -} -dojo.addOnUnload(dojo.hitch(this,"_saveContent")); -} -this.isClosed=false; -var ifr=(this.editorObject=this.iframe=dojo.doc.createElement("iframe")); -ifr.id=this.id+"_iframe"; -this._iframeSrc=this._getIframeDocTxt(); -ifr.style.border="none"; -ifr.style.width="100%"; -if(this._layoutMode){ -ifr.style.height="100%"; -}else{ -if(dojo.isIE>=7){ -if(this.height){ -ifr.style.height=this.height; -} -if(this.minHeight){ -ifr.style.minHeight=this.minHeight; -} -}else{ -ifr.style.height=this.height?this.height:this.minHeight; -} -} -ifr.frameBorder=0; -ifr._loadFunc=dojo.hitch(this,function(win){ -this.window=win; -this.document=this.window.document; -if(dojo.isIE){ -this._localizeEditorCommands(); -} -this.onLoad(_f); -}); -var s="javascript:parent."+dijit._scopeName+".byId(\""+this.id+"\")._iframeSrc"; -ifr.setAttribute("src",s); -this.editingArea.appendChild(ifr); -if(dn.nodeName=="LI"){ -dn.lastChild.style.marginTop="-1.2em"; -} -dojo.addClass(this.domNode,this.baseClass); -},_local2NativeFormatNames:{},_native2LocalFormatNames:{},_getIframeDocTxt:function(){ -var _15=dojo.getComputedStyle(this.domNode); -var _16=""; -var _17=true; -if(dojo.isIE||(!this.height&&!dojo.isMoz)){ -_16="<div id='dijitEditorBody'></div>"; -_17=false; -}else{ -if(dojo.isMoz){ -this._cursorToStart=true; -_16=" "; -} -} -var _18=[_15.fontWeight,_15.fontSize,_15.fontFamily].join(" "); -var _19=_15.lineHeight; -if(_19.indexOf("px")>=0){ -_19=parseFloat(_19)/parseFloat(_15.fontSize); -}else{ -if(_19.indexOf("em")>=0){ -_19=parseFloat(_19); -}else{ -_19="normal"; -} -} -var _1a=""; -var _1b=this; -this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig,function(_1c){ -_1c=_1c.replace(/^;/ig,"")+";"; -var s=_1c.split(":")[0]; -if(s){ -s=dojo.trim(s); -s=s.toLowerCase(); -var i; -var sC=""; -for(i=0;i<s.length;i++){ -var c=s.charAt(i); -switch(c){ -case "-": -i++; -c=s.charAt(i).toUpperCase(); -default: -sC+=c; -} -} -dojo.style(_1b.domNode,sC,""); -} -_1a+=_1c+";"; -}); -var _1d=dojo.query("label[for=\""+this.id+"\"]"); -return [this.isLeftToRight()?"<html>\n<head>\n":"<html dir='rtl'>\n<head>\n",(dojo.isMoz&&_1d.length?"<title>"+_1d[0].innerHTML+"</title>\n":""),"<meta http-equiv='Content-Type' content='text/html'>\n","<style>\n","\tbody,html {\n","\t\tbackground:transparent;\n","\t\tpadding: 1px 0 0 0;\n","\t\tmargin: -1px 0 0 0;\n",((dojo.isWebKit)?"\t\twidth: 100%;\n":""),((dojo.isWebKit)?"\t\theight: 100%;\n":""),"\t}\n","\tbody{\n","\t\ttop:0px;\n","\t\tleft:0px;\n","\t\tright:0px;\n","\t\tfont:",_18,";\n",((this.height||dojo.isOpera)?"":"\t\tposition: fixed;\n"),"\t\tmin-height:",this.minHeight,";\n","\t\tline-height:",_19,";\n","\t}\n","\tp{ margin: 1em 0; }\n",(!_17&&!this.height?"\tbody,html {overflow-y: hidden;}\n":""),"\t#dijitEditorBody{overflow-x: auto; overflow-y:"+(this.height?"auto;":"hidden;")+"}\n","\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n","\tli{ min-height:1.2em; }\n","</style>\n",this._applyEditingAreaStyleSheets(),"\n","</head>\n<body ",(_17?"id='dijitEditorBody' ":""),"onload='frameElement._loadFunc(window,document)' style='"+_1a+"'>",_16,"</body>\n</html>"].join(""); -},_applyEditingAreaStyleSheets:function(){ -var _1e=[]; -if(this.styleSheets){ -_1e=this.styleSheets.split(";"); -this.styleSheets=""; -} -_1e=_1e.concat(this.editingAreaStyleSheets); -this.editingAreaStyleSheets=[]; -var _1f="",i=0,url; -while((url=_1e[i++])){ -var _20=(new dojo._Url(dojo.global.location,url)).toString(); -this.editingAreaStyleSheets.push(_20); -_1f+="<link rel=\"stylesheet\" type=\"text/css\" href=\""+_20+"\"/>"; -} -return _1f; -},addStyleSheet:function(uri){ -var url=uri.toString(); -if(url.charAt(0)=="."||(url.charAt(0)!="/"&&!uri.host)){ -url=(new dojo._Url(dojo.global.location,url)).toString(); -} -if(dojo.indexOf(this.editingAreaStyleSheets,url)>-1){ -return; -} -this.editingAreaStyleSheets.push(url); -this.onLoadDeferred.addCallback(dojo.hitch(function(){ -if(this.document.createStyleSheet){ -this.document.createStyleSheet(url); -}else{ -var _21=this.document.getElementsByTagName("head")[0]; -var _22=this.document.createElement("link"); -_22.rel="stylesheet"; -_22.type="text/css"; -_22.href=url; -_21.appendChild(_22); -} -})); -},removeStyleSheet:function(uri){ -var url=uri.toString(); -if(url.charAt(0)=="."||(url.charAt(0)!="/"&&!uri.host)){ -url=(new dojo._Url(dojo.global.location,url)).toString(); -} -var _23=dojo.indexOf(this.editingAreaStyleSheets,url); -if(_23==-1){ -return; -} -delete this.editingAreaStyleSheets[_23]; -dojo.withGlobal(this.window,"query",dojo,["link:[href=\""+url+"\"]"]).orphan(); -},disabled:false,_mozSettingProps:{"styleWithCSS":false},_setDisabledAttr:function(_24){ -this.disabled=_24; -if(!this.isLoaded){ -return; -} -_24=!!_24; -if(dojo.isIE||dojo.isWebKit||dojo.isOpera){ -var _25=dojo.isIE&&(this.isLoaded||!this.focusOnLoad); -if(_25){ -this.editNode.unselectable="on"; -} -this.editNode.contentEditable=!_24; -if(_25){ -var _26=this; -setTimeout(function(){ -_26.editNode.unselectable="off"; -},0); -} -}else{ -try{ -this.document.designMode=(_24?"off":"on"); -} -catch(e){ -return; -} -if(!_24&&this._mozSettingProps){ -var ps=this._mozSettingProps; -for(var n in ps){ -if(ps.hasOwnProperty(n)){ -try{ -this.document.execCommand(n,false,ps[n]); -} -catch(e2){ -} -} -} -} -} -this._disabledOK=true; -},onLoad:function(_27){ -if(!this.window.__registeredWindow){ -this.window.__registeredWindow=true; -this._iframeRegHandle=dijit.registerIframe(this.iframe); -} -if(!dojo.isIE&&(this.height||dojo.isMoz)){ -this.editNode=this.document.body; -}else{ -this.editNode=this.document.body.firstChild; -var _28=this; -if(dojo.isIE){ -var _29=(this.tabStop=dojo.doc.createElement("<div tabIndex=-1>")); -this.editingArea.appendChild(_29); -this.iframe.onfocus=function(){ -_28.editNode.setActive(); -}; -} -} -this.focusNode=this.editNode; -var _2a=this.events.concat(this.captureEvents); -var ap=this.iframe?this.document:this.editNode; -dojo.forEach(_2a,function(_2b){ -this.connect(ap,_2b.toLowerCase(),_2b); -},this); -if(dojo.isIE){ -this.connect(this.document,"onmousedown","_onIEMouseDown"); -this.editNode.style.zoom=1; -}else{ -this.connect(this.document,"onmousedown",function(){ -delete this._cursorToStart; -}); -} -if(dojo.isWebKit){ -this._webkitListener=this.connect(this.document,"onmouseup","onDisplayChanged"); -} -if(dojo.isIE){ -try{ -this.document.execCommand("RespectVisibilityInDesign",true,null); -} -catch(e){ -} -} -this.isLoaded=true; -this.set("disabled",this.disabled); -var _2c=dojo.hitch(this,function(){ -this.setValue(_27); -if(this.onLoadDeferred){ -this.onLoadDeferred.callback(true); -} -this.onDisplayChanged(); -if(this.focusOnLoad){ -dojo.addOnLoad(dojo.hitch(this,function(){ -setTimeout(dojo.hitch(this,"focus"),this.updateInterval); -})); -} -this.savedContent=this.getValue(true); -}); -if(this.setValueDeferred){ -this.setValueDeferred.addCallback(_2c); -}else{ -_2c(); -} -},onKeyDown:function(e){ -if(e.keyCode===dojo.keys.TAB&&this.isTabIndent){ -dojo.stopEvent(e); -if(this.queryCommandEnabled((e.shiftKey?"outdent":"indent"))){ -this.execCommand((e.shiftKey?"outdent":"indent")); -} -} -if(dojo.isIE){ -if(e.keyCode==dojo.keys.TAB&&!this.isTabIndent){ -if(e.shiftKey&&!e.ctrlKey&&!e.altKey){ -this.iframe.focus(); -}else{ -if(!e.shiftKey&&!e.ctrlKey&&!e.altKey){ -this.tabStop.focus(); -} -} -}else{ -if(e.keyCode===dojo.keys.BACKSPACE&&this.document.selection.type==="Control"){ -dojo.stopEvent(e); -this.execCommand("delete"); -}else{ -if((65<=e.keyCode&&e.keyCode<=90)||(e.keyCode>=37&&e.keyCode<=40)){ -e.charCode=e.keyCode; -this.onKeyPress(e); -} -} -} -} -return true; -},onKeyUp:function(e){ -return; -},setDisabled:function(_2d){ -dojo.deprecated("dijit.Editor::setDisabled is deprecated","use dijit.Editor::attr(\"disabled\",boolean) instead",2); -this.set("disabled",_2d); -},_setValueAttr:function(_2e){ -this.setValue(_2e); -},_setDisableSpellCheckAttr:function(_2f){ -if(this.document){ -dojo.attr(this.document.body,"spellcheck",!_2f); -}else{ -this.onLoadDeferred.addCallback(dojo.hitch(this,function(){ -dojo.attr(this.document.body,"spellcheck",!_2f); -})); -} -this.disableSpellCheck=_2f; -},onKeyPress:function(e){ -var c=(e.keyChar&&e.keyChar.toLowerCase())||e.keyCode,_30=this._keyHandlers[c],_31=arguments; -if(_30&&!e.altKey){ -dojo.some(_30,function(h){ -if(!(h.shift^e.shiftKey)&&!(h.ctrl^(e.ctrlKey||e.metaKey))){ -if(!h.handler.apply(this,_31)){ -e.preventDefault(); -} -return true; -} -},this); -} -if(!this._onKeyHitch){ -this._onKeyHitch=dojo.hitch(this,"onKeyPressed"); -} -setTimeout(this._onKeyHitch,1); -return true; -},addKeyHandler:function(key,_32,_33,_34){ -if(!dojo.isArray(this._keyHandlers[key])){ -this._keyHandlers[key]=[]; -} -this._keyHandlers[key].push({shift:_33||false,ctrl:_32||false,handler:_34}); -},onKeyPressed:function(){ -this.onDisplayChanged(); -},onClick:function(e){ -this.onDisplayChanged(e); -},_onIEMouseDown:function(e){ -if(!this._focused&&!this.disabled){ -this.focus(); -} -},_onBlur:function(e){ -this.inherited(arguments); -var _35=this.getValue(true); -if(_35!=this.savedContent){ -this.onChange(_35); -this.savedContent=_35; -} -},_onFocus:function(e){ -if(!this.disabled){ -if(!this._disabledOK){ -this.set("disabled",false); -} -this.inherited(arguments); -} -},blur:function(){ -if(!dojo.isIE&&this.window.document.documentElement&&this.window.document.documentElement.focus){ -this.window.document.documentElement.focus(); -}else{ -if(dojo.doc.body.focus){ -dojo.doc.body.focus(); -} -} -},focus:function(){ -if(!this.isLoaded){ -this.focusOnLoad=true; -return; -} -if(this._cursorToStart){ -delete this._cursorToStart; -if(this.editNode.childNodes){ -this.placeCursorAtStart(); -return; -} -} -if(!dojo.isIE){ -dijit.focus(this.iframe); -}else{ -if(this.editNode&&this.editNode.focus){ -this.iframe.fireEvent("onfocus",document.createEventObject()); -} -} -},updateInterval:200,_updateTimer:null,onDisplayChanged:function(e){ -if(this._updateTimer){ -clearTimeout(this._updateTimer); -} -if(!this._updateHandler){ -this._updateHandler=dojo.hitch(this,"onNormalizedDisplayChanged"); -} -this._updateTimer=setTimeout(this._updateHandler,this.updateInterval); -},onNormalizedDisplayChanged:function(){ -delete this._updateTimer; -},onChange:function(_36){ -},_normalizeCommand:function(cmd,_37){ -var _38=cmd.toLowerCase(); -if(_38=="formatblock"){ -if(dojo.isSafari&&_37===undefined){ -_38="heading"; -} -}else{ -if(_38=="hilitecolor"&&!dojo.isMoz){ -_38="backcolor"; -} -} -return _38; -},_qcaCache:{},queryCommandAvailable:function(_39){ -var ca=this._qcaCache[_39]; -if(ca!==undefined){ -return ca; -} -return (this._qcaCache[_39]=this._queryCommandAvailable(_39)); -},_queryCommandAvailable:function(_3a){ -var ie=1; -var _3b=1<<1; -var _3c=1<<2; -var _3d=1<<3; -var _3e=1<<4; -function _3f(_40){ -return {ie:Boolean(_40&ie),mozilla:Boolean(_40&_3b),webkit:Boolean(_40&_3c),webkit420:Boolean(_40&_3e),opera:Boolean(_40&_3d)}; -}; -var _41=null; -switch(_3a.toLowerCase()){ -case "bold": -case "italic": -case "underline": -case "subscript": -case "superscript": -case "fontname": -case "fontsize": -case "forecolor": -case "hilitecolor": -case "justifycenter": -case "justifyfull": -case "justifyleft": -case "justifyright": -case "delete": -case "selectall": -case "toggledir": -_41=_3f(_3b|ie|_3c|_3d); -break; -case "createlink": -case "unlink": -case "removeformat": -case "inserthorizontalrule": -case "insertimage": -case "insertorderedlist": -case "insertunorderedlist": -case "indent": -case "outdent": -case "formatblock": -case "inserthtml": -case "undo": -case "redo": -case "strikethrough": -case "tabindent": -_41=_3f(_3b|ie|_3d|_3e); -break; -case "blockdirltr": -case "blockdirrtl": -case "dirltr": -case "dirrtl": -case "inlinedirltr": -case "inlinedirrtl": -_41=_3f(ie); -break; -case "cut": -case "copy": -case "paste": -_41=_3f(ie|_3b|_3e); -break; -case "inserttable": -_41=_3f(_3b|ie); -break; -case "insertcell": -case "insertcol": -case "insertrow": -case "deletecells": -case "deletecols": -case "deleterows": -case "mergecells": -case "splitcell": -_41=_3f(ie|_3b); -break; -default: -return false; -} -return (dojo.isIE&&_41.ie)||(dojo.isMoz&&_41.mozilla)||(dojo.isWebKit&&_41.webkit)||(dojo.isWebKit>420&&_41.webkit420)||(dojo.isOpera&&_41.opera); -},execCommand:function(_42,_43){ -var _44; -this.focus(); -_42=this._normalizeCommand(_42,_43); -if(_43!==undefined){ -if(_42=="heading"){ -throw new Error("unimplemented"); -}else{ -if((_42=="formatblock")&&dojo.isIE){ -_43="<"+_43+">"; -} -} -} -var _45="_"+_42+"Impl"; -if(this[_45]){ -_44=this[_45](_43); -}else{ -_43=arguments.length>1?_43:null; -if(_43||_42!="createlink"){ -_44=this.document.execCommand(_42,false,_43); -} -} -this.onDisplayChanged(); -return _44; -},queryCommandEnabled:function(_46){ -if(this.disabled||!this._disabledOK){ -return false; -} -_46=this._normalizeCommand(_46); -if(dojo.isMoz||dojo.isWebKit){ -if(_46=="unlink"){ -return this._sCall("hasAncestorElement",["a"]); -}else{ -if(_46=="inserttable"){ -return true; -} -} -} -if(dojo.isWebKit){ -if(_46=="copy"){ -_46="cut"; -}else{ -if(_46=="paste"){ -return true; -} -} -} -var _47=dojo.isIE?this.document.selection.createRange():this.document; -try{ -return _47.queryCommandEnabled(_46); -} -catch(e){ -return false; -} -},queryCommandState:function(_48){ -if(this.disabled||!this._disabledOK){ -return false; -} -_48=this._normalizeCommand(_48); -try{ -return this.document.queryCommandState(_48); -} -catch(e){ -return false; -} -},queryCommandValue:function(_49){ -if(this.disabled||!this._disabledOK){ -return false; -} -var r; -_49=this._normalizeCommand(_49); -if(dojo.isIE&&_49=="formatblock"){ -r=this._native2LocalFormatNames[this.document.queryCommandValue(_49)]; -}else{ -if(dojo.isMoz&&_49==="hilitecolor"){ -var _4a; -try{ -_4a=this.document.queryCommandValue("styleWithCSS"); -} -catch(e){ -_4a=false; -} -this.document.execCommand("styleWithCSS",false,true); -r=this.document.queryCommandValue(_49); -this.document.execCommand("styleWithCSS",false,_4a); -}else{ -r=this.document.queryCommandValue(_49); -} -} -return r; -},_sCall:function(_4b,_4c){ -return dojo.withGlobal(this.window,_4b,dijit._editor.selection,_4c); -},placeCursorAtStart:function(){ -this.focus(); -var _4d=false; -if(dojo.isMoz){ -var _4e=this.editNode.firstChild; -while(_4e){ -if(_4e.nodeType==3){ -if(_4e.nodeValue.replace(/^\s+|\s+$/g,"").length>0){ -_4d=true; -this._sCall("selectElement",[_4e]); -break; -} -}else{ -if(_4e.nodeType==1){ -_4d=true; -var tg=_4e.tagName?_4e.tagName.toLowerCase():""; -if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){ -this._sCall("selectElement",[_4e]); -}else{ -this._sCall("selectElementChildren",[_4e]); -} -break; -} -} -_4e=_4e.nextSibling; -} -}else{ -_4d=true; -this._sCall("selectElementChildren",[this.editNode]); -} -if(_4d){ -this._sCall("collapse",[true]); -} -},placeCursorAtEnd:function(){ -this.focus(); -var _4f=false; -if(dojo.isMoz){ -var _50=this.editNode.lastChild; -while(_50){ -if(_50.nodeType==3){ -if(_50.nodeValue.replace(/^\s+|\s+$/g,"").length>0){ -_4f=true; -this._sCall("selectElement",[_50]); -break; -} -}else{ -if(_50.nodeType==1){ -_4f=true; -if(_50.lastChild){ -this._sCall("selectElement",[_50.lastChild]); -}else{ -this._sCall("selectElement",[_50]); -} -break; -} -} -_50=_50.previousSibling; -} -}else{ -_4f=true; -this._sCall("selectElementChildren",[this.editNode]); -} -if(_4f){ -this._sCall("collapse",[false]); -} -},getValue:function(_51){ -if(this.textarea){ -if(this.isClosed||!this.isLoaded){ -return this.textarea.value; -} -} -return this._postFilterContent(null,_51); -},_getValueAttr:function(){ -return this.getValue(true); -},setValue:function(_52){ -if(!this.isLoaded){ -this.onLoadDeferred.addCallback(dojo.hitch(this,function(){ -this.setValue(_52); -})); -return; -} -this._cursorToStart=true; -if(this.textarea&&(this.isClosed||!this.isLoaded)){ -this.textarea.value=_52; -}else{ -_52=this._preFilterContent(_52); -var _53=this.isClosed?this.domNode:this.editNode; -if(!_52&&dojo.isWebKit){ -_52=" "; -} -_53.innerHTML=_52; -this._preDomFilterContent(_53); -} -this.onDisplayChanged(); -},replaceValue:function(_54){ -if(this.isClosed){ -this.setValue(_54); -}else{ -if(this.window&&this.window.getSelection&&!dojo.isMoz){ -this.setValue(_54); -}else{ -if(this.window&&this.window.getSelection){ -_54=this._preFilterContent(_54); -this.execCommand("selectall"); -if(!_54){ -this._cursorToStart=true; -_54=" "; -} -this.execCommand("inserthtml",_54); -this._preDomFilterContent(this.editNode); -}else{ -if(this.document&&this.document.selection){ -this.setValue(_54); -} -} -} -} -},_preFilterContent:function(_55){ -var ec=_55; -dojo.forEach(this.contentPreFilters,function(ef){ -if(ef){ -ec=ef(ec); -} -}); -return ec; -},_preDomFilterContent:function(dom){ -dom=dom||this.editNode; -dojo.forEach(this.contentDomPreFilters,function(ef){ -if(ef&&dojo.isFunction(ef)){ -ef(dom); -} -},this); -},_postFilterContent:function(dom,_56){ -var ec; -if(!dojo.isString(dom)){ -dom=dom||this.editNode; -if(this.contentDomPostFilters.length){ -if(_56){ -dom=dojo.clone(dom); -} -dojo.forEach(this.contentDomPostFilters,function(ef){ -dom=ef(dom); -}); -} -ec=dijit._editor.getChildrenHtml(dom); -}else{ -ec=dom; -} -if(!dojo.trim(ec.replace(/^\xA0\xA0*/,"").replace(/\xA0\xA0*$/,"")).length){ -ec=""; + + +// used to restore content when user leaves this page then comes back +// but do not try doing dojo.doc.write if we are using xd loading. +// dojo.doc.write will only work if RichText.js is included in the dojo.js +// file. If it is included in dojo.js and you want to allow rich text saving +// for back/forward actions, then set dojo.config.allowXdRichTextSave = true. +if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){ + if(dojo._postLoad){ + (function(){ + var savetextarea = dojo.doc.createElement('textarea'); + savetextarea.id = dijit._scopeName + "._editor.RichText.value"; + dojo.style(savetextarea, { + display:'none', + position:'absolute', + top:"-100px", + height:"3px", + width:"3px" + }); + dojo.body().appendChild(savetextarea); + })(); + }else{ + //dojo.body() is not available before onLoad is fired + try{ + dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.value" ' + + 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>'); + }catch(e){ } + } } -dojo.forEach(this.contentPostFilters,function(ef){ -ec=ef(ec); + +dojo.declare("dijit._editor.RichText", [dijit._Widget, dijit._CssStateMixin], { + constructor: function(params){ + // summary: + // dijit._editor.RichText is the core of dijit.Editor, which provides basic + // WYSIWYG editing features. + // + // description: + // dijit._editor.RichText is the core of dijit.Editor, which provides basic + // WYSIWYG editing features. It also encapsulates the differences + // of different js engines for various browsers. Do not use this widget + // with an HTML <TEXTAREA> tag, since the browser unescapes XML escape characters, + // like <. This can have unexpected behavior and lead to security issues + // such as scripting attacks. + // + // tags: + // private + + // contentPreFilters: Function(String)[] + // Pre content filter function register array. + // these filters will be executed before the actual + // editing area gets the html content. + this.contentPreFilters = []; + + // contentPostFilters: Function(String)[] + // post content filter function register array. + // These will be used on the resulting html + // from contentDomPostFilters. The resulting + // content is the final html (returned by getValue()). + this.contentPostFilters = []; + + // contentDomPreFilters: Function(DomNode)[] + // Pre content dom filter function register array. + // These filters are applied after the result from + // contentPreFilters are set to the editing area. + this.contentDomPreFilters = []; + + // contentDomPostFilters: Function(DomNode)[] + // Post content dom filter function register array. + // These filters are executed on the editing area dom. + // The result from these will be passed to contentPostFilters. + this.contentDomPostFilters = []; + + // editingAreaStyleSheets: dojo._URL[] + // array to store all the stylesheets applied to the editing area + this.editingAreaStyleSheets = []; + + // Make a copy of this.events before we start writing into it, otherwise we + // will modify the prototype which leads to bad things on pages w/multiple editors + this.events = [].concat(this.events); + + this._keyHandlers = {}; + + if(params && dojo.isString(params.value)){ + this.value = params.value; + } + + this.onLoadDeferred = new dojo.Deferred(); + }, + + baseClass: "dijitEditor", + + // inheritWidth: Boolean + // whether to inherit the parent's width or simply use 100% + inheritWidth: false, + + // focusOnLoad: [deprecated] Boolean + // Focus into this widget when the page is loaded + focusOnLoad: false, + + // name: String? + // Specifies the name of a (hidden) <textarea> node on the page that's used to save + // the editor content on page leave. Used to restore editor contents after navigating + // to a new page and then hitting the back button. + name: "", + + // styleSheets: [const] String + // semicolon (";") separated list of css files for the editing area + styleSheets: "", + + // height: String + // Set height to fix the editor at a specific height, with scrolling. + // By default, this is 300px. If you want to have the editor always + // resizes to accommodate the content, use AlwaysShowToolbar plugin + // and set height="". If this editor is used within a layout widget, + // set height="100%". + height: "300px", + + // minHeight: String + // The minimum height that the editor should have. + minHeight: "1em", + + // isClosed: [private] Boolean + isClosed: true, + + // isLoaded: [private] Boolean + isLoaded: false, + + // _SEPARATOR: [private] String + // Used to concat contents from multiple editors into a single string, + // so they can be saved into a single <textarea> node. See "name" attribute. + _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@", + + // _NAME_CONTENT_SEP: [private] String + // USed to separate name from content. Just a colon isn't safe. + _NAME_CONTENT_SEP: "@@**%%:%%**@@", + + // onLoadDeferred: [readonly] dojo.Deferred + // Deferred which is fired when the editor finishes loading. + // Call myEditor.onLoadDeferred.then(callback) it to be informed + // when the rich-text area initialization is finalized. + onLoadDeferred: null, + + // isTabIndent: Boolean + // Make tab key and shift-tab indent and outdent rather than navigating. + // Caution: sing this makes web pages inaccessible to users unable to use a mouse. + isTabIndent: false, + + // disableSpellCheck: [const] Boolean + // When true, disables the browser's native spell checking, if supported. + // Works only in Firefox. + disableSpellCheck: false, + + postCreate: function(){ + if("textarea" == this.domNode.tagName.toLowerCase()){ + console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs."); + } + + // Push in the builtin filters now, making them the first executed, but not over-riding anything + // users passed in. See: #6062 + this.contentPreFilters = [dojo.hitch(this, "_preFixUrlAttributes")].concat(this.contentPreFilters); + if(dojo.isMoz){ + this.contentPreFilters = [this._normalizeFontStyle].concat(this.contentPreFilters); + this.contentPostFilters = [this._removeMozBogus].concat(this.contentPostFilters); + } + if(dojo.isWebKit){ + // Try to clean up WebKit bogus artifacts. The inserted classes + // made by WebKit sometimes messes things up. + this.contentPreFilters = [this._removeWebkitBogus].concat(this.contentPreFilters); + this.contentPostFilters = [this._removeWebkitBogus].concat(this.contentPostFilters); + } + if(dojo.isIE){ + // IE generates <strong> and <em> but we want to normalize to <b> and <i> + this.contentPostFilters = [this._normalizeFontStyle].concat(this.contentPostFilters); + } + this.inherited(arguments); + + dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]); + this.open(); + this.setupDefaultShortcuts(); + }, + + setupDefaultShortcuts: function(){ + // summary: + // Add some default key handlers + // description: + // Overwrite this to setup your own handlers. The default + // implementation does not use Editor commands, but directly + // executes the builtin commands within the underlying browser + // support. + // tags: + // protected + var exec = dojo.hitch(this, function(cmd, arg){ + return function(){ + return !this.execCommand(cmd,arg); + }; + }); + + var ctrlKeyHandlers = { + b: exec("bold"), + i: exec("italic"), + u: exec("underline"), + a: exec("selectall"), + s: function(){ this.save(true); }, + m: function(){ this.isTabIndent = !this.isTabIndent; }, + + "1": exec("formatblock", "h1"), + "2": exec("formatblock", "h2"), + "3": exec("formatblock", "h3"), + "4": exec("formatblock", "h4"), + + "\\": exec("insertunorderedlist") + }; + + if(!dojo.isIE){ + ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo? + } + + for(var key in ctrlKeyHandlers){ + this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]); + } + }, + + // events: [private] String[] + // events which should be connected to the underlying editing area + events: ["onKeyPress", "onKeyDown", "onKeyUp"], // onClick handled specially + + // captureEvents: [deprecated] String[] + // Events which should be connected to the underlying editing + // area, events in this array will be addListener with + // capture=true. + // TODO: looking at the code I don't see any distinction between events and captureEvents, + // so get rid of this for 2.0 if not sooner + captureEvents: [], + + _editorCommandsLocalized: false, + _localizeEditorCommands: function(){ + // summary: + // When IE is running in a non-English locale, the API actually changes, + // so that we have to say (for example) danraku instead of p (for paragraph). + // Handle that here. + // tags: + // private + if(dijit._editor._editorCommandsLocalized){ + // Use the already generate cache of mappings. + this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames; + this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames; + return; + } + dijit._editor._editorCommandsLocalized = true; + dijit._editor._local2NativeFormatNames = {}; + dijit._editor._native2LocalFormatNames = {}; + this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames; + this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames; + //in IE, names for blockformat is locale dependent, so we cache the values here + + //put p after div, so if IE returns Normal, we show it as paragraph + //We can distinguish p and div if IE returns Normal, however, in order to detect that, + //we have to call this.document.selection.createRange().parentElement() or such, which + //could slow things down. Leave it as it is for now + var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address']; + var localhtml = "", format, i=0; + while((format=formats[i++])){ + //append a <br> after each element to separate the elements more reliably + if(format.charAt(1) !== 'l'){ + localhtml += "<"+format+"><span>content</span></"+format+"><br/>"; + }else{ + localhtml += "<"+format+"><li>content</li></"+format+"><br/>"; + } + } + // queryCommandValue returns empty if we hide editNode, so move it out of screen temporary + // Also, IE9 does weird stuff unless we do it inside the editor iframe. + var style = { position: "absolute", top: "0px", zIndex: 10, opacity: 0.01 }; + var div = dojo.create('div', {style: style, innerHTML: localhtml}); + dojo.body().appendChild(div); + + // IE9 has a timing issue with doing this right after setting + // the inner HTML, so put a delay in. + var inject = dojo.hitch(this, function(){ + var node = div.firstChild; + while(node){ + try{ + dijit._editor.selection.selectElement(node.firstChild); + var nativename = node.tagName.toLowerCase(); + this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock"); + this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename; + node = node.nextSibling.nextSibling; + //console.log("Mapped: ", nativename, " to: ", this._local2NativeFormatNames[nativename]); + }catch(e) { /*Sqelch the occasional IE9 error */ } + } + div.parentNode.removeChild(div); + div.innerHTML = ""; + }); + setTimeout(inject, 0); + }, + + open: function(/*DomNode?*/ element){ + // summary: + // Transforms the node referenced in this.domNode into a rich text editing + // node. + // description: + // Sets up the editing area asynchronously. This will result in + // the creation and replacement with an iframe. + // tags: + // private + + if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){ + this.onLoadDeferred = new dojo.Deferred(); + } + + if(!this.isClosed){ this.close(); } + dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]); + + if(arguments.length == 1 && element.nodeName){ // else unchanged + this.domNode = element; + } + + var dn = this.domNode; + + // "html" will hold the innerHTML of the srcNodeRef and will be used to + // initialize the editor. + var html; + + if(dojo.isString(this.value)){ + // Allow setting the editor content programmatically instead of + // relying on the initial content being contained within the target + // domNode. + html = this.value; + delete this.value; + dn.innerHTML = ""; + }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){ + // if we were created from a textarea, then we need to create a + // new editing harness node. + var ta = (this.textarea = dn); + this.name = ta.name; + html = ta.value; + dn = this.domNode = dojo.doc.createElement("div"); + dn.setAttribute('widgetId', this.id); + ta.removeAttribute('widgetId'); + dn.cssText = ta.cssText; + dn.className += " " + ta.className; + dojo.place(dn, ta, "before"); + var tmpFunc = dojo.hitch(this, function(){ + //some browsers refuse to submit display=none textarea, so + //move the textarea off screen instead + dojo.style(ta, { + display: "block", + position: "absolute", + top: "-1000px" + }); + + if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden + var s = ta.style; + this.__overflow = s.overflow; + s.overflow = "hidden"; + } + }); + if(dojo.isIE){ + setTimeout(tmpFunc, 10); + }else{ + tmpFunc(); + } + + if(ta.form){ + var resetValue = ta.value; + this.reset = function(){ + var current = this.getValue(); + if(current != resetValue){ + this.replaceValue(resetValue); + } + }; + dojo.connect(ta.form, "onsubmit", this, function(){ + // Copy value to the <textarea> so it gets submitted along with form. + // FIXME: should we be calling close() here instead? + dojo.attr(ta, 'disabled', this.disabled); // don't submit the value if disabled + ta.value = this.getValue(); + }); + } + }else{ + html = dijit._editor.getChildrenHtml(dn); + dn.innerHTML = ""; + } + + var content = dojo.contentBox(dn); + this._oldHeight = content.h; + this._oldWidth = content.w; + + this.value = html; + + // If we're a list item we have to put in a blank line to force the + // bullet to nicely align at the top of text + if(dn.nodeName && dn.nodeName == "LI"){ + dn.innerHTML = " <br>"; + } + + // Construct the editor div structure. + this.header = dn.ownerDocument.createElement("div"); + dn.appendChild(this.header); + this.editingArea = dn.ownerDocument.createElement("div"); + dn.appendChild(this.editingArea); + this.footer = dn.ownerDocument.createElement("div"); + dn.appendChild(this.footer); + + if(!this.name){ + this.name = this.id + "_AUTOGEN"; + } + + // User has pressed back/forward button so we lost the text in the editor, but it's saved + // in a hidden <textarea> (which contains the data for all the editors on this page), + // so get editor value from there + if(this.name !== "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){ + var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value"); + if(saveTextarea && saveTextarea.value !== ""){ + var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat; + while((dat=datas[i++])){ + var data = dat.split(this._NAME_CONTENT_SEP); + if(data[0] == this.name){ + html = data[1]; + datas = datas.splice(i, 1); + saveTextarea.value = datas.join(this._SEPARATOR); + break; + } + } + } + + if(!dijit._editor._globalSaveHandler){ + dijit._editor._globalSaveHandler = {}; + dojo.addOnUnload(function() { + var id; + for(id in dijit._editor._globalSaveHandler){ + var f = dijit._editor._globalSaveHandler[id]; + if(dojo.isFunction(f)){ + f(); + } + } + }); + } + dijit._editor._globalSaveHandler[this.id] = dojo.hitch(this, "_saveContent"); + } + + this.isClosed = false; + + var ifr = (this.editorObject = this.iframe = dojo.doc.createElement('iframe')); + ifr.id = this.id+"_iframe"; + this._iframeSrc = this._getIframeDocTxt(); + ifr.style.border = "none"; + ifr.style.width = "100%"; + if(this._layoutMode){ + // iframe should be 100% height, thus getting it's height from surrounding + // <div> (which has the correct height set by Editor) + ifr.style.height = "100%"; + }else{ + if(dojo.isIE >= 7){ + if(this.height){ + ifr.style.height = this.height; + } + if(this.minHeight){ + ifr.style.minHeight = this.minHeight; + } + }else{ + ifr.style.height = this.height ? this.height : this.minHeight; + } + } + ifr.frameBorder = 0; + ifr._loadFunc = dojo.hitch( this, function(win){ + this.window = win; + this.document = this.window.document; + + if(dojo.isIE){ + this._localizeEditorCommands(); + } + + // Do final setup and set initial contents of editor + this.onLoad(html); + }); + + // Set the iframe's initial (blank) content. + var s = 'javascript:parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc'; + ifr.setAttribute('src', s); + this.editingArea.appendChild(ifr); + + if(dojo.isSafari <= 4){ + var src = ifr.getAttribute("src"); + if(!src || src.indexOf("javascript") == -1){ + // Safari 4 and earlier sometimes act oddly + // So we have to set it again. + setTimeout(function(){ifr.setAttribute('src', s);},0); + } + } + + // TODO: this is a guess at the default line-height, kinda works + if(dn.nodeName == "LI"){ + dn.lastChild.style.marginTop = "-1.2em"; + } + + dojo.addClass(this.domNode, this.baseClass); + }, + + //static cache variables shared among all instance of this class + _local2NativeFormatNames: {}, + _native2LocalFormatNames: {}, + + _getIframeDocTxt: function(){ + // summary: + // Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>). + // Editor content (if not blank) should be added afterwards. + // tags: + // private + var _cs = dojo.getComputedStyle(this.domNode); + + // The contents inside of <body>. The real contents are set later via a call to setValue(). + var html = ""; + var setBodyId = true; + if(dojo.isIE || dojo.isWebKit || (!this.height && !dojo.isMoz)){ + // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly + // expand/contract the editor as the content changes. + html = "<div id='dijitEditorBody'></div>"; + setBodyId = false; + }else if(dojo.isMoz){ + // workaround bug where can't select then delete text (until user types something + // into the editor)... and/or issue where typing doesn't erase selected text + this._cursorToStart = true; + html = " "; + } + + var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" "); + + // line height is tricky - applying a units value will mess things up. + // if we can't get a non-units value, bail out. + var lineHeight = _cs.lineHeight; + if(lineHeight.indexOf("px") >= 0){ + lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize); + // console.debug(lineHeight); + }else if(lineHeight.indexOf("em")>=0){ + lineHeight = parseFloat(lineHeight); + }else{ + // If we can't get a non-units value, just default + // it to the CSS spec default of 'normal'. Seems to + // work better, esp on IE, than '1.0' + lineHeight = "normal"; + } + var userStyle = ""; + var self = this; + this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){ + match = match.replace(/^;/ig,"") + ';'; + var s = match.split(":")[0]; + if(s){ + s = dojo.trim(s); + s = s.toLowerCase(); + var i; + var sC = ""; + for(i = 0; i < s.length; i++){ + var c = s.charAt(i); + switch(c){ + case "-": + i++; + c = s.charAt(i).toUpperCase(); + default: + sC += c; + } + } + dojo.style(self.domNode, sC, ""); + } + userStyle += match + ';'; + }); + + + // need to find any associated label element and update iframe document title + var label=dojo.query('label[for="'+this.id+'"]'); + + return [ + this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n", + (dojo.isMoz && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""), + "<meta http-equiv='Content-Type' content='text/html'>\n", + "<style>\n", + "\tbody,html {\n", + "\t\tbackground:transparent;\n", + "\t\tpadding: 1px 0 0 0;\n", + "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox + + // Set the html/body sizing. Webkit always needs this, other browsers + // only set it when height is defined (not auto-expanding), otherwise + // scrollers do not appear. + ((dojo.isWebKit)?"\t\twidth: 100%;\n":""), + ((dojo.isWebKit)?"\t\theight: 100%;\n":""), + "\t}\n", + + // TODO: left positioning will cause contents to disappear out of view + // if it gets too wide for the visible area + "\tbody{\n", + "\t\ttop:0px;\n", + "\t\tleft:0px;\n", + "\t\tright:0px;\n", + "\t\tfont:", font, ";\n", + ((this.height||dojo.isOpera) ? "" : "\t\tposition: fixed;\n"), + // FIXME: IE 6 won't understand min-height? + "\t\tmin-height:", this.minHeight, ";\n", + "\t\tline-height:", lineHeight,";\n", + "\t}\n", + "\tp{ margin: 1em 0; }\n", + + // Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all. + // But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand + // (Mainly IE) we need to kill the y scroller on body and html. + (!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""), + "\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + " outline: 0px;}\n", + "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n", + // Can't set min-height in IE9, it puts layout on li, which puts move/resize handles. + (!dojo.isIE ? "\tli{ min-height:1.2em; }\n" : ""), + "</style>\n", + this._applyEditingAreaStyleSheets(),"\n", + "</head>\n<body ", + (setBodyId?"id='dijitEditorBody' ":""), + "onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>" + ].join(""); // String + }, + + _applyEditingAreaStyleSheets: function(){ + // summary: + // apply the specified css files in styleSheets + // tags: + // private + var files = []; + if(this.styleSheets){ + files = this.styleSheets.split(';'); + this.styleSheets = ''; + } + + //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet + files = files.concat(this.editingAreaStyleSheets); + this.editingAreaStyleSheets = []; + + var text='', i=0, url; + while((url=files[i++])){ + var abstring = (new dojo._Url(dojo.global.location, url)).toString(); + this.editingAreaStyleSheets.push(abstring); + text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>'; + } + return text; + }, + + addStyleSheet: function(/*dojo._Url*/ uri){ + // summary: + // add an external stylesheet for the editing area + // uri: + // A dojo.uri.Uri pointing to the url of the external css file + var url=uri.toString(); + + //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe + if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){ + url = (new dojo._Url(dojo.global.location, url)).toString(); + } + + if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){ +// console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied"); + return; + } + + this.editingAreaStyleSheets.push(url); + this.onLoadDeferred.addCallback(dojo.hitch(this, function(){ + if(this.document.createStyleSheet){ //IE + this.document.createStyleSheet(url); + }else{ //other browser + var head = this.document.getElementsByTagName("head")[0]; + var stylesheet = this.document.createElement("link"); + stylesheet.rel="stylesheet"; + stylesheet.type="text/css"; + stylesheet.href=url; + head.appendChild(stylesheet); + } + })); + }, + + removeStyleSheet: function(/*dojo._Url*/ uri){ + // summary: + // remove an external stylesheet for the editing area + var url=uri.toString(); + //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe + if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){ + url = (new dojo._Url(dojo.global.location, url)).toString(); + } + var index = dojo.indexOf(this.editingAreaStyleSheets, url); + if(index == -1){ +// console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied"); + return; + } + delete this.editingAreaStyleSheets[index]; + dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan(); + }, + + // disabled: Boolean + // The editor is disabled; the text cannot be changed. + disabled: false, + + _mozSettingProps: {'styleWithCSS':false}, + _setDisabledAttr: function(/*Boolean*/ value){ + value = !!value; + this._set("disabled", value); + if(!this.isLoaded){ return; } // this method requires init to be complete + if(dojo.isIE || dojo.isWebKit || dojo.isOpera){ + var preventIEfocus = dojo.isIE && (this.isLoaded || !this.focusOnLoad); + if(preventIEfocus){ this.editNode.unselectable = "on"; } + this.editNode.contentEditable = !value; + if(preventIEfocus){ + var _this = this; + setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0); + } + }else{ //moz + try{ + this.document.designMode=(value?'off':'on'); + }catch(e){ return; } // ! _disabledOK + if(!value && this._mozSettingProps){ + var ps = this._mozSettingProps; + for(var n in ps){ + if(ps.hasOwnProperty(n)){ + try{ + this.document.execCommand(n,false,ps[n]); + }catch(e2){} + } + } + } +// this.document.execCommand('contentReadOnly', false, value); +// if(value){ +// this.blur(); //to remove the blinking caret +// } + } + this._disabledOK = true; + }, + +/* Event handlers + *****************/ + + onLoad: function(/*String*/ html){ + // summary: + // Handler after the iframe finishes loading. + // html: String + // Editor contents should be set to this value + // tags: + // protected + + // TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler? + + if(!this.window.__registeredWindow){ + this.window.__registeredWindow = true; + this._iframeRegHandle = dijit.registerIframe(this.iframe); + } + if(!dojo.isIE && !dojo.isWebKit && (this.height || dojo.isMoz)){ + this.editNode=this.document.body; + }else{ + // there's a wrapper div around the content, see _getIframeDocTxt(). + this.editNode=this.document.body.firstChild; + var _this = this; + if(dojo.isIE){ // #4996 IE wants to focus the BODY tag + this.tabStop = dojo.create('div', { tabIndex: -1 }, this.editingArea); + this.iframe.onfocus = function(){ _this.editNode.setActive(); }; + } + } + this.focusNode = this.editNode; // for InlineEditBox + + + var events = this.events.concat(this.captureEvents); + var ap = this.iframe ? this.document : this.editNode; + dojo.forEach(events, function(item){ + this.connect(ap, item.toLowerCase(), item); + }, this); + + this.connect(ap, "onmouseup", "onClick"); // mouseup in the margin does not generate an onclick event + + if(dojo.isIE){ // IE contentEditable + this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus + + // give the node Layout on IE + // TODO: this may no longer be needed, since we've reverted IE to using an iframe, + // not contentEditable. Removing it would also probably remove the need for creating + // the extra <div> in _getIframeDocTxt() + this.editNode.style.zoom = 1.0; + }else{ + this.connect(this.document, "onmousedown", function(){ + // Clear the moveToStart focus, as mouse + // down will set cursor point. Required to properly + // work with selection/position driven plugins and clicks in + // the window. refs: #10678 + delete this._cursorToStart; + }); + } + + if(dojo.isWebKit){ + //WebKit sometimes doesn't fire right on selections, so the toolbar + //doesn't update right. Therefore, help it out a bit with an additional + //listener. A mouse up will typically indicate a display change, so fire this + //and get the toolbar to adapt. Reference: #9532 + this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged"); + this.connect(this.document, "onmousedown", function(e){ + var t = e.target; + if(t && (t === this.document.body || t === this.document)){ + // Since WebKit uses the inner DIV, we need to check and set position. + // See: #12024 as to why the change was made. + setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0); + } + }); + } + + if(dojo.isIE){ + // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE + // do). See #9103 + try{ + this.document.execCommand('RespectVisibilityInDesign', true, null); + }catch(e){/* squelch */} + } + + this.isLoaded = true; + + this.set('disabled', this.disabled); // initialize content to editable (or not) + + // Note that setValue() call will only work after isLoaded is set to true (above) + + // Set up a function to allow delaying the setValue until a callback is fired + // This ensures extensions like dijit.Editor have a way to hold the value set + // until plugins load (and do things like register filters). + var setContent = dojo.hitch(this, function(){ + this.setValue(html); + if(this.onLoadDeferred){ + this.onLoadDeferred.callback(true); + } + this.onDisplayChanged(); + if(this.focusOnLoad){ + // after the document loads, then set focus after updateInterval expires so that + // onNormalizedDisplayChanged has run to avoid input caret issues + dojo.addOnLoad(dojo.hitch(this, function(){ setTimeout(dojo.hitch(this, "focus"), this.updateInterval); })); + } + // Save off the initial content now + this.value = this.getValue(true); + }); + if(this.setValueDeferred){ + this.setValueDeferred.addCallback(setContent); + }else{ + setContent(); + } + }, + + onKeyDown: function(/* Event */ e){ + // summary: + // Handler for onkeydown event + // tags: + // protected + + // we need this event at the moment to get the events from control keys + // such as the backspace. It might be possible to add this to Dojo, so that + // keyPress events can be emulated by the keyDown and keyUp detection. + + if(e.keyCode === dojo.keys.TAB && this.isTabIndent ){ + dojo.stopEvent(e); //prevent tab from moving focus out of editor + + // FIXME: this is a poor-man's indent/outdent. It would be + // better if it added 4 " " chars in an undoable way. + // Unfortunately pasteHTML does not prove to be undoable + if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){ + this.execCommand((e.shiftKey ? "outdent" : "indent")); + } + } + if(dojo.isIE){ + if(e.keyCode == dojo.keys.TAB && !this.isTabIndent){ + if(e.shiftKey && !e.ctrlKey && !e.altKey){ + // focus the BODY so the browser will tab away from it instead + this.iframe.focus(); + }else if(!e.shiftKey && !e.ctrlKey && !e.altKey){ + // focus the BODY so the browser will tab away from it instead + this.tabStop.focus(); + } + }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){ + // IE has a bug where if a non-text object is selected in the editor, + // hitting backspace would act as if the browser's back button was + // clicked instead of deleting the object. see #1069 + dojo.stopEvent(e); + this.execCommand("delete"); + }else if((65 <= e.keyCode && e.keyCode <= 90) || + (e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead! + ){ //arrow keys + e.charCode = e.keyCode; + this.onKeyPress(e); + } + } + return true; + }, + + onKeyUp: function(e){ + // summary: + // Handler for onkeyup event + // tags: + // callback + return; + }, + + setDisabled: function(/*Boolean*/ disabled){ + // summary: + // Deprecated, use set('disabled', ...) instead. + // tags: + // deprecated + dojo.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0); + this.set('disabled',disabled); + }, + _setValueAttr: function(/*String*/ value){ + // summary: + // Registers that attr("value", foo) should call setValue(foo) + this.setValue(value); + }, + _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){ + if(this.document){ + dojo.attr(this.document.body, "spellcheck", !disabled); + }else{ + // try again after the editor is finished loading + this.onLoadDeferred.addCallback(dojo.hitch(this, function(){ + dojo.attr(this.document.body, "spellcheck", !disabled); + })); + } + this._set("disableSpellCheck", disabled); + }, + + onKeyPress: function(e){ + // summary: + // Handle the various key events + // tags: + // protected + + var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode, + handlers = this._keyHandlers[c], + args = arguments; + + if(handlers && !e.altKey){ + dojo.some(handlers, function(h){ + // treat meta- same as ctrl-, for benefit of mac users + if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){ + if(!h.handler.apply(this, args)){ + e.preventDefault(); + } + return true; + } + }, this); + } + + // function call after the character has been inserted + if(!this._onKeyHitch){ + this._onKeyHitch = dojo.hitch(this, "onKeyPressed"); + } + setTimeout(this._onKeyHitch, 1); + return true; + }, + + addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){ + // summary: + // Add a handler for a keyboard shortcut + // description: + // The key argument should be in lowercase if it is a letter character + // tags: + // protected + if(!dojo.isArray(this._keyHandlers[key])){ + this._keyHandlers[key] = []; + } + //TODO: would be nice to make this a hash instead of an array for quick lookups + this._keyHandlers[key].push({ + shift: shift || false, + ctrl: ctrl || false, + handler: handler + }); + }, + + onKeyPressed: function(){ + // summary: + // Handler for after the user has pressed a key, and the display has been updated. + // (Runs on a timer so that it runs after the display is updated) + // tags: + // private + this.onDisplayChanged(/*e*/); // can't pass in e + }, + + onClick: function(/*Event*/ e){ + // summary: + // Handler for when the user clicks. + // tags: + // private + + // console.info('onClick',this._tryDesignModeOn); + this.onDisplayChanged(e); + }, + + _onIEMouseDown: function(/*Event*/ e){ + // summary: + // IE only to prevent 2 clicks to focus + // tags: + // protected + + if(!this._focused && !this.disabled){ + this.focus(); + } + }, + + _onBlur: function(e){ + // summary: + // Called from focus manager when focus has moved away from this editor + // tags: + // protected + + // console.info('_onBlur') + + this.inherited(arguments); + + var newValue = this.getValue(true); + if(newValue != this.value){ + this.onChange(newValue); + } + this._set("value", newValue); + }, + + _onFocus: function(/*Event*/ e){ + // summary: + // Called from focus manager when focus has moved into this editor + // tags: + // protected + + // console.info('_onFocus') + if(!this.disabled){ + if(!this._disabledOK){ + this.set('disabled', false); + } + this.inherited(arguments); + } + }, + + // TODO: remove in 2.0 + blur: function(){ + // summary: + // Remove focus from this instance. + // tags: + // deprecated + if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){ + this.window.document.documentElement.focus(); + }else if(dojo.doc.body.focus){ + dojo.doc.body.focus(); + } + }, + + focus: function(){ + // summary: + // Move focus to this editor + if(!this.isLoaded){ + this.focusOnLoad = true; + return; + } + if(this._cursorToStart){ + delete this._cursorToStart; + if(this.editNode.childNodes){ + this.placeCursorAtStart(); // this calls focus() so return + return; + } + } + if(!dojo.isIE){ + dijit.focus(this.iframe); + }else if(this.editNode && this.editNode.focus){ + // editNode may be hidden in display:none div, lets just punt in this case + //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe + // if we fire the event manually and let the browser handle the focusing, the latest + // cursor position is focused like in FF + this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE + // }else{ + // TODO: should we throw here? + // console.debug("Have no idea how to focus into the editor!"); + } + }, + + // _lastUpdate: 0, + updateInterval: 200, + _updateTimer: null, + onDisplayChanged: function(/*Event*/ e){ + // summary: + // This event will be fired everytime the display context + // changes and the result needs to be reflected in the UI. + // description: + // If you don't want to have update too often, + // onNormalizedDisplayChanged should be used instead + // tags: + // private + + // var _t=new Date(); + if(this._updateTimer){ + clearTimeout(this._updateTimer); + } + if(!this._updateHandler){ + this._updateHandler = dojo.hitch(this,"onNormalizedDisplayChanged"); + } + this._updateTimer = setTimeout(this._updateHandler, this.updateInterval); + + // Technically this should trigger a call to watch("value", ...) registered handlers, + // but getValue() is too slow to call on every keystroke so we don't. + }, + onNormalizedDisplayChanged: function(){ + // summary: + // This event is fired every updateInterval ms or more + // description: + // If something needs to happen immediately after a + // user change, please use onDisplayChanged instead. + // tags: + // private + delete this._updateTimer; + }, + onChange: function(newContent){ + // summary: + // This is fired if and only if the editor loses focus and + // the content is changed. + }, + _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){ + // summary: + // Used as the advice function by dojo.connect to map our + // normalized set of commands to those supported by the target + // browser. + // tags: + // private + + var command = cmd.toLowerCase(); + if(command == "formatblock"){ + if(dojo.isSafari && argument === undefined){ command = "heading"; } + }else if(command == "hilitecolor" && !dojo.isMoz){ + command = "backcolor"; + } + + return command; + }, + + _qcaCache: {}, + queryCommandAvailable: function(/*String*/ command){ + // summary: + // Tests whether a command is supported by the host. Clients + // SHOULD check whether a command is supported before attempting + // to use it, behaviour for unsupported commands is undefined. + // command: + // The command to test for + // tags: + // private + + // memoizing version. See _queryCommandAvailable for computing version + var ca = this._qcaCache[command]; + if(ca !== undefined){ return ca; } + return (this._qcaCache[command] = this._queryCommandAvailable(command)); + }, + + _queryCommandAvailable: function(/*String*/ command){ + // summary: + // See queryCommandAvailable(). + // tags: + // private + + var ie = 1; + var mozilla = 1 << 1; + var webkit = 1 << 2; + var opera = 1 << 3; + + function isSupportedBy(browsers){ + return { + ie: Boolean(browsers & ie), + mozilla: Boolean(browsers & mozilla), + webkit: Boolean(browsers & webkit), + opera: Boolean(browsers & opera) + }; + } + + var supportedBy = null; + + switch(command.toLowerCase()){ + case "bold": case "italic": case "underline": + case "subscript": case "superscript": + case "fontname": case "fontsize": + case "forecolor": case "hilitecolor": + case "justifycenter": case "justifyfull": case "justifyleft": + case "justifyright": case "delete": case "selectall": case "toggledir": + supportedBy = isSupportedBy(mozilla | ie | webkit | opera); + break; + + case "createlink": case "unlink": case "removeformat": + case "inserthorizontalrule": case "insertimage": + case "insertorderedlist": case "insertunorderedlist": + case "indent": case "outdent": case "formatblock": + case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent": + supportedBy = isSupportedBy(mozilla | ie | opera | webkit); + break; + + case "blockdirltr": case "blockdirrtl": + case "dirltr": case "dirrtl": + case "inlinedirltr": case "inlinedirrtl": + supportedBy = isSupportedBy(ie); + break; + case "cut": case "copy": case "paste": + supportedBy = isSupportedBy( ie | mozilla | webkit); + break; + + case "inserttable": + supportedBy = isSupportedBy(mozilla | ie); + break; + + case "insertcell": case "insertcol": case "insertrow": + case "deletecells": case "deletecols": case "deleterows": + case "mergecells": case "splitcell": + supportedBy = isSupportedBy(ie | mozilla); + break; + + default: return false; + } + + return (dojo.isIE && supportedBy.ie) || + (dojo.isMoz && supportedBy.mozilla) || + (dojo.isWebKit && supportedBy.webkit) || + (dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise + }, + + execCommand: function(/*String*/ command, argument){ + // summary: + // Executes a command in the Rich Text area + // command: + // The command to execute + // argument: + // An optional argument to the command + // tags: + // protected + var returnValue; + + //focus() is required for IE to work + //In addition, focus() makes sure after the execution of + //the command, the editor receives the focus as expected + this.focus(); + + command = this._normalizeCommand(command, argument); + + if(argument !== undefined){ + if(command == "heading"){ + throw new Error("unimplemented"); + }else if((command == "formatblock") && dojo.isIE){ + argument = '<'+argument+'>'; + } + } + + //Check to see if we have any over-rides for commands, they will be functions on this + //widget of the form _commandImpl. If we don't, fall through to the basic native + //exec command of the browser. + var implFunc = "_" + command + "Impl"; + if(this[implFunc]){ + returnValue = this[implFunc](argument); + }else{ + argument = arguments.length > 1 ? argument : null; + if(argument || command!="createlink"){ + returnValue = this.document.execCommand(command, false, argument); + } + } + + this.onDisplayChanged(); + return returnValue; + }, + + queryCommandEnabled: function(/*String*/ command){ + // summary: + // Check whether a command is enabled or not. + // tags: + // protected + if(this.disabled || !this._disabledOK){ return false; } + command = this._normalizeCommand(command); + if(dojo.isMoz || dojo.isWebKit){ + if(command == "unlink"){ // mozilla returns true always + // console.debug(this._sCall("hasAncestorElement", ['a'])); + return this._sCall("hasAncestorElement", ["a"]); + }else if(command == "inserttable"){ + return true; + } + } + //see #4109 + if(dojo.isWebKit){ + if(command == "cut" || command == "copy") { + // WebKit deems clipboard activity as a security threat and natively would return false + var sel = this.window.getSelection(); + if(sel){ sel = sel.toString(); } + return !!sel; + }else if(command == "paste"){ + return true; + } + } + + var elem = dojo.isIE ? this.document.selection.createRange() : this.document; + try{ + return elem.queryCommandEnabled(command); + }catch(e){ + //Squelch, occurs if editor is hidden on FF 3 (and maybe others.) + return false; + } + + }, + + queryCommandState: function(command){ + // summary: + // Check the state of a given command and returns true or false. + // tags: + // protected + + if(this.disabled || !this._disabledOK){ return false; } + command = this._normalizeCommand(command); + try{ + return this.document.queryCommandState(command); + }catch(e){ + //Squelch, occurs if editor is hidden on FF 3 (and maybe others.) + return false; + } + }, + + queryCommandValue: function(command){ + // summary: + // Check the value of a given command. This matters most for + // custom selections and complex values like font value setting. + // tags: + // protected + + if(this.disabled || !this._disabledOK){ return false; } + var r; + command = this._normalizeCommand(command); + if(dojo.isIE && command == "formatblock"){ + r = this._native2LocalFormatNames[this.document.queryCommandValue(command)]; + }else if(dojo.isMoz && command === "hilitecolor"){ + var oldValue; + try{ + oldValue = this.document.queryCommandValue("styleWithCSS"); + }catch(e){ + oldValue = false; + } + this.document.execCommand("styleWithCSS", false, true); + r = this.document.queryCommandValue(command); + this.document.execCommand("styleWithCSS", false, oldValue); + }else{ + r = this.document.queryCommandValue(command); + } + return r; + }, + + // Misc. + + _sCall: function(name, args){ + // summary: + // Run the named method of dijit._editor.selection over the + // current editor instance's window, with the passed args. + // tags: + // private + return dojo.withGlobal(this.window, name, dijit._editor.selection, args); + }, + + // FIXME: this is a TON of code duplication. Why? + + placeCursorAtStart: function(){ + // summary: + // Place the cursor at the start of the editing area. + // tags: + // private + + this.focus(); + + //see comments in placeCursorAtEnd + var isvalid=false; + if(dojo.isMoz){ + // TODO: Is this branch even necessary? + var first=this.editNode.firstChild; + while(first){ + if(first.nodeType == 3){ + if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){ + isvalid=true; + this._sCall("selectElement", [ first ]); + break; + } + }else if(first.nodeType == 1){ + isvalid=true; + var tg = first.tagName ? first.tagName.toLowerCase() : ""; + // Collapse before childless tags. + if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){ + this._sCall("selectElement", [ first ]); + }else{ + // Collapse inside tags with children. + this._sCall("selectElementChildren", [ first ]); + } + break; + } + first = first.nextSibling; + } + }else{ + isvalid=true; + this._sCall("selectElementChildren", [ this.editNode ]); + } + if(isvalid){ + this._sCall("collapse", [ true ]); + } + }, + + placeCursorAtEnd: function(){ + // summary: + // Place the cursor at the end of the editing area. + // tags: + // private + + this.focus(); + + //In mozilla, if last child is not a text node, we have to use + // selectElementChildren on this.editNode.lastChild otherwise the + // cursor would be placed at the end of the closing tag of + //this.editNode.lastChild + var isvalid=false; + if(dojo.isMoz){ + var last=this.editNode.lastChild; + while(last){ + if(last.nodeType == 3){ + if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){ + isvalid=true; + this._sCall("selectElement", [ last ]); + break; + } + }else if(last.nodeType == 1){ + isvalid=true; + if(last.lastChild){ + this._sCall("selectElement", [ last.lastChild ]); + }else{ + this._sCall("selectElement", [ last ]); + } + break; + } + last = last.previousSibling; + } + }else{ + isvalid=true; + this._sCall("selectElementChildren", [ this.editNode ]); + } + if(isvalid){ + this._sCall("collapse", [ false ]); + } + }, + + getValue: function(/*Boolean?*/ nonDestructive){ + // summary: + // Return the current content of the editing area (post filters + // are applied). Users should call get('value') instead. + // nonDestructive: + // defaults to false. Should the post-filtering be run over a copy + // of the live DOM? Most users should pass "true" here unless they + // *really* know that none of the installed filters are going to + // mess up the editing session. + // tags: + // private + if(this.textarea){ + if(this.isClosed || !this.isLoaded){ + return this.textarea.value; + } + } + + return this._postFilterContent(null, nonDestructive); + }, + _getValueAttr: function(){ + // summary: + // Hook to make attr("value") work + return this.getValue(true); + }, + + setValue: function(/*String*/ html){ + // summary: + // This function sets the content. No undo history is preserved. + // Users should use set('value', ...) instead. + // tags: + // deprecated + + // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr() + + if(!this.isLoaded){ + // try again after the editor is finished loading + this.onLoadDeferred.addCallback(dojo.hitch(this, function(){ + this.setValue(html); + })); + return; + } + this._cursorToStart = true; + if(this.textarea && (this.isClosed || !this.isLoaded)){ + this.textarea.value=html; + }else{ + html = this._preFilterContent(html); + var node = this.isClosed ? this.domNode : this.editNode; + if(html && dojo.isMoz && html.toLowerCase() == "<p></p>"){ + html = "<p> </p>"; + } + + // Use to avoid webkit problems where editor is disabled until the user clicks it + if(!html && dojo.isWebKit){ + html = " "; + } + node.innerHTML = html; + this._preDomFilterContent(node); + } + + this.onDisplayChanged(); + this._set("value", this.getValue(true)); + }, + + replaceValue: function(/*String*/ html){ + // summary: + // This function set the content while trying to maintain the undo stack + // (now only works fine with Moz, this is identical to setValue in all + // other browsers) + // tags: + // protected + + if(this.isClosed){ + this.setValue(html); + }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari + // look ma! it's a totally f'd browser! + this.setValue(html); + }else if(this.window && this.window.getSelection){ // Moz + html = this._preFilterContent(html); + this.execCommand("selectall"); + if(!html){ + this._cursorToStart = true; + html = " "; + } + this.execCommand("inserthtml", html); + this._preDomFilterContent(this.editNode); + }else if(this.document && this.document.selection){//IE + //In IE, when the first element is not a text node, say + //an <a> tag, when replacing the content of the editing + //area, the <a> tag will be around all the content + //so for now, use setValue for IE too + this.setValue(html); + } + + this._set("value", this.getValue(true)); + }, + + _preFilterContent: function(/*String*/ html){ + // summary: + // Filter the input before setting the content of the editing + // area. DOM pre-filtering may happen after this + // string-based filtering takes place but as of 1.2, this is not + // guaranteed for operations such as the inserthtml command. + // tags: + // private + + var ec = html; + dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } }); + return ec; + }, + _preDomFilterContent: function(/*DomNode*/ dom){ + // summary: + // filter the input's live DOM. All filter operations should be + // considered to be "live" and operating on the DOM that the user + // will be interacting with in their editing session. + // tags: + // private + dom = dom || this.editNode; + dojo.forEach(this.contentDomPreFilters, function(ef){ + if(ef && dojo.isFunction(ef)){ + ef(dom); + } + }, this); + }, + + _postFilterContent: function( + /*DomNode|DomNode[]|String?*/ dom, + /*Boolean?*/ nonDestructive){ + // summary: + // filter the output after getting the content of the editing area + // + // description: + // post-filtering allows plug-ins and users to specify any number + // of transforms over the editor's content, enabling many common + // use-cases such as transforming absolute to relative URLs (and + // vice-versa), ensuring conformance with a particular DTD, etc. + // The filters are registered in the contentDomPostFilters and + // contentPostFilters arrays. Each item in the + // contentDomPostFilters array is a function which takes a DOM + // Node or array of nodes as its only argument and returns the + // same. It is then passed down the chain for further filtering. + // The contentPostFilters array behaves the same way, except each + // member operates on strings. Together, the DOM and string-based + // filtering allow the full range of post-processing that should + // be necessaray to enable even the most agressive of post-editing + // conversions to take place. + // + // If nonDestructive is set to "true", the nodes are cloned before + // filtering proceeds to avoid potentially destructive transforms + // to the content which may still needed to be edited further. + // Once DOM filtering has taken place, the serialized version of + // the DOM which is passed is run through each of the + // contentPostFilters functions. + // + // dom: + // a node, set of nodes, which to filter using each of the current + // members of the contentDomPostFilters and contentPostFilters arrays. + // + // nonDestructive: + // defaults to "false". If true, ensures that filtering happens on + // a clone of the passed-in content and not the actual node + // itself. + // + // tags: + // private + + var ec; + if(!dojo.isString(dom)){ + dom = dom || this.editNode; + if(this.contentDomPostFilters.length){ + if(nonDestructive){ + dom = dojo.clone(dom); + } + dojo.forEach(this.contentDomPostFilters, function(ef){ + dom = ef(dom); + }); + } + ec = dijit._editor.getChildrenHtml(dom); + }else{ + ec = dom; + } + + if(!dojo.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){ + ec = ""; + } + + // if(dojo.isIE){ + // //removing appended <P> </P> for IE + // ec = ec.replace(/(?:<p> </p>[\n\r]*)+$/i,""); + // } + dojo.forEach(this.contentPostFilters, function(ef){ + ec = ef(ec); + }); + + return ec; + }, + + _saveContent: function(/*Event*/ e){ + // summary: + // Saves the content in an onunload event if the editor has not been closed + // tags: + // private + + var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value"); + if(saveTextarea.value){ + saveTextarea.value += this._SEPARATOR; + } + saveTextarea.value += this.name + this._NAME_CONTENT_SEP + this.getValue(true); + }, + + + escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){ + // summary: + // Adds escape sequences for special characters in XML. + // Optionally skips escapes for single quotes + // tags: + // private + + str = str.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """); + if(!noSingleQuotes){ + str = str.replace(/'/gm, "'"); + } + return str; // string + }, + + getNodeHtml: function(/* DomNode */ node){ + // summary: + // Deprecated. Use dijit._editor._getNodeHtml() instead. + // tags: + // deprecated + dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2); + return dijit._editor.getNodeHtml(node); // String + }, + + getNodeChildrenHtml: function(/* DomNode */ dom){ + // summary: + // Deprecated. Use dijit._editor.getChildrenHtml() instead. + // tags: + // deprecated + dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2); + return dijit._editor.getChildrenHtml(dom); + }, + + close: function(/*Boolean?*/ save){ + // summary: + // Kills the editor and optionally writes back the modified contents to the + // element from which it originated. + // save: + // Whether or not to save the changes. If false, the changes are discarded. + // tags: + // private + + if(this.isClosed){ return; } + + if(!arguments.length){ save = true; } + if(save){ + this._set("value", this.getValue(true)); + } + + // line height is squashed for iframes + // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; } + + if(this.interval){ clearInterval(this.interval); } + + if(this._webkitListener){ + //Cleaup of WebKit fix: #9532 + this.disconnect(this._webkitListener); + delete this._webkitListener; + } + + // Guard against memory leaks on IE (see #9268) + if(dojo.isIE){ + this.iframe.onfocus = null; + } + this.iframe._loadFunc = null; + + if(this._iframeRegHandle){ + dijit.unregisterIframe(this._iframeRegHandle); + delete this._iframeRegHandle; + } + + if(this.textarea){ + var s = this.textarea.style; + s.position = ""; + s.left = s.top = ""; + if(dojo.isIE){ + s.overflow = this.__overflow; + this.__overflow = null; + } + this.textarea.value = this.value; + dojo.destroy(this.domNode); + this.domNode = this.textarea; + }else{ + // Note that this destroys the iframe + this.domNode.innerHTML = this.value; + } + delete this.iframe; + + dojo.removeClass(this.domNode, this.baseClass); + this.isClosed = true; + this.isLoaded = false; + + delete this.editNode; + delete this.focusNode; + + if(this.window && this.window._frameElement){ + this.window._frameElement = null; + } + + this.window = null; + this.document = null; + this.editingArea = null; + this.editorObject = null; + }, + + destroy: function(){ + if(!this.isClosed){ this.close(false); } + this.inherited(arguments); + if(dijit._editor._globalSaveHandler){ + delete dijit._editor._globalSaveHandler[this.id]; + } + }, + + _removeMozBogus: function(/* String */ html){ + // summary: + // Post filter to remove unwanted HTML attributes generated by mozilla + // tags: + // private + return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String + }, + _removeWebkitBogus: function(/* String */ html){ + // summary: + // Post filter to remove unwanted HTML attributes generated by webkit + // tags: + // private + html = html.replace(/\sclass="webkit-block-placeholder"/gi, ''); + html = html.replace(/\sclass="apple-style-span"/gi, ''); + // For some reason copy/paste sometime adds extra meta tags for charset on + // webkit (chrome) on mac.They need to be removed. See: #12007" + html = html.replace(/<meta charset=\"utf-8\" \/>/gi, ''); + return html; // String + }, + _normalizeFontStyle: function(/* String */ html){ + // summary: + // Convert 'strong' and 'em' to 'b' and 'i'. + // description: + // Moz can not handle strong/em tags correctly, so to help + // mozilla and also to normalize output, convert them to 'b' and 'i'. + // + // Note the IE generates 'strong' and 'em' rather than 'b' and 'i' + // tags: + // private + return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2') + .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String + }, + + _preFixUrlAttributes: function(/* String */ html){ + // summary: + // Pre-filter to do fixing to href attributes on <a> and <img> tags + // tags: + // private + return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi, + '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') + .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi, + '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String + }, + + /***************************************************************************** + The following functions implement HTML manipulation commands for various + browser/contentEditable implementations. The goal of them is to enforce + standard behaviors of them. + ******************************************************************************/ + + _inserthorizontalruleImpl: function(argument){ + // summary: + // This function implements the insertion of HTML 'HR' tags. + // into a point on the page. IE doesn't to it right, so + // we have to use an alternate form + // argument: + // arguments to the exec command, if any. + // tags: + // protected + if(dojo.isIE){ + return this._inserthtmlImpl("<hr>"); + } + return this.document.execCommand("inserthorizontalrule", false, argument); + }, + + _unlinkImpl: function(argument){ + // summary: + // This function implements the unlink of an 'a' tag. + // argument: + // arguments to the exec command, if any. + // tags: + // protected + if((this.queryCommandEnabled("unlink")) && (dojo.isMoz || dojo.isWebKit)){ + var a = this._sCall("getAncestorElement", [ "a" ]); + this._sCall("selectElement", [ a ]); + return this.document.execCommand("unlink", false, null); + } + return this.document.execCommand("unlink", false, argument); + }, + + _hilitecolorImpl: function(argument){ + // summary: + // This function implements the hilitecolor command + // argument: + // arguments to the exec command, if any. + // tags: + // protected + var returnValue; + if(dojo.isMoz){ + // mozilla doesn't support hilitecolor properly when useCSS is + // set to false (bugzilla #279330) + this.document.execCommand("styleWithCSS", false, true); + returnValue = this.document.execCommand("hilitecolor", false, argument); + this.document.execCommand("styleWithCSS", false, false); + }else{ + returnValue = this.document.execCommand("hilitecolor", false, argument); + } + return returnValue; + }, + + _backcolorImpl: function(argument){ + // summary: + // This function implements the backcolor command + // argument: + // arguments to the exec command, if any. + // tags: + // protected + if(dojo.isIE){ + // Tested under IE 6 XP2, no problem here, comment out + // IE weirdly collapses ranges when we exec these commands, so prevent it + // var tr = this.document.selection.createRange(); + argument = argument ? argument : null; + } + return this.document.execCommand("backcolor", false, argument); + }, + + _forecolorImpl: function(argument){ + // summary: + // This function implements the forecolor command + // argument: + // arguments to the exec command, if any. + // tags: + // protected + if(dojo.isIE){ + // Tested under IE 6 XP2, no problem here, comment out + // IE weirdly collapses ranges when we exec these commands, so prevent it + // var tr = this.document.selection.createRange(); + argument = argument? argument : null; + } + return this.document.execCommand("forecolor", false, argument); + }, + + _inserthtmlImpl: function(argument){ + // summary: + // This function implements the insertion of HTML content into + // a point on the page. + // argument: + // The content to insert, if any. + // tags: + // protected + argument = this._preFilterContent(argument); + var rv = true; + if(dojo.isIE){ + var insertRange = this.document.selection.createRange(); + if(this.document.selection.type.toUpperCase() == 'CONTROL'){ + var n=insertRange.item(0); + while(insertRange.length){ + insertRange.remove(insertRange.item(0)); + } + n.outerHTML=argument; + }else{ + insertRange.pasteHTML(argument); + } + insertRange.select(); + //insertRange.collapse(true); + }else if(dojo.isMoz && !argument.length){ + //mozilla can not inserthtml an empty html to delete current selection + //so we delete the selection instead in this case + this._sCall("remove"); // FIXME + }else{ + rv = this.document.execCommand("inserthtml", false, argument); + } + return rv; + }, + + _boldImpl: function(argument){ + // summary: + // This function implements an over-ride of the bold command. + // argument: + // Not used, operates by selection. + // tags: + // protected + if(dojo.isIE){ + this._adaptIESelection() + } + return this.document.execCommand("bold", false, argument); + }, + + _italicImpl: function(argument){ + // summary: + // This function implements an over-ride of the italic command. + // argument: + // Not used, operates by selection. + // tags: + // protected + if(dojo.isIE){ + this._adaptIESelection() + } + return this.document.execCommand("italic", false, argument); + }, + + _underlineImpl: function(argument){ + // summary: + // This function implements an over-ride of the underline command. + // argument: + // Not used, operates by selection. + // tags: + // protected + if(dojo.isIE){ + this._adaptIESelection() + } + return this.document.execCommand("underline", false, argument); + }, + + _strikethroughImpl: function(argument){ + // summary: + // This function implements an over-ride of the strikethrough command. + // argument: + // Not used, operates by selection. + // tags: + // protected + if(dojo.isIE){ + this._adaptIESelection() + } + return this.document.execCommand("strikethrough", false, argument); + }, + + getHeaderHeight: function(){ + // summary: + // A function for obtaining the height of the header node + return this._getNodeChildrenHeight(this.header); // Number + }, + + getFooterHeight: function(){ + // summary: + // A function for obtaining the height of the footer node + return this._getNodeChildrenHeight(this.footer); // Number + }, + + _getNodeChildrenHeight: function(node){ + // summary: + // An internal function for computing the cumulative height of all child nodes of 'node' + // node: + // The node to process the children of; + var h = 0; + if(node && node.childNodes){ + // IE didn't compute it right when position was obtained on the node directly is some cases, + // so we have to walk over all the children manually. + var i; + for(i = 0; i < node.childNodes.length; i++){ + var size = dojo.position(node.childNodes[i]); + h += size.h; + } + } + return h; // Number + }, + + _isNodeEmpty: function(node, startOffset){ + // summary: + // Function to test if a node is devoid of real content. + // node: + // The node to check. + // tags: + // private. + if(node.nodeType == 1/*element*/){ + if(node.childNodes.length > 0){ + return this._isNodeEmpty(node.childNodes[0], startOffset); + } + return true; + }else if(node.nodeType == 3/*text*/){ + return (node.nodeValue.substring(startOffset) == ""); + } + return false; + }, + + _removeStartingRangeFromRange: function(node, range){ + // summary: + // Function to adjust selection range by removing the current + // start node. + // node: + // The node to remove from the starting range. + // range: + // The range to adapt. + // tags: + // private + if(node.nextSibling){ + range.setStart(node.nextSibling,0); + }else{ + var parent = node.parentNode; + while(parent && parent.nextSibling == null){ + //move up the tree until we find a parent that has another node, that node will be the next node + parent = parent.parentNode; + } + if(parent){ + range.setStart(parent.nextSibling,0); + } + } + return range; + }, + + _adaptIESelection: function(){ + // summary: + // Function to adapt the IE range by removing leading 'newlines' + // Needed to fix issue with bold/italics/underline not working if + // range included leading 'newlines'. + // In IE, if a user starts a selection at the very end of a line, + // then the native browser commands will fail to execute correctly. + // To work around the issue, we can remove all empty nodes from + // the start of the range selection. + var selection = dijit.range.getSelection(this.window); + if(selection && selection.rangeCount && !selection.isCollapsed){ + var range = selection.getRangeAt(0); + var firstNode = range.startContainer; + var startOffset = range.startOffset; + + while(firstNode.nodeType == 3/*text*/ && startOffset >= firstNode.length && firstNode.nextSibling){ + //traverse the text nodes until we get to the one that is actually highlighted + startOffset = startOffset - firstNode.length; + firstNode = firstNode.nextSibling; + } + + //Remove the starting ranges until the range does not start with an empty node. + var lastNode=null; + while(this._isNodeEmpty(firstNode, startOffset) && firstNode != lastNode){ + lastNode =firstNode; //this will break the loop in case we can't find the next sibling + range = this._removeStartingRangeFromRange(firstNode, range); //move the start container to the next node in the range + firstNode = range.startContainer; + startOffset = 0; //start at the beginning of the new starting range + } + selection.removeAllRanges();// this will work as long as users cannot select multiple ranges. I have not been able to do that in the editor. + selection.addRange(range); + } + } }); -return ec; -},_saveContent:function(e){ -var _57=dojo.byId(dijit._scopeName+"._editor.RichText.savedContent"); -if(_57.value){ -_57.value+=this._SEPARATOR; -} -_57.value+=this.name+":"+this.getValue(true); -},escapeXml:function(str,_58){ -str=str.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">").replace(/"/gm,"""); -if(!_58){ -str=str.replace(/'/gm,"'"); -} -return str; -},getNodeHtml:function(_59){ -dojo.deprecated("dijit.Editor::getNodeHtml is deprecated","use dijit._editor.getNodeHtml instead",2); -return dijit._editor.getNodeHtml(_59); -},getNodeChildrenHtml:function(dom){ -dojo.deprecated("dijit.Editor::getNodeChildrenHtml is deprecated","use dijit._editor.getChildrenHtml instead",2); -return dijit._editor.getChildrenHtml(dom); -},close:function(_5a){ -if(this.isClosed){ -return false; -} -if(!arguments.length){ -_5a=true; -} -this._content=this.getValue(); -var _5b=(this.savedContent!=this._content); -if(this.interval){ -clearInterval(this.interval); -} -if(this._webkitListener){ -this.disconnect(this._webkitListener); -delete this._webkitListener; -} -if(dojo.isIE){ -this.iframe.onfocus=null; -} -this.iframe._loadFunc=null; -if(this._iframeRegHandle){ -dijit.unregisterIframe(this._iframeRegHandle); -delete this._iframeRegHandle; -} -if(this.textarea){ -var s=this.textarea.style; -s.position=""; -s.left=s.top=""; -if(dojo.isIE){ -s.overflow=this.__overflow; -this.__overflow=null; -} -this.textarea.value=_5a?this._content:this.savedContent; -dojo.destroy(this.domNode); -this.domNode=this.textarea; -}else{ -this.domNode.innerHTML=_5a?this._content:this.savedContent; -} -delete this.iframe; -dojo.removeClass(this.domNode,this.baseClass); -this.isClosed=true; -this.isLoaded=false; -delete this.editNode; -delete this.focusNode; -if(this.window&&this.window._frameElement){ -this.window._frameElement=null; -} -this.window=null; -this.document=null; -this.editingArea=null; -this.editorObject=null; -return _5b; -},destroy:function(){ -if(!this.isClosed){ -this.close(false); -} -this.inherited(arguments); -},_removeMozBogus:function(_5c){ -return _5c.replace(/\stype="_moz"/gi,"").replace(/\s_moz_dirty=""/gi,"").replace(/_moz_resizing="(true|false)"/gi,""); -},_removeWebkitBogus:function(_5d){ -_5d=_5d.replace(/\sclass="webkit-block-placeholder"/gi,""); -_5d=_5d.replace(/\sclass="apple-style-span"/gi,""); -return _5d; -},_normalizeFontStyle:function(_5e){ -return _5e.replace(/<(\/)?strong([ \>])/gi,"<$1b$2").replace(/<(\/)?em([ \>])/gi,"<$1i$2"); -},_preFixUrlAttributes:function(_5f){ -return _5f.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,"$1$4$2$3$5$2 _djrealurl=$2$3$5$2").replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,"$1$4$2$3$5$2 _djrealurl=$2$3$5$2"); -},_inserthorizontalruleImpl:function(_60){ -if(dojo.isIE){ -return this._inserthtmlImpl("<hr>"); -} -return this.document.execCommand("inserthorizontalrule",false,_60); -},_unlinkImpl:function(_61){ -if((this.queryCommandEnabled("unlink"))&&(dojo.isMoz||dojo.isWebKit)){ -var a=this._sCall("getAncestorElement",["a"]); -this._sCall("selectElement",[a]); -return this.document.execCommand("unlink",false,null); -} -return this.document.execCommand("unlink",false,_61); -},_hilitecolorImpl:function(_62){ -var _63; -if(dojo.isMoz){ -this.document.execCommand("styleWithCSS",false,true); -_63=this.document.execCommand("hilitecolor",false,_62); -this.document.execCommand("styleWithCSS",false,false); -}else{ -_63=this.document.execCommand("hilitecolor",false,_62); -} -return _63; -},_backcolorImpl:function(_64){ -if(dojo.isIE){ -_64=_64?_64:null; -} -return this.document.execCommand("backcolor",false,_64); -},_forecolorImpl:function(_65){ -if(dojo.isIE){ -_65=_65?_65:null; -} -return this.document.execCommand("forecolor",false,_65); -},_inserthtmlImpl:function(_66){ -_66=this._preFilterContent(_66); -var rv=true; -if(dojo.isIE){ -var _67=this.document.selection.createRange(); -if(this.document.selection.type.toUpperCase()=="CONTROL"){ -var n=_67.item(0); -while(_67.length){ -_67.remove(_67.item(0)); -} -n.outerHTML=_66; -}else{ -_67.pasteHTML(_66); -} -_67.select(); -}else{ -if(dojo.isMoz&&!_66.length){ -this._sCall("remove"); -}else{ -rv=this.document.execCommand("inserthtml",false,_66); -} -} -return rv; -},getHeaderHeight:function(){ -return this._getNodeChildrenHeight(this.header); -},getFooterHeight:function(){ -return this._getNodeChildrenHeight(this.footer); -},_getNodeChildrenHeight:function(_68){ -var h=0; -if(_68&&_68.childNodes){ -var i; -for(i=0;i<_68.childNodes.length;i++){ -var _69=dojo.position(_68.childNodes[i]); -h+=_69.h; -} -} -return h; -}}); + } |