📄 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.html.selection");dojo.require("dojo.event.*");dojo.require("dojo.string.extras");dojo.require("dojo.uri.Uri");dojo.require("dojo.Deferred");// 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){ }}dojo.widget.defineWidget( "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 <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. if(this.onLoadDeferred.fired >= 0){ this.onLoadDeferred = new dojo.Deferred(); } var h = dojo.render.html; if (!this.isClosed) { this.close(); } dojo.event.topic.publish("dojo.widget.RichText::open", this); 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); 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('before', 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 = this.domNode.innerHTML; this.domNode.innerHTML = ''; 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 || this._safariIsLeopard() || h.opera){ // 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><style>body{margin:0;padding:0;border:0;overflow:hidden;}</style></head><body><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 //do not use _cacheLocalBlockFormatNames here, as it will trigger security warning in IE7 //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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -