Files
yjs/build/browser/index.js

2935 lines
91 KiB
JavaScript
Raw Normal View History

2014-08-12 19:13:42 +02:00
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var createIwcConnector;
2014-08-14 14:43:39 +02:00
createIwcConnector = function(callback, initial_user_id) {
2014-08-14 20:22:07 +02:00
var IwcConnector, duiClient, init, iwcHandler, received_HB;
2014-08-12 19:13:42 +02:00
iwcHandler = {};
duiClient = new DUIClient();
duiClient.connect(function(intent) {
var _ref;
return (_ref = iwcHandler[intent.action]) != null ? _ref.map(function(f) {
return setTimeout(function() {
return f(intent);
}, 0);
}) : void 0;
});
duiClient.initOK();
received_HB = null;
IwcConnector = (function() {
function IwcConnector(engine, HB, execution_listener, yatta) {
2014-08-14 19:33:10 +02:00
var receiveHB, receive_, sendHistoryBuffer, send_;
2014-08-12 19:13:42 +02:00
this.engine = engine;
this.HB = HB;
this.execution_listener = execution_listener;
this.yatta = yatta;
this.duiClient = duiClient;
this.iwcHandler = iwcHandler;
send_ = (function(_this) {
return function(o) {
2014-08-14 20:22:07 +02:00
if (Object.getOwnPropertyNames(_this.initialized).length !== 0) {
return _this.send(o);
}
2014-08-12 19:13:42 +02:00
};
})(this);
this.execution_listener.push(send_);
2014-08-14 20:22:07 +02:00
this.initialized = {};
2014-08-14 19:33:10 +02:00
receiveHB = (function(_this) {
return function(json) {
2014-08-14 20:22:07 +02:00
var him;
HB = json.extras.HB;
him = json.extras.user;
_this.engine.applyOpsCheckDouble(HB);
return _this.initialized[him] = true;
2014-08-14 19:33:10 +02:00
};
})(this);
iwcHandler["Yatta_push_HB_element"] = [receiveHB];
2014-08-14 20:22:07 +02:00
this.sendIwcIntent("Yatta_get_HB_element", {});
2014-08-12 19:13:42 +02:00
receive_ = (function(_this) {
return function(intent) {
var o;
o = intent.extras;
2014-08-14 20:22:07 +02:00
if (_this.initialized[o.uid.creator] != null) {
return _this.receive(o);
}
2014-08-12 19:13:42 +02:00
};
})(this);
this.iwcHandler["Yatta_new_operation"] = [receive_];
if (received_HB != null) {
this.engine.applyOpsCheckDouble(received_HB);
}
sendHistoryBuffer = (function(_this) {
return function() {
var json;
json = {
2014-08-14 20:22:07 +02:00
HB: _this.yatta.getHistoryBuffer()._encode(),
user: _this.yatta.getUserId()
2014-08-12 19:13:42 +02:00
};
return _this.sendIwcIntent("Yatta_push_HB_element", json);
};
})(this);
this.iwcHandler["Yatta_get_HB_element"] = [sendHistoryBuffer];
}
IwcConnector.prototype.send = function(o) {
if (o.uid.creator === this.HB.getUserId() && (typeof o.uid.op_number !== "string")) {
return this.sendIwcIntent("Yatta_new_operation", o);
}
};
IwcConnector.prototype.receive = function(o) {
if (o.uid.creator !== this.HB.getUserId()) {
return this.engine.applyOp(o);
}
};
IwcConnector.prototype.sendIwcIntent = function(action_name, content) {
var intent;
intent = {
action: action_name,
component: "",
data: "",
dataType: "",
2014-08-14 14:43:39 +02:00
flags: ["PUBLISH_GLOBAL"],
2014-08-12 19:13:42 +02:00
extras: content
};
return this.duiClient.sendIntent(intent);
};
return IwcConnector;
})();
init = function() {
2014-08-14 19:33:10 +02:00
var proposed_user_id;
proposed_user_id = null;
if (initial_user_id != null) {
proposed_user_id = initial_user_id;
} else {
proposed_user_id = Math.floor(Math.random() * 1000000);
}
return callback(IwcConnector, proposed_user_id);
2014-08-12 19:13:42 +02:00
};
2014-08-14 20:22:07 +02:00
setTimeout(init, 5000);
2014-08-12 19:13:42 +02:00
return void 0;
};
module.exports = createIwcConnector;
if (typeof window !== "undefined" && window !== null) {
2014-08-14 20:33:55 +02:00
if (window.Y == null) {
window.Y = {};
}
window.Y.createIwcConnector = createIwcConnector;
2014-08-12 19:13:42 +02:00
}
},{}],2:[function(require,module,exports){
var _;
_ = require("underscore");
module.exports = function(user_list) {
var TestConnector;
return TestConnector = (function() {
function TestConnector(engine, HB, execution_listener) {
var appliedOperationsListener, send_;
this.engine = engine;
this.HB = HB;
this.execution_listener = execution_listener;
send_ = (function(_this) {
return function(o) {
return _this.send(o);
};
})(this);
this.execution_listener.push(send_);
this.applied_operations = [];
appliedOperationsListener = (function(_this) {
return function(o) {
return _this.applied_operations.push(o);
};
})(this);
this.execution_listener.push(appliedOperationsListener);
if (!((user_list != null ? user_list.length : void 0) === 0)) {
this.engine.applyOps(user_list[0].getHistoryBuffer()._encode());
}
this.unexecuted = {};
}
TestConnector.prototype.getOpsInExecutionOrder = function() {
return this.applied_operations;
};
TestConnector.prototype.send = function(o) {
var user, _i, _len, _results;
if ((o.uid.creator === this.HB.getUserId()) && (typeof o.uid.op_number !== "string")) {
_results = [];
for (_i = 0, _len = user_list.length; _i < _len; _i++) {
user = user_list[_i];
if (user.getUserId() !== this.HB.getUserId()) {
_results.push(user.getConnector().receive(o));
} else {
_results.push(void 0);
}
}
return _results;
}
};
TestConnector.prototype.receive = function(o) {
var _base, _name;
if ((_base = this.unexecuted)[_name = o.uid.creator] == null) {
_base[_name] = [];
}
return this.unexecuted[o.uid.creator].push(o);
};
TestConnector.prototype.flushOne = function(user) {
var _ref;
if (((_ref = this.unexecuted[user]) != null ? _ref.length : void 0) > 0) {
return this.engine.applyOp(this.unexecuted[user].shift());
}
};
TestConnector.prototype.flushOneRandom = function() {
return this.flushOne(_.random(0, user_list.length - 1));
};
TestConnector.prototype.flushAll = function() {
var n, ops, _ref;
_ref = this.unexecuted;
for (n in _ref) {
ops = _ref[n];
this.engine.applyOps(ops);
}
return this.unexecuted = {};
};
return TestConnector;
})();
};
},{"underscore":12}],3:[function(require,module,exports){
var Engine;
Engine = (function() {
function Engine(HB, parser) {
this.HB = HB;
this.parser = parser;
this.unprocessed_ops = [];
}
Engine.prototype.parseOperation = function(json) {
var typeParser;
typeParser = this.parser[json.type];
if (typeParser != null) {
return typeParser(json);
} else {
throw new Error("You forgot to specify a parser for type " + json.type + ". The message is " + (JSON.stringify(json)) + ".");
}
};
Engine.prototype.applyOpsBundle = function(ops_json) {
var o, ops, _i, _j, _k, _len, _len1, _len2;
ops = [];
for (_i = 0, _len = ops_json.length; _i < _len; _i++) {
o = ops_json[_i];
ops.push(this.parseOperation(o));
}
for (_j = 0, _len1 = ops.length; _j < _len1; _j++) {
o = ops[_j];
this.HB.addOperation(o);
}
for (_k = 0, _len2 = ops.length; _k < _len2; _k++) {
o = ops[_k];
if (!o.execute()) {
this.unprocessed_ops.push(o);
}
}
return this.tryUnprocessed();
};
Engine.prototype.applyOpsCheckDouble = function(ops_json) {
var o, _i, _len, _results;
_results = [];
for (_i = 0, _len = ops_json.length; _i < _len; _i++) {
o = ops_json[_i];
2014-08-14 19:33:10 +02:00
if (this.HB.getOperation(o.uid) == null) {
2014-08-12 19:13:42 +02:00
_results.push(this.applyOp(o));
} else {
_results.push(void 0);
}
}
return _results;
};
Engine.prototype.applyOps = function(ops_json) {
var o, _i, _len, _results;
_results = [];
for (_i = 0, _len = ops_json.length; _i < _len; _i++) {
o = ops_json[_i];
_results.push(this.applyOp(o));
}
return _results;
};
Engine.prototype.applyOp = function(op_json) {
var o;
o = this.parseOperation(op_json);
this.HB.addToCounter(o);
if (!o.execute()) {
this.unprocessed_ops.push(o);
} else {
this.HB.addOperation(o);
}
return this.tryUnprocessed();
};
Engine.prototype.tryUnprocessed = function() {
var old_length, op, unprocessed, _i, _len, _ref, _results;
_results = [];
while (true) {
old_length = this.unprocessed_ops.length;
unprocessed = [];
_ref = this.unprocessed_ops;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
op = _ref[_i];
if (!op.execute()) {
unprocessed.push(op);
} else {
this.HB.addOperation(op);
}
}
this.unprocessed_ops = unprocessed;
if (this.unprocessed_ops.length === old_length) {
break;
} else {
_results.push(void 0);
}
}
return _results;
};
return Engine;
})();
module.exports = Engine;
},{}],4:[function(require,module,exports){
var Engine, HistoryBuffer, JsonYatta, json_types_uninitialized;
json_types_uninitialized = require("../Types/JsonTypes");
HistoryBuffer = require("../HistoryBuffer");
Engine = require("../Engine");
JsonYatta = (function() {
function JsonYatta(user_id, Connector) {
var first_word, json_types;
this.HB = new HistoryBuffer(user_id);
json_types = json_types_uninitialized(this.HB);
this.engine = new Engine(this.HB, json_types.parser);
this.connector = new Connector(this.engine, this.HB, json_types.execution_listener, this);
first_word = new json_types.types.JsonType(this.HB.getReservedUniqueIdentifier());
this.HB.addOperation(first_word).execute();
this.root_element = first_word;
}
JsonYatta.prototype.getRootElement = function() {
return this.root_element;
};
JsonYatta.prototype.getEngine = function() {
return this.engine;
};
JsonYatta.prototype.getConnector = function() {
return this.connector;
};
JsonYatta.prototype.getHistoryBuffer = function() {
return this.HB;
};
JsonYatta.prototype.setMutableDefault = function(mutable) {
return this.root_element.setMutableDefault(mutable);
};
JsonYatta.prototype.getUserId = function() {
return this.HB.getUserId();
};
JsonYatta.prototype.val = function(name, content, mutable) {
return this.root_element.val(name, content, mutable);
};
Object.defineProperty(JsonYatta.prototype, 'value', {
get: function() {
return this.root_element.value;
},
set: function(o) {
var o_name, o_obj, _results;
if (o.constructor === {}.constructor) {
_results = [];
for (o_name in o) {
o_obj = o[o_name];
_results.push(this.val(o_name, o_obj, 'immutable'));
}
return _results;
} else {
throw new Error("You must only set Object values!");
}
}
});
return JsonYatta;
})();
2014-08-14 20:33:55 +02:00
module.exports = JsonYatta;
2014-08-12 19:13:42 +02:00
if (typeof window !== "undefined" && window !== null) {
2014-08-14 20:33:55 +02:00
if (window.Y == null) {
window.Y = {};
}
window.Y.JsonYatta = JsonYatta;
2014-08-12 19:13:42 +02:00
}
},{"../Engine":3,"../HistoryBuffer":6,"../Types/JsonTypes":8}],5:[function(require,module,exports){
var Engine, HistoryBuffer, TextYatta, text_types_uninitialized;
text_types_uninitialized = require("../Types/TextTypes");
HistoryBuffer = require("../HistoryBuffer");
Engine = require("../Engine");
TextYatta = (function() {
function TextYatta(user_id, Connector) {
var first_word, text_types;
this.HB = new HistoryBuffer(user_id);
text_types = text_types_uninitialized(this.HB);
this.engine = new Engine(this.HB, text_types.parser);
this.connector = new Connector(this.engine, this.HB, text_types.execution_listener);
first_word = new text_types.types.Word(void 0);
this.HB.addOperation(first_word).execute();
this.root_element = first_word;
}
TextYatta.prototype.getRootElement = function() {
return this.root_element;
};
TextYatta.prototype.getEngine = function() {
return this.engine;
};
TextYatta.prototype.getConnector = function() {
return this.connector;
};
TextYatta.prototype.getHistoryBuffer = function() {
return this.HB;
};
TextYatta.prototype.getUserId = function() {
return this.HB.getUserId();
};
TextYatta.prototype.val = function() {
return this.root_element.val();
};
TextYatta.prototype.insertText = function(pos, content) {
return this.root_element.insertText(pos, content);
};
TextYatta.prototype.deleteText = function(pos, length) {
return this.root_element.deleteText(pos, length);
};
TextYatta.prototype.replaceText = function(text) {
return this.root_element.replaceText(text);
};
return TextYatta;
})();
module.exports = TextYatta;
2014-08-14 20:33:55 +02:00
if (typeof window !== "undefined" && window !== null) {
if (window.Y == null) {
window.Y = {};
}
window.Y.TextYatta = TextYatta;
}
2014-08-12 19:13:42 +02:00
},{"../Engine":3,"../HistoryBuffer":6,"../Types/TextTypes":10}],6:[function(require,module,exports){
var HistoryBuffer;
HistoryBuffer = (function() {
function HistoryBuffer(user_id) {
this.user_id = user_id;
this.operation_counter = {};
this.buffer = {};
this.change_listeners = [];
}
HistoryBuffer.prototype.getUserId = function() {
return this.user_id;
};
HistoryBuffer.prototype.getReservedUniqueIdentifier = function() {
return {
creator: '_',
op_number: '_'
};
};
HistoryBuffer.prototype.getOperationCounter = function() {
var ctn, res, user, _ref;
res = {};
_ref = this.operation_counter;
for (user in _ref) {
ctn = _ref[user];
res[user] = ctn;
}
return res;
};
HistoryBuffer.prototype._encode = function(state_vector) {
var json, o, o_json, o_next, o_number, o_prev, u_name, unknown, user, _ref;
if (state_vector == null) {
state_vector = {};
}
json = [];
unknown = function(user, o_number) {
if ((user == null) || (o_number == null)) {
throw new Error("dah!");
}
return (state_vector[user] == null) || state_vector[user] <= o_number;
};
_ref = this.buffer;
for (u_name in _ref) {
user = _ref[u_name];
for (o_number in user) {
o = user[o_number];
2014-08-14 20:33:55 +02:00
if ((!isNaN(parseInt(o_number))) && unknown(u_name, o_number)) {
2014-08-12 19:13:42 +02:00
o_json = o._encode();
if (o.next_cl != null) {
o_next = o.next_cl;
while ((o_next.next_cl != null) && unknown(o_next.creator, o_next.op_number)) {
o_next = o_next.next_cl;
}
o_json.next = o_next.getUid();
} else if (o.prev_cl != null) {
o_prev = o.prev_cl;
while ((o_prev.prev_cl != null) && unknown(o_next.creator, o_next.op_number)) {
o_prev = o_prev.prev_cl;
}
o_json.prev = o_prev.getUid();
}
json.push(o_json);
}
}
}
return json;
};
HistoryBuffer.prototype.getNextOperationIdentifier = function(user_id) {
var uid;
if (user_id == null) {
user_id = this.user_id;
}
if (this.operation_counter[user_id] == null) {
this.operation_counter[user_id] = 0;
}
uid = {
'creator': user_id,
'op_number': this.operation_counter[user_id]
};
this.operation_counter[user_id]++;
return uid;
};
HistoryBuffer.prototype.getOperation = function(uid) {
var _ref;
if (uid instanceof Object) {
return (_ref = this.buffer[uid.creator]) != null ? _ref[uid.op_number] : void 0;
} else if (uid == null) {
} else {
throw new Error("This type of uid is not defined!");
}
};
HistoryBuffer.prototype.addOperation = function(o) {
if (this.buffer[o.creator] == null) {
this.buffer[o.creator] = {};
}
if (this.buffer[o.creator][o.op_number] != null) {
throw new Error("You must not overwrite operations!");
}
this.buffer[o.creator][o.op_number] = o;
return o;
};
HistoryBuffer.prototype.addToCounter = function(o) {
if (this.operation_counter[o.creator] == null) {
this.operation_counter[o.creator] = 0;
}
if (typeof o.op_number === 'number' && o.creator !== this.getUserId()) {
return this.operation_counter[o.creator]++;
}
};
return HistoryBuffer;
})();
module.exports = HistoryBuffer;
},{}],7:[function(require,module,exports){
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
module.exports = function(HB) {
var Delete, Delimiter, ImmutableObject, Insert, Operation, execution_listener, parser;
parser = {};
execution_listener = [];
Operation = (function() {
function Operation(uid) {
if (uid == null) {
uid = HB.getNextOperationIdentifier();
}
this.creator = uid['creator'], this.op_number = uid['op_number'];
}
Operation.prototype.on = function(event, f) {
var _base;
if (this.event_listeners == null) {
this.event_listeners = {};
}
if ((_base = this.event_listeners)[event] == null) {
_base[event] = [];
}
return this.event_listeners[event].push(f);
};
Operation.prototype.callEvent = function(event, args) {
var f, _i, _len, _ref, _results;
if (this.event_listeners[event] != null) {
_ref = this.event_listeners[event];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
f = _ref[_i];
_results.push(f.call(this, event, args));
}
return _results;
}
};
Operation.prototype.setParent = function(o) {
return this.parent = o;
};
Operation.prototype.getUid = function() {
return {
'creator': this.creator,
'op_number': this.op_number
};
};
Operation.prototype.execute = function() {
var l, _i, _len;
this.is_executed = true;
for (_i = 0, _len = execution_listener.length; _i < _len; _i++) {
l = execution_listener[_i];
l(this._encode());
}
return this;
};
Operation.prototype.saveOperation = function(name, op) {
if ((op != null ? op.execute : void 0) != null) {
return this[name] = op;
} else if (op != null) {
if (this.unchecked == null) {
this.unchecked = {};
}
return this.unchecked[name] = op;
}
};
Operation.prototype.validateSavedOperations = function() {
var name, op, op_uid, success, uninstantiated, _ref;
uninstantiated = {};
success = this;
_ref = this.unchecked;
for (name in _ref) {
op_uid = _ref[name];
op = HB.getOperation(op_uid);
if (op) {
this[name] = op;
} else {
uninstantiated[name] = op_uid;
success = false;
}
}
delete this.unchecked;
if (!success) {
this.unchecked = uninstantiated;
}
return success;
};
return Operation;
})();
Delete = (function(_super) {
__extends(Delete, _super);
function Delete(uid, deletes) {
this.saveOperation('deletes', deletes);
Delete.__super__.constructor.call(this, uid);
}
Delete.prototype._encode = function() {
return {
'type': "Delete",
'uid': this.getUid(),
'deletes': this.deletes.getUid()
};
};
Delete.prototype.execute = function() {
if (this.validateSavedOperations()) {
this.deletes.applyDelete(this);
Delete.__super__.execute.apply(this, arguments);
return this;
} else {
return false;
}
};
return Delete;
})(Operation);
parser['Delete'] = function(o) {
var deletes_uid, uid;
uid = o['uid'], deletes_uid = o['deletes'];
return new Delete(uid, deletes_uid);
};
Insert = (function(_super) {
__extends(Insert, _super);
function Insert(uid, prev_cl, next_cl, origin) {
this.saveOperation('prev_cl', prev_cl);
this.saveOperation('next_cl', next_cl);
if (origin != null) {
this.saveOperation('origin', origin);
} else {
this.saveOperation('origin', prev_cl);
}
Insert.__super__.constructor.call(this, uid);
}
Insert.prototype.applyDelete = function(o) {
if (this.deleted_by == null) {
this.deleted_by = [];
}
return this.deleted_by.push(o);
};
Insert.prototype.isDeleted = function() {
var _ref;
return ((_ref = this.deleted_by) != null ? _ref.length : void 0) > 0;
};
Insert.prototype.getDistanceToOrigin = function() {
var d, o;
d = 0;
o = this.prev_cl;
while (true) {
if (this.origin === o) {
break;
}
d++;
if (this === this.prev_cl) {
throw new Error("this should not happen ;) ");
}
o = o.prev_cl;
}
return d;
};
Insert.prototype.update_sl = function() {
var o;
o = this.prev_cl;
({
update: function(dest_cl, dest_sl) {
var _results;
_results = [];
while (true) {
if (o.isDeleted()) {
_results.push(o = o[dest_cl]);
} else {
this[dest_sl] = o;
break;
}
}
return _results;
}
});
update("prev_cl", "prev_sl");
return update("next_cl", "prev_sl");
};
Insert.prototype.execute = function() {
var distance_to_origin, i, o, _ref, _ref1;
if (this.is_executed != null) {
return this;
}
if (!this.validateSavedOperations()) {
return false;
} else {
if (((_ref = this.prev_cl) != null ? _ref.validateSavedOperations() : void 0) && ((_ref1 = this.next_cl) != null ? _ref1.validateSavedOperations() : void 0) && this.prev_cl.next_cl !== this) {
distance_to_origin = 0;
o = this.prev_cl.next_cl;
i = 0;
while (true) {
if (o == null) {
console.log(JSON.stringify(this.prev_cl.getUid()));
console.log(JSON.stringify(this.next_cl.getUid()));
}
if (o !== this.next_cl) {
if (o.getDistanceToOrigin() === i) {
if (o.creator < this.creator) {
this.prev_cl = o;
distance_to_origin = i + 1;
} else {
}
} else if (o.getDistanceToOrigin() < i) {
if (i - distance_to_origin <= o.getDistanceToOrigin()) {
this.prev_cl = o;
distance_to_origin = i + 1;
} else {
}
} else {
break;
}
i++;
o = o.next_cl;
} else {
break;
}
}
this.next_cl = this.prev_cl.next_cl;
this.prev_cl.next_cl = this;
this.next_cl.prev_cl = this;
}
2014-08-14 19:33:10 +02:00
return Insert.__super__.execute.apply(this, arguments);
2014-08-12 19:13:42 +02:00
}
};
return Insert;
})(Operation);
ImmutableObject = (function(_super) {
__extends(ImmutableObject, _super);
function ImmutableObject(uid, content, prev, next, origin) {
this.content = content;
ImmutableObject.__super__.constructor.call(this, uid, prev, next, origin);
}
ImmutableObject.prototype.val = function() {
return this.content;
};
ImmutableObject.prototype._encode = function() {
var json;
json = {
'type': "ImmutableObject",
'uid': this.getUid(),
'content': this.content
};
if (this.prev_cl != null) {
json['prev'] = this.prev_cl.getUid();
}
if (this.next_cl != null) {
json['next'] = this.next_cl.getUid();
}
if ((this.origin != null) && this.origin !== this.prev_cl) {
json["origin"] = this.origin.getUid();
}
return json;
};
return ImmutableObject;
})(Insert);
parser['ImmutableObject'] = function(json) {
var content, next, origin, prev, uid;
uid = json['uid'], content = json['content'], prev = json['prev'], next = json['next'], origin = json['origin'];
return new ImmutableObject(uid, content, prev, next, origin);
};
Delimiter = (function(_super) {
__extends(Delimiter, _super);
function Delimiter(uid, prev_cl, next_cl, origin) {
this.saveOperation('prev_cl', prev_cl);
this.saveOperation('next_cl', next_cl);
this.saveOperation('origin', prev_cl);
Delimiter.__super__.constructor.call(this, uid);
}
Delimiter.prototype.isDeleted = function() {
return false;
};
Delimiter.prototype.execute = function() {
var _ref, _ref1;
if (((_ref = this.unchecked) != null ? _ref['next_cl'] : void 0) != null) {
return Delimiter.__super__.execute.apply(this, arguments);
} else if ((_ref1 = this.unchecked) != null ? _ref1['prev_cl'] : void 0) {
if (this.validateSavedOperations()) {
if (this.prev_cl.next_cl != null) {
throw new Error("Probably duplicated operations");
}
this.prev_cl.next_cl = this;
delete this.prev_cl.unchecked.next_cl;
return Delimiter.__super__.execute.apply(this, arguments);
} else {
return false;
}
} else if ((this.prev_cl != null) && (this.prev_cl.next_cl == null)) {
delete this.prev_cl.unchecked.next_cl;
return this.prev_cl.next_cl = this;
} else if ((this.prev_cl != null) || (this.next_cl != null)) {
return Delimiter.__super__.execute.apply(this, arguments);
} else {
throw new Error("Delimiter is unsufficient defined!");
}
};
Delimiter.prototype._encode = function() {
var _ref, _ref1;
return {
'type': "Delimiter",
'uid': this.getUid(),
'prev': (_ref = this.prev_cl) != null ? _ref.getUid() : void 0,
'next': (_ref1 = this.next_cl) != null ? _ref1.getUid() : void 0
};
};
return Delimiter;
})(Operation);
parser['Delimiter'] = function(json) {
var next, prev, uid;
uid = json['uid'], prev = json['prev'], next = json['next'];
return new Delimiter(uid, prev, next);
};
return {
'types': {
'Delete': Delete,
'Insert': Insert,
'Delimiter': Delimiter,
'Operation': Operation,
'ImmutableObject': ImmutableObject
},
'parser': parser,
'execution_listener': execution_listener
};
};
},{}],8:[function(require,module,exports){
var text_types_uninitialized,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
text_types_uninitialized = require("./TextTypes");
module.exports = function(HB) {
var JsonType, createJsonWrapper, parser, text_types, types;
text_types = text_types_uninitialized(HB);
types = text_types.types;
parser = text_types.parser;
createJsonWrapper = function(_jsonType) {
var JsonWrapper;
JsonWrapper = (function() {
function JsonWrapper(jsonType) {
var name, obj, _fn, _ref;
_ref = jsonType.map;
_fn = function(name, obj) {
return Object.defineProperty(JsonWrapper.prototype, name, {
get: function() {
var x;
x = obj.val();
if (x instanceof JsonType) {
return createJsonWrapper(x);
} else if (x instanceof types.ImmutableObject) {
return x.val();
} else {
return x;
}
},
set: function(o) {
var o_name, o_obj, overwrite, _results;
if (o.constructor === {}.constructor) {
overwrite = jsonType.val(name);
_results = [];
for (o_name in o) {
o_obj = o[o_name];
_results.push(overwrite.val(o_name, o_obj, 'immutable'));
}
return _results;
} else {
return jsonType.val(name, o, 'immutable');
}
},
enumerable: true,
configurable: false
});
};
for (name in _ref) {
obj = _ref[name];
_fn(name, obj);
}
}
return JsonWrapper;
})();
return new JsonWrapper(_jsonType);
};
JsonType = (function(_super) {
__extends(JsonType, _super);
function JsonType(uid, initial_value, mutable) {
var name, o;
JsonType.__super__.constructor.call(this, uid);
if (initial_value != null) {
if (typeof initial_value !== "object") {
throw new Error("The initial value of JsonTypes must be of type Object! (current type: " + (typeof initial_value) + ")");
}
for (name in initial_value) {
o = initial_value[name];
this.val(name, o, mutable);
}
}
}
JsonType.prototype.mutable_default = true;
JsonType.prototype.setMutableDefault = function(mutable) {
if (mutable === true || mutable === 'mutable') {
JsonType.prototype.mutable_default = true;
} else if (mutable === false || mutable === 'immutable') {
JsonType.prototype.mutable_default = false;
} else {
throw new Error('Set mutable either "mutable" or "immutable"!');
}
return 'OK';
};
JsonType.prototype.val = function(name, content, mutable) {
var json, o, o_name, obj, word;
if (typeof name === 'object') {
for (o_name in name) {
o = name[o_name];
this.val(o_name, o, content);
}
return this;
} else if ((name != null) && (content != null)) {
if (mutable != null) {
if (mutable === true || mutable === 'mutable') {
mutable = true;
} else {
mutable = false;
}
} else {
mutable = this.mutable_default;
}
if (typeof content === 'function') {
return this;
} else if (((!mutable) || typeof content === 'number') && content.constructor !== Object) {
obj = HB.addOperation(new types.ImmutableObject(void 0, content)).execute();
return JsonType.__super__.val.call(this, name, obj);
} else {
if (typeof content === 'string') {
word = HB.addOperation(new types.Word(void 0)).execute();
word.insertText(0, content);
return JsonType.__super__.val.call(this, name, word);
} else if (content.constructor === Object) {
json = HB.addOperation(new JsonType(void 0, content, mutable)).execute();
return JsonType.__super__.val.call(this, name, json);
} else {
throw new Error("You must not set " + (typeof content) + "-types in collaborative Json-objects!");
}
}
} else {
return JsonType.__super__.val.call(this, name, content);
}
};
Object.defineProperty(JsonType.prototype, 'value', {
get: function() {
return createJsonWrapper(this);
},
set: function(o) {
var o_name, o_obj, _results;
if (o.constructor === {}.constructor) {
_results = [];
for (o_name in o) {
o_obj = o[o_name];
_results.push(this.val(o_name, o_obj, 'immutable'));
}
return _results;
} else {
throw new Error("You must only set Object values!");
}
}
});
JsonType.prototype._encode = function() {
return {
'type': "JsonType",
'uid': this.getUid()
};
};
return JsonType;
})(types.MapManager);
parser['JsonType'] = function(json) {
var uid;
uid = json['uid'];
return new JsonType(uid);
};
types['JsonType'] = JsonType;
return text_types;
};
},{"./TextTypes":10}],9:[function(require,module,exports){
var basic_types_uninitialized,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
basic_types_uninitialized = require("./BasicTypes");
module.exports = function(HB) {
var AddName, ListManager, MapManager, ReplaceManager, Replaceable, basic_types, parser, types;
basic_types = basic_types_uninitialized(HB);
types = basic_types.types;
parser = basic_types.parser;
MapManager = (function(_super) {
__extends(MapManager, _super);
function MapManager(uid) {
this.map = {};
MapManager.__super__.constructor.call(this, uid);
}
MapManager.prototype.val = function(name, content) {
var o, obj, result, _ref, _ref1;
if (content != null) {
if (this.map[name] == null) {
HB.addOperation(new AddName(void 0, this, name)).execute();
}
this.map[name].replace(content);
return this;
} else if (name != null) {
obj = (_ref = this.map[name]) != null ? _ref.val() : void 0;
if (obj instanceof types.ImmutableObject) {
return obj.val();
} else {
return obj;
}
} else {
result = {};
_ref1 = this.map;
for (name in _ref1) {
o = _ref1[name];
obj = o.val();
if (obj instanceof types.ImmutableObject || obj instanceof MapManager) {
obj = obj.val();
}
result[name] = obj;
}
return result;
}
};
return MapManager;
})(types.Operation);
AddName = (function(_super) {
__extends(AddName, _super);
function AddName(uid, map_manager, name) {
this.name = name;
this.saveOperation('map_manager', map_manager);
AddName.__super__.constructor.call(this, uid);
}
AddName.prototype.execute = function() {
var beg, end, uid_beg, uid_end, uid_r;
if (!this.validateSavedOperations()) {
return false;
} else {
uid_r = this.map_manager.getUid();
uid_r.op_number = "_" + uid_r.op_number + "_RM_" + this.name;
if (HB.getOperation(uid_r) == null) {
uid_beg = this.map_manager.getUid();
uid_beg.op_number = "_" + uid_beg.op_number + "_RM_" + this.name + "_beginning";
uid_end = this.map_manager.getUid();
uid_end.op_number = "_" + uid_end.op_number + "_RM_" + this.name + "_end";
beg = HB.addOperation(new types.Delimiter(uid_beg, void 0, uid_end)).execute();
end = HB.addOperation(new types.Delimiter(uid_end, beg, void 0)).execute();
this.map_manager.map[this.name] = HB.addOperation(new ReplaceManager(void 0, uid_r, beg, end)).execute();
}
return AddName.__super__.execute.apply(this, arguments);
}
};
AddName.prototype._encode = function() {
return {
'type': "AddName",
'uid': this.getUid(),
'map_manager': this.map_manager.getUid(),
'name': this.name
};
};
return AddName;
})(types.Operation);
parser['AddName'] = function(json) {
var map_manager, name, uid;
map_manager = json['map_manager'], uid = json['uid'], name = json['name'];
return new AddName(uid, map_manager, name);
};
ListManager = (function(_super) {
__extends(ListManager, _super);
function ListManager(uid, beginning, end, prev, next, origin) {
if ((beginning != null) && (end != null)) {
this.saveOperation('beginning', beginning);
this.saveOperation('end', end);
} else {
this.beginning = HB.addOperation(new types.Delimiter(void 0, void 0, void 0));
this.end = HB.addOperation(new types.Delimiter(void 0, this.beginning, void 0));
this.beginning.next_cl = this.end;
this.beginning.execute();
this.end.execute();
}
ListManager.__super__.constructor.call(this, uid, prev, next, origin);
}
ListManager.prototype.getLastOperation = function() {
return this.end.prev_cl;
};
ListManager.prototype.getFirstOperation = function() {
return this.beginning.next_cl;
};
ListManager.prototype.toArray = function() {
var o, result;
o = this.beginning.next_cl;
result = [];
while (o !== this.end) {
result.push(o);
o = o.next_cl;
}
return result;
};
ListManager.prototype.getOperationByPosition = function(position) {
var o;
o = this.beginning.next_cl;
if (position > 0) {
while (true) {
o = o.next_cl;
if (!o.isDeleted()) {
position -= 1;
}
if (position === 0) {
break;
}
if (o instanceof types.Delimiter) {
throw new Error("position parameter exceeded the length of the document!");
}
}
}
return o;
};
return ListManager;
})(types.Insert);
ReplaceManager = (function(_super) {
__extends(ReplaceManager, _super);
function ReplaceManager(initial_content, uid, beginning, end, prev, next, origin) {
ReplaceManager.__super__.constructor.call(this, uid, beginning, end, prev, next, origin);
if (initial_content != null) {
this.replace(initial_content);
}
}
ReplaceManager.prototype.replace = function(content) {
var o, op;
o = this.getLastOperation();
op = new Replaceable(content, this, void 0, o, o.next_cl);
return HB.addOperation(op).execute();
};
ReplaceManager.prototype.val = function() {
var o;
o = this.getLastOperation();
if (o instanceof types.Delimiter) {
throw new Error("dtrn");
}
return o.val();
};
ReplaceManager.prototype._encode = function() {
var json;
json = {
'type': "ReplaceManager",
'uid': this.getUid(),
'beginning': this.beginning.getUid(),
'end': this.end.getUid()
};
if ((this.prev_cl != null) && (this.next_cl != null)) {
json['prev'] = this.prev_cl.getUid();
json['next'] = this.next_cl.getUid();
}
if ((this.origin != null) && this.origin !== this.prev_cl) {
json["origin"] = this.origin.getUid();
}
return json;
};
return ReplaceManager;
})(ListManager);
parser["ReplaceManager"] = function(json) {
var beginning, content, end, next, origin, prev, uid;
content = json['content'], uid = json['uid'], prev = json['prev'], next = json['next'], origin = json['origin'], beginning = json['beginning'], end = json['end'];
return new ReplaceManager(content, uid, beginning, end, prev, next, origin);
};
Replaceable = (function(_super) {
__extends(Replaceable, _super);
function Replaceable(content, parent, uid, prev, next, origin) {
this.saveOperation('content', content);
this.saveOperation('parent', parent);
if (!((prev != null) && (next != null) && (content != null))) {
throw new Error("You must define content, prev, and next for Replaceable-types!");
}
Replaceable.__super__.constructor.call(this, uid, prev, next, origin);
}
Replaceable.prototype.val = function() {
return this.content;
};
Replaceable.prototype.replace = function(content) {
return this.parent.replace(content);
};
Replaceable.prototype.execute = function() {
var _base;
if (!this.validateSavedOperations()) {
return false;
} else {
if (typeof (_base = this.content).setReplaceManager === "function") {
_base.setReplaceManager(this.parent);
}
2014-08-14 19:33:10 +02:00
return Replaceable.__super__.execute.apply(this, arguments);
2014-08-12 19:13:42 +02:00
}
};
Replaceable.prototype._encode = function() {
var json;
json = {
'type': "Replaceable",
'content': this.content.getUid(),
'ReplaceManager': this.parent.getUid(),
'prev': this.prev_cl.getUid(),
'next': this.next_cl.getUid(),
'uid': this.getUid()
};
if ((this.origin != null) && this.origin !== this.prev_cl) {
json["origin"] = this.origin.getUid();
}
return json;
};
return Replaceable;
})(types.Insert);
parser["Replaceable"] = function(json) {
var content, next, origin, parent, prev, uid;
content = json['content'], parent = json['ReplaceManager'], uid = json['uid'], prev = json['prev'], next = json['next'], origin = json['origin'];
return new Replaceable(content, parent, uid, prev, next, origin);
};
types['ListManager'] = ListManager;
types['MapManager'] = MapManager;
types['ReplaceManager'] = ReplaceManager;
types['Replaceable'] = Replaceable;
return basic_types;
};
},{"./BasicTypes":7}],10:[function(require,module,exports){
var structured_types_uninitialized,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
structured_types_uninitialized = require("./StructuredTypes");
module.exports = function(HB) {
var TextDelete, TextInsert, Word, parser, structured_types, types;
structured_types = structured_types_uninitialized(HB);
types = structured_types.types;
parser = structured_types.parser;
TextDelete = (function(_super) {
__extends(TextDelete, _super);
function TextDelete() {
return TextDelete.__super__.constructor.apply(this, arguments);
}
return TextDelete;
})(types.Delete);
parser["TextDelete"] = parser["Delete"];
TextInsert = (function(_super) {
__extends(TextInsert, _super);
function TextInsert(content, uid, prev, next, origin) {
this.content = content;
if (!((prev != null) && (next != null))) {
throw new Error("You must define prev, and next for TextInsert-types!");
}
TextInsert.__super__.constructor.call(this, uid, prev, next, origin);
}
TextInsert.prototype.getLength = function() {
if (this.isDeleted()) {
return 0;
} else {
return this.content.length;
}
};
TextInsert.prototype.val = function(current_position) {
if (this.isDeleted()) {
return "";
} else {
return this.content;
}
};
TextInsert.prototype._encode = function() {
var json;
json = {
'type': "TextInsert",
'content': this.content,
'uid': this.getUid(),
'prev': this.prev_cl.getUid(),
'next': this.next_cl.getUid()
};
if ((this.origin != null) && this.origin !== this.prev_cl) {
json["origin"] = this.origin.getUid();
}
return json;
};
return TextInsert;
})(types.Insert);
parser["TextInsert"] = function(json) {
var content, next, origin, prev, uid;
content = json['content'], uid = json['uid'], prev = json['prev'], next = json['next'], origin = json['origin'];
return new TextInsert(content, uid, prev, next, origin);
};
Word = (function(_super) {
__extends(Word, _super);
function Word(uid, beginning, end, prev, next, origin) {
Word.__super__.constructor.call(this, uid, beginning, end, prev, next, origin);
}
Word.prototype.insertText = function(position, content) {
var c, o, op, _i, _len, _results;
o = this.getOperationByPosition(position);
_results = [];
for (_i = 0, _len = content.length; _i < _len; _i++) {
c = content[_i];
op = new TextInsert(c, void 0, o.prev_cl, o);
_results.push(HB.addOperation(op).execute());
}
return _results;
};
Word.prototype.deleteText = function(position, length) {
var d, i, o, _i, _results;
o = this.getOperationByPosition(position);
_results = [];
for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) {
d = HB.addOperation(new TextDelete(void 0, o)).execute();
o = o.next_cl;
while (o.isDeleted()) {
if (o instanceof types.Delimiter) {
throw new Error("You can't delete more than there is..");
}
o = o.next_cl;
}
_results.push(d._encode());
}
return _results;
};
Word.prototype.replaceText = function(text) {
var word;
if (this.replace_manager != null) {
word = HB.addOperation(new Word(void 0)).execute();
word.insertText(0, text);
return this.replace_manager.replace(word);
} else {
throw new Error("This type is currently not maintained by a ReplaceManager!");
}
};
Word.prototype.val = function() {
var c, o;
c = (function() {
var _i, _len, _ref, _results;
_ref = this.toArray();
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
o = _ref[_i];
if (o.val != null) {
_results.push(o.val());
} else {
_results.push("");
}
}
return _results;
}).call(this);
return c.join('');
};
Word.prototype.setReplaceManager = function(op) {
this.saveOperation('replace_manager', op);
return this.validateSavedOperations;
};
Word.prototype._encode = function() {
var json;
json = {
'type': "Word",
'uid': this.getUid(),
'beginning': this.beginning.getUid(),
'end': this.end.getUid()
};
if (this.prev_cl != null) {
json['prev'] = this.prev_cl.getUid();
}
if (this.next_cl != null) {
json['next'] = this.next_cl.getUid();
}
if ((this.origin != null) && this.origin !== this.prev_cl) {
json["origin"] = this.origin.getUid();
}
return json;
};
return Word;
})(types.ListManager);
parser['Word'] = function(json) {
var beginning, end, next, origin, prev, uid;
uid = json['uid'], beginning = json['beginning'], end = json['end'], prev = json['prev'], next = json['next'], origin = json['origin'];
return new Word(uid, beginning, end, prev, next, origin);
};
types['TextInsert'] = TextInsert;
types['TextDelete'] = TextDelete;
types['Word'] = Word;
return structured_types;
};
},{"./StructuredTypes":9}],11:[function(require,module,exports){
exports['IwcConnector'] = require('./Connectors/IwcConnector');
exports['TestConnector'] = require('./Connectors/TestConnector');
exports['JsonYatta'] = require('./Frameworks/JsonYatta');
exports['TextYatta'] = require('./Frameworks/TextYatta');
},{"./Connectors/IwcConnector":1,"./Connectors/TestConnector":2,"./Frameworks/JsonYatta":4,"./Frameworks/TextYatta":5}],12:[function(require,module,exports){
// Underscore.js 1.6.0
// http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.6.0';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
return obj;
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
any(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, function(value, index, list) {
return !predicate.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
each(obj, function(value, index, list) {
if (!(result = result && predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
each(obj, function(value, index, list) {
if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
return _.filter(obj, _.matches(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matches(attrs));
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
var result = -Infinity, lastComputed = -Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed > lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
var result = Infinity, lastComputed = Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed < lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherYates_shuffle).
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (n == null || guard) {
if (obj.length !== +obj.length) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
if (value == null) return _.identity;
if (_.isFunction(value)) return value;
return _.property(value);
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
iterator = lookupIterator(iterator);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, iterator, context) {
var result = {};
iterator = lookupIterator(iterator);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, key, value) {
_.has(result, key) ? result[key].push(value) : result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, key, value) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, key) {
_.has(result, key) ? result[key]++ : result[key] = 1;
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[0];
if (n < 0) return [];
return slice.call(array, 0, n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[array.length - 1];
return slice.call(array, Math.max(array.length - n, 0));
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Split an array into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(array, predicate) {
var pass = [], fail = [];
each(array, function(elem) {
(predicate(elem) ? pass : fail).push(elem);
});
return [pass, fail];
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.contains(other, item);
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var length = _.max(_.pluck(arguments, 'length').concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, length = list.length; i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(length);
while(idx < length) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
_.partial = function(func) {
var boundArgs = slice.call(arguments, 1);
return function() {
var position = 0;
var args = boundArgs.slice();
for (var i = 0, length = args.length; i < length; i++) {
if (args[i] === _) args[i] = arguments[position++];
}
while (position < arguments.length) args.push(arguments[position++]);
return func.apply(this, args);
};
};
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error('bindAll must be passed function names');
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
context = args = null;
};
return function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
var last = _.now() - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return _.partial(wrapper, func);
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = new Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
_.constant = function(value) {
return function () {
return value;
};
};
_.property = function(key) {
return function(obj) {
return obj[key];
};
};
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) {
return function(obj) {
if (obj === attrs) return true; //avoid comparing an object to itself.
for (var key in attrs) {
if (attrs[key] !== obj[key])
return false;
}
return true;
}
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() { return new Date().getTime(); };
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;'
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
}).call(this);
2014-08-14 19:33:10 +02:00
},{}]},{},[11])