Files
atom/vendor/ace/placeholder.js

215 lines
9.2 KiB
JavaScript

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* 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/
*
* 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):
* Zef Hemel <zef@c9.io>
*
* 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";
var Range = require('./range').Range;
var EventEmitter = require("./lib/event_emitter").EventEmitter;
var oop = require("./lib/oop");
var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) {
var _self = this;
this.length = length;
this.session = session;
this.doc = session.getDocument();
this.mainClass = mainClass;
this.othersClass = othersClass;
this.$onUpdate = this.onUpdate.bind(this);
this.doc.on("change", this.$onUpdate);
this.$others = others;
this.$onCursorChange = function() {
setTimeout(function() {
_self.onCursorChange();
});
};
this.$pos = pos;
// Used for reset
this.$undoStackDepth = session.getUndoManager().$undoStack ? session.getUndoManager().$undoStack.length : -1;
this.setup();
session.selection.on("changeCursor", this.$onCursorChange);
};
(function() {
oop.implement(this, EventEmitter);
this.setup = function() {
var _self = this;
var doc = this.doc;
var session = this.session;
var pos = this.$pos;
this.pos = doc.createAnchor(pos.row, pos.column);
this.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + this.length), this.mainClass, null, false);
this.pos.on("change", function(event) {
session.removeMarker(_self.markerId);
_self.markerId = session.addMarker(new Range(event.value.row, event.value.column, event.value.row, event.value.column+_self.length), _self.mainClass, null, false);
});
this.others = [];
this.$others.forEach(function(other) {
var anchor = doc.createAnchor(other.row, other.column);
_self.others.push(anchor);
});
session.setUndoSelect(false);
};
this.showOtherMarkers = function() {
if(this.othersActive) return;
var session = this.session;
var _self = this;
this.othersActive = true;
this.others.forEach(function(anchor) {
anchor.markerId = session.addMarker(new Range(anchor.row, anchor.column, anchor.row, anchor.column+_self.length), _self.othersClass, null, false);
anchor.on("change", function(event) {
session.removeMarker(anchor.markerId);
anchor.markerId = session.addMarker(new Range(event.value.row, event.value.column, event.value.row, event.value.column+_self.length), _self.othersClass, null, false);
});
});
};
this.hideOtherMarkers = function() {
if(!this.othersActive) return;
this.othersActive = false;
for (var i = 0; i < this.others.length; i++) {
this.session.removeMarker(this.others[i].markerId);
}
};
this.onUpdate = function(event) {
var delta = event.data;
var range = delta.range;
if(range.start.row !== range.end.row) return;
if(range.start.row !== this.pos.row) return;
if (this.$updating) return;
this.$updating = true;
var lengthDiff = delta.action === "insertText" ? range.end.column - range.start.column : range.start.column - range.end.column;
if(range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1) {
var distanceFromStart = range.start.column - this.pos.column;
this.length += lengthDiff;
if(!this.session.$fromUndo) {
if(delta.action === "insertText") {
for (var i = this.others.length - 1; i >= 0; i--) {
var otherPos = this.others[i];
var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart};
if(otherPos.row === range.start.row && range.start.column < otherPos.column)
newPos.column += lengthDiff;
this.doc.insert(newPos, delta.text);
}
} else if(delta.action === "removeText") {
for (var i = this.others.length - 1; i >= 0; i--) {
var otherPos = this.others[i];
var newPos = {row: otherPos.row, column: otherPos.column + distanceFromStart};
if(otherPos.row === range.start.row && range.start.column < otherPos.column)
newPos.column += lengthDiff;
this.doc.remove(new Range(newPos.row, newPos.column, newPos.row, newPos.column - lengthDiff));
}
}
// Special case: insert in beginning
if(range.start.column === this.pos.column && delta.action === "insertText") {
setTimeout(function() {
this.pos.setPosition(this.pos.row, this.pos.column - lengthDiff);
for (var i = 0; i < this.others.length; i++) {
var other = this.others[i];
var newPos = {row: other.row, column: other.column - lengthDiff};
if(other.row === range.start.row && range.start.column < other.column)
newPos.column += lengthDiff;
other.setPosition(newPos.row, newPos.column);
}
}.bind(this), 0);
}
else if(range.start.column === this.pos.column && delta.action === "removeText") {
setTimeout(function() {
for (var i = 0; i < this.others.length; i++) {
var other = this.others[i];
if(other.row === range.start.row && range.start.column < other.column) {
other.setPosition(other.row, other.column - lengthDiff);
}
}
}.bind(this), 0);
}
}
this.pos._emit("change", {value: this.pos});
for (var i = 0; i < this.others.length; i++) {
this.others[i]._emit("change", {value: this.others[i]});
}
}
this.$updating = false;
};
this.onCursorChange = function(event) {
if (this.$updating) return;
var pos = this.session.selection.getCursor();
if(pos.row === this.pos.row && pos.column >= this.pos.column && pos.column <= this.pos.column + this.length) {
this.showOtherMarkers();
this._emit("cursorEnter", event);
} else {
this.hideOtherMarkers();
this._emit("cursorLeave", event);
}
};
this.detach = function() {
this.session.removeMarker(this.markerId);
this.hideOtherMarkers();
this.doc.removeEventListener("change", this.$onUpdate);
this.session.selection.removeEventListener("changeCursor", this.$onCursorChange);
this.pos.detach();
for (var i = 0; i < this.others.length; i++) {
this.others[i].detach();
}
this.session.setUndoSelect(true);
};
this.cancel = function() {
if(this.$undoStackDepth === -1)
throw Error("Canceling placeholders only supported with undo manager attached to session.");
var undoManager = this.session.getUndoManager();
var undosRequired = undoManager.$undoStack.length - this.$undoStackDepth;
for (var i = 0; i < undosRequired; i++) {
undoManager.undo(true);
}
};
}).call(PlaceHolder.prototype);
exports.PlaceHolder = PlaceHolder;
});