📄 xquared.js
字号:
{className:"strike", title:"Strike", handler:"xed.handleStrike()"}, {className:"superscription", title:"Superscription", handler:"xed.handleSuperscription()"}, {className:"subscription", title:"Subscription", handler:"xed.handleSubscription()"} ], [ {className:"removeFormat", title:"Remove format", handler:"xed.handleRemoveFormat()"} ], [ {className:"justifyLeft", title:"Justify left", handler:"xed.handleJustify('left')"}, {className:"justifyCenter", title:"Justify center", handler:"xed.handleJustify('center')"}, {className:"justifyRight", title:"Justify right", handler:"xed.handleJustify('right')"}, {className:"justifyBoth", title:"Justify both", handler:"xed.handleJustify('both')"} ], [ {className:"indent", title:"Indent", handler:"xed.handleIndent()"}, {className:"outdent", title:"Outdent", handler:"xed.handleOutdent()"} ], [ {className:"unorderedList", title:"Unordered list", handler:"xed.handleList('UL')"}, {className:"orderedList", title:"Ordered list", handler:"xed.handleList('OL')"} ], [ {className:"paragraph", title:"Paragraph", handler:"xed.handleApplyBlock('P')"}, {className:"heading1", title:"Heading 1", handler:"xed.handleApplyBlock('H1')"}, {className:"blockquote", title:"Blockquote", handler:"xed.handleApplyBlock('BLOCKQUOTE')"}, {className:"code", title:"Code", handler:"xed.handleList('CODE')"}, {className:"division", title:"Division", handler:"xed.handleApplyBlock('DIV')"} ], [ {className:"table", title:"Table", handler:"xed.handleTable(3,3,'tl')"}, {className:"separator", title:"Separator", handler:"xed.handleSeparator()"} ], [ {className:"html", title:"Edit source", handler:"xed.toggleSourceAndWysiwygMode()"} ], [ {className:"undo", title:"Undo", handler:"xed.handleUndo()"}, {className:"redo", title:"Redo", handler:"xed.handleRedo()"} ] ]; this.config.imagePathForDefaultToobar = 'images/toolbar/'; this.config.imagePathForContent = 'images/content/'; // relative | host_relative | absolute | browser_default this.config.urlValidationMode = 'absolute'; this.config.automaticallyHookSubmitEvent = true; this.config.allowedTags = ['a', 'abbr', 'acronym', 'address', 'blockquote', 'br', 'caption', 'cite', 'code', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'span', 'sup', 'sub', 'strong', 'table', 'thead', 'tbody', 'td', 'th', 'tr', 'ul', 'var']; this.config.allowedAttributes = ['alt', 'cite', 'class', 'datetime', 'height', 'href', 'id', 'rel', 'rev', 'src', 'style', 'title', 'width']; this.config.shortcuts = {}; this.config.autocorrections = {}; this.config.autocompletions = {}; this.config.templateProcessors = {}; this.config.contextMenuHandlers = {}; /** * Original content element * @type Element */ this.contentElement = contentElement; /** * Owner document of content element * @type Document */ this.doc = this.contentElement.ownerDocument; /** * Body of content element * @type Element */ this.body = this.doc.body; /** * False or 'readonly' means read-only mode, true or 'wysiwyg' means WYSIWYG editing mode, and 'source' means source editing mode. * @type Object */ this.currentEditMode = 'readonly'; /** * RichDom instance * @type xq.RichDom */ this.rdom = xq.RichDom.createInstance(); /** * Validator instance * @type xq.Validator */ this.validator = null; /** * Outmost wrapper div * @type Element */ this.outmostWrapper = null; /** * Source editor container * @type Element */ this.sourceEditorDiv = null; /** * Source editor textarea * @type Element */ this.sourceEditorTextarea = null; /** * WYSIWYG editor container * @type Element */ this.wysiwygEditorDiv = null; /** * Design mode iframe * @type IFrame */ this.editorFrame = null; /** * Window that contains design mode iframe * @type Window */ this.editorWin = null; /** * Document that contained by design mode iframe * @type Document */ this.editorDoc = null; /** * Body that contained by design mode iframe * @type Element */ this.editorBody = null; /** * Toolbar container * @type Element */ this.toolbarContainer = toolbarContainer; /** * Toolbar buttons * @type Array */ this.toolbarButtons = null; this._toolbarAnchorsCache = []; /** * Undo/redo manager * @type xq.EditHistory */ this.editHistory = null; this._contextMenuContainer = null; this._contextMenuItems = null; this._validContentCache = null; this._lastModified = null; this.addShortcuts(this._getDefaultShortcuts()); this.addTemplateProcessors(this._getDefaultTemplateProcessors()); this.addListener({ onEditorCurrentContentChanged: function(xed) { var curFocusElement = xed.rdom.getCurrentElement(); if(!curFocusElement) return; if(xed._lastFocusElement != curFocusElement) { if(!xed.rdom.tree.isBlockOnlyContainer(xed._lastFocusElement) && xed.rdom.tree.isBlock(xed._lastFocusElement)) { xed.rdom.removeTrailingWhitespace(xed._lastFocusElement); } xed._fireOnElementChanged(xed._lastFocusElement, curFocusElement); xed._lastFocusElement = curFocusElement; } xed.updateAllToolbarButtonsStatus(curFocusElement); } }); }, finalize: function() { for(var i = 0; i < this._toolbarAnchorsCache.length; i++) { this._toolbarAnchorsCache[i].xed = null; this._toolbarAnchorsCache[i].handler = null; this._toolbarAnchorsCache[i] = null; } this._toolbarAnchorsCache = null; }, ///////////////////////////////////////////// // Configuration Management _getDefaultShortcuts: function() { if(xq.Browser.isMac) { // Mac FF & Safari return [ {event:"Ctrl+Shift+SPACE", handler:"this.handleAutocompletion(); stop = true;"}, {event:"ENTER", handler:"this.handleEnter(false, false)"}, {event:"Ctrl+ENTER", handler:"this.handleEnter(true, false)"}, {event:"Ctrl+Shift+ENTER", handler:"this.handleEnter(true, true)"}, {event:"TAB", handler:"this.handleTab()"}, {event:"Shift+TAB", handler:"this.handleShiftTab()"}, {event:"DELETE", handler:"this.handleDelete()"}, {event:"BACKSPACE", handler:"this.handleBackspace()"}, {event:"Ctrl+B", handler:"this.handleStrongEmphasis()"}, {event:"Ctrl+I", handler:"this.handleEmphasis()"}, {event:"Ctrl+U", handler:"this.handleUnderline()"}, {event:"Ctrl+K", handler:"this.handleStrike()"}, {event:"Meta+Z", handler:"this.handleUndo()"}, {event:"Meta+Shift+Z", handler:"this.handleRedo()"}, {event:"Meta+Y", handler:"this.handleRedo()"} ]; } else if(xq.Browser.isUbuntu) { // Ubunto FF return [ {event:"Ctrl+SPACE", handler:"this.handleAutocompletion(); stop = true;"}, {event:"ENTER", handler:"this.handleEnter(false, false)"}, {event:"Ctrl+ENTER", handler:"this.handleEnter(true, false)"}, {event:"Ctrl+Shift+ENTER", handler:"this.handleEnter(true, true)"}, {event:"TAB", handler:"this.handleTab()"}, {event:"Shift+TAB", handler:"this.handleShiftTab()"}, {event:"DELETE", handler:"this.handleDelete()"}, {event:"BACKSPACE", handler:"this.handleBackspace()"}, {event:"Ctrl+B", handler:"this.handleStrongEmphasis()"}, {event:"Ctrl+I", handler:"this.handleEmphasis()"}, {event:"Ctrl+U", handler:"this.handleUnderline()"}, {event:"Ctrl+K", handler:"this.handleStrike()"}, {event:"Ctrl+Z", handler:"this.handleUndo()"}, {event:"Ctrl+Y", handler:"this.handleRedo()"} ]; } else { // Win IE & FF return [ {event:"Ctrl+SPACE", handler:"this.handleAutocompletion(); stop = true;"}, {event:"ENTER", handler:"this.handleEnter(false, false)"}, {event:"Ctrl+ENTER", handler:"this.handleEnter(true, false)"}, {event:"Ctrl+Shift+ENTER", handler:"this.handleEnter(true, true)"}, {event:"TAB", handler:"this.handleTab()"}, {event:"Shift+TAB", handler:"this.handleShiftTab()"}, {event:"DELETE", handler:"this.handleDelete()"}, {event:"BACKSPACE", handler:"this.handleBackspace()"}, {event:"Ctrl+B", handler:"this.handleStrongEmphasis()"}, {event:"Ctrl+I", handler:"this.handleEmphasis()"}, {event:"Ctrl+U", handler:"this.handleUnderline()"}, {event:"Ctrl+K", handler:"this.handleStrike()"}, {event:"Ctrl+Z", handler:"this.handleUndo()"}, {event:"Ctrl+Y", handler:"this.handleRedo()"} ]; } }, _getDefaultTemplateProcessors: function() { return [ { id:"predefinedKeywordProcessor", handler:function(html) { var today = Date.get(); var keywords = { year: today.getFullYear(), month: today.getMonth() + 1, date: today.getDate(), hour: today.getHours(), min: today.getMinutes(), sec: today.getSeconds() }; return html.replace(/\{xq:(year|month|date|hour|min|sec)\}/img, function(text, keyword) { return keywords[keyword] || keyword; }); } } ]; }, /** * Adds or replaces keyboard shortcut. * * @param {String} shortcut keymap expression like "CTRL+Space" * @param {Object} handler string or function to be evaluated or called */ addShortcut: function(shortcut, handler) { this.config.shortcuts[shortcut] = {"event":new xq.Shortcut(shortcut), "handler":handler}; }, /** * Adds several keyboard shortcuts at once. * * @param {Array} list of shortcuts. each element should have following structure: {event:"keymap expression", handler:handler} */ addShortcuts: function(list) { for(var i = 0; i < list.length; i++) { this.addShortcut(list[i].event, list[i].handler); } }, /** * Returns keyboard shortcut matches with given keymap expression. * * @param {String} shortcut keymap expression like "CTRL+Space" */ getShortcut: function(shortcut) {return this.config.shortcuts[shortcut];}, /** * Returns entire keyboard shortcuts' map */ getShortcuts: function() {return this.config.shortcuts;}, /** * Remove keyboard shortcut matches with given keymap expression. * * @param {String} shortcut keymap expression like "CTRL+Space" */ removeShortcut: function(shortcut) {delete this.config.shortcuts[shortcut];}, /** * Adds or replaces autocorrection handler. * * @param {String} id unique identifier * @param {Object} criteria regex pattern or function to be used as a criterion for match * @param {Object} handler string or function to be evaluated or called when criteria met */ addAutocorrection: function(id, criteria, handler) { if(criteria.exec) { var pattern = criteria; criteria = function(text) {return text.match(pattern)}; } this.config.autocorrections[id] = {"criteria":criteria, "handler":handler}; }, /** * Adds several autocorrection handlers at once. * * @param {Array} list of autocorrection. each element should have following structure: {id:"identifier", criteria:criteria, handler:handler} */ addAutocorrections: function(list) { for(var i = 0; i < list.length; i++) { this.addAutocorrection(list[i].id, list[i].criteria, list[i].handler); } }, /** * Returns autocorrection handler matches with given id * * @param {String} id unique identifier */ getAutocorrection: function(id) {return this.config.autocorrection[id];}, /** * Returns entire autocorrections' map */ getAutocorrections: function() {return this.config.autocorrections;}, /** * Removes autocorrection handler matches with given id * * @param {String} id unique identifier */ removeAutocorrection: function(id) {delete this.config.autocorrections[id];}, /** * Adds or replaces autocompletion handler. * * @param {String} id unique identifier * @param {Object} criteria regex pattern or function to be used as a criterion for match * @param {Object} handler string or function to be evaluated or called when criteria met */ addAutocompletion: function(id, criteria, handler) { if(criteria.exec) { var pattern = criteria; criteria = function(text) { var m = pattern.exec(text); return m ? m.index : -1; }; } this.config.autocompletions[id] = {"criteria":criteria, "handler":handler}; }, /** * Adds several autocompletion handlers at once. * * @param {Array} list of autocompletion. each element should have following structure: {id:"identifier", criteria:criteria, handler:handler} */ addAutocompletions: function(list) { for(var i = 0; i < list.length; i++) { this.addAutocompletion(list[i].id, list[i].criteria, list[i].handler); } }, /** * Returns autocompletion handler matches with given id * * @param {String} id unique identifier */ getAutocompletion: function(id) {return this.config.autocompletions[id];}, /** * Returns entire autocompletions' map */ getAutocompletions: function() {return this.config.autocompletions;}, /** * Removes autocompletion handler matches with given id * * @param {String} id unique identifier */ removeAutocompletion: function(id) {delete this.config.autocompletions[id];}, /** * Adds or replaces template processor. * * @param {String} id unique identifier * @param {Object} handler string or function to be evaluated or called when template inserted */ addTemplateProcessor: function(id, handler) { this.config.templateProcessors[id] = {"handler":handler}; }, /** * Adds several template processors at once. * * @param {Array} list of template processors. Each element should have following structure: {id:"identifier", handler:handler} */ addTemplateProcessors: function(list) { for(var i = 0; i < list.length; i++) { this.addTemplateProcessor(list[i].id, list[i].handler); } }, /** * Returns template processor matches with given id * * @param {String} id unique identifier */ getTemplateProcessor: function(id) {return this.config.templateProcessors[id];}, /** * Returns entire template processors' map */ getTemplateProcessors: function() {return this.config.templateProcessors;}, /** * Removes template processor matches with given id *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -