📄 richtextcanvas.js
字号:
startLineNum = 0; startLine = this.getLine(0); } html = startLine.innerHTML; } else { var endLine = this.getSelectionStartLine(); var endLineNum = this.getLineNumber(endLine); if (endLineNum < startLineNum) { startLine = endLine; startLineNum = endLineNum; }// this.logWarn("endLine: " + endLine.innerHTML);// this.logWarn("nextSibling: " + isc.Log.echoAll(endLine.nextSibling)); var currentLine = startLine; var numLines = 0; while (currentLine && currentLine != endLine) { if (currentLine.innerHTML) { html += currentLine.innerHTML; // this.logWarn("html is now: " + html); } numLines++; currentLine = currentLine.nextSibling; } // repair bonus BR that gets shunted out of the original line span by FF var nextNode = endLine.nextSibling; if (nextNode && nextNode.tagName.toLowerCase() == "br") { nextNode.parentNode.removeChild(nextNode); endLine.appendChild(nextNode);// this.logWarn("repaired br, endline is now: " + endLine.innerHTML); } html += endLine.innerHTML; // if we pasted a line span into the middle of another line span, then it will be // fragmented - pick up the trailing fragment if (!html.replace(/\n|\r/g, isc.emptyString).match(/<br>$/i)) { if (endLine.nextSibling) { html += endLine.nextSibling.innerHTML; numLines++; } } } // this.logWarn("linesChanged: " + html); if (!oldVal) { oldVal = this.contents; newVal = this.getContents(); } // fire linesChanged if it's defined if (this.linesChanged) { this.linesChanged(oldVal, newVal, startLineNum, numLines, html, selectionId); } else if (this.syntaxHiliter) { // currently syntaxHiliter is not compatible with linesChanged - use one or the other. this.doSyntaxHilite(oldVal, newVal, startLineNum, numLines, html, selectionId); } }, doSyntaxHilite : function (oldVal, newVal, startLineNum, numLines, changedHTML, selectionId) { // keeping a marker in the code sent to the colorizer, breaks some colorization cases - // specifically, this happens if the marker is in the middle of something that would be // matched by a regex. For example in XML colorization of the marker is next to the // equal sign in this expression: foo="bar", then that expression won't colorize until // something else is edited such that the selection marker moves.// this.logWarn("source before html removal: " + changedHTML); // remove markup, but keep the selectionSpan so we can extract its index for // repositioning the selection correctly after syntax hiliting var source = this.removeMarkup(changedHTML, true); // this.logWarn("source after html removal: " + source); // save off the index of the locationMarker var selectionMarkerIndex = this.getSelectionMarkerIndex(source); if (selectionMarkerIndex == -1) { // marker has been wiped out by a select-all, re-hilite everything this.doFullSyntaxHilite(); return; } // remove the selectionMarker from the source source = this.removeMarkup(changedHTML); // if the modified source contains a token that requires us to reformat queue a full // hilite, but still do the partial update immediately// if (this.syntaxHiliter.containsMultilineToken(source)) this.queueFullHilite(); // apply the syntax hiliting to just the lines passed in var newLines = this.syntaxHiliter.hilite(source, true, selectionMarkerIndex, this._getSelectionSpanHTML(selectionId)); this.overwriteLines(startLineNum, numLines, newLines); this.moveSelectionToMarker(selectionId); }, doFullSyntaxHilite : function () {// this.logWarn("full syntax hilite running"); var selectionId = this.markCurrentSelection(); var contents = this._getContents(); var source = this.removeMarkup(contents, true); var selectionMarkerIndex = this.getSelectionMarkerIndex(source); if (selectionMarkerIndex == -1) { selectionMarkerIndex = contents.length; } // remove the selectionMarker from the source source = this.removeMarkup(contents); this.setContents(source, true, selectionMarkerIndex, this._getSelectionSpanHTML(selectionId)); this.moveSelectionToMarker(selectionId); delete this.fullHiliteTimer; }, queueFullHilite : function () {// this.logWarn("(re)queueing full hilite"); if (this.fullHiliteTimer) isc.Timer.clearTimeout(this.fullHiliteTimer); this.fullHiliteTimer = this.delayCall("doFullSyntaxHilite", [], this.fullSyntaxHiliteDelay); }, selectionIsCollapsed : function () { if (isc.Browser.isIE) { var range = document.selection.createRange(); return range.text.length == 0; } else if (isc.Browser.isMoz) { var selection = this.getContentWindow().getSelection(); return selection.isCollapsed; } }, rememberSelectionStartLine : function () { this.startLineNum = this.getLineNumber(this.getSelectionStartLine()); }, getLastSelectionStartLine : function () { return this.startLineNum; }, _setPasteTimer : function () { this._pasteTimer = this.delayCall("doLinesChanged", [], 0); }, // onpaste/onbeforepaste for IE - caching theset strings _getOnBeforePaste : function () { if (!this._onBeforePaste) this._onBeforePaste = this.getID()+".rememberSelectionStartLine();event.returnValue=true"; return this._onBeforePaste; }, _getOnPaste : function () { if (!this._onPaste) this._onPaste = this.getID()+"._setPasteTimer();event.returnValue=true" return this._onPaste; }, // line span HTML caching _getLineSpanHTML : function () { if (!this._lineSpanHTML) { this._lineSpanHTML = "<span isLine='true'"; // prevent the "bouncing line" effect where adding a space on a line that's clipped // causes it to be broken into two lines by the browser (since that space is the first // available place to wrap it). The rendering then snaps back into a single line when // the syntaxHiliter converts the space into an if (this.syntaxHiliter && !this.syntaxHiliter.autoWrap) this._lineSpanHTML +=" style='white-space:nowrap'"; if (isc.Browser.isIE) { this._lineSpanHTML += " onbeforepaste='"+this._getOnBeforePaste() +"' onpaste='"+this._getOnPaste()+"'" } this._lineSpanHTML += ">$1</span>"; } return this._lineSpanHTML; }, createLine : function (contents) { var doc = this.getContentDocument(); var line = doc.createElement("span"); line.setAttribute("isLine", "true"); // prevent the "bouncing line" effect where adding a space on a line that's clipped // causes it to be broken into two lines by the browser (since that space is the first // available place to wrap it). The rendering then snaps back into a single line when // the syntaxHiliter converts the space into an if (this.syntaxHiliter && !this.syntaxHiliter.autoWrap) line.setAttribute("style", "white-space:nowrap"); if (isc.Browser.isIE) { line.setAttribute("onbeforepaste", this._getOnBeforePaste()); line.setAttribute("onpaste", this._getOnPaste()); } line.innerHTML = contents ? contents : "<br>"; return line; }, // returns a string containing an incrementing counter usable unique identifier for the // selection span. _getNextSelectionId : function () { if (!this.selectionIdSequence) this.selectionIdSequence = 0; return this.getID()+"_selection_"+this.selectionIdSequence++; }, // returns the DOM node that is the line span containing the start of the current selection. getSelectionStartLine : function () { var doc = this.getContentDocument(); var line; if (isc.Browser.isIE) { var selectionId = this._getNextSelectionId(); var range = doc.selection.createRange(); // collapse the selection range to the start of the current selection range.collapse(); range.pasteHTML("<span id='"+selectionId+"'></span>"); var selNode = doc.getElementById(selectionId); line = selNode.parentNode;// this.logWarn("startLine: " + line.outerHTML); line.removeChild(selNode); } else if (isc.Browser.isMoz) { var selection = this.getContentWindow().getSelection(); var line = selection.anchorNode; }// this.logWarn("anchorNode: " + Log.echo(line)); // IE will paste the isLine spans into the current line, so we need to find the // top-most element that is actually a line var lastLine = line; while(line.parentNode != null) { if (line.getAttribute && line.getAttribute("isLine") != null) lastLine = line; line = line.parentNode; } return lastLine; }, _getSelectionSpanHTML : function (selectionId) { return "<span isSelectionSpan='true' id='"+selectionId+"'></span>"; }, // inserts an HTML marker at the start of the current selection. In IE, the marker will be // inserted immediately before the current selection. In FF, immediately after. FF can be // made to work as IE for collapsed selections, but for multichar selections it may be // impossible. See the notes below for enabling IE-style behavior in FF for collapsed // selections. markCurrentSelection : function () { var selectionId = this._getNextSelectionId(); var doc = this.getContentDocument(); if (isc.Browser.isIE) { var range = doc.selection.createRange(); // collapse the selection range to the start of the current selection range.collapse(); // insert our marker span range.pasteHTML(this._getSelectionSpanHTML(selectionId)); } else if (isc.Browser.isMoz) { // create the selection span var selectionNode = doc.createElement("span"); selectionNode.setAttribute('isSelectionSpan', "true"); selectionNode.setAttribute('id', selectionId); // grab the current selection range var selection = this.getContentWindow().getSelection(); var range = selection.getRangeAt(0); if (selection.isCollapsed) { range.insertNode(selectionNode); } else { // create a new range whose start and end match the selection range start boundary var collapsedRange = range.cloneRange(); // collapse the new range to the end of the selection collapsedRange.collapse(false); // insert the selection node at the collapsed range collapsedRange.insertNode(selectionNode); collapsedRange.detach(); /* // The code below will insert the selection marker immediately before the start // of the current selection, but it only works for a collapsed selection. This // is because range.insertNode() puts the new node right after the start of the // selection which means that we need to jump the start of the selection over // the newly inserted node. This works fine for a collapsed selection, but a // multicharacter selection gets destroyed by insertNode() - at least when the // start and end of the multichar selection are in one text node that is // fragmented by insertNode(). After insertNode(), the selection reflects // the character immediately before the previous start of the selection! // Weird FF bug - we need to clear the selection range and then re-add it after // we're done manipulating our clone selection - because if we don't it expands // to the parentNode for no apparent reason. selection.removeAllRanges(); // create a new range whose start and end match the selection range start boundary var collapsedRange = doc.createRange(); collapsedRange.setStart(range.startContainer, range.startOffset); collapsedRange.setEnd(range.startContainer, range.startOffset); // insert the selection node at the collapsed range collapsedRange.insertNode(selectionNode); collapsedRange.detach(); // the above should have been all, but unfortunately the above insertion happened // AFTER the start of the current selection range, so now we need to move the // current selection range start to after the node we inserted if(range.startContainer.nodeType == 3) { // if it's a text node, then the above insertion fragmented the text node into // a two, so just jump over the selectionNode we just inserted. range.setEnd(range.startContainer.nextSibling.nextSibling, 0); range.setStart(range.startContainer.nextSibling.nextSibling, 0); } else { range.setEndAfter(selectionNode); range.setStartAfter(selectionNode); } // add the modified range back so the cursor shows up. selection.addRange(range); */ } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -