📄 xquared.js
字号:
this.wysiwygEditorDiv.style.display = "none"; this.outmostWrapper.appendChild(this.wysiwygEditorDiv); // create designmode iframe for WYSIWYG editor this.editorFrame = this.doc.createElement('iframe'); this.rdom.setAttributes(this.editorFrame, { "frameBorder": "0", "marginWidth": "0", "marginHeight": "0", "leftMargin": "0", "topMargin": "0", "allowTransparency": "true" }); this.wysiwygEditorDiv.appendChild(this.editorFrame); var doc = this.editorFrame.contentWindow.document; if(xq.Browser.isTrident) doc.designMode = 'On'; doc.open(); doc.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'); doc.write('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko">'); doc.write('<head>'); // it is needed to force href of pasted content to be an absolute url if(!xq.Browser.isTrident) doc.write('<base href="./" />'); doc.write('<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />'); doc.write('<title>XQuared</title>'); if(this.config.changeCursorOnLink) doc.write('<style>.xed a {cursor: pointer !important;}</style>'); doc.write('</head>'); doc.write('<body><p>' + this.rdom.makePlaceHolderString() + '</p></body>'); doc.write('</html>'); doc.close(); this.editorWin = this.editorFrame.contentWindow; this.editorDoc = this.editorWin.document; this.editorBody = this.editorDoc.body; this.editorBody.className = "xed"; // it is needed to fix IE6 horizontal scrollbar problem if(xq.Browser.isIE6) { this.editorDoc.documentElement.style.overflowY='auto'; this.editorDoc.documentElement.style.overflowX='hidden'; } // override image path if(this.config.generateDefaultToolbar) { this._addStyleRules([ {selector:".xquared div.toolbar", rule:"background-image: url(" + this.config.imagePathForDefaultToobar + "toolbarBg.gif)"}, {selector:".xquared ul.buttons li", rule:"background-image: url(" + this.config.imagePathForDefaultToobar + "toolbarButtonBg.gif)"}, {selector:".xquared ul.buttons li.xq_separator", rule:"background-image: url(" + this.config.imagePathForDefaultToobar + "toolbarSeparator.gif)"} ]); } this.rdom.setWin(this.editorWin); this.rdom.setRoot(this.editorBody); this.validator = xq.Validator.createInstance(this.doc.location.href, this.config.urlValidationMode, this.config.allowedTags, this.config.allowedAttributes); // hook onsubmit of form if(this.config.automaticallyHookSubmitEvent && this.contentElement.nodeName == 'TEXTAREA' && this.contentElement.form) { var original = this.contentElement.form.onsubmit; this.contentElement.form.onsubmit = function() { this.contentElement.value = this.getCurrentContent(true); if(original) { return original(); } else { return true; } }.bind(this); } }, _addStyleRules: function(rules) { if(!this.dynamicStyle) { if(xq.Browser.isTrident) { this.dynamicStyle = this.doc.createStyleSheet(); } else { var style = this.doc.createElement('style'); this.doc.body.appendChild(style); this.dynamicStyle = xq.$A(this.doc.styleSheets).last(); } } for(var i = 0; i < rules.length; i++) { var rule = rules[i]; if(xq.Browser.isTrident) { this.dynamicStyle.addRule(rules[i].selector, rules[i].rule); } else { this.dynamicStyle.insertRule(rules[i].selector + " {" + rules[i].rule + "}", this.dynamicStyle.cssRules.length); } } }, _defaultToolbarClickHandler: function(e) { var src = e.target || e.srcElement; while(src.nodeName != "A") src = src.parentNode; if(xq.hasClassName(src.parentNode, 'disabled') || xq.hasClassName(this.toolbarContainer, 'disabled')) { xq.stopEvent(e); return false; } if(xq.Browser.isTrident) this.focus(); var handler = src.handler; var xed = this; var stop = (typeof handler == "function") ? handler(this) : eval(handler); if(stop) { xq.stopEvent(e); return false; } else { return true; } }, _generateDefaultToolbar: function() { // outmost container var container = this.doc.createElement('div'); container.className = 'toolbar'; // button container var buttons = this.doc.createElement('ul'); buttons.className = 'buttons'; container.appendChild(buttons); // Generate buttons from map and append it to button container var map = this.config.defaultToolbarButtonMap; for(var i = 0; i < map.length; i++) { for(var j = 0; j < map[i].length; j++) { var buttonConfig = map[i][j]; var li = this.doc.createElement('li'); buttons.appendChild(li); li.className = buttonConfig.className; var span = this.doc.createElement('span'); li.appendChild(span); var a = this.doc.createElement('a'); span.appendChild(a); a.href = '#'; a.title = buttonConfig.title; a.handler = buttonConfig.handler; this._toolbarAnchorsCache.push(a); xq.observe(a, 'mousedown', xq.cancelHandler); xq.observe(a, 'click', this._defaultToolbarClickHandler.bindAsEventListener(this)); var img = this.doc.createElement('img'); a.appendChild(img); img.src = this.config.imagePathForDefaultToobar + buttonConfig.className + '.gif'; if(j == 0 && i != 0) li.className += ' xq_separator'; } } return container; }, ///////////////////////////////////////////// // Event Management _registerEventHandlers: function() { var events = ['keydown', 'click', 'keyup', 'mouseup', 'contextmenu']; if(xq.Browser.isTrident && this.config.changeCursorOnLink) events.push('mousemove'); if(xq.Browser.isMac && xq.Browser.isGecko) events.push('keypress'); for(var i = 0; i < events.length; i++) { xq.observe(this.getDoc(), events[i], this._handleEvent.bindAsEventListener(this)); } }, _handleEvent: function(e) { this._fireOnBeforeEvent(this, e); var stop = false; var modifiedByCorrection = false; if(e.type == 'mousemove' && this.config.changeCursorOnLink) { // Trident only var link = !!this.rdom.getParentElementOf(e.srcElement, ["A"]); var editable = this.editorBody.contentEditable; editable = editable == 'inherit' ? false : editable; if(editable != link && !this.rdom.hasSelection()) this.editorBody.contentEditable = !link; } else if(e.type == 'click' && e.button == 0 && this.config.enableLinkClick) { var a = this.rdom.getParentElementOf(e.target || e.srcElement, ["A"]); if(a) stop = this.handleClick(e, a); } else if(e.type == (xq.Browser.isMac && xq.Browser.isGecko ? "keypress" : "keydown")) { var undoPerformed = false; modifiedByCorrection = this.rdom.correctParagraph(); for(var key in this.config.shortcuts) { if(!this.config.shortcuts[key].event.matches(e)) continue; var handler = this.config.shortcuts[key].handler; var xed = this; stop = (typeof handler == "function") ? handler(this) : eval(handler); if(key == "undo") undoPerformed = true; } } else if(["mouseup", "keyup"].indexOf(e.type) != -1) { modifiedByCorrection = this.rdom.correctParagraph(); } else if(["contextmenu"].indexOf(e.type) != -1) { this._handleContextMenu(e); } if(stop) xq.stopEvent(e); this._fireOnCurrentContentChanged(this); this._fireOnAfterEvent(this, e); if(!undoPerformed && !modifiedByCorrection) this.editHistory.onEvent(e); return !stop; }, /** * TODO: remove dup with handleAutocompletion */ handleAutocorrection: function() { var block = this.rdom.getCurrentBlockElement(); // TODO: use complete unescape algorithm var text = this.rdom.getInnerText(block).replace(/ /gi, " "); var acs = this.config.autocorrections; var performed = false; var stop = false; for(var key in acs) { var ac = acs[key]; if(ac.criteria(text)) { try { this.editHistory.onCommand(); this.editHistory.disable(); if(typeof ac.handler == "String") { var xed = this; var rdom = this.rdom; eval(ac.handler); } else { stop = ac.handler(this, this.rdom, block, text); } this.editHistory.enable(); } catch(ignored) {} block = this.rdom.getCurrentBlockElement(); text = this.rdom.getInnerText(block); performed = true; if(stop) break; } } return stop; }, /** * TODO: remove dup with handleAutocorrection */ handleAutocompletion: function() { var acs = this.config.autocompletions; if(xq.isEmptyHash(acs)) return; if(this.rdom.hasSelection()) { var text = this.rdom.getSelectionAsText(); this.rdom.deleteSelection(); var wrapper = this.rdom.insertNode(this.rdom.createElement("SPAN")); wrapper.innerHTML = text; var marker = this.rdom.pushMarker(); var filtered = []; for(var key in acs) { filtered.push([key, acs[key].criteria(text)]); } filtered = filtered.findAll(function(elem) { return elem[1] != -1; }); if(filtered.length == 0) { this.rdom.popMarker(true); return; } var minIndex = 0; var min = filtered[0][1]; for(var i = 0; i < filtered.length; i++) { if(filtered[i][1] < min) { minIndex = i; min = filtered[i][1]; } } var ac = acs[filtered[minIndex][0]]; this.editHistory.disable(); } else { var marker = this.rdom.pushMarker(); var filtered = []; for(var key in acs) { filtered.push([key, this.rdom.testSmartWrap(marker, acs[key].criteria).textIndex]); } filtered = filtered.findAll(function(elem) { return elem[1] != -1; }); if(filtered.length == 0) { this.rdom.popMarker(true); return; } var minIndex = 0; var min = filtered[0][1]; for(var i = 0; i < filtered.length; i++) { if(filtered[i][1] < min) { minIndex = i; min = filtered[i][1]; } } var ac = acs[filtered[minIndex][0]]; this.editHistory.disable(); var wrapper = this.rdom.smartWrap(marker, "SPAN", ac.criteria); } var block = this.rdom.getCurrentBlockElement(); // TODO: use complete unescape algorithm var text = this.rdom.getInnerText(wrapper).replace(/ /gi, " "); try { // call handler if(typeof ac.handler == "String") { var xed = this; var rdom = this.rdom; eval(ac.handler); } else { ac.handler(this, this.rdom, block, wrapper, text); } } catch(ignored) {} try { this.rdom.unwrapElement(wrapper); } catch(ignored) {} if(this.rdom.isEmptyBlock(block)) this.rdom.correctEmptyElement(block); this.editHistory.enable(); this.editHistory.onCommand(); this.rdom.popMarker(true); }, /** * Handles click event * * @param {Event} e click event * @param {Element} target target element(usually has A tag) */ handleClick: function(e, target) { var href = decodeURI(target.href); if(!xq.Browser.isTrident) { if(!e.ctrlKey && !e.shiftKey && e.button != 1) { window.location.href = href; return true; } } else { if(e.shiftKey) { window.open(href, "_blank"); } else { window.location.href = href; } return true; } return false; }, /** * Show link dialog * * TODO: should support modify/unlink */ handleLink: function() { var text = this.rdom.getSelectionAsText() || ''; var dialog = new xq.controls.FormDialog( this, xq.ui_templates.basicLinkDialog, function(dialog) { if(text) { dialog.form.text.value = text; dialog.form.url.focus(); dialog.form.url.select(); } }, function(data) { this.focus(); if(xq.Browser.isTrident) { var rng = this.rdom.rng(); rng.moveToBookmark(bm); rng.select(); } if(!data) return; this.handleInsertLink(false, data.url, data.text, data.text); }.bind(this) ); if(xq.Browser.isTrident) var bm = this.rdom.rng().getBookmark(); dialog.show({position: 'centerOfEditor'}); return true; }, /** * Inserts link or apply link into selected area * * @param {boolean} autoSelection if set true and there's no selection, automatically select word to link(if possible) * @param {String} url url * @param {String} title title of link * @param {String} text text of link. If there's a selection(manually or automatically), it will be replaced with this text * * @returns {Element} created element */ handleInsertLink: function(autoSelection, url, title, text) { if(autoSelection && !this.rdom.hasSelection()) { var marker = this.rdom.pushMarker(); var a = this.rdom.smartWrap(marker, "A", function(text) { var index = text.lastIndexOf(" "); return index == -1 ? index : index + 1; }); a.href = url; a.title = title; if(text) { a.innerHTML = "" a.appendChild(this.rdom.createTextNode(text)); } else if(!a.hasChildNodes()) { this.rdom.deleteNode(a); } this.rdom.popMarker(true); } else { text = text || (this.rdom.hasSelection() ? this.rdom.getSelectionAsText() : null);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -