c9-core/plugins/node_modules/ace/lib/ace/layer/gutter.js

452 wiersze
16 KiB
JavaScript

/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* 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.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var dom = require("../lib/dom");
var oop = require("../lib/oop");
var lang = require("../lib/lang");
var EventEmitter = require("../lib/event_emitter").EventEmitter;
var Lines = require("./lines").Lines;
var Gutter = function(parentEl) {
this.element = dom.createElement("div");
this.element.className = "ace_layer ace_gutter-layer";
parentEl.appendChild(this.element);
this.setShowFoldWidgets(this.$showFoldWidgets);
this.gutterWidth = 0;
this.$annotations = [];
this.$updateAnnotations = this.$updateAnnotations.bind(this);
this.$lines = new Lines(this.element);
this.$lines.$offsetCoefficient = 1;
};
(function() {
oop.implement(this, EventEmitter);
this.setSession = function(session) {
if (this.session)
this.session.removeEventListener("change", this.$updateAnnotations);
this.session = session;
if (session)
session.on("change", this.$updateAnnotations);
};
this.addGutterDecoration = function(row, className) {
if (window.console)
console.warn && console.warn("deprecated use session.addGutterDecoration");
this.session.addGutterDecoration(row, className);
};
this.removeGutterDecoration = function(row, className) {
if (window.console)
console.warn && console.warn("deprecated use session.removeGutterDecoration");
this.session.removeGutterDecoration(row, className);
};
this.setAnnotations = function(annotations) {
// iterate over sparse array
this.$annotations = [];
for (var i = 0; i < annotations.length; i++) {
var annotation = annotations[i];
var row = annotation.row;
var rowInfo = this.$annotations[row];
if (!rowInfo)
rowInfo = this.$annotations[row] = {text: []};
var annoText = annotation.text;
annoText = annoText ? lang.escapeHTML(annoText) : annotation.html || "";
if (rowInfo.text.indexOf(annoText) === -1)
rowInfo.text.push(annoText);
var type = annotation.type;
if (type == "error")
rowInfo.className = " ace_error";
else if (type == "warning" && rowInfo.className != " ace_error")
rowInfo.className = " ace_warning";
else if (type == "info" && (!rowInfo.className))
rowInfo.className = " ace_info";
}
};
this.$updateAnnotations = function (delta) {
if (!this.$annotations.length)
return;
var firstRow = delta.start.row;
var len = delta.end.row - firstRow;
if (len === 0) {
// do nothing
} else if (delta.action == 'remove') {
this.$annotations.splice(firstRow, len + 1, null);
} else {
var args = new Array(len + 1);
args.unshift(firstRow, 1);
this.$annotations.splice.apply(this.$annotations, args);
}
};
this.update = function(config) {
this.config = config;
var session = this.session;
var firstRow = config.firstRow;
var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar
session.getLength() - 1);
this.oldLastRow = lastRow;
this.config = config;
this.$lines.moveContainer(config);
this.$updateCursorRow();
var fold = session.getNextFoldLine(firstRow);
var foldStart = fold ? fold.start.row : Infinity;
var cell = null;
var index = -1;
var row = firstRow;
var cells = Array.prototype.slice.call(this.element.childNodes, 0);
while (true) {
if (row > foldStart) {
row = fold.end.row + 1;
fold = session.getNextFoldLine(row, fold);
foldStart = fold ? fold.start.row : Infinity;
}
if (row > lastRow) {
while (this.$lines.getLength() > index + 1)
this.$lines.pop();
break;
}
cell = this.$lines.get(++index);
if (!cell) {
cell = this.$lines.createCell(row, config, this.session, onCreateCell);
this.$lines.push(cell);
}
this.$renderCell(cell, config, fold, row);
row++;
}
this._signal("afterRender");
this.$updateGutterWidth(config);
};
this.$updateGutterWidth = function(config) {
var session = this.session;
var gutterRenderer = session.gutterRenderer || this.$renderer;
var firstLineNumber = session.$firstLineNumber;
var lastLineText = this.$lines.last() ? this.$lines.last().text : "";
if (this.$fixedWidth || session.$useWrapMode)
lastLineText = session.getLength() + firstLineNumber;
var gutterWidth = gutterRenderer
? gutterRenderer.getWidth(session, lastLineText, config)
: lastLineText.toString().length * config.characterWidth;
var padding = this.$padding || this.$computePadding();
gutterWidth += padding.left + padding.right;
if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) {
this.gutterWidth = gutterWidth;
this.element.parentNode.style.width =
this.element.style.width = Math.ceil(this.gutterWidth) + "px";
this._signal("changeGutterWidth", gutterWidth);
}
};
this.$updateCursorRow = function() {
if (!this.$highlightGutterLine)
return;
var position = this.session.selection.getCursor();
if (this.$cursorRow === position.row)
return;
this.$cursorRow = position.row;
};
this.updateLineHighlight = function() {
if (!this.$highlightGutterLine)
return;
var row = this.session.selection.cursor.row;
this.$cursorRow = row;
if (this.$cursorCell && this.$cursorCell.row == row)
return;
if (this.$cursorCell)
this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", "");
var cells = this.$lines.cells;
this.$cursorCell = null;
for (var i = 0; i < cells.length; i++) {
var cell = cells[i];
if (cell.row >= this.$cursorRow) {
if (cell.row > this.$cursorRow) {
var fold = this.session.getFoldLine(this.$cursorRow);
if (i > 0 && fold && fold.start.row == cells[i - 1].row)
cell = cells[i - 1];
else
break;
}
cell.element.className = "ace_gutter-active-line " + cell.element.className;
this.$cursorCell = cell;
break;
}
}
};
this.scrollLines = function(config) {
var oldConfig = this.config;
this.config = config;
this.$updateCursorRow();
if (this.$lines.pageChanged(oldConfig, config))
return this.update(config);
this.$lines.moveContainer(config);
var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar
this.session.getLength() - 1);
var oldLastRow = this.oldLastRow;
this.oldLastRow = lastRow;
if (!oldConfig || oldLastRow < config.firstRow)
return this.update(config);
if (lastRow < oldConfig.firstRow)
return this.update(config);
if (oldConfig.firstRow < config.firstRow)
for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--)
this.$lines.shift();
if (oldLastRow > lastRow)
for (var row=this.session.getFoldedRowCount(lastRow + 1, oldLastRow); row>0; row--)
this.$lines.pop();
if (config.firstRow < oldConfig.firstRow) {
this.$lines.unshift(this.$renderLines(config, config.firstRow, oldConfig.firstRow - 1));
}
if (lastRow > oldLastRow) {
this.$lines.push(this.$renderLines(config, oldLastRow + 1, lastRow));
}
this.updateLineHighlight();
this._signal("afterRender");
this.$updateGutterWidth(config);
};
this.$renderLines = function(config, firstRow, lastRow) {
var fragment = [];
var row = firstRow;
var foldLine = this.session.getNextFoldLine(row);
var foldStart = foldLine ? foldLine.start.row : Infinity;
while (true) {
if (row > foldStart) {
row = foldLine.end.row+1;
foldLine = this.session.getNextFoldLine(row, foldLine);
foldStart = foldLine ? foldLine.start.row : Infinity;
}
if (row > lastRow)
break;
var cell = this.$lines.createCell(row, config, this.session, onCreateCell);
this.$renderCell(cell, config, foldLine, row);
fragment.push(cell);
row++;
}
return fragment;
};
this.$renderCell = function(cell, config, fold, row) {
var element = cell.element;
var session = this.session;
var textNode = element.childNodes[0];
var foldWidget = element.childNodes[1];
var firstLineNumber = session.$firstLineNumber;
var breakpoints = session.$breakpoints;
var decorations = session.$decorations;
var gutterRenderer = session.gutterRenderer || this.$renderer;
var foldWidgets = this.$showFoldWidgets && session.foldWidgets;
var foldStart = fold ? fold.start.row : Number.MAX_VALUE;
var className = "ace_gutter-cell ";
if (this.$highlightGutterLine) {
if (row == this.$cursorRow || (fold && row < this.$cursorRow && row >= foldStart && this.$cursorRow <= fold.end.row)) {
className += "ace_gutter-active-line ";
if (this.$cursorCell != cell) {
if (this.$cursorCell)
this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", "");
this.$cursorCell = cell;
}
}
}
if (breakpoints[row])
className += breakpoints[row];
if (decorations[row])
className += decorations[row];
if (this.$annotations[row])
className += this.$annotations[row].className;
if (element.className != className)
element.className = className;
if (foldWidgets) {
var c = foldWidgets[row];
// check if cached value is invalidated and we need to recompute
if (c == null)
c = foldWidgets[row] = session.getFoldWidget(row);
}
if (c) {
var className = "ace_fold-widget ace_" + c;
if (c == "start" && row == foldStart && row < fold.end.row)
className += " ace_closed";
else
className += " ace_open";
if (foldWidget.className != className)
foldWidget.className = className;
var foldHeight = config.lineHeight + "px";
dom.setStyle(foldWidget.style, "height", foldHeight);
dom.setStyle(foldWidget.style, "display", "inline-block");
} else {
if (foldWidget) {
dom.setStyle(foldWidget.style, "display", "none");
}
}
var text = (gutterRenderer
? gutterRenderer.getText(session, row)
: row + firstLineNumber).toString();
if (text !== textNode.data) {
textNode.data = text;
}
dom.setStyle(cell.element.style, "height", this.$lines.computeLineHeight(row, config, session) + "px");
dom.setStyle(cell.element.style, "top", this.$lines.computeLineTop(row, config, session) + "px");
cell.text = text;
return cell;
};
this.$fixedWidth = false;
this.$highlightGutterLine = true;
this.$renderer = "";
this.setHighlightGutterLine = function(highlightGutterLine) {
this.$highlightGutterLine = highlightGutterLine;
};
this.$showLineNumbers = true;
this.$renderer = "";
this.setShowLineNumbers = function(show) {
this.$renderer = !show && {
getWidth: function() {return "";},
getText: function() {return "";}
};
};
this.getShowLineNumbers = function() {
return this.$showLineNumbers;
};
this.$showFoldWidgets = true;
this.setShowFoldWidgets = function(show) {
if (show)
dom.addCssClass(this.element, "ace_folding-enabled");
else
dom.removeCssClass(this.element, "ace_folding-enabled");
this.$showFoldWidgets = show;
this.$padding = null;
};
this.getShowFoldWidgets = function() {
return this.$showFoldWidgets;
};
this.$computePadding = function() {
if (!this.element.firstChild)
return {left: 0, right: 0};
var style = dom.computedStyle(this.element.firstChild);
this.$padding = {};
this.$padding.left = (parseInt(style.borderLeftWidth) || 0)
+ (parseInt(style.paddingLeft) || 0) + 1;
this.$padding.right = (parseInt(style.borderRightWidth) || 0)
+ (parseInt(style.paddingRight) || 0);
return this.$padding;
};
this.getRegion = function(point) {
var padding = this.$padding || this.$computePadding();
var rect = this.element.getBoundingClientRect();
if (point.x < padding.left + rect.left)
return "markers";
if (this.$showFoldWidgets && point.x > rect.right - padding.right)
return "foldWidgets";
};
}).call(Gutter.prototype);
function onCreateCell(element) {
var textNode = document.createTextNode('');
element.appendChild(textNode);
var foldWidget = dom.createElement("span");
element.appendChild(foldWidget);
return element;
}
exports.Gutter = Gutter;
});