lib/gollum/frontend/public/gollum/livepreview/js/ace/lib/ace/layer/text.js in gollum-2.4.4 vs lib/gollum/frontend/public/gollum/livepreview/js/ace/lib/ace/layer/text.js in gollum-2.4.5

- old
+ new

@@ -1,44 +1,33 @@ -/* vim:ts=4:sts=4:sw=4: - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Ajax.org Code Editor (ACE). - * - * The Initial Developer of the Original Code is - * Ajax.org B.V. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Fabian Jakobs <fabian AT ajax DOT org> - * Julian Viereck <julian DOT viereck AT gmail DOT com> - * Mihai Sucan <mihai.sucan@gmail.com> - * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { "use strict"; @@ -51,11 +40,12 @@ var Text = function(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_text-layer"; parentEl.appendChild(this.element); - this.$characterSize = this.$measureSizes() || {width: 0, height: 0}; + this.$characterSize = {width: 0, height: 0}; + this.checkForSizeChanges(); this.$pollSizeChanges(); }; (function() { @@ -81,11 +71,15 @@ }; this.checkForSizeChanges = function() { var size = this.$measureSizes(); if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) { + this.$measureNode.style.fontWeight = "bold"; + var boldSize = this.$measureSizes(); + this.$measureNode.style.fontWeight = ""; this.$characterSize = size; + this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height; this._emit("changeCharacterSize", {data: size}); } }; this.$pollSizeChanges = function() { @@ -194,40 +188,65 @@ return size; }; this.setSession = function(session) { this.session = session; + this.$computeTabString(); }; this.showInvisibles = false; this.setShowInvisibles = function(showInvisibles) { if (this.showInvisibles == showInvisibles) return false; this.showInvisibles = showInvisibles; + this.$computeTabString(); return true; }; + this.displayIndentGuides = true; + this.setDisplayIndentGuides = function(display) { + if (this.displayIndentGuides == display) + return false; + + this.displayIndentGuides = display; + this.$computeTabString(); + return true; + }; + this.$tabStrings = []; + this.onChangeTabSize = this.$computeTabString = function() { var tabSize = this.session.getTabSize(); + this.tabSize = tabSize; var tabStr = this.$tabStrings = [0]; for (var i = 1; i < tabSize + 1; i++) { if (this.showInvisibles) { tabStr.push("<span class='ace_invisible'>" + this.TAB_CHAR - + new Array(i).join("&#160;") + + Array(i).join("&#160;") + "</span>"); } else { tabStr.push(new Array(i+1).join("&#160;")); } } + if (this.displayIndentGuides) { + this.$indentGuideRe = /\s\S| \t|\t |\s$/; + var className = "ace_indent-guide"; + var content = Array(this.tabSize + 1).join("&#160;"); + var tabContent = content; + if (this.showInvisibles) { + className += " ace_invisible"; + tabContent = this.TAB_CHAR + content.substr(6); + } + this.$tabStrings[" "] = "<span class='" + className + "'>" + content + "</span>"; + this.$tabStrings["\t"] = "<span class='" + className + "'>" + tabContent + "</span>"; + } }; this.updateLines = function(config, firstRow, lastRow) { - this.$computeTabString(); // Due to wrap line changes there can be new lines if e.g. // the line to updated wrapped in the meantime. if (this.config.lastRow != config.lastRow || this.config.firstRow != config.firstRow) { this.scrollLines(config); @@ -251,26 +270,36 @@ } } lineElementsIdx ++; } - for (var i=first; i<=last; i++) { - var lineElement = lineElements[lineElementsIdx++]; - if (!lineElement) - continue; + var row = first; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; - var html = []; - var tokens = this.session.getTokens(i); - this.$renderLine(html, i, tokens, !this.$useLineGroups()); - lineElement = dom.setInnerHtml(lineElement, html.join("")); + while (true) { + if (row > foldStart) { + row = foldLine.end.row+1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row :Infinity; + } + if (row > last) + break; - i = this.session.getRowFoldEnd(i); + var lineElement = lineElements[lineElementsIdx++]; + if (lineElement) { + var html = []; + this.$renderLine( + html, row, !this.$useLineGroups(), row == foldStart ? foldLine : false + ); + dom.setInnerHtml(lineElement, html.join("")); + } + row++; } }; this.scrollLines = function(config) { - this.$computeTabString(); var oldConfig = this.config; this.config = config; if (!oldConfig || oldConfig.lastRow < config.firstRow) return this.update(config); @@ -319,12 +348,11 @@ var container = dom.createElement("div"); var html = []; // Get the tokens per line as there might be some lines in between // beeing folded. - var tokens = this.session.getTokens(row); - this.$renderLine(html, row, tokens, false); + this.$renderLine(html, row, false, row == foldStart ? foldLine : false); // don't use setInnerHtml since we are working with an empty DIV container.innerHTML = html.join(""); if (this.$useLineGroups()) { container.className = 'ace_line_group'; @@ -339,11 +367,10 @@ } return fragment; }; this.update = function(config) { - this.$computeTabString(); this.config = config; var html = []; var firstRow = config.firstRow, lastRow = config.lastRow; @@ -361,14 +388,11 @@ break; if (this.$useLineGroups()) html.push("<div class='ace_line_group'>") - // Get the tokens per line as there might be some lines in between - // beeing folded. - var tokens = this.session.getTokens(row); - this.$renderLine(html, row, tokens, false); + this.$renderLine(html, row, false, row == foldStart ? foldLine : false); if (this.$useLineGroups()) html.push("</div>"); // end the line group row++; @@ -382,11 +406,11 @@ "lparen": true }; this.$renderToken = function(stringBuilder, screenColumn, token, value) { var self = this; - var replaceReg = /\t|&|<|( +)|([\u0000-\u0019\u00a0\u1680\u180E\u2000-\u200b\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g; + var replaceReg = /\t|&|<|( +)|([\x00-\x1f\x80-\xa0\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g; var replaceFunc = function(c, a, b, tabIdx, idx4) { if (a) { return new Array(c.length+1).join("&#160;"); } else if (c == "&") { return "&#38;"; @@ -427,93 +451,131 @@ stringBuilder.push(output); } return screenColumn + value.length; }; - this.$renderLineCore = function(stringBuilder, lastRow, tokens, splits, onlyContents) { + this.renderIndentGuide = function(stringBuilder, value) { + var cols = value.search(this.$indentGuideRe); + if (cols <= 0) + return value; + if (value[0] == " ") { + cols -= cols % this.tabSize; + stringBuilder.push(Array(cols/this.tabSize + 1).join(this.$tabStrings[" "])); + return value.substr(cols); + } else if (value[0] == "\t") { + stringBuilder.push(Array(cols + 1).join(this.$tabStrings["\t"])); + return value.substr(cols); + } + return value; + }; + + this.$renderWrappedLine = function(stringBuilder, tokens, splits, onlyContents) { var chars = 0; var split = 0; - var splitChars; + var splitChars = splits[0]; var screenColumn = 0; - var self = this; - if (!splits || splits.length == 0) - splitChars = Number.MAX_VALUE; - else - splitChars = splits[0]; - - if (!onlyContents) { - stringBuilder.push("<div class='ace_line' style='height:", - this.config.lineHeight, "px", - "'>" - ); - } - for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; var value = token.value; + if (i == 0 && this.displayIndentGuides) { + chars = value.length; + value = this.renderIndentGuide(stringBuilder, value); + if (!value) + continue; + chars -= value.length; + } if (chars + value.length < splitChars) { - screenColumn = self.$renderToken( - stringBuilder, screenColumn, token, value - ); + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); chars += value.length; - } - else { + } else { while (chars + value.length >= splitChars) { - screenColumn = self.$renderToken( + screenColumn = this.$renderToken( stringBuilder, screenColumn, token, value.substring(0, splitChars - chars) ); value = value.substring(splitChars - chars); chars = splitChars; if (!onlyContents) { stringBuilder.push("</div>", "<div class='ace_line' style='height:", - this.config.lineHeight, "px", - "'>" + this.config.lineHeight, "px'>" ); } split ++; screenColumn = 0; splitChars = splits[split] || Number.MAX_VALUE; } if (value.length != 0) { chars += value.length; - screenColumn = self.$renderToken( + screenColumn = this.$renderToken( stringBuilder, screenColumn, token, value ); } } } + }; - if (this.showInvisibles) { - if (lastRow !== this.session.getLength() - 1) - stringBuilder.push("<span class='ace_invisible'>" + this.EOL_CHAR + "</span>"); - else - stringBuilder.push("<span class='ace_invisible'>" + this.EOF_CHAR + "</span>"); + this.$renderSimpleLine = function(stringBuilder, tokens) { + var screenColumn = 0; + var token = tokens[0]; + var value = token.value; + if (this.displayIndentGuides) + value = this.renderIndentGuide(stringBuilder, value); + if (value) + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); + for (var i = 1; i < tokens.length; i++) { + token = tokens[i]; + value = token.value; + screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value); } - if (!onlyContents) - stringBuilder.push("</div>"); }; - this.$renderLine = function(stringBuilder, row, tokens, onlyContents) { - // Check if the line to render is folded or not. If not, things are - // simple, otherwise, we need to fake some things... - if (!this.session.isRowFolded(row)) { + // row is either first row of foldline or not in fold + this.$renderLine = function(stringBuilder, row, onlyContents, foldLine) { + if (!foldLine && foldLine != false) + foldLine = this.session.getFoldLine(row); + + if (foldLine) + var tokens = this.$getFoldLineTokens(row, foldLine); + else + var tokens = this.session.getTokens(row); + + + if (!onlyContents) { + stringBuilder.push( + "<div class='ace_line' style='height:", this.config.lineHeight, "px'>" + ); + } + + if (tokens.length) { var splits = this.session.getRowSplitData(row); - this.$renderLineCore(stringBuilder, row, tokens, splits, onlyContents); - } else { - this.$renderFoldLine(stringBuilder, row, tokens, onlyContents); + if (splits && splits.length) + this.$renderWrappedLine(stringBuilder, tokens, splits, onlyContents); + else + this.$renderSimpleLine(stringBuilder, tokens); } + + if (this.showInvisibles) { + if (foldLine) + row = foldLine.end.row + + stringBuilder.push( + "<span class='ace_invisible'>", + row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR, + "</span>" + ); + } + if (!onlyContents) + stringBuilder.push("</div>"); }; - this.$renderFoldLine = function(stringBuilder, row, tokens, onlyContents) { + this.$getFoldLineTokens = function(row, foldLine) { var session = this.session; - var foldLine = session.getFoldLine(row); var renderTokens = []; function addTokens(tokens, from, to) { var idx = 0, col = 0; while ((col + tokens[idx].value.length) < from) { @@ -550,12 +612,13 @@ col += value.length; idx += 1; } } + var tokens = session.getTokens(row); foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) { - if (placeholder) { + if (placeholder != null) { renderTokens.push({ type: "fold", value: placeholder }); } else { @@ -565,12 +628,10 @@ if (tokens.length) addTokens(tokens, lastColumn, column); } }, foldLine.end.row, this.session.getLine(foldLine.end.row).length); - // splits for foldline are stored at its' first row - var splits = this.session.$useWrapMode ? this.session.$wrapData[row] : null; - this.$renderLineCore(stringBuilder, row, renderTokens, splits, onlyContents); + return renderTokens; }; this.$useLineGroups = function() { // For the updateLines function to work correctly, it's important that the // child nodes of this.element correspond on a 1-to-1 basis to rows in the