';
-
- this._cacheLocalBlockFormatNames();
- },
-
- //static cache variables shared among all instance of this class
- _local2NativeFormatNames: {},
- _native2LocalFormatNames: {},
- //in IE, names for blockformat is locale dependent, so we cache the values here
- //we use activeX to obtain the list, if success or the names are already cached,
- //return true
- _cacheLocalBlockFormatNames: function(){
- // summary:
- // in IE, names for blockformat is locale dependent, so we cache the values here
- // we use activeX to obtain the list, if success or the names are already cached,
- // return true
- if(!this._native2LocalFormatNames['p']){
- var obj = this.object;
- var error = false;
- if(!obj){
- //create obj temporarily
- try{
- obj = dojo.html.createExternalElement(dojo.doc(), "object");
- obj.classid = "clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A";
- dojo.body().appendChild(obj);
- obj.DocumentHTML = "";
- }catch(e){ error = true; }
- }
- try{
- var oNamesParm = new ActiveXObject("DEGetBlockFmtNamesParam.DEGetBlockFmtNamesParam");
- obj.ExecCommand(this._activeX.command['getblockformatnames'], 0, oNamesParm);
- var vbNamesArray = new VBArray(oNamesParm.Names);
- var localFormats = vbNamesArray.toArray();
- var nativeFormats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', '', '', '','','div'];
- for(var i=0;i
0){
- this._local2NativeFormatNames[localFormats[i]] = nativeFormats[i];
- this._native2LocalFormatNames[nativeFormats[i]] = localFormats[i];
- }
- }
- }catch(e){ error = true; }
- if(obj && !this.object){
- //delete the temporary obj
- dojo.body().removeChild(obj);
- }
- }
- return !error;
- },
- /* Event handlers
- *****************/
-
- _isResized: function(){ return false; },
-
- onLoad: function(e){
- // summary: handler after the content of the document finishes loading
- this.isLoaded = true;
- if (this.object){
- this.document = this.object.DOM;
- this.window = this.document.parentWindow;
- this.editNode = this.document.body.firstChild;
- this.editingArea.style.height = this.height ? this.height : this.minHeight;
- if(!this.height){
- this.connect(this, "onDisplayChanged", "_updateHeight");
- }
- //pretend the object as an iframe, so that the context menu for the
- //editor can be placed correctly when shown
- this.window._frameElement = this.object;
- }else if (this.iframe && !dojo.render.html.ie){
- this.editNode = this.document.body;
- if(!this.height){
- this.connect(this, "onDisplayChanged", "_updateHeight");
- }
-
- try { // sanity check for Mozilla
- this.document.execCommand("useCSS", false, true); // old moz call
- this.document.execCommand("styleWithCSS", false, false); // new moz call
- //this.document.execCommand("insertBrOnReturn", false, false); // new moz call
- }catch(e2){ }
-
- if (dojo.render.html.safari) {
- /*
- this.iframe.style.visiblity = "visible";
- this.iframe.style.border = "1px solid black";
- this.editNode.style.visiblity = "visible";
- this.editNode.style.border = "1px solid black";
- */
- // this.onDisplayChanged();
- this.connect(this.editNode, "onblur", "onBlur");
- this.connect(this.editNode, "onfocus", "onFocus");
- this.connect(this.editNode, "onclick", "onFocus");
-
- this.interval = setInterval(dojo.lang.hitch(this, "onDisplayChanged"), 750);
- // dojo.raise("onload");
- // dojo.debug(this.editNode.parentNode.parentNode.parentNode.nodeName);
- } else if (dojo.render.html.mozilla || dojo.render.html.opera) {
- var doc = this.document;
- var addListener = dojo.event.browser.addListener;
- var self = this;
- dojo.lang.forEach(this.events, function(e){
- var l = addListener(self.document, e.substr(2).toLowerCase(), dojo.lang.hitch(self, e));
- if(e=="onBlur"){
- // We need to unhook the blur event listener on close as we
- // can encounter a garunteed crash in FF if another event is
- // also fired
- var unBlur = { unBlur: function(e){
- dojo.event.browser.removeListener(doc, "blur", l);
- } };
- dojo.event.connect("before", self, "close", unBlur, "unBlur");
- }
- });
- }
- // FIXME: when scrollbars appear/disappear this needs to be fired
- }else if(dojo.render.html.ie){
- // IE contentEditable
- if(!this.height){
- this.connect(this, "onDisplayChanged", "_updateHeight");
- }
- this.editNode.style.zoom = 1.0;
- }
-
- this._applyEditingAreaStyleSheets();
-
- if(this.focusOnLoad){
- this.focus();
- }
- this.onDisplayChanged(e);
- if(this.onLoadDeferred){
- this.onLoadDeferred.callback(true);
- }
- },
-
- onKeyDown: function(e){
- // summary: Fired on keydown
- if((!e)&&(this.object)){
- e = dojo.event.browser.fixEvent(this.window.event);
- }
- // dojo.debug("onkeydown:", e.keyCode);
- // 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((dojo.render.html.ie)&&(e.keyCode == e.KEY_TAB)){
- e.preventDefault();
- e.stopPropagation();
- // FIXME: this is a poor-man's indent/outdent. It would be
- // better if it added 4 " " chars in an undoable way.
- // Unfortuantly pasteHTML does not prove to be undoable
- this.execCommand((e.shiftKey ? "outdent" : "indent"));
- }else if(dojo.render.html.ie){
- if((65 <= e.keyCode)&&(e.keyCode <= 90)){
- e.charCode = e.keyCode;
- this.onKeyPress(e);
- }
- // dojo.debug(e.ctrlKey);
- // dojo.debug(e.keyCode);
- // dojo.debug(e.charCode);
- // this.onKeyPress(e);
- }
- },
-
- onKeyUp: function(e){
- // summary: Fired on keyup
- return;
- },
-
- KEY_CTRL: 1,
-
- onKeyPress: function(e){
- // summary: Fired on keypress
- if((!e)&&(this.object)){
- e = dojo.event.browser.fixEvent(this.window.event);
- }
- // handle the various key events
-
- var modifiers = e.ctrlKey ? this.KEY_CTRL : 0;
-
- if (this._keyHandlers[e.key]) {
- // dojo.debug("char:", e.key);
- var handlers = this._keyHandlers[e.key], i = 0, handler;
- while (handler = handlers[i++]) {
- if (modifiers == handler.modifiers) {
- e.preventDefault();
- handler.handler.call(this);
- break;
- }
- }
- }
-
- // function call after the character has been inserted
- dojo.lang.setTimeout(this, this.onKeyPressed, 1, e);
- },
-
- addKeyHandler: function (/*String*/key, /*Int*/modifiers, /*Function*/handler) {
- // summary: add a handler for a keyboard shortcut
- if (!(this._keyHandlers[key] instanceof Array)) { this._keyHandlers[key] = []; }
- this._keyHandlers[key].push({
- modifiers: modifiers || 0,
- handler: handler
- });
- },
-
- onKeyPressed: function (e) {
- // summary:
- // Fired after a keypress event has occured and it's action taken. This
- // is useful if action needs to be taken after text operations have finished
-
- // Mozilla adds a single with an embedded
when you hit enter once:
- //
\n
- // when you hit enter again it adds another
inside your enter
- //
\n
\n
- // and if you hit enter again it splits the
s over 2 s
- //
\n
\n
\n
- // now this assumes that s have double the line-height of
s to work
- // and so we need to remove the
s to ensure the position of the cursor
- // changes from the users perspective when they hit enter, as the second two
- // html snippets render the same when margins are set to 0.
-
- // TODO: doesn't really work; is this really needed?
- //if (dojo.render.html.moz) {
- // for (var i = 0; i < this.document.getElementsByTagName("p").length; i++) {
- // var p = this.document.getElementsByTagName("p")[i];
- // if (p.innerHTML.match(/^
\s$/m)) {
- // while (p.hasChildNodes()) { p.parentNode.insertBefore(p.firstChild, p); }
- // p.parentNode.removeChild(p);
- // }
- // }
- //}
- this.onDisplayChanged(/*e*/); // can't pass in e
- },
-
- onClick: function(e){ this.onDisplayChanged(e); },
- onBlur: function(e){ },
- _initialFocus: true,
- onFocus: function(e){
- // summary: Fired on focus
- if( (dojo.render.html.mozilla)&&(this._initialFocus) ){
- this._initialFocus = false;
- if(dojo.string.trim(this.editNode.innerHTML) == " "){
- this.placeCursorAtStart();
-// this.execCommand("selectall");
-// this.window.getSelection().collapseToStart();
- }
- }
- /*
- // FIXME: attempted (but ultimately unworkable) solution for #2066
- if(dojo.render.html.safari){
- var sel = this.window.getSelection();
- dojo.debug(dojo.dom.isDescendantOf(sel.focusNode, this.document.body));
-
- var isLastNode = (sel.focusNode == this.document.body.lastChild);
- if(!isLastNode){ return; }
- var isText = (sel.focusNode.nodeType == 3);
- if(!isText){ return; }
- var isLastChar = (sel.focusOffset == sel.focusNode.nodeValue.length);
- if(!isLastChar){ return; }
- dojo.debug(sel.focusOffset, sel.focusNode.nodeValue.length);
- if(isLastNode && isText && isLastChar){
- this.document.body.appendChild(this.document.createTextNode(" "));
- this.document.body.appendChild(this.document.createElement("br"));
- this.document.body.appendChild(this.document.createElement("p"));
- this.document.body.appendChild(this.document.createElement("p"));
- sel.collapse(sel.focusNode, sel.focusOffset-1);
- sel.collapse(sel.focusNode, sel.focusOffset+1);
- }
- }
- */
- },
-
- blur: function () {
- // summary: remove focus from this instance
- if(this.iframe) { this.window.blur(); }
- else if(this.object) { this.document.body.blur(); }
- else if(this.editNode) { this.editNode.blur(); }
- },
-
- focus: function () {
- // summary: move focus to this instance
- if(this.iframe && !dojo.render.html.ie) { this.window.focus(); }
- else if(this.object) { this.document.focus(); }
- // editNode may be hidden in display:none div, lets just punt in this case
- else if(this.editNode && this.editNode.focus) { this.editNode.focus(); }
- else{
- dojo.debug("Have no idea how to focus into the editor!");
- }
- },
-
- /** this event will be fired everytime the display context changes and the
- result needs to be reflected in the UI */
- onDisplayChanged: function (e){ },
-
-
- /* Formatting commands
- **********************/
-
- // Object: IE's Active X codes: see http://www.computerbytesman.com/js/activex/dhtmledit.htm
- _activeX: {
- command: {
- bold: 5000,
- italic: 5023,
- underline: 5048,
-
- justifycenter: 5024,
- justifyleft: 5025,
- justifyright: 5026,
-
- cut: 5003,
- copy: 5002,
- paste: 5032,
- "delete": 5004,
-
- undo: 5049,
- redo: 5033,
-
- removeformat: 5034,
- selectall: 5035,
- unlink: 5050,
-
- indent: 5018,
- outdent: 5031,
-
- insertorderedlist: 5030,
- insertunorderedlist: 5051,
-
- // table commands
- inserttable: 5022,
- insertcell: 5019,
- insertcol: 5020,
- insertrow: 5021,
- deletecells: 5005,
- deletecols: 5006,
- deleterows: 5007,
- mergecells: 5029,
- splitcell: 5047,
-
- // the command need mapping, they don't translate directly
- // to the contentEditable commands
- setblockformat: 5043,
- getblockformat: 5011,
- getblockformatnames: 5012,
- setfontname: 5044,
- getfontname: 5013,
- setfontsize: 5045,
- getfontsize: 5014,
- setbackcolor: 5042,
- getbackcolor: 5010,
- setforecolor: 5046,
- getforecolor: 5015,
-
- findtext: 5008,
- font: 5009,
- hyperlink: 5016,
- image: 5017,
-
- lockelement: 5027,
- makeabsolute: 5028,
- sendbackward: 5036,
- bringforward: 5037,
- sendbelowtext: 5038,
- bringabovetext: 5039,
- sendtoback: 5040,
- bringtofront: 5041,
-
- properties: 5052
- },
-
- ui: {
- "default": 0,
- prompt: 1,
- noprompt: 2
- },
-
- status: {
- notsupported: 0,
- disabled: 1,
- enabled: 3,
- latched: 7,
- ninched: 11
- },
-
- appearance: {
- flat: 0,
- inset: 1
- },
-
- state: {
- unchecked: 0,
- checked: 1,
- gray: 2
- }
- },
-
- _normalizeCommand: function (/*String*/cmd){
- // summary:
- // Used as the advice function by dojo.event.connect to map our
- // normalized set of commands to those supported by the target
- // browser
- var drh = dojo.render.html;
-
- var command = cmd.toLowerCase();
- if(command == "formatblock"){
- if(drh.safari){ command = "heading"; }
- }else if(this.object){
- switch(command){
- case "createlink":
- command = "hyperlink";
- break;
- case "insertimage":
- command = "image";
- break;
- }
- }else if(command == "hilitecolor" && !drh.mozilla){
- command = "backcolor";
- }
-
- return command;
- },
-
- _safariIsLeopard: function(){
- var gt420 = false;
- if(dojo.render.html.safari){
- var tmp = dojo.render.html.UA.split("AppleWebKit/")[1];
- var ver = parseFloat(tmp.split(" ")[0]);
- if(ver >= 420){ gt420 = true; }
- }
- return gt420;
- },
-
- 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
- var ie = 1;
- var mozilla = 1 << 1;
- var safari = 1 << 2;
- var opera = 1 << 3;
- var safari420 = 1 << 4;
-
- var gt420 = this._safariIsLeopard();
-
- function isSupportedBy (browsers) {
- return {
- ie: Boolean(browsers & ie),
- mozilla: Boolean(browsers & mozilla),
- safari: Boolean(browsers & safari),
- safari420: Boolean(browsers & safari420),
- 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":
- supportedBy = isSupportedBy(mozilla | ie | safari | 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":
- supportedBy = isSupportedBy(mozilla | ie | opera | safari420);
- 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 | safari420);
- break;
-
- case "inserttable":
- supportedBy = isSupportedBy(mozilla | (this.object ? ie : 0));
- break;
-
- case "insertcell": case "insertcol": case "insertrow":
- case "deletecells": case "deletecols": case "deleterows":
- case "mergecells": case "splitcell":
- supportedBy = isSupportedBy(this.object ? ie : 0);
- break;
-
- default: return false;
- }
-
- return (dojo.render.html.ie && supportedBy.ie) ||
- (dojo.render.html.mozilla && supportedBy.mozilla) ||
- (dojo.render.html.safari && supportedBy.safari) ||
- (gt420 && supportedBy.safari420) ||
- (dojo.render.html.opera && 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
- var returnValue;
-
- //focus() is required for IE (none-activeX mode) 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);
- if (argument != undefined) {
- if(command == "heading") { throw new Error("unimplemented"); }
- else if(command == "formatblock"){
- if(this.object){ //IE activeX mode
- argument = this._native2LocalFormatNames[argument];
- }
- else if(dojo.render.html.ie){ argument = '<'+argument+'>'; }
- }
- }
- if(this.object){
- switch (command) {
- case "hilitecolor":
- command = "setbackcolor";
- break;
- case "forecolor":
- case "backcolor":
- case "fontsize":
- case "fontname":
- command = "set" + command;
- break;
- case "formatblock":
- command = "setblockformat";
- }
-
- if(command == "strikethrough"){
- command = "inserthtml";
- var range = this.document.selection.createRange();
- if(!range.htmlText){
- return;
- }
- argument=range.htmlText.strike();
- }else if(command == "inserthorizontalrule"){
- command = "inserthtml";
- argument="
";
- }
-
- if(command == "inserthtml"){
- var range = this.document.selection.createRange();
- if(this.document.selection.type.toUpperCase() == "CONTROL"){
- //if selection is controlrange, no pasteHTML is available,
- //we replace the outerHTML directly
- for(var i=0;i 1 ? argument : null;
- returnValue = this.document.execCommand(command, false, argument);
-
- // timeout is workaround for weird IE behavior were the text
- // selection gets correctly re-created, but subsequent input
- // apparently isn't bound to it
-// setTimeout(function(){tr.select();}, 1);
- }else{
- // dojo.debug("command:", command, "arg:", argument);
-
- argument = arguments.length > 1 ? argument : null;
-// if(dojo.render.html.moz){
-// this.document = this.iframe.contentWindow.document
-// }
-
- 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
- command = this._normalizeCommand(command);
- if(this.object){
- switch (command) {
- case "hilitecolor":
- command = "setbackcolor";
- break;
- case "forecolor":
- case "backcolor":
- case "fontsize":
- case "fontname":
- command = "set" + command;
- break;
- case "formatblock":
- command = "setblockformat";
- break;
- //below are not natively supported commands, we fake them
- case "strikethrough":
- command = "bold"; //whenever bold is enabled, strikethrough should be so as well
- break;
- case "inserthorizontalrule":
- return true;
- }
-
- if(typeof this._activeX.command[command] == "undefined"){ return false; }
- var status = this.object.QueryStatus(this._activeX.command[command]);
- return ((status != this._activeX.status.notsupported)&&
- (status != this._activeX.status.disabled));
- }else{
- if(dojo.render.html.mozilla){
- if(command == "unlink"){ // mozilla returns true always
- return dojo.withGlobal(this.window, "hasAncestorElement", dojo.html.selection, ['a']);
- } else if (command == "inserttable") {
- return true;
- }
- }
-
- // return this.document.queryCommandEnabled(command);
- var elem = (dojo.render.html.ie) ? this.document.selection.createRange() : this.document;
- return elem.queryCommandEnabled(command);
- }
- },
-
- queryCommandState: function(command){
- // summary: check the state of a given command
- command = this._normalizeCommand(command);
- if(this.object){
- if(command == "forecolor"){
- command = "setforecolor";
- }else if(command == "backcolor"){
- command = "setbackcolor";
- }else if(command == "strikethrough"){
- //check whether we are under a
- return dojo.withGlobal(this.window, "hasAncestorElement", dojo.html.selection, ['strike']);
- }else if(command == "inserthorizontalrule"){
- return false;
- }
-
- if(typeof this._activeX.command[command] == "undefined"){ return null; }
- var status = this.object.QueryStatus(this._activeX.command[command]);
- return ((status == this._activeX.status.latched)||
- (status == this._activeX.status.ninched));
- }else{
- return this.document.queryCommandState(command);
- }
- },
-
- queryCommandValue: function (command) {
- // summary: check the value of a given command
- command = this._normalizeCommand(command);
- if (this.object) {
- switch (command) {
- case "forecolor":
- case "backcolor":
- case "fontsize":
- case "fontname":
- command = "get" + command;
- return this.object.execCommand(
- this._activeX.command[command],
- this._activeX.ui.noprompt);
- case "formatblock":
- var retvalue = this.object.execCommand(
- this._activeX.command["getblockformat"],
- this._activeX.ui.noprompt);
- if(retvalue){
- return this._local2NativeFormatNames[retvalue];
- }
- }
- } else {
- if(dojo.render.html.ie && command == "formatblock"){
- return this._local2NativeFormatNames[this.document.queryCommandValue(command)] || this.document.queryCommandValue(command);
- }
- return this.document.queryCommandValue(command);
- }
- },
-
-
- /* Misc.
- ********/
-
- placeCursorAtStart: function(){
- // summary:
- // place the cursor at the start of the editing area
- this.focus();
- //see comments in placeCursorAtEnd
- if(dojo.render.html.moz && this.editNode.firstChild &&
- this.editNode.firstChild.nodeType != dojo.dom.TEXT_NODE){
- dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, [this.editNode.firstChild]);
- }else{
- dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, [this.editNode]);
- }
- dojo.withGlobal(this.window, "collapse", dojo.html.selection, [true]);
- },
-
- placeCursorAtEnd: function(){
- // summary:
- // place the cursor at the end of the editing area
- 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
- if(dojo.render.html.moz && this.editNode.lastChild &&
- this.editNode.lastChild.nodeType != dojo.dom.TEXT_NODE){
- dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, [this.editNode.lastChild]);
- }else{
- dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, [this.editNode]);
- }
- dojo.withGlobal(this.window, "collapse", dojo.html.selection, [false]);
- },
-
- replaceEditorContent: function(/*String*/html){
- // summary:
- // this function set the content while trying to maintain the undo stack
- html = this._preFilterContent(html);
- if(this.isClosed){
- this.domNode.innerHTML = html;
- }else if(this.window && this.window.getSelection && !dojo.render.html.moz){ // Safari
- // look ma! it's a totally f'd browser!
- this.editNode.innerHTML = html;
- }else if((this.window && this.window.getSelection) || (this.document && this.document.selection)){ // Moz/IE
- this.execCommand("selectall");
- if(dojo.render.html.moz && !html){ html = " " }
- this.execCommand("inserthtml", html);
- }
- },
-
- _preFilterContent: function(/*String*/html){
- // summary:
- // filter the input before setting the content of the editing area
- var ec = html;
- dojo.lang.forEach(this.contentPreFilters, function(ef){
- ec = ef(ec);
- });
- if(this.contentDomPreFilters.length>0){
- var dom = dojo.doc().createElement('div');
- dom.style.display = "none";
- dojo.body().appendChild(dom);
- dom.innerHTML = ec;
- dojo.lang.forEach(this.contentDomPreFilters, function(ef){
- dom = ef(dom);
- });
- ec = dom.innerHTML;
- dojo.body().removeChild(dom);
- }
- return ec;
- },
- _postFilterContent: function(/*String*/html){
- // summary:
- // filter the output after getting the content of the editing area
- var ec = html;
- if(this.contentDomPostFilters.length>0){
- var dom = this.document.createElement('div');
- dom.innerHTML = ec;
- dojo.lang.forEach(this.contentDomPostFilters, function(ef){
- dom = ef(dom);
- });
- ec = dom.innerHTML;
- }
- dojo.lang.forEach(this.contentPostFilters, function(ef){
- ec = ef(ec);
- });
- return ec;
- },
-
- //Int: stored last time height
- _lastHeight: 0,
-
- _updateHeight: function(){
- // summary:
- // Updates the height of the editor area to fit the contents.
- if(!this.isLoaded){ return; }
- if(this.height){ return; }
-
- var height = dojo.html.getBorderBox(this.editNode).height;
- //height maybe zero in some cases even though the content is not empty,
- //we try the height of body instead
- if(!height){
- height = dojo.html.getBorderBox(this.document.body).height;
- }
- if(height == 0){
- dojo.debug("Can not figure out the height of the editing area!");
- return; //prevent setting height to 0
- }
- this._lastHeight = height;
- this.editorObject.style.height = this._lastHeight + "px";
- this.window.scrollTo(0, 0);
- },
-
- _saveContent: function(e){
- // summary:
- // Saves the content in an onunload event if the editor has not been closed
- var saveTextarea = dojo.doc().getElementById("dojo.widget.RichText.savedContent");
- saveTextarea.value += this._SEPARATOR + this.saveName + ":" + this.getEditorContent();
- },
-
- getEditorContent: function(){
- // summary:
- // return the current content of the editing area (post filters are applied)
- var ec = "";
- try{
- ec = (this._content.length > 0) ? this._content : this.editNode.innerHTML;
- if(dojo.string.trim(ec) == " "){ ec = ""; }
- }catch(e){ /* squelch */ }
-
- if(dojo.render.html.ie && !this.object){
- //removing appended
for IE in none-activeX mode
- var re = new RegExp("(?:
[\n\r]*)+$", "i");
- ec = ec.replace(re,"");
- }
-
- ec = this._postFilterContent(ec);
-
- if (this.relativeImageUrls) {
- // why use a regexp instead of dom? because IE is stupid
- // and won't let us set img.src to a relative URL
- // this comes after contentPostFilters because once content
- // gets innerHTML'd img urls will be fully qualified
- var siteBase = dojo.global().location.protocol + "//" + dojo.global().location.host;
- var pathBase = dojo.global().location.pathname;
- if (pathBase.match(/\/$/)) {
- // ends with slash, match full path
- } else {
- // match parent path to find siblings
- var pathParts = pathBase.split("/");
- if (pathParts.length) {
- pathParts.pop();
- }
- pathBase = pathParts.join("/") + "/";
-
- }
-
- var sameSite = new RegExp("(
]*\ src=[\"'])("+siteBase+"("+pathBase+")?)", "ig");
- ec = ec.replace(sameSite, "$1");
- }
- return ec;
- },
-
- close: function(/*Boolean*/save, /*Boolean*/force){
- // 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.
- // force:
- if(this.isClosed){return false; }
-
- if (arguments.length == 0) { save = true; }
- this._content = this._postFilterContent(this.editNode.innerHTML);
- var changed = (this.savedContent != this._content);
-
- // 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(dojo.render.html.ie && !this.object){
- dojo.event.browser.clean(this.editNode);
- }
-
- if (this.iframe) {
- // FIXME: should keep iframe around for later re-use
- delete this.iframe;
- }
-
- if(this.textarea){
- with(this.textarea.style){
- position = "";
- left = top = "";
- if(dojo.render.html.ie){
- overflow = this.__overflow;
- this.__overflow = null;
- }
- }
- if(save){
- this.textarea.value = this._content;
- }else{
- this.textarea.value = this.savedContent;
- }
- dojo.html.removeNode(this.domNode);
- this.domNode = this.textarea;
- }else{
- if(save){
- if(dojo.render.html.moz){
- var nc = dojo.doc().createElement("span");
- this.domNode.appendChild(nc);
- nc.innerHTML = this.editNode.innerHTML;
- }else{
- this.domNode.innerHTML = this._content;
- }
- }else{
- this.domNode.innerHTML = this.savedContent;
- }
- }
-
- dojo.html.removeClass(this.domNode, "RichTextEditable");
- this.isClosed = true;
- this.isLoaded = false;
- // FIXME: is this always the right thing to do?
- delete this.editNode;
-
- if(this.window._frameElement){
- this.window._frameElement = null;
- }
-
- this.window = null;
- this.document = null;
- this.object = null;
- this.editingArea = null;
- this.editorObject = null;
-
- return changed; // Boolean: whether the content has been modified
- },
-
- destroyRendering: function(){}, // stub!
-
- destroy: function (){
- this.destroyRendering();
- if(!this.isClosed){ this.close(false); }
-
- dojo.widget.RichText.superclass.destroy.call(this);
- },
-
- connect: function (targetObj, targetFunc, thisFunc) {
- // summary: convenient method for dojo.event.connect
- dojo.event.connect(targetObj, targetFunc, this, thisFunc);
- },
-
- disconnect: function (targetObj, targetFunc, thisFunc) {
- // summary: convenient method for dojo.event.disconnect
- dojo.event.disconnect(targetObj, targetFunc, this, thisFunc);
- },
-
- disconnectAllWithRoot: function (targetObj) {
- dojo.deprecated("disconnectAllWithRoot", "is deprecated. No need to disconnect manually", "0.5");
- },
-
- _fixContentForMoz: function(html){
- // summary:
- // Moz can not handle strong/em tags correctly, correct them here
- html = html.replace(/])/gi, '/gi, '<\/b>' );
- html = html.replace(/])/gi, '/gi, '<\/i>' );
- return html;
- }
- }
+ "dojo.widget.RichText",
+ dojo.widget.HtmlWidget,
+ function(){
+ // summary:
+ // dojo.widget.RichText is the core of the WYSIWYG editor in dojo, which
+ // provides the basic editing features. It also encapsulates the differences
+ // of different js engines for various browsers
+
+ // contentPreFilters: Array
+ // pre content filter function register array
+ this.contentPreFilters = [];
+
+ // contentPostFilters: Array
+ // post content filter function register array
+ this.contentPostFilters = [];
+
+ // contentDomPreFilters: Array
+ // pre content dom filter function register array
+ this.contentDomPreFilters = [];
+
+ // contentDomPostFilters: Array
+ // post content dom filter function register array
+ this.contentDomPostFilters = [];
+
+ // editingAreaStyleSheets: Array
+ // array to store all the stylesheets applied to the editing area
+ this.editingAreaStyleSheets=[];
+
+ if(dojo.render.html.moz){
+ this.contentPreFilters.push(this._fixContentForMoz);
+ }
+
+ this._keyHandlers = {};
+
+ if(dojo.Deferred){
+ this.onLoadDeferred = new dojo.Deferred();
+ }
+ },
+ {
+ // inheritWidth: Boolean
+ // whether to inherit the parent's width or simply use 100%
+ inheritWidth: false,
+
+ // focusOnLoad: Boolean
+ // whether focusing into this instance of richtext when page onload
+ focusOnLoad: false,
+
+ // saveName: String
+ // If a save name is specified the content is saved and restored when the user
+ // leave this page can come back, or if the editor is not properly closed after
+ // editing has started.
+ saveName: "",
+
+ // styleSheets: String
+ // semicolon (";") separated list of css files for the editing area
+ styleSheets: "",
+
+ // _content: String
+ // temporary content storage
+ _content: "",
+
+ // height: String
+ // set height to fix the editor at a specific height, with scrolling
+ height: "",
+
+ // minHeight: String
+ // The minimum height that the editor should have
+ minHeight: "1em",
+
+ // isClosed: Boolean
+ isClosed: true,
+
+ // isLoaded: Boolean
+ isLoaded: false,
+
+ // useActiveX: Boolean
+ // whether to use the active-x object in IE
+ useActiveX: false,
+
+ // relativeImageUrls: Boolean
+ // whether to use relative URLs for images - if this is enabled
+ // images will be given absolute URLs when inside the editor but
+ // will be changed to use relative URLs (to the current page) on save
+ relativeImageUrls: false,
+
+ // _SEPARATOR: String
+ // used to concat contents from multiple textareas into a single string
+ _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
+
+ // onLoadDeferred: dojo.Deferred
+ // deferred that can be used to connect to the onLoad function. This
+ // will only be set if dojo.Deferred is required
+ onLoadDeferred: null,
+
+ /* Init
+ *******/
+
+ fillInTemplate: function(){
+ // summary: see dojo.widget.DomWidget
+ dojo.event.topic.publish("dojo.widget.RichText::init", this);
+ this.open();
+
+
+ // backwards compatibility, needs to be removed
+ dojo.event.connect(this, "onKeyPressed", this, "afterKeyPress");
+ dojo.event.connect(this, "onKeyPress", this, "keyPress");
+ dojo.event.connect(this, "onKeyDown", this, "keyDown");
+ dojo.event.connect(this, "onKeyUp", this, "keyUp");
+
+ this.setupDefaultShortcuts();
+ },
+
+ setupDefaultShortcuts: function(){
+ // summary: add some default key handlers
+ // description:
+ // Overwrite this to setup your own handlers. The default
+ // implementation does not use Editor2 commands, but directly
+ // executes the builtin commands within the underlying browser
+ // support.
+ var ctrl = this.KEY_CTRL;
+ var exec = function (cmd, arg) {
+ return arguments.length == 1 ? function () { this.execCommand(cmd); } :
+ function () { this.execCommand(cmd, arg); }
+ }
+ this.addKeyHandler("b", ctrl, exec("bold"));
+ this.addKeyHandler("i", ctrl, exec("italic"));
+ this.addKeyHandler("u", ctrl, exec("underline"));
+ this.addKeyHandler("a", ctrl, exec("selectall"));
+ this.addKeyHandler("s", ctrl, function () { this.save(true); });
+
+ this.addKeyHandler("1", ctrl, exec("formatblock", "h1"));
+ this.addKeyHandler("2", ctrl, exec("formatblock", "h2"));
+ this.addKeyHandler("3", ctrl, exec("formatblock", "h3"));
+ this.addKeyHandler("4", ctrl, exec("formatblock", "h4"));
+
+ this.addKeyHandler("\\", ctrl, exec("insertunorderedlist"));
+ if(!dojo.render.html.ie){
+ this.addKeyHandler("Z", ctrl, exec("redo"));
+ }
+ },
+
+ // events: Array
+ // events which should be connected to the underlying editing area
+ events: ["onBlur", "onFocus", "onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
+
+ /**
+ * Transforms the node referenced in this.domNode into a rich text editing
+ * node. This can result in the creation and replacement with an