📄 richtext.js
字号:
/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml*/ /* -*- tab-width: 4 -*- */dojo.provide("dojo.widget.RichText");dojo.require("dojo.widget.*");dojo.require("dojo.html.*");dojo.require("dojo.html.layout");dojo.require("dojo.event.*");dojo.require("dojo.string.extras");dojo.require("dojo.uri.Uri");// used to save contentif(dojo.hostenv.post_load_){ (function(){ var savetextarea = dojo.doc().createElement('textarea'); savetextarea.id = "dojo.widget.RichText.savedContent"; savetextarea.style = "display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"; dojo.body().appendChild(savetextarea); })();}else{ //dojo.body() is not available before onLoad is fired try { dojo.doc().write('<textarea id="dojo.widget.RichText.savedContent" ' + 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>'); }catch(e){ }}// 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 browsersdojo.widget.defineWidget( "dojo.widget.RichText", dojo.widget.HtmlWidget, { // Boolean: // whether to inherit the parent's width or simply use 100% inheritWidth: false, // Boolean: // whether focusing into this instance of richtext when page onload focusOnLoad: true, // String: // If a save name is specified the content is saved and restored if the // editor is not properly closed after editing has started. saveName: "", // String: // temporary content storage _content: "", // String: // set height to fix the editor at a specific height, with scrolling height: "", // String: // The minimum height that the editor should have minHeight: "1em", // Boolean: isClosed: true, // Boolean: isLoaded: false, // Boolean: // whether to use the active-x object in IE useActiveX: false, // 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, // String: // used to concat contents from multiple textareas into a single string _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@", /* 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"); // add default some key handlers 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("k", ctrl, exec("createlink", "")); //this.addKeyHandler("K", ctrl, exec("unlink")); 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")); } }, // 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 <iframe> if * designMode is used, an <object> and active-x component if inside of IE or * a reguler element if contentEditable is available. */ open: function (/*DomNode, optional*/element) { // summary: // Transforms the node referenced in this.domNode into a rich text editing // node. This can result in the creation and replacement with an <iframe> if // designMode is used, an <object> and active-x component if inside of IE or // a reguler element if contentEditable is available. var h = dojo.render.html; dojo.event.topic.publish("dojo.widget.RichText::open", this); if (!this.isClosed) { this.close(); } this._content = ""; if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged if( (this.domNode["nodeName"])&& (this.domNode.nodeName.toLowerCase() == "textarea")){ this.textarea = this.domNode; var html = dojo.string.trim(this.textarea.value); if(html == ""){ html = " "; } this.domNode = dojo.doc().createElement("div"); dojo.html.copyStyle(this.domNode, this.textarea); var tmpFunc = dojo.lang.hitch(this, function(){ //some browsers refuse to submit display=none textarea, so //move the textarea out of screen instead with(this.textarea.style){ display = "block"; position = "absolute"; left = top = "-1000px"; if(h.ie){ //nasty IE bug: abnormal formatting if overflow is not hidden this.__overflow = overflow; overflow = "hidden"; } } }); if(h.ie){ setTimeout(tmpFunc, 10); }else{ tmpFunc(); } if(!h.safari){ // FIXME: VERY STRANGE safari 2.0.4 behavior here caused by // moving the textarea. Often crashed the browser!!! Seems // fixed on webkit nightlies. dojo.html.insertBefore(this.domNode, this.textarea); } // this.domNode.innerHTML = html; if(this.textarea.form){ dojo.event.connect(this.textarea.form, "onsubmit", // FIXME: should we be calling close() here instead? dojo.lang.hitch(this, function(){ this.textarea.value = this.getEditorContent(); }) ); } // dojo plucks our original domNode from the document so we need // to go back and put ourselves back in var editor = this; dojo.event.connect(this, "postCreate", function (){ dojo.html.insertAfter(editor.textarea, editor.domNode); }); }else{ var html = this._preFilterContent(dojo.string.trim(this.domNode.innerHTML)); if(html == ""){ html = " "; } } var content = dojo.html.getContentBox(this.domNode); this._oldHeight = content.height; this._oldWidth = content.width; this._firstChildContributingMargin = this._getContributingMargin(this.domNode, "top"); this._lastChildContributingMargin = this._getContributingMargin(this.domNode, "bottom"); this.savedContent = dojo.doc().createElement("div"); while (this.domNode.hasChildNodes()) { this.savedContent.appendChild(this.domNode.firstChild); } this.editingArea = dojo.doc().createElement("div"); this.domNode.appendChild(this.editingArea); // 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( (this.domNode["nodeName"])&& (this.domNode.nodeName == "LI")){ this.domNode.innerHTML = " <br>"; } if(this.saveName != ""){ var saveTextarea = dojo.doc().getElementById("dojo.widget.RichText.savedContent"); if (saveTextarea.value != "") { var datas = saveTextarea.value.split(this._SEPARATOR); for (var i = 0; i < datas.length; i++) { var data = datas[i].split(":"); if (data[0] == this.saveName) { html = data[1]; datas.splice(i, 1); break; } } } dojo.event.connect("before", window, "onunload", this, "_saveContent"); // dojo.event.connect(window, "onunload", this, "_saveContent"); } if(h.ie70 && this.useActiveX){ dojo.debug("activeX in ie70 is not currently supported, useActiveX is ignored for now."); this.useActiveX = false; } // Safari's selections go all out of whack if we do it inline, // so for now IE is our only hero //if (typeof document.body.contentEditable != "undefined") { if(this.useActiveX && h.ie){ // active-x var self = this; //if call _drawObject directly here, textarea replacement //won't work: no content is shown. However, add a delay //can workaround this. No clue why. setTimeout(function(){self._drawObject(html);}, 0); }else if(h.ie){ // contentEditable, easy this.iframe = dojo.doc().createElement( 'iframe' ) ; this.iframe.src = 'javascript:void(0)'; this.editorObject = this.iframe; with(this.iframe.style){ border = '0'; width = "100%"; } this.iframe.frameBorder = 0; this.editingArea.appendChild(this.iframe) this.window = this.iframe.contentWindow; this.document = this.window.document; this.document.open(); this.document.write("<html><head></head><body style='margin: 0; padding: 0;border: 0; overflow: hidden;'><div></div></body></html>"); this.document.close(); this.editNode = this.document.body.firstChild;//document.createElement("div"); this.editNode.contentEditable = true; with (this.iframe.style) { if(h.ie70){ if(this.height){ height = this.height; } if(this.minHeight){ minHeight = this.minHeight; } }else{ height = this.height ? this.height : this.minHeight; } } // FIXME: setting contentEditable on switches this element to // IE's hasLayout mode, triggering weird margin collapsing // behavior. It's particularly bad if the element you're editing // contains childnodes that don't have margin: defined in local // css rules. It would be nice if it was possible to hack around // this. Sadly _firstChildContributingMargin and // _lastChildContributingMargin don't work on IE unless all // elements have margins set in CSS :-( //if the normal way fails, we try the hard way to get the list if(!this._cacheLocalBlockFormatNames()){ //in the array below, ul can not come directly after ol, otherwise the queryCommandValue returns Normal for it var formats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'div', 'ul']; var localhtml = ""; for(var i in formats){ if(formats[i].charAt(1) != 'l'){ localhtml += "<"+formats[i]+"><span>content</span></"+formats[i]+">"; }else{ localhtml += "<"+formats[i]+"><li>content</li></"+formats[i]+">"; } } //queryCommandValue returns empty if we hide editNode, so move it out of screen temporary with(this.editNode.style){ position = "absolute"; left = "-2000px"; top = "-2000px"; } this.editNode.innerHTML = localhtml; var node = this.editNode.firstChild; while(node){ dojo.withGlobal(this.window, "selectElement", dojo.html.selection, [node.firstChild]); var nativename = node.tagName.toLowerCase(); this._local2NativeFormatNames[nativename] = this.queryCommandValue("formatblock");// dojo.debug([nativename,this._local2NativeFormatNames[nativename]]); this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename; node = node.nextSibling; } with(this.editNode.style){ position = ""; left = ""; top = ""; } } this.editNode.innerHTML = html; if(this.height){ this.document.body.style.overflowY="scroll"; } dojo.lang.forEach(this.events, function(e){ dojo.event.connect(this.editNode, e.toLowerCase(), this, e); }, this); this.onLoad(); } else { // designMode in iframe this._drawIframe(html); this.editorObject = this.iframe; } // TODO: this is a guess at the default line-height, kinda works if (this.domNode.nodeName == "LI") { this.domNode.lastChild.style.marginTop = "-1.2em"; } dojo.html.addClass(this.domNode, "RichTextEditable"); this.isClosed = false; }, _hasCollapseableMargin: function(/*DomNode*/element, /*String*/side) { // summary: // check if an element has padding or borders on the given side // which would prevent it from collapsing margins if (dojo.html.getPixelValue(element, 'border-'+side+'-width', false)) { return false; } else if (dojo.html.getPixelValue(element, 'padding-'+side, false)) { return false; } else { return true; } }, _getContributingMargin: function(/*DomNode*/element, /*String*/topOrBottom) { // summary: // calculate how much margin this element and its first or last // child are contributing to the total margin between this element // and the adjacent node. CSS border collapsing makes this // necessary. if (topOrBottom == "top") { var siblingAttr = "previousSibling"; var childSiblingAttr = "nextSibling"; var childAttr = "firstChild"; var marginProp = "margin-top"; var siblingMarginProp = "margin-bottom"; } else { var siblingAttr = "nextSibling"; var childSiblingAttr = "previousSibling"; var childAttr = "lastChild"; var marginProp = "margin-bottom"; var siblingMarginProp = "margin-top"; } var elementMargin = dojo.html.getPixelValue(element, marginProp, false); function isSignificantNode(element) { // see if an node is significant in the current context // for calulating margins return !(element.nodeType==3 && dojo.string.isBlank(element.data)) && dojo.html.getStyle(element, "display") != "none" && !dojo.html.isPositionAbsolute(element); } // walk throuh first/last children to find total collapsed margin size var childMargin = 0; var child = element[childAttr]; while (child) { // skip over insignificant elements (whitespace, etc) while ((!isSignificantNode(child)) && child[childSiblingAttr]) { child = child[childSiblingAttr]; } childMargin = Math.max(childMargin, dojo.html.getPixelValue(child, marginProp, false)); // stop if we hit a bordered/padded element if (!this._hasCollapseableMargin(child, topOrBottom)) break; child = child[childAttr]; } // if this element has a border, return full child margin immediately // as there won't be any margin collapsing if (!this._hasCollapseableMargin(element, topOrBottom)){ return parseInt(childMargin); } // find margin supplied by nearest sibling var contextMargin = 0; var sibling = element[siblingAttr]; while (sibling) { if (isSignificantNode(sibling)) { contextMargin = dojo.html.getPixelValue(sibling,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -