// resources (26):
// [function] ../../src/basis/data/dataset.js -> d.js
// [function] library.js -> 0.js
// [function] ../../src/basis/l10n.js -> 2.js
// [function] ../../src/basis/event.js -> 3.js
// [function] ../../src/basis/data.js -> 4.js
// [function] ../../src/basis/dom/wrapper.js -> 5.js
// [function] ../../src/basis/template.js -> 6.js
// [function] ../../src/basis/template/html.js -> 7.js
// [function] ../../src/basis/dom/event.js -> 8.js
// [function] ../../src/basis/template/htmlfgen.js -> 9.js
// [function] ../../src/basis/dragdrop.js -> a.js
// [function] ../../src/basis/dom/computedStyle.js -> b.js
// [function] ../../src/basis/layout.js -> c.js
// [function] ../../src/basis/ui.js -> 1.js
// [function] ../../src/basis/data/value.js -> e.js
// [function] ../../src/basis/data/index.js -> f.js
// [function] ../../src/basis/data/object.js -> g.js
// [function] ../../src/basis/entity.js -> h.js
// [function] ../../src/basis/net/jsonp.js -> i.js
// [function] ../../src/basis/net.js -> j.js
// [function] ../../src/basis/net/service.js -> k.js
// [function] ../../src/basis/net/ajax.js -> l.js
// [function] ../../src/basis/ua.js -> m.js
// [function] ../../src/basis/net/action.js -> n.js
// [function] ../../src/basis/router.js -> o.js
// [function] ../../src/basis/app.js -> p.js
//
// filelist (1):
// library.js
(function(){
"use strict";
var __namespace_map__ = {"0.js":"library","1.js":"basis.ui","2.js":"basis.l10n","3.js":"basis.event","4.js":"basis.data","5.js":"basis.dom.wrapper","6.js":"basis.template","7.js":"basis.template.html","8.js":"basis.dom.event","9.js":"basis.template.htmlfgen","a.js":"basis.dragdrop","b.js":"basis.dom.computedStyle","c.js":"basis.layout","d.js":"basis.data.dataset","e.js":"basis.data.value","f.js":"basis.data.index","g.js":"basis.data.object","h.js":"basis.entity","i.js":"basis.net.jsonp","j.js":"basis.net","k.js":"basis.net.service","l.js":"basis.net.ajax","m.js":"basis.ua","n.js":"basis.net.action","o.js":"basis.router","p.js":"basis.app"};
var library;
var __resources__ = {
"d.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./3.js");
basis.require("./4.js");
var namespace = this.path;
var Class = basis.Class;
var oneFunctionProperty = Class.oneFunctionProperty;
var extend = basis.object.extend;
var values = basis.object.values;
var getter = basis.getter;
var $self = basis.fn.$self;
var $true = basis.fn.$true;
var $false = basis.fn.$false;
var $undef = basis.fn.$undef;
var arrayFrom = basis.array.from;
var createEvent = basis.event.create;
var SUBSCRIPTION = basis.data.SUBSCRIPTION;
var DataObject = basis.data.Object;
var KeyObjectMap = basis.data.KeyObjectMap;
var ReadOnlyDataset = basis.data.ReadOnlyDataset;
var Dataset = basis.data.Dataset;
var DatasetWrapper = basis.data.DatasetWrapper;
var setAccumulateState = Dataset.setAccumulateState;
SUBSCRIPTION.add("SOURCE", {
sourceChanged: function(object, oldSource) {
if (oldSource) SUBSCRIPTION.unlink("source", object, oldSource);
if (object.source) SUBSCRIPTION.link("source", object, object.source);
},
sourcesChanged: function(object, delta) {
var array;
if (array = delta.inserted) for (var i = 0, item; item = array[i]; i++) SUBSCRIPTION.link("source", object, array[i]);
if (array = delta.deleted) for (var i = 0, item; item = array[i]; i++) SUBSCRIPTION.unlink("source", object, array[i]);
}
}, function(action, object) {
var sources = object.sources || (object.source ? [ object.source ] : []);
for (var i = 0, source; source = sources[i++]; ) action("source", object, source);
});
SUBSCRIPTION.addProperty("minuend");
SUBSCRIPTION.addProperty("subtrahend");
function getDelta(inserted, deleted) {
var delta = {};
var result;
if (inserted && inserted.length) result = delta.inserted = inserted;
if (deleted && deleted.length) result = delta.deleted = deleted;
if (result) return delta;
}
function createRuleEvents(fn, events) {
return function createRuleEventsExtend(events) {
if (!events) return null;
if (events.__extend__) return events;
if (typeof events != "string" && !Array.isArray(events)) events = null;
return extend(basis.event.createHandler(events, fn), {
__extend__: createRuleEventsExtend
});
}(events);
}
function createKeyMap(config, keyGetter, ItemClass, SubsetClass) {
return new KeyObjectMap(extend({
keyGetter: keyGetter,
itemClass: ItemClass,
create: function(key, object) {
var datasetWrapper = KeyObjectMap.prototype.create.call(this, key, object);
datasetWrapper.ruleValue = key;
datasetWrapper.setDataset(new SubsetClass({
ruleValue: key
}));
return datasetWrapper;
}
}, config));
}
var MERGE_DATASET_HANDLER = {
itemsChanged: function(source, delta) {
var memberMap = this.members_;
var updated = {};
var object;
var objectId;
if (delta.inserted) {
for (var i = 0; object = delta.inserted[i]; i++) {
objectId = object.basisObjectId;
if (memberMap[objectId]) {
memberMap[objectId].count++;
} else {
memberMap[objectId] = {
count: 1,
object: object
};
}
updated[objectId] = memberMap[objectId];
}
}
if (delta.deleted) {
for (var i = 0; object = delta.deleted[i]; i++) {
objectId = object.basisObjectId;
updated[objectId] = memberMap[objectId];
memberMap[objectId].count--;
}
}
this.applyRule(updated);
}
};
var Merge = Class(ReadOnlyDataset, {
className: namespace + ".Merge",
subscribeTo: SUBSCRIPTION.SOURCE,
emit_sourcesChanged: createEvent("sourcesChanged", "delta"),
sources: null,
sourceValues_: null,
sourcesMap_: null,
sourceDelta_: null,
rule: function(count, sourceCount) {
return count > 0;
},
emit_ruleChanged: createEvent("ruleChanged", "oldRule"),
listen: {
source: MERGE_DATASET_HANDLER,
sourceValue: {
destroy: function(sender) {
this.removeSource(sender);
}
}
},
init: function() {
ReadOnlyDataset.prototype.init.call(this);
var sources = this.sources;
this.sources = [];
this.sourcesMap_ = {};
this.sourceValues_ = [];
if (sources) this.setSources(sources);
},
setRule: function(rule) {
rule = getter(rule || Merge.UNION);
if (this.rule !== rule) {
var oldRule = this.rule;
this.rule = rule;
this.emit_ruleChanged(oldRule);
return this.applyRule();
}
},
applyRule: function(scope) {
var memberMap = this.members_;
var rule = this.rule;
var sourceCount = this.sources.length;
var inserted = [];
var deleted = [];
var memberCounter;
var isMember;
var delta;
if (!scope) scope = memberMap;
for (var objectId in scope) {
memberCounter = memberMap[objectId];
isMember = sourceCount && memberCounter.count && rule(memberCounter.count, sourceCount);
if (isMember != objectId in this.items_) {
if (isMember) inserted.push(memberCounter.object); else deleted.push(memberCounter.object);
}
if (memberCounter.count == 0) delete memberMap[objectId];
}
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
return delta;
},
addDataset_: function(dataset) {
this.sources.push(dataset);
if (this.listen.source) dataset.addHandler(this.listen.source, this);
var memberMap = this.members_;
for (var objectId in dataset.items_) {
if (memberMap[objectId]) {
memberMap[objectId].count++;
} else {
memberMap[objectId] = {
count: 1,
object: dataset.items_[objectId]
};
}
}
return true;
},
removeDataset_: function(dataset) {
basis.array.remove(this.sources, dataset);
if (this.listen.source) dataset.removeHandler(this.listen.source, this);
var memberMap = this.members_;
for (var objectId in dataset.items_) memberMap[objectId].count--;
},
updateDataset_: function(source) {
var merge = this.owner;
var sourcesMap_ = merge.sourcesMap_;
var dataset = basis.data.resolveDataset(this, merge.updateDataset_, source, "adapter");
var inserted;
var deleted;
var delta;
if (this.dataset === dataset) return;
if (dataset) {
var count = (sourcesMap_[dataset.basisObjectId] || 0) + 1;
sourcesMap_[dataset.basisObjectId] = count;
if (count == 1) {
merge.addDataset_(dataset);
inserted = [ dataset ];
}
}
if (this.dataset) {
var count = (sourcesMap_[this.dataset.basisObjectId] || 0) - 1;
sourcesMap_[this.dataset.basisObjectId] = count;
if (count == 0) {
merge.removeDataset_(this.dataset);
deleted = [ this.dataset ];
}
}
this.dataset = dataset;
merge.applyRule();
if (delta = getDelta(inserted, deleted)) {
var setSourcesTransaction = merge.sourceDelta_;
if (setSourcesTransaction) {
if (delta.inserted) delta.inserted.forEach(function(source) {
if (!basis.array.remove(this.deleted, source)) basis.array.add(this.inserted, source);
}, setSourcesTransaction);
if (delta.deleted) delta.deleted.forEach(function(source) {
if (!basis.array.remove(this.inserted, source)) basis.array.add(this.deleted, source);
}, setSourcesTransaction);
} else {
merge.emit_sourcesChanged(delta);
}
}
return delta;
},
getSourceValues: function() {
return this.sourceValues_.map(function(item) {
return item.source;
});
},
addSource: function(source) {
if (!source || typeof source != "object" && typeof source != "function") {
basis.dev.warn(this.constructor.className + ".addSource: value should be a dataset instance or to be able to resolve in dataset");
return;
}
if (this.hasSource(source)) {
basis.dev.warn(this.constructor.className + ".addSource: value is already in source list");
return;
}
var sourceInfo = {
owner: this,
source: source,
adapter: null,
dataset: null
};
this.sourceValues_.push(sourceInfo);
this.updateDataset_.call(sourceInfo, source);
if (this.listen.sourceValue && source instanceof basis.event.Emitter) source.addHandler(this.listen.sourceValue, this);
},
removeSource: function(source) {
for (var i = 0, sourceInfo; sourceInfo = this.sourceValues_[i]; i++) if (sourceInfo.source === source) {
if (this.listen.sourceValue && source instanceof basis.event.Emitter) source.removeHandler(this.listen.sourceValue, this);
this.updateDataset_.call(sourceInfo, null);
this.sourceValues_.splice(i, 1);
return;
}
basis.dev.warn(this.constructor.className + ".removeSource: source value isn't found in source list");
},
hasSource: function(source) {
for (var i = 0, sourceInfo; sourceInfo = this.sourceValues_[i]; i++) if (sourceInfo.source === source) return true;
return false;
},
setSources: function(sources) {
var exists = this.sourceValues_.map(function(sourceInfo) {
return sourceInfo.source;
});
var inserted = [];
var deleted = [];
var delta;
if (!sources) sources = [];
this.sourceDelta_ = {
inserted: inserted,
deleted: deleted
};
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
if (!basis.array.remove(exists, source)) this.addSource(source);
}
exists.forEach(this.removeSource, this);
this.sourceDelta_ = null;
if (delta = getDelta(inserted, deleted)) this.emit_sourcesChanged(delta);
return delta;
},
destroy: function() {
this.setSources();
ReadOnlyDataset.prototype.destroy.call(this);
this.sourceValues_ = null;
this.sourcesMap_ = null;
this.sourceDelta_ = null;
this.sources = null;
}
});
Merge.UNION = Merge.prototype.rule;
Merge.INTERSECTION = function(count, sourceCount) {
return count == sourceCount;
};
Merge.DIFFERENCE = function(count, sourceCount) {
return count == 1;
};
Merge.MORE_THAN_ONE_INCLUDE = function(count, sourceCount) {
return sourceCount == 1 || count > 1;
};
Merge.AT_LEAST_ONE_EXCLUDE = function(count, sourceCount) {
return sourceCount == 1 || count < sourceCount;
};
var datasetAbsentFilter = function(item) {
return !this.has(item);
};
var SUBTRACTDATASET_MINUEND_HANDLER = {
itemsChanged: function(dataset, delta) {
if (!this.subtrahend) return;
var newDelta = getDelta(delta.inserted && delta.inserted.filter(datasetAbsentFilter, this.subtrahend), delta.deleted && delta.deleted.filter(this.has, this));
if (newDelta) this.emit_itemsChanged(newDelta);
},
destroy: function() {
if (!this.minuendAdapter_) this.setMinuend(null);
}
};
var SUBTRACTDATASET_SUBTRAHEND_HANDLER = {
itemsChanged: function(dataset, delta) {
if (!this.minuend) return;
var newDelta = getDelta(delta.deleted && delta.deleted.filter(this.minuend.has, this.minuend), delta.inserted && delta.inserted.filter(this.has, this));
if (newDelta) this.emit_itemsChanged(newDelta);
},
destroy: function() {
if (!this.subtrahendAdapter_) this.setSubtrahend(null);
}
};
var Subtract = Class(ReadOnlyDataset, {
className: namespace + ".Subtract",
subscribeTo: SUBSCRIPTION.MINUEND + SUBSCRIPTION.SUBTRAHEND,
minuend: null,
minuendAdapter_: null,
emit_minuendChanged: createEvent("minuendChanged", "oldMinuend"),
subtrahend: null,
subtrahendAdapter_: null,
emit_subtrahendChanged: createEvent("subtrahendChanged", "oldSubtrahend"),
listen: {
minuend: SUBTRACTDATASET_MINUEND_HANDLER,
subtrahend: SUBTRACTDATASET_SUBTRAHEND_HANDLER
},
init: function() {
ReadOnlyDataset.prototype.init.call(this);
var minuend = this.minuend;
var subtrahend = this.subtrahend;
this.minuend = null;
this.subtrahend = null;
if (minuend || subtrahend) this.setOperands(minuend, subtrahend);
},
setOperands: function(minuend, subtrahend) {
var delta;
var operandsChanged = false;
minuend = basis.data.resolveDataset(this, this.setMinuend, minuend, "minuendAdapter_");
subtrahend = basis.data.resolveDataset(this, this.setSubtrahend, subtrahend, "subtrahendAdapter_");
var oldMinuend = this.minuend;
var oldSubtrahend = this.subtrahend;
if (oldMinuend !== minuend) {
operandsChanged = true;
this.minuend = minuend;
var listenHandler = this.listen.minuend;
if (listenHandler) {
if (oldMinuend) oldMinuend.removeHandler(listenHandler, this);
if (minuend) minuend.addHandler(listenHandler, this);
}
this.emit_minuendChanged(oldMinuend);
}
if (oldSubtrahend !== subtrahend) {
operandsChanged = true;
this.subtrahend = subtrahend;
var listenHandler = this.listen.subtrahend;
if (listenHandler) {
if (oldSubtrahend) oldSubtrahend.removeHandler(listenHandler, this);
if (subtrahend) subtrahend.addHandler(listenHandler, this);
}
this.emit_subtrahendChanged(oldSubtrahend);
}
if (!operandsChanged) return false;
if (!minuend || !subtrahend) {
if (this.itemCount) this.emit_itemsChanged(delta = {
deleted: this.getItems()
});
} else {
var deleted = [];
var inserted = [];
for (var key in this.items_) if (!minuend.items_[key] || subtrahend.items_[key]) deleted.push(this.items_[key]);
for (var key in minuend.items_) if (!this.items_[key] && !subtrahend.items_[key]) inserted.push(minuend.items_[key]);
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
}
return delta;
},
setMinuend: function(minuend) {
return this.setOperands(minuend, this.subtrahendAdapter_ ? this.subtrahendAdapter_.source : this.subtrahend);
},
setSubtrahend: function(subtrahend) {
return this.setOperands(this.minuendAdapter_ ? this.minuendAdapter_.source : this.minuend, subtrahend);
},
destroy: function() {
this.setOperands();
ReadOnlyDataset.prototype.destroy.call(this);
}
});
var SourceDataset = Class(ReadOnlyDataset, {
className: namespace + ".SourceDataset",
subscribeTo: SUBSCRIPTION.SOURCE,
source: null,
emit_sourceChanged: createEvent("sourceChanged", "oldSource"),
sourceAdapter_: null,
sourceMap_: null,
listen: {
source: {
destroy: function() {
if (!this.sourceAdapter_) this.setSource();
}
}
},
init: function() {
this.sourceMap_ = {};
ReadOnlyDataset.prototype.init.call(this);
var source = this.source;
if (source) {
this.source = null;
this.setSource(source);
}
},
setSource: function(source) {
source = basis.data.resolveDataset(this, this.setSource, source, "sourceAdapter_");
if (this.source !== source) {
var oldSource = this.source;
var listenHandler = this.listen.source;
this.source = source;
if (listenHandler) {
var itemsChangedHandler = listenHandler.itemsChanged;
setAccumulateState(true);
if (oldSource) {
oldSource.removeHandler(listenHandler, this);
if (itemsChangedHandler) itemsChangedHandler.call(this, oldSource, {
deleted: oldSource.getItems()
});
}
if (source) {
source.addHandler(listenHandler, this);
if (itemsChangedHandler) itemsChangedHandler.call(this, source, {
inserted: source.getItems()
});
}
setAccumulateState(false);
}
this.emit_sourceChanged(oldSource);
}
},
destroy: function() {
this.setSource();
ReadOnlyDataset.prototype.destroy.call(this);
this.sourceMap_ = null;
}
});
var MAPFILTER_SOURCEOBJECT_UPDATE = function(sourceObject) {
var newMember = this.map ? this.map(sourceObject) : object;
if (newMember instanceof DataObject == false || this.filter(newMember)) newMember = null;
var sourceMap = this.sourceMap_[sourceObject.basisObjectId];
var curMember = sourceMap.member;
if (curMember !== newMember) {
var memberMap = this.members_;
var delta;
var inserted;
var deleted;
sourceMap.member = newMember;
if (curMember) {
var curMemberId = curMember.basisObjectId;
if (this.removeMemberRef) this.removeMemberRef(curMember, sourceObject);
if (--memberMap[curMemberId] == 0) {
delete memberMap[curMemberId];
deleted = [ curMember ];
}
}
if (newMember) {
var newMemberId = newMember.basisObjectId;
if (this.addMemberRef) this.addMemberRef(newMember, sourceObject);
if (memberMap[newMemberId]) {
memberMap[newMemberId]++;
} else {
memberMap[newMemberId] = 1;
inserted = [ newMember ];
}
}
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
}
};
var MAPFILTER_SOURCE_HANDLER = {
itemsChanged: function(source, delta) {
var sourceMap = this.sourceMap_;
var memberMap = this.members_;
var inserted = [];
var deleted = [];
var sourceObject;
var sourceObjectId;
var member;
var updateHandler = this.ruleEvents;
setAccumulateState(true);
if (delta.inserted) {
for (var i = 0; sourceObject = delta.inserted[i]; i++) {
member = this.map ? this.map(sourceObject) : sourceObject;
if (member instanceof DataObject == false || this.filter(member)) member = null;
if (updateHandler) sourceObject.addHandler(updateHandler, this);
sourceMap[sourceObject.basisObjectId] = {
sourceObject: sourceObject,
member: member
};
if (member) {
var memberId = member.basisObjectId;
if (memberMap[memberId]) {
memberMap[memberId]++;
} else {
memberMap[memberId] = 1;
inserted.push(member);
}
if (this.addMemberRef) this.addMemberRef(member, sourceObject);
}
}
}
if (delta.deleted) {
for (var i = 0; sourceObject = delta.deleted[i]; i++) {
sourceObjectId = sourceObject.basisObjectId;
member = sourceMap[sourceObjectId].member;
if (updateHandler) sourceObject.removeHandler(updateHandler, this);
delete sourceMap[sourceObjectId];
if (member) {
var memberId = member.basisObjectId;
if (--memberMap[memberId] == 0) {
delete memberMap[memberId];
deleted.push(member);
}
if (this.removeMemberRef) this.removeMemberRef(member, sourceObject);
}
}
}
setAccumulateState(false);
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
}
};
var MapFilter = Class(SourceDataset, {
className: namespace + ".MapFilter",
map: $self,
filter: $false,
rule: getter($true),
emit_ruleChanged: createEvent("ruleChanged", "oldRule"),
ruleEvents: createRuleEvents(MAPFILTER_SOURCEOBJECT_UPDATE, "update"),
addMemberRef: null,
removeMemberRef: null,
listen: {
source: MAPFILTER_SOURCE_HANDLER
},
setMap: function(map) {
if (typeof map != "function") map = $self;
if (this.map !== map) {
this.map = map;
return this.applyRule();
}
},
setFilter: function(filter) {
if (typeof filter != "function") filter = $false;
if (this.filter !== filter) {
this.filter = filter;
return this.applyRule();
}
},
setRule: function(rule) {
rule = getter(rule || $true);
if (this.rule !== rule) {
var oldRule = this.rule;
this.rule = rule;
this.emit_ruleChanged(oldRule);
return this.applyRule();
}
},
applyRule: function() {
var sourceMap = this.sourceMap_;
var memberMap = this.members_;
var curMember;
var newMember;
var curMemberId;
var newMemberId;
var sourceObject;
var sourceObjectInfo;
var inserted = [];
var deleted = [];
var delta;
for (var sourceObjectId in sourceMap) {
sourceObjectInfo = sourceMap[sourceObjectId];
sourceObject = sourceObjectInfo.sourceObject;
curMember = sourceObjectInfo.member;
newMember = this.map ? this.map(sourceObject) : sourceObject;
if (newMember instanceof DataObject == false || this.filter(newMember)) newMember = null;
if (curMember != newMember) {
sourceObjectInfo.member = newMember;
if (curMember) {
curMemberId = curMember.basisObjectId;
if (this.removeMemberRef) this.removeMemberRef(curMember, sourceObject);
memberMap[curMemberId]--;
}
if (newMember) {
newMemberId = newMember.basisObjectId;
if (this.addMemberRef) this.addMemberRef(newMember, sourceObject);
if (newMemberId in memberMap) {
memberMap[newMemberId]++;
} else {
memberMap[newMemberId] = 1;
inserted.push(newMember);
}
}
}
}
for (curMemberId in this.items_) if (memberMap[curMemberId] == 0) {
delete memberMap[curMemberId];
deleted.push(this.items_[curMemberId]);
}
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
return delta;
}
});
var Filter = Class(MapFilter, {
className: namespace + ".Filter",
filter: function(object) {
return !this.rule(object);
}
});
var Split = Class(MapFilter, {
className: namespace + ".Split",
subsetClass: ReadOnlyDataset,
subsetWrapperClass: DatasetWrapper,
keyMap: null,
map: function(sourceObject) {
return this.keyMap.resolve(sourceObject);
},
rule: getter($undef),
setRule: function(rule) {
rule = getter(rule || $undef);
if (this.rule !== rule) {
var oldRule = this.rule;
this.rule = rule;
this.keyMap.keyGetter = rule;
this.emit_ruleChanged(oldRule);
return this.applyRule();
}
},
addMemberRef: function(wrapper, sourceObject) {
wrapper.dataset.emit_itemsChanged({
inserted: [ sourceObject ]
});
},
removeMemberRef: function(wrapper, sourceObject) {
wrapper.dataset.emit_itemsChanged({
deleted: [ sourceObject ]
});
},
init: function() {
if (!this.keyMap || this.keyMap instanceof KeyObjectMap == false) this.keyMap = createKeyMap(this.keyMap, this.rule, this.subsetWrapperClass, this.subsetClass);
MapFilter.prototype.init.call(this);
},
getSubset: function(data, autocreate) {
return this.keyMap.get(data, autocreate);
},
destroy: function() {
MapFilter.prototype.destroy.call(this);
this.keyMap.destroy();
this.keyMap = null;
}
});
function binarySearchPos(array, map) {
if (!array.length) return 0;
var value = map.value;
var id = map.object.basisObjectId;
var cmpValue;
var cmpId;
var pos;
var item;
var l = 0;
var r = array.length - 1;
do {
pos = l + r >> 1;
item = array[pos];
cmpValue = item.value;
if (value < cmpValue) r = pos - 1; else if (value > cmpValue) l = pos + 1; else {
cmpId = item.object.basisObjectId;
if (id < cmpId) r = pos - 1; else if (id > cmpId) l = pos + 1; else return pos;
}
} while (l <= r);
return pos + (cmpValue == value ? cmpId < id : cmpValue < value);
}
var SLICE_SOURCEOBJECT_UPDATE = function(sourceObject) {
var sourceObjectInfo = this.sourceMap_[sourceObject.basisObjectId];
var newValue = this.rule(sourceObject);
var index = this.index_;
if (newValue !== sourceObjectInfo.value) {
var pos = binarySearchPos(index, sourceObjectInfo);
var prev = index[pos - 1];
var next = index[pos + 1];
sourceObjectInfo.value = newValue;
if (prev && (prev.value > newValue || prev.value == newValue && prev.object.basisObjectId > sourceObjectInfo.object.basisObjectId) || next && (next.value < newValue || next.value == newValue && next.object.basisObjectId < sourceObjectInfo.object.basisObjectId)) {
index.splice(pos, 1);
index.splice(binarySearchPos(index, sourceObjectInfo), 0, sourceObjectInfo);
this.applyRule();
}
}
};
function sliceIndexSort(a, b) {
return +(a.value > b.value) || -(a.value < b.value) || a.object.basisObjectId - b.object.basisObjectId;
}
var SLICE_SOURCE_HANDLER = {
itemsChanged: function(source, delta) {
var sourceMap = this.sourceMap_;
var index = this.index_;
var updateHandler = this.ruleEvents;
var dropIndex = false;
var buildIndex = false;
var sourceObjectInfo;
var inserted = delta.inserted;
var deleted = delta.deleted;
if (deleted) {
if (deleted.length > index.length - deleted.length) {
dropIndex = true;
buildIndex = deleted.length != index.length;
index.length = 0;
}
for (var i = 0, sourceObject; sourceObject = deleted[i]; i++) {
if (!dropIndex) {
sourceObjectInfo = sourceMap[sourceObject.basisObjectId];
index.splice(binarySearchPos(index, sourceObjectInfo), 1);
}
delete sourceMap[sourceObject.basisObjectId];
if (updateHandler) sourceObject.removeHandler(updateHandler, this);
}
if (buildIndex) for (var key in sourceMap) {
sourceObjectInfo = sourceMap[key];
index.splice(binarySearchPos(index, sourceObjectInfo), 0, sourceObjectInfo);
}
}
if (inserted) {
buildIndex = !index.length;
for (var i = 0, sourceObject; sourceObject = inserted[i]; i++) {
sourceObjectInfo = {
object: sourceObject,
value: this.rule(sourceObject)
};
sourceMap[sourceObject.basisObjectId] = sourceObjectInfo;
if (!buildIndex) index.splice(binarySearchPos(index, sourceObjectInfo), 0, sourceObjectInfo); else index.push(sourceObjectInfo);
if (updateHandler) sourceObject.addHandler(updateHandler, this);
}
if (buildIndex) index.sort(sliceIndexSort);
}
this.applyRule();
}
};
var Slice = Class(SourceDataset, {
className: namespace + ".Slice",
rule: getter($true),
emit_ruleChanged: createEvent("ruleChanged", "oldRule", "oldOrderDesc"),
ruleEvents: createRuleEvents(SLICE_SOURCEOBJECT_UPDATE, "update"),
index_: null,
orderDesc: false,
offset: 0,
limit: 10,
listen: {
source: SLICE_SOURCE_HANDLER
},
emit_rangeChanged: createEvent("rangeChanged", "oldOffset", "oldLimit"),
init: function() {
this.index_ = [];
SourceDataset.prototype.init.call(this);
},
setRange: function(offset, limit) {
var oldOffset = this.offset;
var oldLimit = this.limit;
var delta = false;
if (oldOffset != offset || oldLimit != limit) {
this.offset = offset;
this.limit = limit;
delta = this.applyRule();
this.emit_rangeChanged(oldOffset, oldLimit);
}
return delta;
},
setOffset: function(offset) {
return this.setRange(offset, this.limit);
},
setLimit: function(limit) {
return this.setRange(this.offset, limit);
},
setRule: function(rule, orderDesc) {
rule = getter(rule || $true);
orderDesc = !!orderDesc;
if (this.rule != rule || this.orderDesc != orderDesc) {
var oldRule = this.rule;
var oldOrderDesc = this.orderDesc;
if (this.rule != rule) {
var index = this.index_;
for (var i = 0; i < index.length; i++) index[i].value = rule(index[i].object);
index.sort(sliceIndexSort);
this.rule = rule;
}
this.orderDesc = orderDesc;
this.rule = rule;
this.emit_ruleChanged(oldRule, oldOrderDesc);
return this.applyRule();
}
},
applyRule: function() {
var start = this.offset;
var end = start + this.limit;
if (this.orderDesc) {
start = this.index_.length - end;
end = start + this.limit;
}
var curSet = basis.object.slice(this.members_);
var newSet = this.index_.slice(Math.max(0, start), Math.max(0, end));
var inserted = [];
var delta;
for (var i = 0, item; item = newSet[i]; i++) {
var objectId = item.object.basisObjectId;
if (curSet[objectId]) delete curSet[objectId]; else {
inserted.push(item.object);
this.members_[objectId] = item.object;
}
}
for (var objectId in curSet) delete this.members_[objectId];
if (delta = getDelta(inserted, values(curSet))) this.emit_itemsChanged(delta);
return delta;
},
destroy: function() {
SourceDataset.prototype.destroy.call(this);
this.index_ = null;
}
});
var CLOUD_SOURCEOBJECT_UPDATE = function(sourceObject) {
var sourceMap = this.sourceMap_;
var memberMap = this.members_;
var sourceObjectId = sourceObject.basisObjectId;
var oldList = sourceMap[sourceObjectId].list;
var newList = sourceMap[sourceObjectId].list = {};
var list = this.rule(sourceObject);
var delta;
var inserted = [];
var deleted = [];
var subset;
if (Array.isArray(list)) for (var j = 0; j < list.length; j++) {
subset = this.keyMap.get(list[j], true);
if (subset && !subset.has(sourceObject)) {
subsetId = subset.basisObjectId;
newList[subsetId] = subset;
if (!oldList[subsetId]) {
subset.dataset.emit_itemsChanged({
inserted: [ sourceObject ]
});
if (!memberMap[subsetId]) {
inserted.push(subset);
memberMap[subsetId] = 1;
} else memberMap[subsetId]++;
}
}
}
for (var subsetId in oldList) if (!newList[subsetId]) {
var subset = oldList[subsetId];
subset.dataset.emit_itemsChanged({
deleted: [ sourceObject ]
});
if (!--memberMap[subsetId]) {
delete memberMap[subsetId];
deleted.push(subset);
}
}
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
};
var CLOUD_SOURCE_HANDLER = {
itemsChanged: function(dataset, delta) {
var sourceMap = this.sourceMap_;
var memberMap = this.members_;
var updateHandler = this.ruleEvents;
var array;
var subset;
var subsetId;
var inserted = [];
var deleted = [];
setAccumulateState(true);
if (array = delta.inserted) for (var i = 0, sourceObject; sourceObject = array[i]; i++) {
var list = this.rule(sourceObject);
var sourceObjectInfo = {
object: sourceObject,
list: {}
};
sourceMap[sourceObject.basisObjectId] = sourceObjectInfo;
if (Array.isArray(list)) for (var j = 0, dupFilter = {}; j < list.length; j++) {
subset = this.keyMap.get(list[j], true);
if (subset && !dupFilter[subset.basisObjectId]) {
subsetId = subset.basisObjectId;
dupFilter[subsetId] = true;
sourceObjectInfo.list[subsetId] = subset;
subset.dataset.emit_itemsChanged({
inserted: [ sourceObject ]
});
if (!memberMap[subsetId]) {
inserted.push(subset);
memberMap[subsetId] = 1;
} else memberMap[subsetId]++;
}
}
if (updateHandler) sourceObject.addHandler(updateHandler, this);
}
if (array = delta.deleted) for (var i = 0, sourceObject; sourceObject = array[i]; i++) {
var sourceObjectId = sourceObject.basisObjectId;
var list = sourceMap[sourceObjectId].list;
delete sourceMap[sourceObjectId];
for (var subsetId in list) {
subset = list[subsetId];
subset.dataset.emit_itemsChanged({
deleted: [ sourceObject ]
});
if (!--memberMap[subsetId]) {
delete memberMap[subsetId];
deleted.push(subset);
}
}
if (updateHandler) sourceObject.removeHandler(updateHandler, this);
}
setAccumulateState(false);
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
}
};
var Cloud = Class(SourceDataset, {
className: namespace + ".Cloud",
subsetClass: ReadOnlyDataset,
subsetWrapperClass: DatasetWrapper,
rule: getter($undef),
ruleEvents: createRuleEvents(CLOUD_SOURCEOBJECT_UPDATE, "update"),
keyMap: null,
map: $self,
listen: {
source: CLOUD_SOURCE_HANDLER
},
init: function() {
if (!this.keyMap || this.keyMap instanceof KeyObjectMap == false) this.keyMap = createKeyMap(this.keyMap, this.rule, this.subsetWrapperClass, this.subsetClass);
SourceDataset.prototype.init.call(this);
},
getSubset: function(data, autocreate) {
return this.keyMap.get(data, autocreate);
},
destroy: function() {
SourceDataset.prototype.destroy.call(this);
this.keyMap.destroy();
this.keyMap = null;
}
});
var EXTRACT_SOURCEOBJECT_UPDATE = function(sourceObject) {
var sourceObjectInfo = this.sourceMap_[sourceObject.basisObjectId];
var newValue = this.rule(sourceObject) || null;
var oldValue = sourceObjectInfo.value;
var inserted;
var deleted;
var delta;
if (newValue === oldValue) return;
if (newValue instanceof DataObject || newValue instanceof ReadOnlyDataset) inserted = addToExtract(this, newValue, sourceObject);
if (oldValue) deleted = removeFromExtract(this, oldValue, sourceObject);
sourceObjectInfo.value = newValue;
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
};
var EXTRACT_DATASET_ITEMSCHANGED = function(dataset, delta) {
var inserted = delta.inserted;
var deleted = delta.deleted;
var delta;
if (inserted) inserted = addToExtract(this, inserted, dataset);
if (deleted) deleted = removeFromExtract(this, deleted, dataset);
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
};
var EXTRACT_DATASET_HANDLER = {
itemsChanged: EXTRACT_DATASET_ITEMSCHANGED,
destroy: function(dataset) {
var sourceMap = this.sourceMap_;
for (var cursor = sourceMap[dataset.basisObjectId]; cursor = cursor.ref; ) sourceMap[cursor.object.basisObjectId].value = null;
delete sourceMap[dataset.basisObjectId];
}
};
function hasExtractSourceRef(extract, object, marker) {
var sourceObjectInfo = extract.sourceMap_[object.basisObjectId];
if (sourceObjectInfo && sourceObjectInfo.visited !== marker) {
for (var cursor = sourceObjectInfo; cursor = cursor.ref; ) if (cursor.object === extract.source) return true;
sourceObjectInfo.visited = marker;
for (var cursor = sourceObjectInfo; cursor = cursor.ref; ) if (hasExtractSourceRef(extract, cursor.object, marker || {})) return true;
}
}
function addToExtract(extract, items, ref) {
var sourceMap = extract.sourceMap_;
var members = extract.members_;
var queue = arrayFrom(items);
var inserted = [];
for (var i = 0; i < queue.length; i++) {
var item = queue[i];
var sourceObjectId = item.basisObjectId;
if (!sourceObjectId) {
ref = item.ref;
item = item.object;
sourceObjectId = item.basisObjectId;
}
var sourceObjectInfo = sourceMap[sourceObjectId];
if (sourceObjectInfo) {
sourceObjectInfo.ref = {
object: ref,
ref: sourceObjectInfo.ref
};
} else {
sourceObjectInfo = sourceMap[sourceObjectId] = {
source: item,
ref: {
object: ref,
ref: null
},
visited: null,
value: null
};
if (item instanceof DataObject) {
var value = extract.rule(item) || null;
if (value instanceof DataObject || value instanceof ReadOnlyDataset) {
sourceObjectInfo.value = value;
queue.push({
object: value,
ref: item
});
}
members[sourceObjectId] = sourceObjectInfo;
inserted.push(item);
if (extract.ruleEvents) item.addHandler(extract.ruleEvents, extract);
} else {
item.addHandler(EXTRACT_DATASET_HANDLER, extract);
for (var j = 0, datasetItems = item.getItems(); j < datasetItems.length; j++) queue.push({
object: datasetItems[j],
ref: item
});
}
}
}
return inserted;
}
function removeFromExtract(extract, items, ref) {
var sourceMap = extract.sourceMap_;
var members = extract.members_;
var queue = arrayFrom(items);
var deleted = [];
for (var i = 0; i < queue.length; i++) {
var item = queue[i];
var sourceObjectId = item.basisObjectId;
if (!sourceObjectId) {
ref = item.ref;
item = item.object;
sourceObjectId = item.basisObjectId;
}
var sourceObjectInfo = sourceMap[sourceObjectId];
var sourceObjectValue = sourceObjectInfo.value;
for (var cursor = sourceObjectInfo, prevCursor = sourceObjectInfo; cursor = cursor.ref; ) {
if (cursor.object === ref) {
prevCursor.ref = cursor.ref;
break;
}
prevCursor = cursor;
}
if (!sourceObjectInfo.ref) {
if (item instanceof DataObject) {
delete members[sourceObjectId];
deleted.push(item);
if (extract.ruleEvents) item.removeHandler(extract.ruleEvents, extract);
if (sourceObjectValue) queue.push({
object: sourceObjectValue,
ref: item
});
} else {
item.removeHandler(EXTRACT_DATASET_HANDLER, extract);
for (var j = 0, datasetItems = item.getItems(); j < datasetItems.length; j++) queue.push({
object: datasetItems[j],
ref: item
});
}
delete sourceMap[sourceObjectId];
} else {
if (sourceObjectValue && !hasExtractSourceRef(extract, item)) {
sourceObjectInfo.value = null;
queue.push({
object: sourceObjectValue,
ref: item
});
}
}
}
return deleted;
}
var Extract = SourceDataset.subclass({
className: namespace + ".Extract",
rule: getter($undef),
emit_ruleChanged: createEvent("ruleChanged", "oldRule"),
ruleEvents: createRuleEvents(EXTRACT_SOURCEOBJECT_UPDATE, "update"),
listen: {
source: {
itemsChanged: EXTRACT_DATASET_ITEMSCHANGED
}
},
setRule: function(rule) {
rule = getter(rule || $undef);
if (this.rule !== rule) {
var oldRule = this.rule;
this.rule = rule;
this.emit_ruleChanged(oldRule);
return this.applyRule();
}
},
applyRule: function() {
var insertedMap = {};
var deletedMap = {};
var array;
var delta;
for (var key in this.sourceMap_) {
var sourceObjectInfo = this.sourceMap_[key];
var sourceObject = sourceObjectInfo.source;
if (sourceObject instanceof DataObject) {
var newValue = this.rule(sourceObject) || null;
var oldValue = sourceObjectInfo.value;
if (newValue === oldValue) continue;
if (newValue instanceof DataObject || newValue instanceof ReadOnlyDataset) {
var inserted = addToExtract(this, newValue, sourceObject);
for (var i = 0; i < inserted.length; i++) {
var item = inserted[i];
var id = item.basisObjectId;
if (deletedMap[id]) delete deletedMap[id]; else insertedMap[id] = item;
}
}
if (oldValue) {
var deleted = removeFromExtract(this, oldValue, sourceObject);
for (var i = 0; i < deleted.length; i++) {
var item = deleted[i];
var id = item.basisObjectId;
if (insertedMap[id]) delete insertedMap[id]; else deletedMap[id] = item;
}
}
sourceObjectInfo.value = newValue;
}
}
if (delta = getDelta(values(insertedMap), values(deletedMap))) this.emit_itemsChanged(delta);
return delta;
}
});
module.exports = {
getDelta: getDelta,
createRuleEvents: createRuleEvents,
Merge: Merge,
Subtract: Subtract,
SourceDataset: SourceDataset,
MapFilter: MapFilter,
Filter: Filter,
Split: Split,
Extract: Extract,
Slice: Slice,
Cloud: Cloud
};
},
"0.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./1.js");
basis.require("./a.js");
basis.require("./d.js");
basis.require("./e.js");
basis.require("./f.js");
basis.require("./g.js");
basis.require("./h.js");
basis.require("./i.js");
basis.require("./k.js");
basis.require("./n.js");
basis.require("./o.js");
basis.require("./p.js");
global["basis"] = basis;
},
"2.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./3.js");
var namespace = this.path;
var Class = basis.Class;
var Emitter = basis.event.Emitter;
var hasOwnProperty = Object.prototype.hasOwnProperty;
basis.resource.extensions[".l10n"] = function(content, url) {
return resolveDictionary(url).update(basis.resource.extensions[".json"](content, url));
};
function ownKeys(object) {
var result = [];
for (var key in object) if (hasOwnProperty.call(object, key)) result.push(key);
return result;
}
var tokenIndex = [];
var tokenComputeFn = {};
var tokenComputes = {};
var updateToken = basis.Token.prototype.set;
var ComputeToken = Class(basis.Token, {
className: namespace + ".ComputeToken",
init: function(value, token) {
token.computeTokens[this.basisObjectId] = this;
this.token = token;
basis.Token.prototype.init.call(this, value);
},
get: function() {
var key = this.token.type == "plural" ? cultures[currentCulture].plural(this.value) : this.value;
return this.token.dictionary.getValue(this.token.name + "." + key);
},
toString: function() {
return this.get();
},
destroy: function() {
delete this.token.computeTokens[this.basisObjectId];
this.token = null;
basis.Token.prototype.destroy.call(this);
}
});
var Token = Class(basis.Token, {
className: namespace + ".Token",
index: NaN,
dictionary: null,
name: "",
type: "default",
computeTokens: null,
init: function(dictionary, tokenName, type, value) {
basis.Token.prototype.init.call(this, value);
this.index = tokenIndex.push(this) - 1;
this.name = tokenName;
this.dictionary = dictionary;
this.computeTokens = {};
if (type) this.setType(type); else this.apply();
},
toString: function() {
return this.get();
},
apply: function() {
for (var key in this.computeTokens) this.computeTokens[key].apply();
basis.Token.prototype.apply.call(this);
},
set: function() {
basis.dev.warn("basis.l10n: Value for l10n token can't be set directly, but through dictionary update only");
},
setType: function(type) {
if (type != "plural" && (!basis.l10n.enableMarkup || type != "markup")) type = "default";
if (this.type != type) {
this.type = type;
this.apply();
}
},
compute: function(events, getter) {
if (arguments.length == 1) {
getter = events;
events = "";
}
getter = basis.getter(getter);
events = String(events).trim().split(/\s+|\s*,\s*/).sort();
var tokenId = this.basisObjectId;
var enumId = events.concat(tokenId, getter[basis.getter.ID]).join("_");
if (tokenComputeFn[enumId]) return tokenComputeFn[enumId];
var token = this;
var objectTokenMap = {};
var updateValue = function(object) {
updateToken.call(this, getter(object));
};
var handler = {
destroy: function(object) {
delete objectTokenMap[object.basisObjectId];
this.destroy();
}
};
for (var i = 0, eventName; eventName = events[i]; i++) if (eventName != "destroy") handler[eventName] = updateValue;
return tokenComputeFn[enumId] = function(object) {
if (object instanceof Emitter == false) throw "basis.l10n.Token#compute: object must be an instanceof Emitter";
var objectId = object.basisObjectId;
var computeToken = objectTokenMap[objectId];
if (!computeToken) {
computeToken = objectTokenMap[objectId] = new ComputeToken(getter(object), token);
object.addHandler(handler, computeToken);
}
return computeToken;
};
},
computeToken: function(value) {
return new ComputeToken(value, this);
},
token: function(name) {
if (this.type == "plural") name = cultures[currentCulture].plural(name);
if (this.dictionary) return this.dictionary.token(this.name + "." + name);
},
destroy: function() {
for (var key in this.computeTokens) this.computeTokens[key].destroy();
this.computeTokens = null;
this.value = null;
basis.Token.prototype.destroy.call(this);
}
});
function resolveToken(path) {
if (path.charAt(0) == "#") {
return tokenIndex[parseInt(path.substr(1), 36)];
} else {
var parts = path.match(/^(.+?)@(.+)$/);
if (parts) return resolveDictionary(basis.path.resolve(parts[2])).token(parts[1]);
basis.dev.warn("basis.l10n.token accepts token references in format `token.path@path/to/dict.l10n` only");
}
}
var dictionaries = [];
var dictionaryByUrl = {};
var createDictionaryNotifier = new basis.Token;
function walkTokens(dictionary, culture, tokens, path) {
var cultureValues = dictionary.cultureValues[culture];
path = path ? path + "." : "";
for (var name in tokens) if (hasOwnProperty.call(tokens, name)) {
var tokenName = path + name;
var tokenValue = tokens[name];
cultureValues[tokenName] = tokenValue;
if (tokenValue && (typeof tokenValue == "object" || Array.isArray(tokenValue))) walkTokens(dictionary, culture, tokenValue, tokenName);
}
}
var Dictionary = Class(null, {
className: namespace + ".Dictionary",
tokens: null,
types: null,
cultureValues: null,
index: NaN,
resource: null,
init: function(content) {
this.tokens = {};
this.types = {};
this.cultureValues = {};
this.index = dictionaries.push(this) - 1;
if (basis.resource.isResource(content)) {
var resource = content;
this.resource = resource;
if (!dictionaryByUrl[resource.url]) {
dictionaryByUrl[resource.url] = this;
createDictionaryNotifier.set(resource.url);
}
resource.fetch();
} else {
basis.dev.warn("Use object as content of dictionary is experimental and not production-ready");
this.update(content || {});
}
},
update: function(data) {
if (!data) data = {};
this.cultureValues = {};
for (var culture in data) if (!/^_|_$/.test(culture)) {
this.cultureValues[culture] = {};
walkTokens(this, culture, data[culture]);
}
this.types = data._meta && data._meta.type || {};
for (var key in this.tokens) this.tokens[key].setType(this.types[key]);
this.syncValues();
return this;
},
syncValues: function() {
for (var tokenName in this.tokens) updateToken.call(this.tokens[tokenName], this.getValue(tokenName));
},
getValue: function(tokenName) {
var fallback = cultureFallback[currentCulture] || [];
for (var i = 0, cultureName; cultureName = fallback[i]; i++) {
var cultureValues = this.cultureValues[cultureName];
if (cultureValues && tokenName in cultureValues) return cultureValues[tokenName];
}
},
getCultureValue: function(culture, tokenName) {
return this.cultureValues[culture] && this.cultureValues[culture][tokenName];
},
token: function(tokenName) {
var token = this.tokens[tokenName];
if (!token) {
token = this.tokens[tokenName] = new Token(this, tokenName, this.types[tokenName], this.getValue(tokenName));
}
return token;
},
destroy: function() {
this.tokens = null;
this.cultureValues = null;
basis.array.remove(dictionaries, this);
if (this.resource) {
delete dictionaryByUrl[this.resource.url];
this.resource = null;
}
}
});
function resolveDictionary(source) {
var dictionary;
if (typeof source == "string") {
var location = source;
var extname = basis.path.extname(location);
if (extname != ".l10n") location = basis.path.dirname(location) + "/" + basis.path.basename(location, extname) + ".l10n";
source = basis.resource(location);
}
if (basis.resource.isResource(source)) dictionary = dictionaryByUrl[source.url];
return dictionary || new Dictionary(source);
}
function getDictionaries() {
return dictionaries.slice(0);
}
var cultureList = [];
var currentCulture = null;
var cultures = {};
var cultureFallback = {};
var pluralFormsMap = {};
var pluralForms = [ [ 1, function(n) {
return 0;
} ], [ 2, function(n) {
return n == 1 || n % 10 == 1 ? 0 : 1;
} ], [ 2, function(n) {
return n == 0 ? 0 : 1;
} ], [ 2, function(n) {
return n == 1 ? 0 : 1;
} ], [ 2, function(n) {
return n == 0 || n == 1 ? 0 : 1;
} ], [ 2, function(n) {
return n % 10 != 1 || n % 100 == 11 ? 1 : 0;
} ], [ 3, function(n) {
return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
} ], [ 3, function(n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
} ], [ 3, function(n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
} ], [ 3, function(n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
} ], [ 3, function(n) {
return n == 0 ? 0 : n == 1 ? 1 : 2;
} ], [ 3, function(n) {
return n == 1 ? 0 : n == 0 || n % 100 > 0 && n % 100 < 20 ? 1 : 2;
} ], [ 3, function(n) {
return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
} ], [ 3, function(n) {
return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
} ], [ 4, function(n) {
return n == 1 ? 0 : n == 2 ? 1 : n != 8 && n != 11 ? 2 : 3;
} ], [ 4, function(n) {
return n == 1 ? 0 : n == 2 ? 1 : n == 3 ? 2 : 3;
} ], [ 4, function(n) {
return n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0;
} ], [ 4, function(n) {
return n == 1 ? 0 : n == 0 || n % 100 > 1 && n % 100 < 11 ? 1 : n % 100 > 10 && n % 100 < 20 ? 2 : 3;
} ], [ 4, function(n) {
return n == 1 || n == 11 ? 0 : n == 2 || n == 12 ? 1 : n > 2 && n < 20 ? 2 : 3;
} ], [ 5, function(n) {
return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
} ], [ 6, function(n) {
return n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
} ] ];
[ "ay bo cgg dz fa id ja jbo ka kk km ko ky lo ms my sah su th tt ug vi wo zh", "mk", "jv", "af an ast az bg bn brx ca da de doi el en eo es es-AR et eu ff fi fo fur fy gl gu ha he hi hne hu hy ia it kn ku lb mai ml mn mni mr nah nap nb ne nl nn no nso or pa pap pms ps pt rm rw sat sco sd se si so son sq sv sw ta te tk ur yo", "ach ak am arn br fil fr gun ln mfe mg mi oc pt-BR tg ti tr uz wa zh", "is", "csb", "lv", "lt", "be bs hr ru sr uk", "mnk", "ro", "pl", "cs sk", "cy", "kw", "sl", "mt", "gd", "ga", "ar" ].forEach(function(langs, idx) {
langs.split(" ").forEach(function(lang) {
pluralFormsMap[lang] = this;
}, pluralForms[idx]);
});
var Culture = basis.Class(null, {
className: namespace + ".Culture",
name: "",
pluralForm: null,
init: function(name, pluralForm) {
this.name = name;
if (!cultures[name]) cultures[name] = this;
this.pluralForm = pluralForm || pluralFormsMap[name] || pluralFormsMap[name.split("-")[0]] || pluralForms[0];
},
plural: function(value) {
return Number(this.pluralForm[1](Math.abs(parseInt(value, 10))));
}
});
function resolveCulture(name, pluralForm) {
if (name && !cultures[name]) cultures[name] = new Culture(name, pluralForm);
return cultures[name || currentCulture];
}
basis.object.extend(resolveCulture, new basis.Token);
resolveCulture.set = setCulture;
function getCulture() {
return currentCulture;
}
function setCulture(culture) {
if (!culture) return;
if (currentCulture != culture) {
if (cultureList.indexOf(culture) == -1) {
basis.dev.warn("basis.l10n.setCulture: culture `" + culture + "` not in the list, the culture isn't changed");
return;
}
currentCulture = culture;
for (var i = 0, dictionary; dictionary = dictionaries[i]; i++) dictionary.syncValues();
basis.Token.prototype.set.call(resolveCulture, culture);
}
}
function getCultureList() {
return cultureList.slice(0);
}
function setCultureList(list) {
if (typeof list == "string") list = list.trim().split(" ");
if (!list.length) {
basis.dev.warn("basis.l10n.setCultureList: culture list can't be empty, the culture list isn't changed");
return;
}
var cultures = {};
var cultureRow;
var baseCulture;
cultureFallback = {};
for (var i = 0, culture, cultureName; culture = list[i]; i++) {
cultureRow = culture.split("/");
if (cultureRow.length > 2) {
basis.dev.warn("basis.l10n.setCultureList: only one fallback culture can be set for certain culture, try to set `" + culture + "`; other cultures except first one was ignored");
cultureRow = cultureRow.slice(0, 2);
}
cultureName = cultureRow[0];
if (!baseCulture) baseCulture = cultureName;
cultures[cultureName] = resolveCulture(cultureName);
cultureFallback[cultureName] = cultureRow;
}
for (var cultureName in cultureFallback) {
cultureFallback[cultureName] = basis.array.flatten(cultureFallback[cultureName].map(function(name) {
return cultureFallback[name];
})).concat(baseCulture).filter(function(item, idx, array) {
return !idx || array.lastIndexOf(item, idx - 1) == -1;
});
}
cultureList = basis.object.keys(cultures);
if (currentCulture in cultures == false) setCulture(baseCulture);
}
function onCultureChange(fn, context, fire) {
resolveCulture.attach(fn, context);
if (fire) fn.call(context, currentCulture);
}
setCultureList("en-US");
setCulture("en-US");
module.exports = {
ComputeToken: ComputeToken,
Token: Token,
token: resolveToken,
Dictionary: Dictionary,
dictionary: resolveDictionary,
getDictionaries: getDictionaries,
addCreateDictionaryHandler: createDictionaryNotifier.attach.bind(createDictionaryNotifier),
removeCreateDictionaryHandler: createDictionaryNotifier.detach.bind(createDictionaryNotifier),
Culture: Culture,
culture: resolveCulture,
getCulture: getCulture,
setCulture: setCulture,
getCultureList: getCultureList,
setCultureList: setCultureList,
pluralForms: pluralForms,
onCultureChange: onCultureChange
};
},
"3.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
var namespace = this.path;
var Class = basis.Class;
var extend = basis.object.extend;
var slice = Array.prototype.slice;
var NULL_HANDLER = {};
var events = {};
var warnOnDestroy = function() {
basis.dev.warn("Object had been destroyed before. Destroy method must not be called more than once.");
};
function createDispatcher(eventName) {
var eventFunction = events[eventName];
if (!eventFunction) {
eventFunction = function() {
var cursor = this;
var args;
var fn;
while (cursor = cursor.handler) {
fn = cursor.callbacks[eventName];
if (typeof fn == "function") {
if (!args) {
args = [ this ];
for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
}
fn.apply(cursor.context || this, args);
}
fn = cursor.callbacks["*"];
if (typeof fn == "function") {
if (!args) {
args = [ this ];
for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
}
fn.call(cursor.context || this, {
sender: this,
type: eventName,
args: args
});
}
}
if (this.debug_emit) {
args = [];
for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
this.debug_emit({
sender: this,
type: eventName,
args: args
});
}
};
eventFunction = (new Function("slice", 'return {"' + namespace + ".events." + eventName + '":\n\n ' + "function(" + slice.call(arguments, 1).join(", ") + "){" + eventFunction.toString().replace(/\beventName\b/g, '"' + eventName + '"').replace(/^function[^(]*\(\)[^{]*\{|\}$/g, "") + "}" + '\n\n}["' + namespace + ".events." + eventName + '"];'))(slice);
events[eventName] = eventFunction;
}
return eventFunction;
}
function createHandler(events, eventCallback) {
var handler = {
events: []
};
if (events) {
events = String(events).trim().split(/\s+|\s*,\s*/).sort();
handler = {
events: events
};
for (var i = 0, eventName; eventName = events[i]; i++) if (eventName != "destroy") handler[eventName] = eventCallback;
}
return handler;
}
var Emitter = Class(null, {
className: namespace + ".Emitter",
extendConstructor_: true,
handler: null,
emit_destroy: createDispatcher("destroy"),
listen: Class.nestedExtendProperty(),
debug_handlers: function() {
var result = [];
var cursor = this;
while (cursor = cursor.handler) result.push([ cursor.callbacks, cursor.context ]);
return result;
},
debug_emit: null,
init: function() {
if (this.handler && !this.handler.callbacks) this.handler = {
callbacks: this.handler,
context: this,
handler: null
};
},
addHandler: function(callbacks, context) {
if (!callbacks) basis.dev.warn(namespace + ".Emitter#addHandler: callbacks is not an object (", callbacks, ")");
context = context || this;
var cursor = this;
while (cursor = cursor.handler) {
if (cursor.callbacks === callbacks && cursor.context === context) {
basis.dev.warn(namespace + ".Emitter#addHandler: add duplicate event callbacks", callbacks, "to Emitter instance:", this);
break;
}
}
this.handler = {
callbacks: callbacks,
context: context,
handler: this.handler
};
},
removeHandler: function(callbacks, context) {
var cursor = this;
var prev;
context = context || this;
while (prev = cursor, cursor = cursor.handler) if (cursor.callbacks === callbacks && cursor.context === context) {
cursor.callbacks = NULL_HANDLER;
prev.handler = cursor.handler;
return;
}
basis.dev.warn(namespace + ".Emitter#removeHandler: no handler removed");
},
destroy: function() {
this.destroy = warnOnDestroy;
this.emit_destroy();
this.handler = null;
}
});
module.exports = {
create: createDispatcher,
createHandler: createHandler,
events: events,
Emitter: Emitter
};
},
"4.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./3.js");
var namespace = this.path;
var Class = basis.Class;
var sliceArray = Array.prototype.slice;
var values = basis.object.values;
var $self = basis.fn.$self;
var Emitter = basis.event.Emitter;
var createEvent = basis.event.create;
var events = basis.event.events;
var NULL_OBJECT = {};
var EMPTY_ARRAY = [];
var STATE_EXISTS = {};
var STATE = {
priority: [],
values: {},
add: function(state, order) {
var name = state;
var value = state.toLowerCase();
STATE[name] = value;
STATE_EXISTS[value] = name;
this.values[value] = name;
if (order) order = this.priority.indexOf(order); else order = -1;
if (order == -1) this.priority.push(value); else this.priority.splice(order, 0, value);
},
getList: function() {
return values(STATE_EXISTS);
}
};
STATE.add("READY");
STATE.add("DEPRECATED");
STATE.add("UNDEFINED");
STATE.add("ERROR");
STATE.add("PROCESSING");
var subscriptionConfig = {};
var subscriptionSeed = 1;
var SUBSCRIPTION = {
NONE: 0,
ALL: 0,
link: function(type, from, to) {
var subscriberId = type + from.basisObjectId;
var subscribers = to.subscribers_;
if (!subscribers) subscribers = to.subscribers_ = {};
if (!subscribers[subscriberId]) {
subscribers[subscriberId] = from;
var count = to.subscriberCount += 1;
if (count == 1) to.emit_subscribersChanged(+1);
} else {
basis.dev.warn("Attempt to add duplicate subscription");
}
},
unlink: function(type, from, to) {
var subscriberId = type + from.basisObjectId;
var subscribers = to.subscribers_;
if (subscribers && subscribers[subscriberId]) {
delete subscribers[subscriberId];
var count = to.subscriberCount -= 1;
if (count == 0) {
to.emit_subscribersChanged(-1);
to.subscribers_ = null;
}
} else {
basis.dev.warn("Trying remove non-exists subscription");
}
},
add: function(name, handler, action) {
subscriptionConfig[subscriptionSeed] = {
handler: handler,
action: action
};
SUBSCRIPTION[name] = subscriptionSeed;
SUBSCRIPTION.ALL |= subscriptionSeed;
subscriptionSeed <<= 1;
},
addProperty: function(propertyName, eventName) {
var handler = {};
handler[eventName || propertyName + "Changed"] = function(object, oldValue) {
if (oldValue instanceof AbstractData) SUBSCRIPTION.unlink(propertyName, object, oldValue);
if (object[propertyName] instanceof AbstractData) SUBSCRIPTION.link(propertyName, object, object[propertyName]);
};
this.add(propertyName.toUpperCase(), handler, function(fn, object) {
if (object[propertyName]) fn(propertyName, object, object[propertyName]);
});
}
};
var maskConfig = {};
function mixFunctions(fnA, fnB) {
return function() {
fnA.apply(this, arguments);
fnB.apply(this, arguments);
};
}
function getMaskConfig(mask) {
var config = maskConfig[mask];
if (!config) {
var actions = [];
var handler = {};
var idx = 1;
config = maskConfig[mask] = {
actions: actions,
handler: handler
};
while (mask) {
if (mask & 1) {
var cfg = subscriptionConfig[idx];
actions.push(cfg.action);
for (var key in cfg.handler) handler[key] = handler[key] ? mixFunctions(handler[key], cfg.handler[key]) : cfg.handler[key];
}
idx <<= 1;
mask >>= 1;
}
}
return config;
}
function addSub(object, mask) {
var config = getMaskConfig(mask);
for (var i = 0, action; action = config.actions[i]; i++) action(SUBSCRIPTION.link, object);
object.addHandler(config.handler);
}
function remSub(object, mask) {
var config = getMaskConfig(mask);
for (var i = 0, action; action = config.actions[i++]; ) action(SUBSCRIPTION.unlink, object);
object.removeHandler(config.handler);
}
SUBSCRIPTION.addProperty("delegate");
SUBSCRIPTION.addProperty("target");
SUBSCRIPTION.addProperty("dataset");
SUBSCRIPTION.addProperty("value", "change");
var AbstractData = Class(Emitter, {
className: namespace + ".AbstractData",
state: STATE.UNDEFINED,
emit_stateChanged: createEvent("stateChanged", "oldState"),
active: false,
emit_activeChanged: createEvent("activeChanged"),
subscribeTo: SUBSCRIPTION.NONE,
subscriberCount: 0,
subscribers_: null,
emit_subscribersChanged: createEvent("subscribersChanged", "delta"),
syncEvents: Class.oneFunctionProperty(function() {
if (this.isSyncRequired()) this.syncAction();
}, {
stateChanged: true,
subscribersChanged: true
}),
syncAction: null,
init: function() {
Emitter.prototype.init.call(this);
if (this.active) this.addHandler(getMaskConfig(this.subscribeTo).handler);
var syncAction = this.syncAction;
if (syncAction) {
this.syncAction = null;
this.setSyncAction(syncAction);
}
},
setState: function(state, data) {
var stateCode = String(state);
if (!STATE_EXISTS[stateCode]) throw new Error("Wrong state value");
if (this.state != stateCode || this.state.data != data) {
var oldState = this.state;
this.state = Object(stateCode);
this.state.data = data;
this.emit_stateChanged(oldState);
return true;
}
return false;
},
deprecate: function() {
if (this.state != STATE.PROCESSING) this.setState(STATE.DEPRECATED);
},
setActive: function(isActive) {
isActive = !!isActive;
if (this.active != isActive) {
this.active = isActive;
this.emit_activeChanged();
if (isActive) addSub(this, this.subscribeTo); else remSub(this, this.subscribeTo);
return true;
}
return false;
},
setSubscription: function(subscriptionType) {
var curSubscriptionType = this.subscribeTo;
var newSubscriptionType = subscriptionType & SUBSCRIPTION.ALL;
var delta = curSubscriptionType ^ newSubscriptionType;
if (delta) {
this.subscribeTo = newSubscriptionType;
if (this.active) {
var curConfig = getMaskConfig(curSubscriptionType);
var newConfig = getMaskConfig(newSubscriptionType);
this.removeHandler(curConfig.handler);
this.addHandler(newConfig.handler);
var idx = 1;
while (delta) {
if (delta & 1) {
var cfg = subscriptionConfig[idx];
if (curSubscriptionType & idx) cfg.action(SUBSCRIPTION.unlink, this); else cfg.action(SUBSCRIPTION.link, this);
}
idx <<= 1;
delta >>= 1;
}
}
return true;
}
return false;
},
isSyncRequired: function() {
return this.subscriberCount > 0 && (this.state == STATE.UNDEFINED || this.state == STATE.DEPRECATED);
},
setSyncAction: function(syncAction) {
var oldAction = this.syncAction;
if (typeof syncAction != "function") syncAction = null;
this.syncAction = syncAction;
if (syncAction) {
if (!oldAction) this.addHandler(this.syncEvents);
if (this.isSyncRequired()) this.syncAction();
} else {
if (oldAction) this.removeHandler(this.syncEvents);
}
},
destroy: function() {
Emitter.prototype.destroy.call(this);
if (this.active) {
var config = getMaskConfig(this.subscribeTo);
for (var i = 0, action; action = config.actions[i]; i++) action(SUBSCRIPTION.unlink, this);
}
this.state = STATE.UNDEFINED;
}
});
var GETTER_ID = basis.getter.ID;
var VALUE_EMMITER_HANDLER = {
destroy: function(object) {
this.value.unlink(object, this.fn);
}
};
var VALUE_EMMITER_DESTROY_HANDLER = {
destroy: function(object) {
this.set(null);
}
};
var computeFunctions = {};
var valueSetters = {};
var valueSyncToken = function(value) {
this.set(this.fn(value));
};
var Value = Class(AbstractData, {
className: namespace + ".Value",
subscribeTo: SUBSCRIPTION.VALUE,
emit_change: createEvent("change", "oldValue") && function(oldValue) {
events.change.call(this, oldValue);
var cursor = this;
while (cursor = cursor.links_) cursor.fn.call(cursor.context, this.value, oldValue);
},
value: null,
initValue: null,
proxy: null,
locked: 0,
lockedValue_: null,
links_: null,
setNullOnEmitterDestroy: true,
bindingBridge: {
attach: function(host, callback, context) {
host.link(context, callback, true);
},
detach: function(host, callback, context) {
host.unlink(context, callback);
},
get: function(host) {
return host.value;
}
},
init: function() {
AbstractData.prototype.init.call(this);
if (this.proxy) this.value = this.proxy(this.value);
if (this.setNullOnEmitterDestroy && this.value instanceof Emitter) this.value.addHandler(VALUE_EMMITER_DESTROY_HANDLER, this);
this.initValue = this.value;
},
set: function(value) {
var oldValue = this.value;
var newValue = this.proxy ? this.proxy(value) : value;
var changed = newValue !== oldValue;
if (changed) {
if (this.setNullOnEmitterDestroy) {
if (oldValue instanceof Emitter) oldValue.removeHandler(VALUE_EMMITER_DESTROY_HANDLER, this);
if (newValue instanceof Emitter) newValue.addHandler(VALUE_EMMITER_DESTROY_HANDLER, this);
}
this.value = newValue;
if (!this.locked) this.emit_change(oldValue);
}
return changed;
},
reset: function() {
this.set(this.initValue);
},
isLocked: function() {
return this.locked > 0;
},
lock: function() {
this.locked++;
if (this.locked == 1) this.lockedValue_ = this.value;
},
unlock: function() {
if (this.locked) {
this.locked--;
if (!this.locked) {
var lockedValue = this.lockedValue_;
this.lockedValue_ = null;
if (this.value !== lockedValue) this.emit_change(lockedValue);
}
}
},
compute: function(events, fn) {
if (!fn) {
fn = events;
events = null;
}
if (!fn) fn = $self;
var hostValue = this;
var handler = basis.event.createHandler(events, function(object) {
this.set(fn(object, hostValue.value));
});
var fnId = fn[GETTER_ID] || String(fn);
var getComputeTokenId = handler.events.concat(fnId, this.basisObjectId).join("_");
var getComputeToken = computeFunctions[getComputeTokenId];
if (!getComputeToken) {
var tokenMap = {};
handler.destroy = function(object) {
delete tokenMap[object.basisObjectId];
this.destroy();
};
this.addHandler({
change: function() {
for (var key in tokenMap) {
var pair = tokenMap[key];
pair.token.set(fn(pair.object, this.value));
}
},
destroy: function() {
for (var key in tokenMap) {
var pair = tokenMap[key];
pair.object.removeHandler(handler, pair.token);
pair.token.destroy();
}
tokenMap = null;
hostValue = null;
}
});
getComputeToken = computeFunctions[getComputeTokenId] = function(object) {
if (object instanceof basis.event.Emitter == false) basis.dev.warn("basis.data.Value#compute: object should be an instanceof basis.event.Emitter");
var objectId = object.basisObjectId;
var pair = tokenMap[objectId];
var value = fn(object, hostValue.value);
if (!pair) {
var token = new basis.Token(value);
object.addHandler(handler, token);
pair = tokenMap[objectId] = {
token: token,
object: object
};
} else {
pair.token.set(value);
}
return pair.token;
};
getComputeToken.deferred = function() {
return function(object) {
return getComputeToken(object).deferred();
};
};
}
return getComputeToken;
},
as: function(fn, deferred) {
if (!fn) fn = $self;
if (this.links_) {
var cursor = this;
var fnId = fn[GETTER_ID] || String(fn);
while (cursor = cursor.links_) {
var context = cursor.context;
if (context instanceof basis.Token && (context.fn[GETTER_ID] || String(context.fn)) == fnId) return deferred ? context.deferred() : context;
}
}
var token = new basis.Token;
token.fn = fn;
this.link(token, valueSyncToken);
return deferred ? token.deferred() : token;
},
deferred: function(fn) {
return this.as(fn, true);
},
link: function(context, fn, noApply) {
if (typeof fn != "function") {
var property = String(fn);
fn = valueSetters[property];
if (!fn) fn = valueSetters[property] = function(value) {
this[property] = value;
};
}
var cursor = this;
while (cursor = cursor.links_) if (cursor.context === context && cursor.fn === fn) {
basis.dev.warn(this.constructor.className + "#attach: Duplicate link pair context-fn");
break;
}
this.links_ = {
value: this,
context: context,
fn: fn,
links_: this.links_
};
if (context instanceof Emitter) context.addHandler(VALUE_EMMITER_HANDLER, this.links_);
if (!noApply) fn.call(context, this.value);
return context;
},
unlink: function(context, fn) {
var cursor = this;
var prev;
while (prev = cursor, cursor = cursor.links_) if (cursor.context === context && (!fn || cursor.fn === fn)) {
cursor.fn = basis.fn.$undef;
prev.links_ = cursor.links_;
if (cursor.context instanceof Emitter) cursor.context.removeHandler(VALUE_EMMITER_HANDLER, cursor);
}
},
destroy: function() {
AbstractData.prototype.destroy.call(this);
if (this.setNullOnEmitterDestroy && this.value instanceof Emitter) this.value.removeHandler(VALUE_EMMITER_DESTROY_HANDLER, this);
var cursor = this;
while (cursor = cursor.links_) if (cursor.context instanceof Emitter) cursor.context.removeHandler(VALUE_EMMITER_HANDLER, cursor);
this.proxy = null;
this.initValue = null;
this.value = null;
this.lockedValue_ = null;
this.links_ = null;
}
});
var valueFromMap = {};
var valueFromSetProxy = function(object) {
Value.prototype.set.call(this, object);
};
Value.from = function(obj, events, getter) {
var result;
if (!obj) return null;
if (obj instanceof Emitter) {
if (!getter) {
getter = events;
events = null;
}
if (!getter) getter = $self;
var handler = basis.event.createHandler(events, valueFromSetProxy);
var getterId = getter[GETTER_ID] || String(getter);
var id = handler.events.concat(getterId, obj.basisObjectId).join("_");
result = valueFromMap[id];
if (!result) {
result = valueFromMap[id] = new Value({
value: obj,
proxy: basis.getter(getter),
set: basis.fn.$undef,
handler: {
destroy: function() {
valueFromMap[id] = null;
obj.removeHandler(handler, this);
}
}
});
handler.destroy = function(sender) {
valueFromMap[id] = null;
this.destroy();
};
obj.addHandler(handler, result);
}
}
if (!result) {
var id = obj.basisObjectId;
var bindingBridge = obj.bindingBridge;
if (id && bindingBridge) {
result = valueFromMap[id];
if (!result) {
result = valueFromMap[id] = new Value({
value: bindingBridge.get(obj)
});
bindingBridge.attach(obj, result.set, result);
}
}
}
if (!result) throw "Bad object type";
return result;
};
Value.factory = function(events, getter) {
return function(object) {
return Value.from(object, events, getter);
};
};
var INIT_DATA = {};
function isConnected(a, b) {
while (b && b !== a && b !== b.delegate) b = b.delegate;
return b === a;
}
function applyDelegateChanges(object, oldRoot, oldTarget) {
var delegate = object.delegate;
if (delegate) {
object.root = delegate.root;
object.target = delegate.target;
object.data = delegate.data;
object.state = delegate.state;
}
if (object.root !== oldRoot) {
var rootListenHandler = object.listen.root;
if (rootListenHandler) {
if (oldRoot && oldRoot !== object) oldRoot.removeHandler(rootListenHandler, object);
if (object.root && object.root !== object) object.root.addHandler(rootListenHandler, object);
}
object.emit_rootChanged(oldRoot);
}
if (object.target !== oldTarget) {
var targetListenHandler = object.listen.target;
if (targetListenHandler) {
if (oldTarget && oldTarget !== object) oldTarget.removeHandler(targetListenHandler, object);
if (object.target && object.target !== object) object.target.addHandler(targetListenHandler, object);
}
object.emit_targetChanged(oldTarget);
}
var cursor = object.delegates_;
while (cursor) {
if (cursor.delegate) applyDelegateChanges(cursor.delegate, oldRoot, oldTarget);
cursor = cursor.next;
}
}
var DataObject = Class(AbstractData, {
className: namespace + ".Object",
subscribeTo: SUBSCRIPTION.DELEGATE + SUBSCRIPTION.TARGET,
data: null,
emit_update: createEvent("update", "delta") && function(delta) {
var cursor = this.delegates_;
events.update.call(this, delta);
while (cursor) {
if (cursor.delegate) cursor.delegate.emit_update(delta);
cursor = cursor.next;
}
},
emit_stateChanged: function(oldState) {
var cursor = this.delegates_;
AbstractData.prototype.emit_stateChanged.call(this, oldState);
while (cursor) {
if (cursor.delegate) {
cursor.delegate.state = this.state;
cursor.delegate.emit_stateChanged(oldState);
}
cursor = cursor.next;
}
},
delegate: null,
delegateAdapter_: null,
delegates_: null,
debug_delegates: function() {
var cursor = this.delegates_;
var result = [];
while (cursor) {
result.push(cursor.delegate);
cursor = cursor.next;
}
return result;
},
emit_delegateChanged: createEvent("delegateChanged", "oldDelegate"),
target: null,
emit_targetChanged: createEvent("targetChanged", "oldTarget"),
root: null,
emit_rootChanged: createEvent("rootChanged", "oldRoot"),
init: function() {
this.root = this;
AbstractData.prototype.init.call(this);
var delegate = this.delegate;
var data = this.data;
if (delegate) {
this.delegate = null;
this.target = null;
this.data = INIT_DATA;
this.setDelegate(delegate);
if (this.data === INIT_DATA) this.data = data || {};
} else {
if (!data) this.data = {};
if (this.target !== null) this.target = this;
}
},
setSyncAction: function(syncAction) {
if (syncAction && this.delegate) basis.dev.warn(this.constructor.syncAction + " instance has a delegate and syncAction - it may produce conflics with data & state");
AbstractData.prototype.setSyncAction.call(this, syncAction);
},
setDelegate: function(newDelegate) {
newDelegate = resolveObject(this, this.setDelegate, newDelegate, "delegateAdapter_");
if (newDelegate && newDelegate instanceof DataObject) {
if (newDelegate.delegate && isConnected(this, newDelegate)) {
basis.dev.warn("New delegate has already connected to object. Delegate assignment has been ignored.", this, newDelegate);
return false;
}
} else {
newDelegate = null;
}
if (this.delegate !== newDelegate) {
var oldState = this.state;
var oldData = this.data;
var oldDelegate = this.delegate;
var oldTarget = this.target;
var oldRoot = this.root;
var delegateListenHandler = this.listen.delegate;
var dataChanged = false;
var delta;
if (oldDelegate) {
if (delegateListenHandler) oldDelegate.removeHandler(delegateListenHandler, this);
var cursor = oldDelegate.delegates_;
var prev = oldDelegate;
while (cursor) {
if (cursor.delegate === this) {
cursor.delegate = null;
if (prev === oldDelegate) oldDelegate.delegates_ = cursor.next; else prev.next = cursor.next;
break;
}
prev = cursor;
cursor = cursor.next;
}
}
if (newDelegate) {
this.delegate = newDelegate;
if (delegateListenHandler) newDelegate.addHandler(delegateListenHandler, this);
newDelegate.delegates_ = {
delegate: this,
next: newDelegate.delegates_
};
if (this.data !== INIT_DATA) {
delta = {};
for (var key in newDelegate.data) if (key in oldData === false) {
dataChanged = true;
delta[key] = undefined;
}
for (var key in oldData) if (oldData[key] !== newDelegate.data[key]) {
dataChanged = true;
delta[key] = oldData[key];
}
}
} else {
this.delegate = null;
this.target = null;
this.root = this;
this.data = {};
for (var key in oldData) this.data[key] = oldData[key];
}
applyDelegateChanges(this, oldRoot, oldTarget);
if (dataChanged) this.emit_update(delta);
if (delta && oldState !== this.state && (String(oldState) != this.state || oldState.data !== this.state.data)) this.emit_stateChanged(oldState);
this.emit_delegateChanged(oldDelegate);
return true;
}
return false;
},
setState: function(state, data) {
if (this.delegate) return this.root.setState(state, data); else return AbstractData.prototype.setState.call(this, state, data);
},
update: function(data) {
if (this.delegate) return this.root.update(data);
if (data) {
var delta = {};
var changed = false;
for (var prop in data) if (this.data[prop] !== data[prop]) {
changed = true;
delta[prop] = this.data[prop];
this.data[prop] = data[prop];
}
if (changed) {
this.emit_update(delta);
return delta;
}
}
return false;
},
destroy: function() {
AbstractData.prototype.destroy.call(this);
var cursor = this.delegates_;
this.delegates_ = null;
while (cursor) {
cursor.delegate.setDelegate();
cursor = cursor.next;
}
if (this.delegate) this.setDelegate();
this.data = NULL_OBJECT;
this.root = null;
this.target = null;
}
});
var Slot = Class(DataObject, {
className: namespace + ".Slot"
});
var KEYOBJECTMAP_MEMBER_HANDLER = {
destroy: function() {
delete this.map[this.id];
}
};
var KeyObjectMap = Class(AbstractData, {
className: namespace + ".KeyObjectMap",
itemClass: DataObject,
keyGetter: $self,
autoDestroyMembers: true,
map_: null,
extendConstructor_: true,
init: function() {
this.map_ = {};
AbstractData.prototype.init.call(this);
},
resolve: function(object) {
return this.get(this.keyGetter(object), object);
},
create: function(key, object) {
var itemConfig;
if (key instanceof DataObject) itemConfig = {
delegate: key
}; else itemConfig = {
data: {
id: key,
title: key
}
};
return new this.itemClass(itemConfig);
},
get: function(key, autocreate) {
var itemId = key instanceof DataObject ? key.basisObjectId : key;
var itemInfo = this.map_[itemId];
if (!itemInfo && autocreate) {
itemInfo = this.map_[itemId] = {
map: this.map_,
id: itemId,
item: this.create(key, autocreate)
};
itemInfo.item.addHandler(KEYOBJECTMAP_MEMBER_HANDLER, itemInfo);
}
if (itemInfo) return itemInfo.item;
},
destroy: function() {
AbstractData.prototype.destroy.call(this);
var map = this.map_;
this.map_ = null;
for (var itemId in map) {
var itemInfo = map[itemId];
if (this.autoDestroyMembers) itemInfo.item.destroy(); else itemInfo.item.removeHandler(KEYOBJECTMAP_MEMBER_HANDLER, itemInfo);
}
}
});
function getDelta(inserted, deleted) {
var delta = {};
var result;
if (inserted && inserted.length) result = delta.inserted = inserted;
if (deleted && deleted.length) result = delta.deleted = deleted;
if (result) return delta;
}
function getDatasetDelta(a, b) {
if (!a || !a.itemCount) {
if (b && b.itemCount) return {
inserted: b.getItems()
};
} else {
if (!b || !b.itemCount) {
if (a.itemCount) return {
deleted: a.getItems()
};
} else {
var inserted = [];
var deleted = [];
for (var key in a.items_) {
var item = a.items_[key];
if (item.basisObjectId in b.items_ == false) deleted.push(item);
}
for (var key in b.items_) {
var item = b.items_[key];
if (item.basisObjectId in a.items_ == false) inserted.push(item);
}
return getDelta(inserted, deleted);
}
}
}
var DatasetWrapper = Class(DataObject, {
className: namespace + ".DatasetWrapper",
subscribeTo: DataObject.prototype.subscribeTo + SUBSCRIPTION.DATASET,
listen: {
dataset: {
itemsChanged: function(dataset, delta) {
this.itemCount = dataset.itemCount;
this.emit_itemsChanged(delta);
},
destroy: function() {
this.setDataset();
}
}
},
dataset: null,
datasetAdapter_: null,
emit_datasetChanged: createEvent("datasetChanged", "oldDataset"),
emit_itemsChanged: createEvent("itemsChanged", "delta"),
init: function() {
DataObject.prototype.init.call(this);
var dataset = this.dataset;
if (dataset) {
this.dataset = null;
this.setDataset(dataset);
}
},
setDataset: function(dataset) {
dataset = resolveDataset(this, this.setDataset, dataset, "datasetAdapter_");
if (this.dataset !== dataset) {
var listenHandler = this.listen.dataset;
var oldDataset = this.dataset;
var delta;
if (listenHandler) {
if (oldDataset) oldDataset.removeHandler(listenHandler, this);
if (dataset) dataset.addHandler(listenHandler, this);
}
this.itemCount = dataset ? dataset.itemCount : 0;
if (delta = getDatasetDelta(oldDataset, dataset)) this.emit_itemsChanged(delta);
this.dataset = dataset;
this.emit_datasetChanged(oldDataset);
}
},
has: function(object) {
return this.dataset ? this.dataset.has(object) : null;
},
getItems: function() {
return this.dataset ? this.dataset.getItems() : [];
},
pick: function() {
return this.dataset ? this.dataset.pick() : null;
},
top: function(count) {
return this.dataset ? this.dataset.top(count) : [];
},
forEach: function(fn) {
if (this.dataset) return this.dataset.forEach(fn);
},
destroy: function() {
if (this.dataset || this.datasetAdapter_) this.setDataset();
DataObject.prototype.destroy.call(this);
}
});
var ReadOnlyDataset = Class(AbstractData, {
className: namespace + ".ReadOnlyDataset",
itemCount: 0,
items_: null,
members_: null,
cache_: null,
emit_itemsChanged: createEvent("itemsChanged", "delta") && function(delta) {
var items;
var insertCount = 0;
var deleteCount = 0;
var object;
if (items = delta.inserted) {
while (object = items[insertCount]) {
this.items_[object.basisObjectId] = object;
insertCount++;
}
}
if (items = delta.deleted) {
while (object = items[deleteCount]) {
delete this.items_[object.basisObjectId];
deleteCount++;
}
}
this.itemCount += insertCount - deleteCount;
this.cache_ = insertCount == this.itemCount ? delta.inserted : null;
events.itemsChanged.call(this, delta);
},
init: function() {
AbstractData.prototype.init.call(this);
this.members_ = {};
this.items_ = {};
},
has: function(object) {
return !!(object && this.items_[object.basisObjectId]);
},
getItems: function() {
if (!this.cache_) this.cache_ = values(this.items_);
return this.cache_;
},
getValues: function(getter) {
return this.getItems().map(basis.getter(getter || $self));
},
pick: function() {
for (var objectId in this.items_) return this.items_[objectId];
return null;
},
top: function(count) {
var result = [];
if (count) for (var objectId in this.items_) if (result.push(this.items_[objectId]) >= count) break;
return result;
},
forEach: function(fn) {
var items = this.getItems();
for (var i = 0; i < items.length; i++) fn(items[i]);
},
destroy: function() {
AbstractData.prototype.destroy.call(this);
this.cache_ = EMPTY_ARRAY;
this.itemCount = 0;
this.members_ = null;
this.items_ = null;
}
});
var Dataset = Class(ReadOnlyDataset, {
className: namespace + ".Dataset",
listen: {
item: {
destroy: function(object) {
this.remove([ object ]);
}
}
},
init: function() {
ReadOnlyDataset.prototype.init.call(this);
var items = this.items;
if (items) {
this.items = null;
this.set(items);
}
},
add: function(items) {
var memberMap = this.members_;
var listenHandler = this.listen.item;
var inserted = [];
var delta;
if (items && !Array.isArray(items)) items = [ items ];
for (var i = 0; i < items.length; i++) {
var object = items[i];
if (object instanceof DataObject) {
var objectId = object.basisObjectId;
if (!memberMap[objectId]) {
memberMap[objectId] = object;
if (listenHandler) object.addHandler(listenHandler, this);
inserted.push(object);
}
} else {
basis.dev.warn("Wrong data type: value should be an instance of basis.data.Object");
}
}
if (inserted.length) {
this.emit_itemsChanged(delta = {
inserted: inserted
});
}
return delta;
},
remove: function(items) {
var memberMap = this.members_;
var listenHandler = this.listen.item;
var deleted = [];
var delta;
if (items && !Array.isArray(items)) items = [ items ];
for (var i = 0; i < items.length; i++) {
var object = items[i];
if (object instanceof DataObject) {
var objectId = object.basisObjectId;
if (memberMap[objectId]) {
if (listenHandler) object.removeHandler(listenHandler, this);
delete memberMap[objectId];
deleted.push(object);
}
} else {
basis.dev.warn("Wrong data type: value should be an instance of basis.data.Object");
}
}
if (deleted.length) {
this.emit_itemsChanged(delta = {
deleted: deleted
});
}
return delta;
},
set: function(items) {
if (!this.itemCount) return this.add(items);
if (!items || !items.length) return this.clear();
var memberMap = this.members_;
var listenHandler = this.listen.item;
var exists = {};
var deleted = [];
var inserted = [];
var object;
var objectId;
var delta;
for (var i = 0; i < items.length; i++) {
object = items[i];
if (object instanceof DataObject) {
objectId = object.basisObjectId;
exists[objectId] = object;
if (!memberMap[objectId]) {
memberMap[objectId] = object;
if (listenHandler) object.addHandler(listenHandler, this);
inserted.push(object);
}
} else {
basis.dev.warn("Wrong data type: value should be an instance of basis.data.Object");
}
}
for (var objectId in memberMap) {
if (!exists[objectId]) {
object = memberMap[objectId];
if (listenHandler) object.removeHandler(listenHandler, this);
delete memberMap[objectId];
deleted.push(object);
}
}
if (delta = getDelta(inserted, deleted)) this.emit_itemsChanged(delta);
return delta;
},
sync: function(items) {
var delta = this.set(items) || {};
var deleted = delta.deleted;
Dataset.setAccumulateState(true);
if (deleted) for (var i = 0, object; object = deleted[i]; i++) object.destroy();
Dataset.setAccumulateState(false);
return delta.inserted;
},
clear: function() {
var deleted = this.getItems();
var listenHandler = this.listen.item;
var delta;
if (deleted.length) {
if (listenHandler) for (var i = 0; i < deleted.length; i++) deleted[i].removeHandler(listenHandler, this);
this.emit_itemsChanged(delta = {
deleted: deleted
});
this.members_ = {};
}
return delta;
},
destroy: function() {
this.clear();
ReadOnlyDataset.prototype.destroy.call(this);
}
});
var ResolveAdapter = function(context, fn, source, handler) {
this.context = context;
this.fn = fn;
this.source = source;
this.handler = handler;
};
ResolveAdapter.prototype = {
context: null,
fn: null,
source: null,
handler: null,
next: null,
attach: function() {
this.source.addHandler(this.handler, this);
},
detach: function() {
this.source.removeHandler(this.handler, this);
},
proxy: function() {
this.fn.call(this.context, this.source);
}
};
var BBResolveAdapter = function() {
ResolveAdapter.apply(this, arguments);
};
BBResolveAdapter.prototype = new ResolveAdapter;
BBResolveAdapter.prototype.attach = function() {
this.source.bindingBridge.attach(this.source, this.handler, this);
};
BBResolveAdapter.prototype.detach = function() {
this.source.bindingBridge.detach(this.source, this.handler, this);
};
var TOKEN_ADAPTER_HANDLER = function() {
this.fn.call(this.context, this.source);
};
var DATASETWRAPPER_ADAPTER_HANDLER = {
datasetChanged: function() {
this.fn.call(this.context, this.source);
},
destroy: function() {
this.fn.call(this.context, null);
}
};
var VALUE_ADAPTER_HANDLER = {
change: function() {
this.fn.call(this.context, this.source);
},
destroy: function() {
this.fn.call(this.context, null);
}
};
function resolveDataset(context, fn, source, property) {
var oldAdapter = context[property] || null;
var newAdapter = null;
if (typeof source == "function") source = source.call(context, context);
if (source) {
if (source instanceof DatasetWrapper) {
newAdapter = new ResolveAdapter(context, fn, source, DATASETWRAPPER_ADAPTER_HANDLER);
source = source.dataset;
} else if (source instanceof Value) {
newAdapter = new ResolveAdapter(context, fn, source, VALUE_ADAPTER_HANDLER);
source = resolveDataset(newAdapter, newAdapter.proxy, source.value, "next");
} else if (source.bindingBridge) {
newAdapter = new BBResolveAdapter(context, fn, source, TOKEN_ADAPTER_HANDLER);
source = resolveDataset(newAdapter, newAdapter.proxy, source.value, "next");
}
}
if (source instanceof ReadOnlyDataset == false) source = null;
if (property && oldAdapter !== newAdapter) {
if (oldAdapter) {
oldAdapter.detach();
if (oldAdapter.next) resolveDataset(oldAdapter, null, null, "next");
}
if (newAdapter) newAdapter.attach();
context[property] = newAdapter;
}
return source;
}
function resolveObject(context, fn, source, property) {
var oldAdapter = context[property] || null;
var newAdapter = null;
if (typeof source == "function") source = source.call(context, context);
if (source) {
if (source instanceof Value) {
newAdapter = new ResolveAdapter(context, fn, source, VALUE_ADAPTER_HANDLER);
source = resolveObject(newAdapter, newAdapter.proxy, source.value, "next");
} else if (source.bindingBridge) {
newAdapter = new BBResolveAdapter(context, fn, source, TOKEN_ADAPTER_HANDLER);
source = resolveObject(newAdapter, newAdapter.proxy, source.value, "next");
}
}
if (source instanceof DataObject == false) source = null;
if (property && oldAdapter !== newAdapter) {
if (oldAdapter) {
oldAdapter.detach();
if (oldAdapter.next) resolveObject(oldAdapter, null, null, "next");
}
if (newAdapter) newAdapter.attach();
context[property] = newAdapter;
}
return source;
}
Dataset.setAccumulateState = function() {
var proto = ReadOnlyDataset.prototype;
var eventCache = {};
var setStateCount = 0;
var urgentTimer;
var realEvent;
function flushCache(cache) {
var dataset = cache.dataset;
realEvent.call(dataset, cache);
}
function flushAllDataset() {
var eventCacheCopy = eventCache;
eventCache = {};
for (var datasetId in eventCacheCopy) {
var entry = eventCacheCopy[datasetId];
if (entry) flushCache(entry);
}
}
function storeDatasetDelta(delta) {
var dataset = this;
var datasetId = dataset.basisObjectId;
var inserted = delta.inserted;
var deleted = delta.deleted;
var cache = eventCache[datasetId];
if (inserted && deleted || cache && cache.mixed) {
if (cache) {
eventCache[datasetId] = null;
flushCache(cache);
}
realEvent.call(dataset, delta);
return;
}
if (cache) {
var mode = inserted ? "inserted" : "deleted";
var array = cache[mode];
if (!array) {
var inCacheMode = inserted ? "deleted" : "inserted";
var inCache = cache[inCacheMode];
var inCacheMap = {};
var deltaItems = inserted || deleted;
var newInCacheItems = [];
var inCacheRemoves = 0;
for (var i = 0; i < inCache.length; i++) inCacheMap[inCache[i].basisObjectId] = i;
for (var i = 0; i < deltaItems.length; i++) {
var id = deltaItems[i].basisObjectId;
if (id in inCacheMap == false) {
newInCacheItems.push(deltaItems[i]);
} else {
if (!inCacheRemoves) inCache = sliceArray.call(inCache);
inCacheRemoves++;
inCache[inCacheMap[id]] = null;
}
}
if (inCacheRemoves) {
if (inCacheRemoves < inCache.length) {
inCache = inCache.filter(Boolean);
} else {
inCache = null;
}
cache[inCacheMode] = inCache;
}
if (!newInCacheItems.length) {
newInCacheItems = null;
if (!inCache) eventCache[datasetId] = null;
} else {
cache[mode] = newInCacheItems;
if (inCache) cache.mixed = true;
}
} else array.push.apply(array, inserted || deleted);
return;
}
eventCache[datasetId] = {
inserted: inserted,
deleted: deleted,
dataset: dataset,
mixed: false
};
}
function urgentFlush() {
urgentTimer = null;
if (setStateCount) {
basis.dev.warn("(debug) Urgent flush dataset changes");
setStateCount = 0;
setAccumulateStateOff();
}
}
function setAccumulateStateOff() {
proto.emit_itemsChanged = realEvent;
flushAllDataset();
}
return function(state) {
if (state) {
if (setStateCount == 0) {
realEvent = proto.emit_itemsChanged;
proto.emit_itemsChanged = storeDatasetDelta;
if (!urgentTimer) urgentTimer = basis.setImmediate(urgentFlush);
}
setStateCount++;
} else {
setStateCount -= setStateCount > 0;
if (setStateCount == 0) setAccumulateStateOff();
}
};
}();
function wrapData(data) {
if (Array.isArray(data)) return data.map(function(item) {
return {
data: item
};
}); else return {
data: data
};
}
function wrapObject(data) {
if (!data || data.constructor !== Object) data = {
value: data
};
return new DataObject({
data: data
});
}
function wrap(value, retObject) {
var wrapper = retObject ? wrapObject : wrapData;
return Array.isArray(value) ? value.map(wrapper) : wrapper(value);
}
module.exports = {
STATE: STATE,
SUBSCRIPTION: SUBSCRIPTION,
AbstractData: AbstractData,
Value: Value,
Object: DataObject,
Slot: Slot,
KeyObjectMap: KeyObjectMap,
ReadOnlyDataset: ReadOnlyDataset,
Dataset: Dataset,
DatasetWrapper: DatasetWrapper,
isConnected: isConnected,
getDatasetDelta: getDatasetDelta,
ResolveAdapter: ResolveAdapter,
resolveDataset: resolveDataset,
resolveObject: resolveObject,
wrapData: wrapData,
wrapObject: wrapObject,
wrap: wrap
};
},
"5.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./3.js");
basis.require("./4.js");
var namespace = this.path;
var Class = basis.Class;
var complete = basis.object.complete;
var arrayFrom = basis.array;
var arrayRemove = basis.array.remove;
var $undef = basis.fn.$undef;
var getter = basis.getter;
var nullGetter = basis.fn.nullGetter;
var oneFunctionProperty = Class.oneFunctionProperty;
var createEvent = basis.event.create;
var events = basis.event.events;
var SUBSCRIPTION = basis.data.SUBSCRIPTION;
var STATE = basis.data.STATE;
var AbstractData = basis.data.AbstractData;
var DataObject = basis.data.Object;
var ReadOnlyDataset = basis.data.ReadOnlyDataset;
var Dataset = basis.data.Dataset;
var DatasetWrapper = basis.data.DatasetWrapper;
var EXCEPTION_CANT_INSERT = namespace + ": Node can't be inserted at specified point in hierarchy";
var EXCEPTION_NODE_NOT_FOUND = namespace + ": Node was not found";
var EXCEPTION_BAD_CHILD_CLASS = namespace + ": Child node has wrong class";
var EXCEPTION_NULL_CHILD = namespace + ": Child node is null";
var EXCEPTION_DATASOURCE_CONFLICT = namespace + ": Operation is not allowed because node is under dataSource control";
var EXCEPTION_DATASOURCEADAPTER_CONFLICT = namespace + ": Operation is not allowed because node is under dataSource adapter control";
var EXCEPTION_PARENTNODE_OWNER_CONFLICT = namespace + ": Node can't has owner and parentNode";
var EXCEPTION_NO_CHILDCLASS = namespace + ": Node can't has children and dataSource as childClass isn't specified";
var DELEGATE = {
ANY: true,
NONE: false,
PARENT: "parent",
OWNER: "owner"
};
var childNodesDatasetMap = {};
function warnOnDataSourceItemNodeDestoy() {
basis.dev.warn(namespace + ": node can't be destroyed as representing dataSource item, destroy delegate item or remove it from dataSource first");
}
function warnOnAutoSatelliteOwnerChange() {
basis.dev.warn(namespace + ": satellite can't change owner as it auto-satellite");
}
function warnOnAutoSatelliteDestoy() {
basis.dev.warn(namespace + ": satellite can't be destroyed as it auto-create satellite, and could be destroyed on owner destroy");
}
function lockDataSourceItemNode(node) {
node.setDelegate = basis.fn.$undef;
node.destroy = warnOnDataSourceItemNodeDestoy;
}
function unlockDataSourceItemNode(node) {
var proto = node.constructor.prototype;
node.setDelegate = proto.setDelegate;
node.destroy = proto.destroy;
}
function sortingSearch(node) {
return node.sortingValue || 0;
}
function sortAsc(a, b) {
a = a.sortingValue || 0;
b = b.sortingValue || 0;
return +(a > b) || -(a < b);
}
function sortDesc(a, b) {
a = a.sortingValue || 0;
b = b.sortingValue || 0;
return -(a > b) || +(a < b);
}
function sortChildNodes(obj) {
return obj.childNodes.sort(obj.sortingDesc ? sortDesc : sortAsc);
}
function binarySearchPos(array, value, getter_, desc) {
if (!array.length) return 0;
desc = !!desc;
var pos;
var compareValue;
var l = 0;
var r = array.length - 1;
do {
pos = l + r >> 1;
compareValue = getter_(array[pos]);
if (desc ? value > compareValue : value < compareValue) r = pos - 1; else if (desc ? value < compareValue : value > compareValue) l = pos + 1; else return value == compareValue ? pos : 0;
} while (l <= r);
return pos + (compareValue < value ^ desc);
}
function updateNodeContextSelection(root, oldSelection, newSelection, rootUpdate, ignoreRootSelection) {
if (oldSelection === newSelection) return;
var nextNode;
var cursor = root;
var selected = [];
if (rootUpdate) {
root.contextSelection = newSelection;
if (root.selected) selected.push(root);
}
while (cursor) {
nextNode = !cursor.selection || ignoreRootSelection && cursor === root ? cursor.firstChild : null;
if (nextNode && nextNode.contextSelection !== oldSelection) throw "Try change wrong context selection";
while (!nextNode) {
if (cursor === root) {
if (selected.length) {
if (oldSelection) oldSelection.remove(selected);
if (newSelection) newSelection.add(selected);
}
return;
}
nextNode = cursor.nextSibling;
if (!nextNode) cursor = cursor.parentNode;
}
cursor = nextNode;
if (cursor.selected) selected.push(cursor);
cursor.contextSelection = newSelection;
}
}
function updateNodeDisableContext(node, disabled) {
if (node.contextDisabled != disabled) {
node.contextDisabled = disabled;
if (node.disabled) return;
if (disabled) node.emit_disable(); else node.emit_enable();
}
}
SUBSCRIPTION.addProperty("owner");
SUBSCRIPTION.addProperty("dataSource");
function processSatelliteConfig(value) {
if (!value) return null;
if (value.isSatelliteConfig) return value;
if (value instanceof AbstractNode) return value;
if (Class.isClass(value)) value = {
instanceOf: value
};
if (value && value.constructor === Object) {
var handlerRequired = false;
var config = {
isSatelliteConfig: true
};
var instanceClass;
for (var key in value) switch (key) {
case "instance":
if (value[key] instanceof AbstractNode) config[key] = value[key]; else {
basis.dev.warn(namespace + ": `instance` value in satellite config must be an instance of basis.dom.wrapper.AbstractNode");
}
break;
case "instanceOf":
if (Class.isClass(value[key]) && value[key].isSubclassOf(AbstractNode)) instanceClass = value[key]; else {
basis.dev.warn(namespace + ": `instanceOf` value in satellite config must be a subclass of basis.dom.wrapper.AbstractNode");
}
break;
case "existsIf":
case "delegate":
case "dataSource":
handlerRequired = true;
config[key] = getter(value[key]);
break;
case "config":
config[key] = value[key];
break;
}
if (!config.instance) config.instanceOf = instanceClass || AbstractNode; else {
if (instanceClass) basis.dev.warn(namespace + ": `instanceOf` can't be set with `instance` value in satellite config, value ignored");
}
if (handlerRequired) {
var events = "events" in value ? value.events : "update";
if (Array.isArray(events)) events = events.join(" ");
if (typeof events == "string") {
var handler = {};
events = events.split(/\s+/);
for (var i = 0, eventName; eventName = events[i]; i++) {
handler[eventName] = SATELLITE_UPDATE;
config.handler = handler;
}
}
}
return config;
}
return null;
}
function applySatellites(node, satellites) {
for (var name in satellites) if (satellites[name] && typeof satellites[name] == "object") node.setSatellite(name, satellites[name]);
}
var NULL_SATELLITE = Class.customExtendProperty({}, function(result, extend) {
for (var name in extend) result[name] = processSatelliteConfig(extend[name]);
});
var SATELLITE_UPDATE = function(owner) {
var name = this.name;
var config = this.config;
var exists = !config.existsIf || config.existsIf(owner);
var satellite = owner.satellite[name];
if (exists) {
if (satellite) {
if (config.delegate) satellite.setDelegate(config.delegate(owner));
if (config.dataSource) satellite.setDataSource(config.dataSource(owner));
} else {
satellite = config.instance;
if (!satellite) {
var listenHandler;
var satelliteConfig = (typeof config.config == "function" ? config.config(owner) : config.config) || {};
satelliteConfig.owner = owner;
if (config.delegate) {
satelliteConfig.autoDelegate = false;
satelliteConfig.delegate = config.delegate(owner);
}
if (config.dataSource) satelliteConfig.dataSource = config.dataSource(owner);
satellite = new config.instanceOf(satelliteConfig);
satellite.destroy = warnOnAutoSatelliteDestoy;
if (listenHandler = owner.listen.satellite) satellite.addHandler(listenHandler, owner);
if (listenHandler = owner.listen["satellite:" + name]) satellite.addHandler(listenHandler, owner);
} else {
if (config.delegate) satellite.setDelegate(config.delegate(owner));
if (config.dataSource) satellite.setDataSource(config.dataSource(owner));
}
owner.satellite.__auto__[name].instance = satellite;
owner.setSatellite(name, satellite, true);
}
} else {
if (satellite) {
if (config.instance) {
if (config.delegate) satellite.setDelegate();
if (config.dataSource) satellite.setDataSource();
}
owner.satellite.__auto__[name].instance = null;
owner.setSatellite(name, null, true);
}
}
};
var AUTO_SATELLITE_INSTANCE_HANDLER = {
destroy: function() {
this.owner.setSatellite(this.name, null);
}
};
var AbstractNode = Class(DataObject, {
className: namespace + ".AbstractNode",
subscribeTo: DataObject.prototype.subscribeTo + SUBSCRIPTION.DATASOURCE,
isSyncRequired: function() {
return this.state == STATE.UNDEFINED || this.state == STATE.DEPRECATED;
},
syncEvents: {
activeChanged: false
},
emit_update: function(delta) {
DataObject.prototype.emit_update.call(this, delta);
var parentNode = this.parentNode;
if (parentNode) {
if (parentNode.matchFunction) this.match(parentNode.matchFunction);
parentNode.insertBefore(this, this.nextSibling);
}
},
listen: {
owner: {
destroy: function() {
if (!this.ownerSatelliteName) this.setOwner();
}
}
},
autoDelegate: DELEGATE.NONE,
name: null,
childNodes: null,
emit_childNodesModified: createEvent("childNodesModified", "delta") && function(delta) {
events.childNodesModified.call(this, delta);
var listen = this.listen.childNode;
var array;
if (listen) {
if (array = delta.inserted) for (var i = 0, child; child = array[i]; i++) child.addHandler(listen, this);
if (array = delta.deleted) for (var i = 0, child; child = array[i]; i++) child.removeHandler(listen, this);
}
},
childNodesState: STATE.UNDEFINED,
emit_childNodesStateChanged: createEvent("childNodesStateChanged", "oldState"),
childClass: AbstractNode,
dataSource: null,
emit_dataSourceChanged: createEvent("dataSourceChanged", "oldDataSource"),
dataSourceAdapter_: null,
dataSourceMap_: null,
destroyDataSourceMember: true,
parentNode: null,
nextSibling: null,
previousSibling: null,
firstChild: null,
lastChild: null,
sorting: nullGetter,
sortingDesc: false,
emit_sortingChanged: createEvent("sortingChanged", "oldSorting", "oldSortingDesc"),
groupingClass: null,
grouping: null,
emit_groupingChanged: createEvent("groupingChanged", "oldGrouping"),
groupNode: null,
groupId: NaN,
satellite: NULL_SATELLITE,
ownerSatelliteName: null,
emit_satelliteChanged: createEvent("satelliteChanged", "name", "oldSatellite"),
owner: null,
emit_ownerChanged: createEvent("ownerChanged", "oldOwner"),
init: function() {
DataObject.prototype.init.call(this);
var childNodes = this.childNodes;
var dataSource = this.dataSource;
if (childNodes) this.childNodes = null;
if (dataSource) this.dataSource = null;
var grouping = this.grouping;
if (grouping) {
this.grouping = null;
this.setGrouping(grouping);
}
if (this.childClass) {
this.childNodes = [];
if (dataSource) {
this.setDataSource(dataSource);
} else {
if (childNodes) this.setChildNodes(childNodes);
}
}
var satellites = this.satellite;
if (satellites !== NULL_SATELLITE) {
this.satellite = NULL_SATELLITE;
applySatellites(this, satellites);
}
var owner = this.owner;
if (owner) {
this.owner = null;
this.setOwner(owner);
}
},
setChildNodesState: function(state, data) {
var stateCode = String(state);
var oldState = this.childNodesState;
if (!STATE.values[stateCode]) throw new Error("Wrong state value");
if (oldState != stateCode || oldState.data != data) {
this.childNodesState = Object(stateCode);
this.childNodesState.data = data;
this.emit_childNodesStateChanged(oldState);
}
},
appendChild: function(newChild) {},
insertBefore: function(newChild, refChild) {},
removeChild: function(oldChild) {},
replaceChild: function(newChild, oldChild) {},
clear: function(alive) {},
setChildNodes: function(nodes) {},
setGrouping: function(grouping, alive) {},
setSorting: function(sorting, desc) {},
setDataSource: function(dataSource) {},
setOwner: function(owner) {
if (!owner || owner instanceof AbstractNode == false) owner = null;
if (owner && this.parentNode) throw EXCEPTION_PARENTNODE_OWNER_CONFLICT;
var oldOwner = this.owner;
if (oldOwner !== owner) {
var listenHandler = this.listen.owner;
if (oldOwner) {
if (this.ownerSatelliteName && oldOwner.satellite.__auto__ && this.ownerSatelliteName in oldOwner.satellite.__auto__) {
basis.dev.warn(namespace + ": auto-satellite can't change it's owner");
return;
}
if (listenHandler) oldOwner.removeHandler(listenHandler, this);
if (this.ownerSatelliteName) {
this.owner = null;
oldOwner.setSatellite(this.ownerSatelliteName, null);
}
}
if (owner && listenHandler) owner.addHandler(listenHandler, this);
this.owner = owner;
this.emit_ownerChanged(oldOwner);
if (this.autoDelegate == DELEGATE.OWNER || this.autoDelegate === DELEGATE.ANY) this.setDelegate(owner);
}
},
setSatellite: function(name, satellite, autoSet) {
var oldSatellite = this.satellite[name] || null;
var auto = this.satellite.__auto__;
var autoConfig = auto && auto[name];
var preserveAuto = autoSet && autoConfig;
if (preserveAuto) {
satellite = autoConfig.instance;
if (autoConfig.config.instance) {
if (satellite) delete autoConfig.config.instance.setOwner;
}
} else {
satellite = processSatelliteConfig(satellite);
if (satellite && satellite.owner && auto && satellite.ownerSatelliteName && auto[satellite.ownerSatelliteName]) {
basis.dev.warn(namespace + ": auto-create satellite can't change name inside owner");
return;
}
if (autoConfig) {
delete auto[name];
if (autoConfig.config.instance) autoConfig.config.instance.removeHandler(AUTO_SATELLITE_INSTANCE_HANDLER, autoConfig);
if (autoConfig.config.handler) this.removeHandler(autoConfig.config.handler, autoConfig);
}
}
if (oldSatellite !== satellite) {
var satelliteListen = this.listen.satellite;
var satellitePersonalListen = this.listen["satellite:" + name];
var destroySatellite;
if (oldSatellite) {
delete this.satellite[name];
oldSatellite.ownerSatelliteName = null;
if (autoConfig && oldSatellite.destroy === warnOnAutoSatelliteDestoy) {
destroySatellite = oldSatellite;
} else {
if (satelliteListen) oldSatellite.removeHandler(satelliteListen, this);
if (satellitePersonalListen) oldSatellite.removeHandler(satellitePersonalListen, this);
oldSatellite.setOwner(null);
}
if (preserveAuto && !satellite && autoConfig.config.instance) autoConfig.config.instance.setOwner = warnOnAutoSatelliteOwnerChange;
}
if (satellite) {
if (satellite instanceof AbstractNode == false) {
var autoConfig = {
owner: this,
name: name,
config: satellite,
instance: null
};
if (satellite.handler) this.addHandler(satellite.handler, autoConfig);
if (satellite.instance) {
satellite.instance.addHandler(AUTO_SATELLITE_INSTANCE_HANDLER, autoConfig);
satellite.instance.setOwner = warnOnAutoSatelliteOwnerChange;
}
if (!auto) {
if (this.satellite === NULL_SATELLITE) this.satellite = {};
auto = this.satellite.__auto__ = {};
}
auto[name] = autoConfig;
SATELLITE_UPDATE.call(autoConfig, this);
if (!autoConfig.instance && oldSatellite) this.emit_satelliteChanged(name, oldSatellite);
if (destroySatellite) {
delete destroySatellite.destroy;
destroySatellite.destroy();
}
return;
}
if (satellite.owner !== this) {
if (autoConfig && autoConfig.config.delegate) {
var autoDelegate = satellite.autoDelegate;
satellite.autoDelegate = false;
satellite.setOwner(this);
satellite.autoDelegate = autoDelegate;
} else satellite.setOwner(this);
if (satellite.owner !== this) return;
if (satelliteListen) satellite.addHandler(satelliteListen, this);
if (satellitePersonalListen) satellite.addHandler(satellitePersonalListen, this);
} else {
if (satellite.ownerSatelliteName) {
delete this.satellite[satellite.ownerSatelliteName];
this.emit_satelliteChanged(satellite.ownerSatelliteName, satellite);
}
}
if (this.satellite == NULL_SATELLITE) this.satellite = {};
this.satellite[name] = satellite;
satellite.ownerSatelliteName = name;
}
this.emit_satelliteChanged(name, oldSatellite);
if (destroySatellite) {
delete destroySatellite.destroy;
destroySatellite.destroy();
}
}
},
getChildNodesDataset: function() {
return childNodesDatasetMap[this.basisObjectId] || new ChildNodesDataset({
sourceNode: this
});
},
destroy: function() {
DataObject.prototype.destroy.call(this);
if (this.dataSource || this.dataSourceAdapter_) {
this.setDataSource();
} else {
if (this.firstChild) this.clear();
}
if (this.parentNode) this.parentNode.removeChild(this);
if (this.grouping) {
this.grouping.setOwner();
this.grouping = null;
}
if (this.owner) this.setOwner();
var satellites = this.satellite;
if (satellites !== NULL_SATELLITE) {
var auto = satellites.__auto__;
delete satellites.__auto__;
for (var name in auto) if (auto[name].config.instance && !auto[name].instance) auto[name].config.instance.destroy();
for (var name in satellites) {
var satellite = satellites[name];
satellite.owner = null;
satellite.ownerSatelliteName = null;
if (satellite.destroy === warnOnAutoSatelliteDestoy) delete satellite.destroy;
satellite.destroy();
}
this.satellite = null;
}
this.childNodes = null;
this.parentNode = null;
this.previousSibling = null;
this.nextSibling = null;
this.firstChild = null;
this.lastChild = null;
}
});
var PartitionNode = Class(AbstractNode, {
className: namespace + ".PartitionNode",
autoDestroyIfEmpty: false,
nodes: null,
first: null,
last: null,
init: function() {
this.nodes = [];
AbstractNode.prototype.init.call(this);
},
insert: function(newNode, refNode) {
var nodes = this.nodes;
var pos = refNode ? nodes.indexOf(refNode) : -1;
if (pos == -1) {
nodes.push(newNode);
this.last = newNode;
} else nodes.splice(pos, 0, newNode);
this.first = nodes[0];
newNode.groupNode = this;
this.emit_childNodesModified({
inserted: [ newNode ]
});
},
remove: function(oldNode) {
var nodes = this.nodes;
if (arrayRemove(nodes, oldNode)) {
this.first = nodes[0] || null;
this.last = nodes[nodes.length - 1] || null;
oldNode.groupNode = null;
this.emit_childNodesModified({
deleted: [ oldNode ]
});
}
if (!this.first && this.autoDestroyIfEmpty) this.destroy();
},
clear: function() {
if (!this.first) return;
var nodes = this.nodes;
for (var i = nodes.length; i-- > 0; ) nodes[i].groupNode = null;
this.nodes = [];
this.first = null;
this.last = null;
this.emit_childNodesModified({
deleted: nodes
});
if (this.autoDestroyIfEmpty) this.destroy();
},
destroy: function() {
AbstractNode.prototype.destroy.call(this);
this.nodes = null;
this.first = null;
this.last = null;
}
});
var DOMMIXIN_DATASOURCE_HANDLER = {
itemsChanged: function(dataSource, delta) {
var newDelta = {};
var deleted = [];
if (delta.deleted) {
newDelta.deleted = deleted;
if (this.childNodes.length == delta.deleted.length) {
deleted = arrayFrom(this.childNodes);
for (var i = 0, child; child = deleted[i]; i++) unlockDataSourceItemNode(child);
var tmp = this.dataSource;
this.dataSource = null;
this.clear(true);
this.dataSource = tmp;
this.dataSourceMap_ = {};
} else {
for (var i = 0, item; item = delta.deleted[i]; i++) {
var delegateId = item.basisObjectId;
var oldChild = this.dataSourceMap_[delegateId];
unlockDataSourceItemNode(oldChild);
delete this.dataSourceMap_[delegateId];
this.removeChild(oldChild);
deleted.push(oldChild);
}
}
}
if (delta.inserted) {
newDelta.inserted = [];
for (var i = 0, item; item = delta.inserted[i]; i++) {
var newChild = createChildByFactory(this, {
delegate: item
});
lockDataSourceItemNode(newChild);
this.dataSourceMap_[item.basisObjectId] = newChild;
newDelta.inserted.push(newChild);
if (this.firstChild) this.insertBefore(newChild);
}
}
if (!this.firstChild) this.setChildNodes(newDelta.inserted); else this.emit_childNodesModified(newDelta);
if (this.destroyDataSourceMember && deleted.length) for (var i = 0, item; item = deleted[i]; i++) item.destroy();
},
stateChanged: function(dataSource) {
this.setChildNodesState(dataSource.state, dataSource.state.data);
},
destroy: function(dataSource) {
if (!this.dataSourceAdapter_) this.setDataSource();
}
};
var MIXIN_DATASOURCE_WRAPPER_HANDLER = {
datasetChanged: function(wrapper) {
this.setDataSource(wrapper);
},
destroy: function() {
this.setDataSource();
}
};
function fastChildNodesOrder(node, order) {
var lastIndex = order.length - 1;
node.childNodes = order;
node.firstChild = order[0] || null;
node.lastChild = order[lastIndex] || null;
for (var orderNode, i = lastIndex; orderNode = order[i]; i--) {
orderNode.nextSibling = order[i + 1] || null;
orderNode.previousSibling = order[i - 1] || null;
node.insertBefore(orderNode, orderNode.nextSibling);
}
}
function fastChildNodesGroupOrder(node, order) {
for (var i = 0, child; child = order[i]; i++) child.groupNode.nodes.push(child);
order.length = 0;
for (var group = node.grouping.nullGroup; group; group = group.nextSibling) {
var nodes = group.nodes;
group.first = nodes[0] || null;
group.last = nodes[nodes.length - 1] || null;
order.push.apply(order, nodes);
group.emit_childNodesModified({
inserted: nodes
});
}
return order;
}
function createChildByFactory(node, config) {
var child;
if (typeof node.childFactory == "function") {
child = node.childFactory(config);
if (child instanceof node.childClass) return child;
}
if (!child) throw EXCEPTION_NULL_CHILD;
basis.dev.warn(EXCEPTION_BAD_CHILD_CLASS + " (expected " + (node.childClass && node.childClass.className) + " but " + (child && child.constructor && child.constructor.className) + ")");
throw EXCEPTION_BAD_CHILD_CLASS;
}
var DomMixin = {
childClass: AbstractNode,
childFactory: null,
listen: {
dataSource: DOMMIXIN_DATASOURCE_HANDLER
},
getChild: function(value, getter) {
return basis.array.search(this.childNodes, value, getter);
},
getChildByName: function(name) {
return this.getChild(name, "name");
},
appendChild: function(newChild) {
return this.insertBefore(newChild);
},
insertBefore: function(newChild, refChild) {
if (!this.childClass) throw EXCEPTION_NO_CHILDCLASS;
if (newChild.firstChild) {
var cursor = this;
while (cursor = cursor.parentNode) {
if (cursor === newChild) throw EXCEPTION_CANT_INSERT;
}
}
var isChildClassInstance = newChild && newChild instanceof this.childClass;
if (this.dataSource) {
if (!isChildClassInstance || !newChild.delegate || this.dataSourceMap_[newChild.delegate.basisObjectId] !== newChild) throw EXCEPTION_DATASOURCE_CONFLICT;
} else {
if (this.dataSourceAdapter_) throw EXCEPTION_DATASOURCEADAPTER_CONFLICT;
}
if (!isChildClassInstance) newChild = createChildByFactory(this, newChild instanceof DataObject ? {
delegate: newChild
} : newChild);
if (newChild.owner) throw EXCEPTION_PARENTNODE_OWNER_CONFLICT;
var isInside = newChild.parentNode === this;
var childNodes = this.childNodes;
var grouping = this.grouping;
var groupNodes;
var currentNewChildGroup = newChild.groupNode;
var group = null;
var sorting = this.sorting;
var sortingDesc;
var correctSortPos = false;
var newChildValue;
var pos = -1;
var nextSibling;
var prevSibling;
if (isInside) {
nextSibling = newChild.nextSibling;
prevSibling = newChild.previousSibling;
}
if (sorting !== nullGetter) {
refChild = null;
sortingDesc = this.sortingDesc;
newChildValue = sorting(newChild) || 0;
if (isInside) {
if (newChildValue === newChild.sortingValue) {
correctSortPos = true;
} else {
if ((!nextSibling || (sortingDesc ? nextSibling.sortingValue <= newChildValue : nextSibling.sortingValue >= newChildValue)) && (!prevSibling || (sortingDesc ? prevSibling.sortingValue >= newChildValue : prevSibling.sortingValue <= newChildValue))) {
newChild.sortingValue = newChildValue;
correctSortPos = true;
}
}
}
}
if (grouping) {
var cursor;
group = grouping.getGroupNode(newChild, true);
groupNodes = group.nodes;
if (currentNewChildGroup === group) if (correctSortPos || sorting === nullGetter && nextSibling === refChild) return newChild;
if (sorting !== nullGetter) {
if (currentNewChildGroup === group && correctSortPos) {
if (nextSibling && nextSibling.groupNode === group) pos = groupNodes.indexOf(nextSibling); else pos = groupNodes.length;
} else {
pos = binarySearchPos(groupNodes, newChildValue, sortingSearch, sortingDesc);
newChild.sortingValue = newChildValue;
}
} else {
if (refChild && refChild.groupNode === group) pos = groupNodes.indexOf(refChild); else pos = groupNodes.length;
}
if (pos < groupNodes.length) {
refChild = groupNodes[pos];
} else {
if (group.last) {
refChild = group.last.nextSibling;
} else {
cursor = group;
refChild = null;
while (cursor = cursor.nextSibling) if (refChild = cursor.first) break;
}
}
if (newChild === refChild || isInside && nextSibling === refChild) {
if (currentNewChildGroup !== group) {
if (currentNewChildGroup) currentNewChildGroup.remove(newChild);
group.insert(newChild, refChild);
}
return newChild;
}
pos = -1;
} else {
if (sorting !== nullGetter) {
if (correctSortPos) return newChild;
pos = binarySearchPos(childNodes, newChildValue, sortingSearch, sortingDesc);
refChild = childNodes[pos];
newChild.sortingValue = newChildValue;
if (newChild === refChild || isInside && nextSibling === refChild) return newChild;
} else {
if (refChild && refChild.parentNode !== this) throw EXCEPTION_NODE_NOT_FOUND;
if (isInside) {
if (nextSibling === refChild) return newChild;
if (newChild === refChild) throw EXCEPTION_CANT_INSERT;
}
}
}
if (isInside) {
if (nextSibling) {
nextSibling.previousSibling = prevSibling;
newChild.nextSibling = null;
} else this.lastChild = prevSibling;
if (prevSibling) {
prevSibling.nextSibling = nextSibling;
newChild.previousSibling = null;
} else this.firstChild = nextSibling;
if (pos == -1) arrayRemove(childNodes, newChild); else {
var oldPos = childNodes.indexOf(newChild);
childNodes.splice(oldPos, 1);
pos -= oldPos < pos;
}
if (currentNewChildGroup) {
currentNewChildGroup.remove(newChild);
currentNewChildGroup = null;
}
} else {
if (newChild.parentNode) newChild.parentNode.removeChild(newChild);
}
if (currentNewChildGroup != group) group.insert(newChild, refChild);
if (refChild) {
if (pos == -1) pos = childNodes.indexOf(refChild);
if (pos == -1) throw EXCEPTION_NODE_NOT_FOUND;
newChild.nextSibling = refChild;
childNodes.splice(pos, 0, newChild);
} else {
pos = childNodes.length;
childNodes.push(newChild);
refChild = {
previousSibling: this.lastChild
};
this.lastChild = newChild;
}
newChild.parentNode = this;
newChild.previousSibling = refChild.previousSibling;
if (pos == 0) this.firstChild = newChild; else refChild.previousSibling.nextSibling = newChild;
refChild.previousSibling = newChild;
if (!isInside) {
updateNodeContextSelection(newChild, newChild.contextSelection, this.selection || this.contextSelection, true);
updateNodeDisableContext(newChild, this.disabled || this.contextDisabled);
if ((newChild.underMatch_ || this.matchFunction) && newChild.match) newChild.match(this.matchFunction);
if (newChild.autoDelegate == DELEGATE.PARENT || newChild.autoDelegate === DELEGATE.ANY) newChild.setDelegate(this);
if (!this.dataSource) this.emit_childNodesModified({
inserted: [ newChild ]
});
if (newChild.listen.parentNode) this.addHandler(newChild.listen.parentNode, newChild);
}
return newChild;
},
removeChild: function(oldChild) {
if (!oldChild || oldChild.parentNode !== this) throw EXCEPTION_NODE_NOT_FOUND;
if (oldChild instanceof this.childClass == false) throw EXCEPTION_BAD_CHILD_CLASS;
if (this.dataSource) {
if (this.dataSource.has(oldChild.delegate)) throw EXCEPTION_DATASOURCE_CONFLICT;
} else {
if (this.dataSourceAdapter_) throw EXCEPTION_DATASOURCEADAPTER_CONFLICT;
}
var pos = this.childNodes.indexOf(oldChild);
if (pos == -1) throw EXCEPTION_NODE_NOT_FOUND;
this.childNodes.splice(pos, 1);
oldChild.parentNode = null;
if (oldChild.nextSibling) oldChild.nextSibling.previousSibling = oldChild.previousSibling; else this.lastChild = oldChild.previousSibling;
if (oldChild.previousSibling) oldChild.previousSibling.nextSibling = oldChild.nextSibling; else this.firstChild = oldChild.nextSibling;
oldChild.nextSibling = null;
oldChild.previousSibling = null;
if (oldChild.listen.parentNode) this.removeHandler(oldChild.listen.parentNode, oldChild);
updateNodeContextSelection(oldChild, oldChild.contextSelection, null, true);
if (oldChild.groupNode) oldChild.groupNode.remove(oldChild);
if (!this.dataSource) this.emit_childNodesModified({
deleted: [ oldChild ]
});
if (oldChild.autoDelegate == DELEGATE.PARENT || oldChild.autoDelegate === DELEGATE.ANY) oldChild.setDelegate();
return oldChild;
},
replaceChild: function(newChild, oldChild) {
if (this.dataSource) throw EXCEPTION_DATASOURCE_CONFLICT;
if (this.dataSourceAdapter_) throw EXCEPTION_DATASOURCEADAPTER_CONFLICT;
if (oldChild == null || oldChild.parentNode !== this) throw EXCEPTION_NODE_NOT_FOUND;
this.insertBefore(newChild, oldChild);
return this.removeChild(oldChild);
},
clear: function(alive) {
if (this.dataSource && this.dataSource.itemCount) throw EXCEPTION_DATASOURCE_CONFLICT;
if (!this.firstChild) return;
if (alive) updateNodeContextSelection(this, this.selection || this.contextSelection, null, false, true);
var childNodes = this.childNodes;
this.firstChild = null;
this.lastChild = null;
this.childNodes = [];
this.emit_childNodesModified({
deleted: childNodes
});
for (var i = childNodes.length; i-- > 0; ) {
var child = childNodes[i];
if (child.listen.parentNode) child.parentNode.removeHandler(child.listen.parentNode, child);
child.parentNode = null;
child.groupNode = null;
if (alive) {
child.nextSibling = null;
child.previousSibling = null;
if (child.autoDelegate == DELEGATE.PARENT || child.autoDelegate === DELEGATE.ANY) child.setDelegate();
} else child.destroy();
}
if (this.grouping) {
for (var childNodes = this.grouping.childNodes, i = childNodes.length - 1, group; group = childNodes[i]; i--) group.clear();
}
},
setChildNodes: function(newChildNodes, keepAlive) {
if (!this.dataSource && !this.dataSourceAdapter_) this.clear(keepAlive);
if (newChildNodes) {
if ("length" in newChildNodes == false) newChildNodes = [ newChildNodes ];
if (newChildNodes.length) {
var tmp = this.emit_childNodesModified;
this.emit_childNodesModified = $undef;
for (var i = 0, len = newChildNodes.length; i < len; i++) this.insertBefore(newChildNodes[i]);
this.emit_childNodesModified = tmp;
this.emit_childNodesModified({
inserted: this.childNodes
});
}
}
},
setDataSource: function(dataSource) {
if (!this.childClass) throw EXCEPTION_NO_CHILDCLASS;
dataSource = basis.data.resolveDataset(this, this.setDataSource, dataSource, "dataSourceAdapter_");
if (this.dataSource !== dataSource) {
var oldDataSource = this.dataSource;
var listenHandler = this.listen.dataSource;
if (oldDataSource) {
this.dataSourceMap_ = null;
this.dataSource = null;
if (listenHandler) oldDataSource.removeHandler(listenHandler, this);
}
if (this.firstChild) {
if (oldDataSource) for (var i = 0, child; child = this.childNodes[i]; i++) unlockDataSourceItemNode(child);
this.clear();
}
this.dataSource = dataSource;
if (dataSource) {
this.dataSourceMap_ = {};
this.setChildNodesState(dataSource.state, dataSource.state.data);
if (listenHandler) {
dataSource.addHandler(listenHandler, this);
if (dataSource.itemCount && listenHandler.itemsChanged) {
listenHandler.itemsChanged.call(this, dataSource, {
inserted: dataSource.getItems()
});
}
}
} else {
this.setChildNodesState(STATE.UNDEFINED);
}
this.emit_dataSourceChanged(oldDataSource);
}
},
setGrouping: function(grouping, alive) {
if (typeof grouping == "function" || typeof grouping == "string") grouping = {
rule: grouping
};
if (grouping instanceof GroupingNode == false) {
grouping = grouping && typeof grouping == "object" ? new this.groupingClass(grouping) : null;
}
if (this.grouping !== grouping) {
var oldGrouping = this.grouping;
var order;
if (oldGrouping) {
this.grouping = null;
if (!grouping) {
if (this.firstChild) {
if (this.sorting !== nullGetter) order = sortChildNodes(this); else order = this.childNodes;
oldGrouping.nullGroup.clear();
var groups = oldGrouping.childNodes.slice(0);
for (var i = 0; i < groups.length; i++) groups[i].clear();
fastChildNodesOrder(this, order);
}
}
oldGrouping.setOwner();
}
if (grouping) {
this.grouping = grouping;
grouping.setOwner(this);
if (this.firstChild) {
if (this.sorting !== nullGetter) order = sortChildNodes(this); else order = this.childNodes;
for (var i = 0, child; child = order[i]; i++) child.groupNode = this.grouping.getGroupNode(child, true);
order = fastChildNodesGroupOrder(this, order);
fastChildNodesOrder(this, order);
}
}
this.emit_groupingChanged(oldGrouping);
}
},
setSorting: function(sorting, sortingDesc) {
sorting = getter(sorting);
sortingDesc = !!sortingDesc;
if (this.sorting !== sorting || this.sortingDesc != !!sortingDesc) {
var oldSorting = this.sorting;
var oldSortingDesc = this.sortingDesc;
this.sorting = sorting;
this.sortingDesc = !!sortingDesc;
if (sorting !== nullGetter && this.firstChild) {
var order = [];
var nodes;
for (var node = this.firstChild; node; node = node.nextSibling) node.sortingValue = sorting(node) || 0;
if (this.grouping) {
for (var group = this.grouping.nullGroup; group; group = group.nextSibling) {
nodes = group.nodes = sortChildNodes({
childNodes: group.nodes,
sortingDesc: this.sortingDesc
});
group.first = nodes[0] || null;
group.last = nodes[nodes.length - 1] || null;
order.push.apply(order, nodes);
}
} else {
order = sortChildNodes(this);
}
fastChildNodesOrder(this, order);
}
this.emit_sortingChanged(oldSorting, oldSortingDesc);
}
},
setMatchFunction: function(matchFunction) {
if (this.matchFunction != matchFunction) {
var oldMatchFunction = this.matchFunction;
this.matchFunction = matchFunction;
for (var node = this.lastChild; node; node = node.previousSibling) node.match(matchFunction);
this.emit_matchFunctionChanged(oldMatchFunction);
}
}
};
var Node = Class(AbstractNode, DomMixin, {
className: namespace + ".Node",
emit_enable: createEvent("enable") && function() {
for (var child = this.firstChild; child; child = child.nextSibling) updateNodeDisableContext(child, false);
events.enable.call(this);
},
emit_disable: createEvent("disable") && function() {
for (var child = this.firstChild; child; child = child.nextSibling) updateNodeDisableContext(child, true);
events.disable.call(this);
},
emit_satelliteChanged: function(name, oldSatellite) {
AbstractNode.prototype.emit_satelliteChanged.call(this, name, oldSatellite);
if (this.satellite[name] instanceof Node) updateNodeDisableContext(this.satellite[name], this.disabled || this.contextDisabled);
},
emit_select: createEvent("select"),
emit_unselect: createEvent("unselect"),
emit_match: createEvent("match"),
emit_unmatch: createEvent("unmatch"),
emit_matchFunctionChanged: createEvent("matchFunctionChanged", "oldMatchFunction"),
selectable: true,
selected: false,
selection: null,
contextSelection: null,
matchFunction: null,
matched: true,
disabled: false,
contextDisabled: false,
listen: {
owner: {
enable: function() {
updateNodeDisableContext(this, false);
},
disable: function() {
updateNodeDisableContext(this, true);
}
}
},
init: function() {
if (this.selection) {
if (this.selection instanceof ReadOnlyDataset == false) this.selection = new Selection(this.selection);
if (this.listen.selection) this.selection.addHandler(this.listen.selection, this);
}
AbstractNode.prototype.init.call(this);
if (this.disabled) this.emit_disable();
if (this.selected) {
this.selected = false;
this.select(true);
}
},
setSelection: function(selection) {
if (this.selection !== selection) {
updateNodeContextSelection(this, this.selection || this.contextSelection, selection || this.contextSelection, false, true);
if (this.selection && this.listen.selection) this.selection.removeHandler(this.listen.selection, this);
this.selection = selection;
if (selection && this.listen.selection) selection.addHandler(this.listen.selection, this);
return true;
}
},
select: function(multiple) {
var selected = this.selected;
var selection = this.contextSelection;
if (selection) {
if (!multiple) {
if (this.selectable) selection.set([ this ]);
} else {
if (selected) selection.remove([ this ]); else selection.add([ this ]);
}
} else if (!selected && this.selectable) {
this.selected = true;
this.emit_select();
}
return this.selected != selected;
},
unselect: function() {
var selected = this.selected;
if (selected) {
var selection = this.contextSelection;
if (selection) selection.remove([ this ]); else {
this.selected = false;
this.emit_unselect();
}
}
return this.selected != selected;
},
setSelected: function(selected, multiple) {
return selected ? this.select(multiple) : this.unselect();
},
enable: function() {
var disabled = this.disabled;
if (disabled) {
this.disabled = false;
if (!this.contextDisabled) this.emit_enable();
}
return this.disabled != disabled;
},
disable: function() {
var disabled = this.disabled;
if (!disabled) {
this.disabled = true;
if (!this.contextDisabled) this.emit_disable();
}
return this.disabled != disabled;
},
setDisabled: function(disabled) {
return disabled ? this.disable() : this.enable();
},
isDisabled: function() {
return this.disabled || this.contextDisabled;
},
match: function(func) {
if (typeof func != "function") func = null;
if (this.underMatch_ && !func) this.underMatch_(this, true);
this.underMatch_ = func;
var matched = !func || func(this);
if (this.matched != matched) {
this.matched = matched;
if (matched) this.emit_match(); else this.emit_unmatch();
}
},
destroy: function() {
this.unselect();
this.contextSelection = null;
if (this.selection) this.setSelection();
AbstractNode.prototype.destroy.call(this);
}
});
var GroupingNode = Class(AbstractNode, DomMixin, {
className: namespace + ".GroupingNode",
emit_childNodesModified: function(delta) {
events.childNodesModified.call(this, delta);
this.nullGroup.nextSibling = this.firstChild;
var array;
if (array = delta.inserted) {
for (var i = 0, child; child = array[i++]; ) {
child.groupId_ = child.delegate ? child.delegate.basisObjectId : child.data.id;
this.map_[child.groupId_] = child;
}
if (this.dataSource && this.nullGroup.first) {
var parentNode = this.owner;
var nodes = arrayFrom(this.nullGroup.nodes);
for (var i = nodes.length; i-- > 0; ) parentNode.insertBefore(nodes[i], nodes[i].nextSibling);
}
}
},
emit_ownerChanged: function(oldOwner) {
if (oldOwner && oldOwner.grouping === this) oldOwner.setGrouping(null, true);
if (this.owner && this.owner.grouping !== this) this.owner.setGrouping(this);
events.ownerChanged.call(this, oldOwner);
if (!this.owner && this.autoDestroyWithNoOwner) this.destroy();
},
map_: null,
nullGroup: null,
autoDestroyWithNoOwner: true,
autoDestroyEmptyGroups: true,
rule: nullGetter,
childClass: PartitionNode,
childFactory: function(config) {
return new this.childClass(complete({
autoDestroyIfEmpty: this.dataSource ? false : this.autoDestroyEmptyGroups
}, config));
},
init: function() {
this.map_ = {};
this.nullGroup = new PartitionNode;
AbstractNode.prototype.init.call(this);
},
getGroupNode: function(node, autocreate) {
var groupRef = this.rule(node);
var isDelegate = groupRef instanceof DataObject;
var group = this.map_[isDelegate ? groupRef.basisObjectId : groupRef];
if (this.dataSource) autocreate = false;
if (!group && autocreate) {
group = this.appendChild(isDelegate ? groupRef : {
data: {
id: groupRef,
title: groupRef
}
});
}
return group || this.nullGroup;
},
setDataSource: function(dataSource) {
var curDataSource = this.dataSource;
DomMixin.setDataSource.call(this, dataSource);
var owner = this.owner;
if (owner && this.dataSource !== curDataSource) {
var nodes = arrayFrom(owner.childNodes);
for (var i = nodes.length - 1; i >= 0; i--) owner.insertBefore(nodes[i], nodes[i + 1]);
}
},
insertBefore: function(newChild, refChild) {
newChild = DomMixin.insertBefore.call(this, newChild, refChild);
var firstNode = newChild.first;
if (firstNode) {
var parent = firstNode.parentNode;
var lastNode = newChild.last;
var beforePrev;
var beforeNext;
var afterPrev;
var afterNext = null;
var cursor = newChild;
while (cursor = cursor.nextSibling) {
if (afterNext = cursor.first) break;
}
afterPrev = afterNext ? afterNext.previousSibling : parent.lastChild;
beforePrev = firstNode.previousSibling;
beforeNext = lastNode.nextSibling;
if (beforeNext !== afterNext) {
var parentChildNodes = parent.childNodes;
var nodes = newChild.nodes;
var nodesCount = nodes.length;
if (beforePrev) beforePrev.nextSibling = beforeNext;
if (beforeNext) beforeNext.previousSibling = beforePrev;
if (afterPrev) afterPrev.nextSibling = firstNode;
if (afterNext) afterNext.previousSibling = lastNode;
firstNode.previousSibling = afterPrev;
lastNode.nextSibling = afterNext;
var firstPos = parentChildNodes.indexOf(firstNode);
var afterNextPos = afterNext ? parentChildNodes.indexOf(afterNext) : parentChildNodes.length;
if (afterNextPos > firstPos) afterNextPos -= nodesCount;
parentChildNodes.splice(firstPos, nodesCount);
parentChildNodes.splice.apply(parentChildNodes, [ afterNextPos, 0 ].concat(nodes));
if (!afterPrev || !beforePrev) parent.firstChild = parentChildNodes[0];
if (!afterNext || !beforeNext) parent.lastChild = parentChildNodes[parentChildNodes.length - 1];
if (firstNode instanceof PartitionNode) for (var i = nodesCount, insertBefore = afterNext; i-- > 0; ) {
parent.insertBefore(nodes[i], insertBefore);
insertBefore = nodes[i];
}
}
}
return newChild;
},
removeChild: function(oldChild) {
if (oldChild = DomMixin.removeChild.call(this, oldChild)) {
delete this.map_[oldChild.groupId_];
for (var i = 0, node; node = oldChild.nodes[i]; i++) node.parentNode.insertBefore(node);
}
return oldChild;
},
clear: function(alive) {
var nodes = [];
var getGroupNode = this.getGroupNode;
var nullGroup = this.nullGroup;
this.getGroupNode = function() {
return nullGroup;
};
for (var group = this.firstChild; group; group = group.nextSibling) nodes.push.apply(nodes, group.nodes);
for (var i = 0, child; child = nodes[i]; i++) child.parentNode.insertBefore(child);
this.getGroupNode = getGroupNode;
DomMixin.clear.call(this, alive);
this.map_ = {};
},
destroy: function() {
this.autoDestroyWithNoOwner = false;
AbstractNode.prototype.destroy.call(this);
this.nullGroup.destroy();
this.nullGroup = null;
this.map_ = null;
}
});
AbstractNode.prototype.groupingClass = GroupingNode;
var CHILDNODESDATASET_HANDLER = {
childNodesModified: function(sender, delta) {
var memberMap = this.members_;
var newDelta = {};
var node;
var insertCount = 0;
var deleteCount = 0;
var inserted = delta.inserted;
var deleted = delta.deleted;
if (inserted && inserted.length) {
newDelta.inserted = inserted;
while (node = inserted[insertCount]) {
memberMap[node.basisObjectId] = node;
insertCount++;
}
}
if (deleted && deleted.length) {
newDelta.deleted = deleted;
while (node = deleted[deleteCount]) {
delete memberMap[node.basisObjectId];
deleteCount++;
}
}
if (insertCount || deleteCount) this.emit_itemsChanged(newDelta);
},
destroy: function() {
this.destroy();
}
};
var ChildNodesDataset = Class(ReadOnlyDataset, {
className: namespace + ".ChildNodesDataset",
sourceNode: null,
init: function() {
ReadOnlyDataset.prototype.init.call(this);
var sourceNode = this.sourceNode;
childNodesDatasetMap[sourceNode.basisObjectId] = this;
if (sourceNode.firstChild) CHILDNODESDATASET_HANDLER.childNodesModified.call(this, sourceNode, {
inserted: sourceNode.childNodes
});
sourceNode.addHandler(CHILDNODESDATASET_HANDLER, this);
},
destroy: function() {
this.sourceNode.removeHandler(CHILDNODESDATASET_HANDLER, this);
delete childNodesDatasetMap[this.sourceNode.basisObjectId];
ReadOnlyDataset.prototype.destroy.call(this);
}
});
var Selection = Class(Dataset, {
className: namespace + ".Selection",
multiple: false,
emit_itemsChanged: function(delta) {
Dataset.prototype.emit_itemsChanged.call(this, delta);
if (delta.inserted) {
for (var i = 0, node; node = delta.inserted[i]; i++) {
if (!node.selected) {
node.selected = true;
node.emit_select();
}
}
}
if (delta.deleted) {
for (var i = 0, node; node = delta.deleted[i]; i++) {
if (node.selected) {
node.selected = false;
node.emit_unselect();
}
}
}
},
add: function(nodes) {
if (!this.multiple) {
if (this.itemCount) return this.set(nodes); else nodes = [ nodes[0] ];
}
var items = [];
for (var i = 0, node; node = nodes[i]; i++) {
if (node.contextSelection == this && node.selectable) items.push(node);
}
return Dataset.prototype.add.call(this, items);
},
set: function(nodes) {
var items = [];
for (var i = 0, node; node = nodes[i]; i++) {
if (node.contextSelection == this && node.selectable) items.push(node);
}
if (!this.multiple) items.splice(1);
return Dataset.prototype.set.call(this, items);
}
});
module.exports = {
DELEGATE: DELEGATE,
AbstractNode: AbstractNode,
Node: Node,
GroupingNode: GroupingNode,
PartitionNode: PartitionNode,
ChildNodesDataset: ChildNodesDataset,
Selection: Selection,
nullSelection: new ReadOnlyDataset
};
},
"6.js": function(exports, module, basis, global, __filename, __dirname, require, resource) {
basis.require("./2.js");
var namespace = this.path;
var Class = basis.Class;
var cleaner = basis.cleaner;
var path = basis.path;
var arraySearch = basis.array.search;
var arrayAdd = basis.array.add;
var arrayRemove = basis.array.remove;
var templateList = [];
var tmplFilesMap = {};
var DECLARATION_VERSION = 2;
var TYPE_ELEMENT = 1;
var TYPE_ATTRIBUTE = 2;
var TYPE_ATTRIBUTE_CLASS = 4;
var TYPE_ATTRIBUTE_STYLE = 5;
var TYPE_ATTRIBUTE_EVENT = 6;
var TYPE_TEXT = 3;
var TYPE_COMMENT = 8;
var TOKEN_TYPE = 0;
var TOKEN_BINDINGS = 1;
var TOKEN_REFS = 2;
var ATTR_NAME = 3;
var ATTR_VALUE = 4;
var ATTR_EVENT_RX = /^event-(.+)$/;
var ATTR_NAME_BY_TYPE = {
4: "class",
5: "style"
};
var ATTR_TYPE_BY_NAME = {
"class": TYPE_ATTRIBUTE_CLASS,
style: TYPE_ATTRIBUTE_STYLE
};
var ATTR_VALUE_INDEX = {
2: ATTR_VALUE,
4: ATTR_VALUE - 1,
5: ATTR_VALUE - 1,
6: 2
};
var ELEMENT_NAME = 3;
var ELEMENT_ATTRS = 4;
var ELEMENT_CHILDS = 5;
var TEXT_VALUE = 3;
var COMMENT_VALUE = 3;
var SYNTAX_ERROR = "Invalid or unsupported syntax";
var TEXT = /((?:.|[\r\n])*?)(\{(?:l10n:([a-zA-Z_][a-zA-Z0-9_\-]*(?:\.[a-zA-Z_][a-zA-Z0-9_\-]*)*(?:\.\{[a-zA-Z_][a-zA-Z0-9_\-]*\})?)\})?|<(\/|!--(\s*\{)?)?|$)/g;
var TAG_NAME = /([a-z_][a-z0-9\-_]*)(:|\{|\s*(\/?>)?)/ig;
var ATTRIBUTE_NAME_OR_END = /([a-z_][a-z0-9_\-]*)(:|\{|=|\s*)|(\/?>)/ig;
var COMMENT = /(.|[\r\n])*?-->/g;
var CLOSE_TAG = /([a-z_][a-z0-9_\-]*(?::[a-z_][a-z0-9_\-]*)?)>/ig;
var REFERENCE = /([a-z_][a-z0-9_]*)(\||\}\s*)/ig;
var ATTRIBUTE_VALUE = /"((?:(\\")|[^"])*?)"\s*/g;
var BREAK_TAG_PARSE = /^/g;
var SINGLETON_TAG = /^(area|base|br|col|command|embed|hr|img|input|link|meta|param|source)$/i;
var TAG_IGNORE_CONTENT = {
text: /((?:.|[\r\n])*?)(?:<\/b:text>|$)/g,
style: /((?:.|[\r\n])*?)(?:<\/b:style>|$)/g
};
var quoteUnescape = /\\"/g;
var tokenize = function(source) {
var result = [];
var tagStack = [];
var lastTag = {
childs: result
};
var sourceText;
var token;
var bufferPos;
var startPos;
var parseTag = false;
var textStateEndPos = 0;
var textEndPos;
var state = TEXT;
var pos = 0;
var m;
source = source.trim();
result.warns = [];
while (pos < source.length || state != TEXT) {
state.lastIndex = pos;
startPos = pos;
m = state.exec(source);
if (!m || m.index !== pos) {
if (state == REFERENCE && token && token.type == TYPE_COMMENT) {
state = COMMENT;
continue;
}
if (parseTag) lastTag = tagStack.pop();
if (token) lastTag.childs.pop();
if (token = lastTag.childs.pop()) {
if (token.type == TYPE_TEXT && !token.refs) textStateEndPos -= "len" in token ? token.len : token.value.length; else lastTag.childs.push(token);
}
parseTag = false;
state = TEXT;
continue;
}
pos = state.lastIndex;
switch (state) {
case TEXT:
textEndPos = startPos + m[1].length;
if (textStateEndPos != textEndPos) {
sourceText = textStateEndPos == startPos ? m[1] : source.substring(textStateEndPos, textEndPos);
token = sourceText.replace(/\s*(\r\n?|\n\r?)\s*/g, "");
if (token) lastTag.childs.push({
type: TYPE_TEXT,
len: sourceText.length,
value: token
});
}
textStateEndPos = textEndPos;
if (m[3]) {
lastTag.childs.push({
type: TYPE_TEXT,
refs: [ "l10n:" + m[3] ],
value: "{l10n:" + m[3] + "}"
});
} else if (m[2] == "{") {
bufferPos = pos - 1;
lastTag.childs.push(token = {
type: TYPE_TEXT
});
state = REFERENCE;
} else if (m[4]) {
if (m[4] == "/") {
token = null;
state = CLOSE_TAG;
} else {
lastTag.childs.push(token = {
type: TYPE_COMMENT
});
if (m[5]) {
bufferPos = pos - m[5].length;
state = REFERENCE;
} else {
bufferPos = pos;
state = COMMENT;
}
}
} else if (m[2]) {
parseTag = true;
tagStack.push(lastTag);
lastTag.childs.push(token = {
type: TYPE_ELEMENT,
attrs: [],
childs: []
});
lastTag = token;
state = TAG_NAME;
}
break;
case CLOSE_TAG:
if (m[1] !== (lastTag.prefix ? lastTag.prefix + ":" : "") + lastTag.name) {
lastTag.childs.push({
type: TYPE_TEXT,
value: "" + m[0]
});
} else lastTag = tagStack.pop();
state = TEXT;
break;
case TAG_NAME:
case ATTRIBUTE_NAME_OR_END:
if (m[2] == ":") {
if (token.prefix) state = BREAK_TAG_PARSE; else token.prefix = m[1];
break;
}
if (m[1]) {
token.name = m[1];
if (token.type == TYPE_ATTRIBUTE) lastTag.attrs.push(token);
}
if (m[2] == "{") {
if (token.type == TYPE_ELEMENT) state = REFERENCE; else state = BREAK_TAG_PARSE;
break;
}
if (m[3]) {
parseTag = false;
if (m[3] == "/>" || !lastTag.prefix && SINGLETON_TAG.test(lastTag.name)) {
if (m[3] != "/>") result.warns.push("Tag <" + lastTag.name + "> doesn't closed explicit (use `/>` as tag ending)");
lastTag = tagStack.pop();
} else {
if (lastTag.prefix == "b" && lastTag.name in TAG_IGNORE_CONTENT) {
state = TAG_IGNORE_CONTENT[lastTag.name];
break;
}
}
state = TEXT;
break;
}
if (m[2] == "=") {
state = ATTRIBUTE_VALUE;
break;
}
token = {
type: TYPE_ATTRIBUTE
};
state = ATTRIBUTE_NAME_OR_END;
break;
case COMMENT:
token.value = source.substring(bufferPos, pos - 3);
state = TEXT;
break;
case REFERENCE:
if (token.refs) token.refs.push(m[1]); else token.refs = [ m[1] ];
if (m[2] != "|") {
if (token.type == TYPE_TEXT) {
pos -= m[2].length - 1;
token.value = source.substring(bufferPos, pos);
state = TEXT;
} else if (token.type == TYPE_COMMENT) {
state = COMMENT;
} else if (token.type == TYPE_ATTRIBUTE && source[pos] == "=") {
pos++;
state = ATTRIBUTE_VALUE;
} else {
token = {
type: TYPE_ATTRIBUTE
};
state = ATTRIBUTE_NAME_OR_END;
}
}
break;
case ATTRIBUTE_VALUE:
token.value = m[1].replace(quoteUnescape, '"');
token = {
type: TYPE_ATTRIBUTE
};
state = ATTRIBUTE_NAME_OR_END;
break;
case TAG_IGNORE_CONTENT.text:
case TAG_IGNORE_CONTENT.style:
lastTag.childs.push({
type: TYPE_TEXT,
value: m[1]
});
lastTag = tagStack.pop();
state = TEXT;
break;
default:
throw "Parser bug";
}
if (state == TEXT) textStateEndPos = pos;
}
if (textStateEndPos != pos) lastTag.childs.push({
type: TYPE_TEXT,
value: source.substring(textStateEndPos, pos)
});
if (lastTag.name) result.warns.push("No close tag for <" + lastTag.name + ">");
if (!result.warns.length) delete result.warns;
result.templateTokens = true;
return result;
};
var tokenTemplate = {};
var L10nProxyToken = basis.Token.subclass({
className: namespace + ".L10nProxyToken",
token: null,
url: "",
init: function(token) {
this.url = token.dictionary.resource.url + ":" + token.name;
this.token = token;
this.set();
token.attach(this.set, this);
},
set: function() {
return basis.Token.prototype.set.call(this, this.token.type == "markup" ? processMarkup(this.token.value, this.token.name + "@" + this.token.dictionary.resource.url) : "");
},
destroy: function() {
basis.Token.prototype.destroy.call(this);
this.token = null;
}
});
function processMarkup(value, id) {
return '' + String(value) + "";
}
function getL10nTemplate(token) {
if (typeof token == "string") token = basis.l10n.token(token);
if (!token) return null;
var id = token.basisObjectId;
var template = tokenTemplate[id];
if (!template) template = tokenTemplate[id] = new Template(new L10nProxyToken(token));
return template;
}
function genIsolateMarker() {
return "i" + basis.genUID() + "__";
}
function isolateCss(css, prefix) {
function addMatch(prefix) {
if (i > lastMatchPos) {
result.push((prefix || "") + css.substring(lastMatchPos, i));
lastMatchPos = i;
}
}
var result = [];
var sym = css.split("");
var len = sym.length;
var lastMatchPos = 0;
var blockScope = false;
var strSym;
if (!prefix) prefix = genIsolateMarker();
for (var i = 0; i < len; i++) {
switch (sym[i]) {
case "'":
case '"':
strSym = sym[i];
while (++i < len) {
if (sym[i] == "\\") i++; else if (sym[i] == strSym) {
i++;
break;
}
}
break;
case "/":
if (sym[i + 1] == "*") {
i++;
while (++i < len) if (sym[i] == "*" && sym[i + 1] == "/") {
i += 2;
break;
}
}
break;
case "{":
blockScope = true;
break;
case "}":
blockScope = false;
break;
case ".":
if (!blockScope) {
i++;
addMatch();
while (++i < len) if (!/[a-z0-9\-\_]/.test(sym[i])) {
addMatch(prefix);
i -= 1;
break;
}
}
break;
}
}
addMatch();
return result.join("");
}
var makeDeclaration = function() {
var IDENT = /^[a-z_][a-z0-9_\-]*$/i;
var CLASS_ATTR_PARTS = /(\S+)/g;
var CLASS_ATTR_BINDING = /^((?:[a-z_][a-z0-9_\-]*)?(?::(?:[a-z_][a-z0-9_\-]*)?)?)\{((anim:)?[a-z_][a-z0-9_\-]*)\}$/i;
var STYLE_ATTR_PARTS = /\s*[^:]+?\s*:(?:\(.*?\)|".*?"|'.*?'|[^;]+?)+(?:;|$)/gi;
var STYLE_PROPERTY = /\s*([^:]+?)\s*:((?:\(.*?\)|".*?"|'.*?'|[^;]+?)+);?$/i;
var STYLE_ATTR_BINDING = /\{([a-z_][a-z0-9_]*)\}/i;
var ATTR_BINDING = /\{([a-z_][a-z0-9_]*|l10n:[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*(?:\.\{[a-z_][a-z0-9_]*\})?)\}/i;
var NAMED_CHARACTER_REF = /&([a-z]+|#[0-9]+|#x[0-9a-f]{1,4});?/gi;
var tokenMap = basis.NODE_ENV ? __nodejsRequire("./template/htmlentity.json") : {};
var tokenElement = !basis.NODE_ENV ? document.createElement("div") : null;
var includeStack = [];
var styleNamespaceIsolate = {};
function name(token) {
return (token.prefix ? token.prefix + ":" : "") + token.name;
}
function namedCharReplace(m, token) {
if (!tokenMap[token]) {
if (token.charAt(0) == "#") {
tokenMap[token] = String.fromCharCode(token.charAt(1) == "x" || token.charAt(1) == "X" ? parseInt(token.substr(2), 16) : token.substr(1));
} else {
if (tokenElement) {
tokenElement.innerHTML = m;
tokenMap[token] = tokenElement.firstChild ? tokenElement.firstChild.nodeValue : m;
}
}
}
return tokenMap[token] || m;
}
function untoken(value) {
return value.replace(NAMED_CHARACTER_REF, namedCharReplace);
}
function refList(token) {
var array = token.refs;
if (!array || !array.length) return 0;
return array;
}
function buildAttrExpression(parts) {
var bindName;
var names = [];
var expression = [];
var map = {};
for (var j = 0; j < parts.length; j++) if (j % 2) {
bindName = parts[j];
if (!map[bindName]) {
map[bindName] = names.length;
names.push(bindName);
}
expression.push(map[bindName]);
} else {
if (parts[j]) expression.push(untoken(parts[j]));
}
return [ names, expression ];
}
function processAttr(name, value) {
var bindings = 0;
var parts;
var m;
if (value) {
switch (name) {
case "class":
if (parts = value.match(CLASS_ATTR_PARTS)) {
var newValue = [];
bindings = [];
for (var j = 0, part; part = parts[j]; j++) {
if (m = part.match(CLASS_ATTR_BINDING)) bindings.push([ m[1] || "", m[2] ]); else newValue.push(part);
}
value = newValue.join(" ");
}
break;
case "style":
var props = [];
bindings = [];
if (parts = value.match(STYLE_ATTR_PARTS)) {
for (var j = 0, part; part = parts[j]; j++) {
var m = part.match(STYLE_PROPERTY);
var propertyName = m[1];
var value = m[2].trim();
var valueParts = value.split(STYLE_ATTR_BINDING);
if (valueParts.length > 1) {
var expr = buildAttrExpression(valueParts);
expr.push(propertyName);
bindings.push(expr);
} else props.push(propertyName + ": " + untoken(value));
}
} else {
if (/\S/.test(value)) basis.dev.warn("Bad value for style attribute (value ignored):", value);
}
value = props.join("; ");
if (value) value += ";";
break;
default:
parts = value.split(ATTR_BINDING);
if (parts.length > 1) bindings = buildAttrExpression(parts); else value = untoken(value);
}
}
if (bindings && !bindings.length) bindings = 0;
return {
binding: bindings,
value: value,
type: ATTR_TYPE_BY_NAME[name] || 2
};
}
function attrs(token, declToken, optimizeSize) {
var attrs = token.attrs;
var result = [];
var styleAttr;
var display;
var m;
for (var i = 0, attr; attr = attrs[i]; i++) {
if (attr.prefix == "b") {
switch (attr.name) {
case "ref":
var refs = (attr.value || "").trim().split(/\s+/);
for (var j = 0; j < refs.length; j++) addTokenRef(declToken, refs[j]);
break;
case "show":
case "hide":
display = attr;
break;
}
continue;
}
if (m = attr.name.match(ATTR_EVENT_RX)) {
result.push(m[1] == attr.value ? [ TYPE_ATTRIBUTE_EVENT, m[1] ] : [ TYPE_ATTRIBUTE_EVENT, m[1], attr.value ]);
continue;
}
var parsed = processAttr(attr.name, attr.value);
var item = [ parsed.type, parsed.binding, refList(attr) ];
if (parsed.type == 2) item.push(name(attr));
if (parsed.value && (!optimizeSize || !parsed.binding || parsed.type != 2)) item.push(parsed.value);
if (parsed.type == TYPE_ATTRIBUTE_STYLE) styleAttr = item;
result.push(item);
}
if (display) {
if (!styleAttr) {
styleAttr = [ TYPE_ATTRIBUTE_STYLE, 0, 0 ];
result.push(styleAttr);
}
if (!styleAttr[1]) styleAttr[1] = [];
var displayExpr = buildAttrExpression((display.value || display.name).split(ATTR_BINDING));
if (displayExpr[0].length - displayExpr[1].length) {
styleAttr[3] = (styleAttr[3] ? styleAttr[3] + "; " : "") + (display.name == "show" ^ display.value === "" ? "" : "display: none");
} else {
if (display.name == "show") styleAttr[3] = (styleAttr[3] ? styleAttr[3] + "; " : "") + "display: none";
styleAttr[1].push(displayExpr.concat("display", display.name));
}
}
return result.length ? result : 0;
}
function addTokenRef(token, refName) {
if (!token[TOKEN_REFS]) token[TOKEN_REFS] = [];
arrayAdd(token[TOKEN_REFS], refName);
if (refName != "element") token[TOKEN_BINDINGS] = token[TOKEN_REFS].length == 1 ? refName : 0;
}
function removeTokenRef(token, refName) {
var idx = token[TOKEN_REFS].indexOf(refName);
if (idx != -1) {
var indexBinding = token[TOKEN_BINDINGS] && typeof token[TOKEN_BINDINGS] == "number";
token[TOKEN_REFS].splice(idx, 1);
if (indexBinding) if (idx == token[TOKEN_BINDINGS] - 1) token[TOKEN_BINDINGS] = refName;
if (!token[TOKEN_REFS].length) token[TOKEN_REFS] = 0; else {
if (indexBinding) token[TOKEN_BINDINGS] -= idx < token[TOKEN_BINDINGS] - 1;
}
}
}
function tokenAttrs(token) {
var result = {};
if (token.attrs) for (var i = 0, attr; attr = token.attrs[i]; i++) result[name(attr)] = attr.value;
return result;
}
function addUnique(array, items) {
for (var i = 0; i < items.length; i++) arrayAdd(array, items[i]);
}
function addStyles(array, items, prefix) {
for (var i = 0, item; item = items[i]; i++) if (item[1] !== styleNamespaceIsolate) item[1] = prefix + item[1];
array.unshift.apply(array, items);
}
function addStyle(template, token, src, isolatePrefix) {
var url;
if (src) {
if (!/^(\.\/|\.\.|\/)/.test(src)) basis.dev.warn("Bad usage: .\nFilenames should starts with `./`, `..` or `/`. Otherwise it will treats as special reference in next minor release.');
url = path.resolve(template.baseURI + src);
} else {
var text = token.childs[0];
url = basis.resource.virtual("css", text ? text.value : "", template.sourceUrl).url;
}
template.resources.push([ url, isolatePrefix ]);
return url;
}
function process(tokens, template, options, context) {
function modifyAttr(token, name, action) {
var attrs = tokenAttrs(token);
if (name) attrs.name = name;
if (!attrs.name) {
template.warns.push("Instruction has no attribute name");
return;
}
if (!IDENT.test(attrs.name)) {
template.warns.push("Bad attribute name `" + attrs.name + "`");
return;
}
var includedToken = tokenRefMap[attrs.ref || "element"];
if (includedToken) {
if (includedToken.token[TOKEN_TYPE] == TYPE_ELEMENT) {
var itAttrs = includedToken.token;
var isEvent = attrs.name.match(ATTR_EVENT_RX);
var itType = isEvent ? TYPE_ATTRIBUTE_EVENT : ATTR_TYPE_BY_NAME[attrs.name] || TYPE_ATTRIBUTE;
var valueIdx = ATTR_VALUE_INDEX[itType] || ATTR_VALUE;
var itAttrToken = itAttrs && arraySearch(itAttrs, attrs.name, function(token) {
if (token[TOKEN_TYPE] == TYPE_ATTRIBUTE_EVENT) return "event-" + token[1];
return ATTR_NAME_BY_TYPE[token[TOKEN_TYPE]] || token[ATTR_NAME];
}, ELEMENT_ATTRS);
if (!itAttrToken && action != "remove") {
if (isEvent) {
itAttrToken = [ itType, isEvent[1] ];
} else {
itAttrToken = [ itType, 0, 0, itType == TYPE_ATTRIBUTE ? attrs.name : "" ];
if (itType == TYPE_ATTRIBUTE) itAttrToken.push("");
}
if (!itAttrs) {
itAttrs = [];
includedToken.token.push(itAttrs);
}
itAttrs.push(itAttrToken);
}
var classOrStyle = attrs.name == "class" || attrs.name == "style";
switch (action) {
case "set":
if (itAttrToken[TOKEN_TYPE] == TYPE_ATTRIBUTE_EVENT) {
if (attrs.value == isEvent[1]) itAttrToken.length = 2; else itAttrToken[valueIdx] = attrs.value;
return;
}
var parsed = processAttr(attrs.name, attrs.value);
itAttrToken[TOKEN_BINDINGS] = parsed.binding;
if (!options.optimizeSize || !itAttrToken[TOKEN_BINDINGS] || classOrStyle) itAttrToken[valueIdx] = parsed.value || ""; else itAttrToken.length = valueIdx;
if (classOrStyle) if (!itAttrToken[TOKEN_BINDINGS] && !itAttrToken[valueIdx]) {
arrayRemove(itAttrs, itAttrToken);
return;
}
break;
case "append":
var parsed = processAttr(attrs.name, attrs.value);
if (!isEvent) {
if (parsed.binding) {
var attrBindings = itAttrToken[TOKEN_BINDINGS];
if (attrBindings) {
switch (attrs.name) {
case "style":
var oldBindingMap = {};
for (var i = 0, oldBinding; oldBinding = attrBindings[i]; i++) oldBindingMap[oldBinding[2]] = i;
for (var i = 0, newBinding; newBinding = parsed.binding[i]; i++) if (newBinding[2] in oldBindingMap) attrBindings[oldBindingMap[newBinding[2]]] = newBinding; else attrBindings.push(newBinding);
break;
case "class":
attrBindings.push.apply(attrBindings, parsed.binding);
break;
default:
parsed.binding[0].forEach(function(name) {
arrayAdd(this, name);
}, attrBindings[0]);
for (var i = 0; i < parsed.binding[1].length; i++) {
var value = parsed.binding[1][i];
if (typeof value == "number") value = attrBindings[0].indexOf(parsed.binding[0][value]);
attrBindings[1].push(value);
}
}
} else {
itAttrToken[TOKEN_BINDINGS] = parsed.binding;
if (!classOrStyle) itAttrToken[TOKEN_BINDINGS][1].unshift(itAttrToken[valueIdx]);
}
} else {
if (!classOrStyle && itAttrToken[TOKEN_BINDINGS]) itAttrToken[TOKEN_BINDINGS][1].push(attrs.value);
}
}
if (parsed.value) itAttrToken[valueIdx] = (itAttrToken[valueIdx] || "") + (itAttrToken[valueIdx] && (isEvent || classOrStyle) ? " " : "") + parsed.value;
if (classOrStyle) if (!itAttrToken[TOKEN_BINDINGS] && !itAttrToken[valueIdx]) {
arrayRemove(itAttrs, itAttrToken);
return;
}
break;
case "remove":
if (itAttrToken) arrayRemove(itAttrs, itAttrToken);
break;
}
} else {
template.warns.push("Attribute modificator is not reference to element token (reference name: " + (attrs.ref || "element") + ")");
}
}
}
var result = [];
for (var i = 0, token, item; token = tokens[i]; i++) {
var refs = refList(token);
var bindings = refs && refs.length == 1 ? refs[0] : 0;
switch (token.type) {
case TYPE_ELEMENT:
if (token.prefix == "b") {
var elAttrs = tokenAttrs(token);
switch (token.name) {
case "style":
var styleNamespace = elAttrs.namespace || elAttrs.ns;
var styleIsolate = styleNamespace ? styleNamespaceIsolate : context && context.isolate || "";
var src = addStyle(template, token, elAttrs.src, styleIsolate);
if (styleNamespace) {
if (src in styleNamespaceIsolate == false) styleNamespaceIsolate[src] = genIsolateMarker();
template.styleNSPrefix[styleNamespace] = styleNamespaceIsolate[src];
}
break;
case "isolate":
if (!template.isolate) template.isolate = elAttrs.prefix || options.isolate || genIsolateMarker(); else basis.dev.warn(" is set already to `" + template.isolate + "`");
break;
case "l10n":
if (template.l10nResolved) template.warns.push(" must be declared before any `l10n:` token (instruction ignored)");
if (elAttrs.src) {
if (!/^(\.\/|\.\.|\/)/.test(elAttrs.src)) basis.dev.warn("Bad usage: .\nFilenames should starts with `./`, `..` or `/`. Otherwise it will treats as special reference in next minor release.');
template.dictURI = path.resolve(template.baseURI, elAttrs.src);
}
break;
case "define":
if ("name" in elAttrs && !template.defines[elAttrs.name]) {
switch (elAttrs.type) {
case "bool":
template.defines[elAttrs.name] = [ elAttrs["default"] == "true" ? 1 : 0 ];
break;
case "enum":
var values = elAttrs.values ? elAttrs.values.trim().split(" ") : [];
template.defines[elAttrs.name] = [ values.indexOf(elAttrs["default"]) + 1, values ];
break;
default:
template.warns.push("Bad define type `" + elAttrs.type + "` for " + elAttrs.name);
}
}
break;
case "text":
var text = token.childs[0];
tokens[i--] = basis.object.extend(text, {
refs: (elAttrs.ref || "").trim().split(/\s+/),
value: "notrim" in elAttrs ? text.value : text.value.replace(/^\s*[\r\n]+|[\r\n]\s*$/g, "")
});
break;
case "include":
var templateSrc = elAttrs.src;
if (templateSrc) {
var isTemplateRef = /^#\d+$/.test(templateSrc);
var isDocumentIdRef = /^id:/.test(templateSrc);
var url = isTemplateRef ? templateSrc.substr(1) : templateSrc;
var resource;
if (isTemplateRef) {
resource = templateList[url];
} else if (isDocumentIdRef) {
resource = resolveSourceByDocumentId(url.substr(3));
} else if (/^[a-z0-9\.]+$/i.test(url) && !/\.tmpl$/.test(url)) {
resource = getSourceByPath(url);
} else {
if (!/^(\.\/|\.\.|\/)/.test(url)) basis.dev.warn('Bad usage: .\nFilenames should starts with `./`, `..` or `/`. Otherwise it will treats as special reference in next minor release.');
resource = basis.resource(path.resolve(template.baseURI + url));
}
if (!resource) {
template.warns.push(' is not resolved, instruction ignored');
basis.dev.warn(' is not resolved, instruction ignored');
continue;
}
if (includeStack.indexOf(resource) == -1) {
var isolatePrefix = "isolate" in elAttrs ? elAttrs.isolate || genIsolateMarker() : "";
var decl;
if (!isDocumentIdRef) arrayAdd(template.deps, resource);
if (isTemplateRef) {
if (resource.source.bindingBridge) arrayAdd(template.deps, resource.source);
decl = getDeclFromSource(resource.source, resource.baseURI, true, options);
} else {
decl = getDeclFromSource(resource, resource.url ? path.dirname(resource.url) + "/" : "", true, options);
}
if (decl.resources && "no-style" in elAttrs == false) addStyles(template.resources, decl.resources, isolatePrefix);
if (decl.deps) addUnique(template.deps, decl.deps);
if (decl.l10n) addUnique(template.l10n, decl.l10n);
var tokenRefMap = normalizeRefs(decl.tokens);
var instructions = (token.childs || []).slice();
var styleNSPrefixMap = basis.object.slice(decl.styleNSPrefix);
if (elAttrs["class"]) instructions.push({
type: TYPE_ELEMENT,
prefix: "b",
name: "append-class",
attrs: [ {
type: TYPE_ATTRIBUTE,
name: "value",
value: elAttrs["class"]
} ]
});
if (elAttrs.id) instructions.push({
type: TYPE_ELEMENT,
prefix: "b",
name: "set-attr",
attrs: [ {
type: TYPE_ATTRIBUTE,
name: "name",
value: "id"
}, {
type: TYPE_ATTRIBUTE,
name: "value",
value: elAttrs.id
} ]
});
if (elAttrs.ref) if (tokenRefMap.element) elAttrs.ref.trim().split(/\s+/).map(function(refName) {
addTokenRef(tokenRefMap.element.token, refName);
});
for (var j = 0, child; child = instructions[j]; j++) {
if (child.type == TYPE_ELEMENT && child.prefix == "b") {
switch (child.name) {
case "style":
var childAttrs = tokenAttrs(child);
var styleNamespace = childAttrs.namespace || childAttrs.ns;
var styleIsolate = styleNamespace ? styleNamespaceIsolate : isolatePrefix;
var src = addStyle(template, child, childAttrs.src, styleIsolate);
if (styleNamespace) {
if (src in styleNamespaceIsolate == false) styleNamespaceIsolate[src] = genIsolateMarker();
styleNSPrefixMap[styleNamespace] = styleNamespaceIsolate[src];
}
break;
case "replace":
case "remove":
case "before":
case "after":
var replaceOrRemove = child.name == "replace" || child.name == "remove";
var childAttrs = tokenAttrs(child);
var ref = "ref" in childAttrs || !replaceOrRemove ? childAttrs.ref : "element";
var tokenRef = ref && tokenRefMap[ref];
if (tokenRef) {
var pos = tokenRef.owner.indexOf(tokenRef.token);
if (pos != -1) {
var args = [ pos + (child.name == "after"), replaceOrRemove ];
if (child.name != "remove") args = args.concat(process(child.childs, template, options) || []);
tokenRef.owner.splice.apply(tokenRef.owner, args);
}
}
break;
case "prepend":
case "append":
var childAttrs = tokenAttrs(child);
var ref = "ref" in childAttrs ? childAttrs.ref : "element";
var tokenRef = ref && tokenRefMap[ref];
var token = tokenRef && tokenRef.token;
if (token && token[TOKEN_TYPE] == TYPE_ELEMENT) {
var childs = process(child.childs, template, options) || [];
if (child.name == "prepend") token.splice.apply(token, [ ELEMENT_ATTRS, 0 ].concat(childs)); else token.push.apply(token, childs);
}
break;
case "attr":
case "set-attr":
modifyAttr(child, false, "set");
break;
case "append-attr":
modifyAttr(child, false, "append");
break;
case "remove-attr":
modifyAttr(child, false, "remove");
break;
case "class":
case "append-class":
modifyAttr(child, "class", "append");
break;
case "set-class":
modifyAttr(child, "class", "set");
break;
case "remove-class":
modifyAttr(child, "class", "remove");
break;
case "add-ref":
var childAttrs = tokenAttrs(child);
var ref = "ref" in childAttrs ? childAttrs.ref : "element";
var tokenRef = ref && tokenRefMap[ref];
var token = tokenRef && tokenRef.token;
if (token && childAttrs.name) addTokenRef(token, childAttrs.name);
break;
case "remove-ref":
var childAttrs = tokenAttrs(child);
var ref = "ref" in childAttrs ? childAttrs.ref : "element";
var tokenRef = ref && tokenRefMap[ref];
var token = tokenRef && tokenRef.token;
if (token) removeTokenRef(token, childAttrs.name || childAttrs.ref);
break;
default:
template.warns.push("Unknown instruction tag ");
}
} else decl.tokens.push.apply(decl.tokens, process([ child ], template, options) || []);
}
if (tokenRefMap.element) removeTokenRef(tokenRefMap.element.token, "element");
basis.object.complete(template.styleNSPrefix, styleNSPrefixMap);
if (isolatePrefix) isolateTokens(decl.tokens, isolatePrefix); else if (decl.isolate && !template.isolate) template.isolate = options.isolate || genIsolateMarker();
result.push.apply(result, decl.tokens);
} else {
var stack = includeStack.slice(includeStack.indexOf(resource) || 0).concat(resource).map(function(res) {
if (res instanceof Template) res = res.source;
if (res instanceof L10nProxyToken) return "{l10n:" + res.token.name + "@" + res.token.dictionary.resource.url + "}";
return res.url || "[inline template]";
});
template.warns.push("Recursion: ", stack.join(" -> "));
basis.dev.warn("Recursion in template: ", stack.join(" -> "));
}
}
break;
}
continue;
}
item = [ 1, bindings, refs, name(token) ];
item.push.apply(item, attrs(token, item, options.optimizeSize) || []);
item.push.apply(item, process(token.childs, template, options) || []);
break;
case TYPE_TEXT:
if (refs && refs.length == 2 && arraySearch(refs, "element")) bindings = refs[+!refs.lastSearchIndex];
if (bindings) {
var l10nBinding = absl10n(bindings, template.dictURI);
var parts = l10nBinding.split(/[:@\{]/);
if (parts[0] == "l10n" && parts.length == 3) {
if (!parts[2]) {
arrayRemove(refs, bindings);
if (refs.length == 0) refs = null;
bindings = 0;
token.value = token.value.replace(/\}$/, "@undefined}");
} else {
var l10nId = parts.slice(1).join("@");
var l10nToken = basis.l10n.token(l10nId);
var l10nTemplate = getL10nTemplate(l10nToken);
template.l10nResolved = true;
if (l10nTemplate && l10nToken.type == "markup") {
tokens[i--] = tokenize('')[0];
continue;
} else arrayAdd(template.l10n, l10nId);
}
}
}
item = [ 3, bindings, refs ];
if (!refs || token.value != "{" + refs.join("|") + "}") item.push(untoken(token.value));
break;
case TYPE_COMMENT:
if (options.optimizeSize && !bindings && !refs) continue;
item = [ 8, bindings, refs ];
if (!options.optimizeSize) if (!refs || token.value != "{" + refs.join("|") + "}") item.push(untoken(token.value));
break;
}
while (item[item.length - 1] === 0) item.pop();
result.push(item);
}
return result.length ? result : 0;
}
function absl10n(value, dictURI) {
if (typeof value != "string") return value;
var parts = value.split(":");
if (parts.length == 2 && parts[0] == "l10n" && parts[1].indexOf("@") == -1) parts[1] = parts[1] + "@" + dictURI;
return parts.join(":");
}
function normalizeRefs(tokens, dictURI, map, stIdx) {
if (!map) map = {};
for (var i = stIdx || 0, token; token = tokens[i]; i++) {
if (token[TOKEN_TYPE] == TYPE_ATTRIBUTE_EVENT) continue;
var refs = token[TOKEN_REFS];
if (refs) {
for (var j = refs.length - 1, refName; refName = refs[j]; j--) {
if (refName.indexOf(":") != -1) {
removeTokenRef(token, refName);
continue;
}
if (map[refName]) removeTokenRef(map[refName].token, refName);
if (token[TOKEN_BINDINGS] == refName) token[TOKEN_BINDINGS] = j + 1;
map[refName] = {
owner: tokens,
token: token
};
}
}
switch (token[TOKEN_TYPE]) {
case TYPE_TEXT:
token[TOKEN_BINDINGS] = absl10n(token[TOKEN_BINDINGS], dictURI);
break;
case TYPE_ATTRIBUTE:
if (token[TOKEN_BINDINGS]) {
var array = token[TOKEN_BINDINGS][0];
for (var j = 0; j < array.length; j++) array[j] = absl10n(array[j], dictURI);
}
break;
case TYPE_ELEMENT:
normalizeRefs(token, dictURI, map, ELEMENT_ATTRS);
break;
}
}
return map;
}
function applyDefines(tokens, template, options, stIdx) {
var unpredictable = 0;
for (var i = stIdx || 0, token; token = tokens[i]; i++) {
var tokenType = token[TOKEN_TYPE];
if (tokenType == TYPE_ELEMENT) unpredictable += applyDefines(token, template, options, ELEMENT_ATTRS);
if (tokenType == TYPE_ATTRIBUTE_CLASS || tokenType == TYPE_ATTRIBUTE && token[ATTR_NAME] == "class") {
var bindings = token[TOKEN_BINDINGS];
var valueIdx = ATTR_VALUE_INDEX[tokenType];
if (bindings) {
var newAttrValue = (token[valueIdx] || "").trim().split(" ");
for (var k = 0, bind; bind = bindings[k]; k++) {
if (bind.length > 2) continue;
var bindName = bind[1].split(":").pop();
var bindDef = template.defines[bindName];
if (bindDef) {
bind.push.apply(bind, bindDef);
bindDef.used = true;
if (bindDef[0]) {
if (bindDef.length == 1) arrayAdd(newAttrValue, bind[0] + bindName); else arrayAdd(newAttrValue, bind[0] + bindDef[1][bindDef[0] - 1]);
}
} else {
template.warns.push("Unpredictable value `" + bindName + "` in class binding: " + bind[0] + "{" + bind[1] + "}");
unpredictable++;
}
}
token[valueIdx] = newAttrValue.join(" ");
if (options.optimizeSize && !token[valueIdx]) token.length = valueIdx;
}
}
}
return unpredictable;
}
function isolateTokens(tokens, isolate, template, stIdx) {
function processName(name) {
var parts = name.split(":");
if (parts.length == 1) return isolate + parts[0];
if (!template) return name;
if (!parts[0]) return parts[1];
if (parts[0] in template.styleNSPrefix == false) {
template.warns.push("Namespace `" + parts[0] + "` is not defined in template, no prefix added");
return name;
}
return template.styleNSPrefix[parts[0]] + parts[1];
}
for (var i = stIdx || 0, token; token = tokens[i]; i++) {
var tokenType = token[TOKEN_TYPE];
if (tokenType == TYPE_ELEMENT) isolateTokens(token, isolate, template, ELEMENT_ATTRS);
if (tokenType == TYPE_ATTRIBUTE_CLASS || tokenType == TYPE_ATTRIBUTE && token[ATTR_NAME] == "class") {
var bindings = token[TOKEN_BINDINGS];
var valueIndex = ATTR_VALUE_INDEX[tokenType];
if (token[valueIndex]) token[valueIndex] = token[valueIndex].split(/\s+/).map(processName).join(" ");
if (bindings) for (var k = 0, bind; bind = bindings[k]; k++) bind[0] = processName(bind[0]);
}
}
}
return function makeDeclaration(source, baseURI, options, sourceUrl, sourceOrigin) {
options = options || {};
var warns = [];
var source_;
var result = {
sourceUrl: sourceUrl,
baseURI: baseURI || "",
tokens: null,
resources: [],
styleNSPrefix: {},
deps: [],
l10n: [],
defines: {},
unpredictable: true,
warns: warns,
isolate: false
};
result.dictURI = sourceUrl ? basis.path.resolve(sourceUrl) : baseURI || "";
if (result.dictURI) {
var extname = basis.path.extname(result.dictURI);
if (extname && extname != ".l10n") result.dictURI = result.dictURI.substr(0, result.dictURI.length - extname.length) + ".l10n";
}
if (!source.templateTokens) {
source_ = source;
source = tokenize(String(source));
}
if (source.warns) warns.push.apply(warns, source.warns);
includeStack.push(sourceOrigin !== true && sourceOrigin || {});
result.tokens = process(source, result, options);
includeStack.pop();
if (!result.tokens) result.tokens = [ [ 3, 0, 0, "" ] ];
if (source_) result.tokens.source_ = source_;
addTokenRef(result.tokens[0], "element");
normalizeRefs(result.tokens, result.dictURI);
result.unpredictable = !!applyDefines(result.tokens, result, options);
if (/^[^a-z]/i.test(result.isolate)) basis.dev.error("basis.template: isolation prefix `" + result.isolate + "` should not starts with symbol other than letter, otherwise it leads to incorrect css class names and broken styles");
if (includeStack.length == 0) {
isolateTokens(result.tokens, result.isolate || "", result);
if (result.isolate) for (var i = 0, item; item = result.resources[i]; i++) if (item[1] !== styleNamespaceIsolate) item[1] = result.isolate + item[1];
result.resources = result.resources.filter(function(item, idx, array) {
return !basis.array.search(array, String(item), String, idx + 1);
}).map(function(item) {
var url = item[0];
var isolate = item[1];
if (isolate === styleNamespaceIsolate) isolate = styleNamespaceIsolate[url];
if (!isolate) return url;
var resource = basis.resource.virtual("css", "").ready(function(cssResource) {
sourceResource();
basis.object.extend(cssResource, {
url: url + "?isolate-prefix=" + isolate,
baseURI: basis.path.dirname(url) + "/"
});
});
var sourceResource = basis.resource(url).ready(function(cssResource) {
var cssText = isolateCss(cssResource.cssText || "", isolate);
if (typeof btoa == "function") cssText += "\n/*# sourceMappingURL=data:application/json;base64," + btoa('{"version":3,"sources":["' + basis.path.origin + url + '"],' + '"mappings":"AAAA' + basis.string.repeat(";AACA", cssText.split("\n").length) + '"}') + " */";
resource.update(cssText);
});
return resource.url;
});
}
for (var key in result.defines) if (!result.defines[key].used) warns.push("Unused define for " + key);
delete result.defines;
delete result.l10nResolved;
if (!warns.length) result.warns = false;
return result;
};
}();
function startUseResource(uri) {
var resource = basis.resource(uri).fetch();
if (typeof resource.startUse == "function") resource.startUse();
}
function stopUseResource(uri) {
var resource = basis.resource(uri).fetch();
if (typeof resource.stopUse == "function") resource.stopUse();
}
function templateSourceUpdate() {
if (this.destroyBuilder) buildTemplate.call(this);
for (var i = 0, attach; attach = this.attaches_[i]; i++) attach.handler.call(attach.context);
}
function cloneDecl(array) {
var result = [];
if (array.source_) result.source_ = array.source_;
for (var i = 0; i < array.length; i++) result.push(Array.isArray(array[i]) ? cloneDecl(array[i]) : array[i]);
return result;
}
function getDeclFromSource(source, baseURI, clone, options) {
var result = source;
var sourceUrl;
if (typeof result == "function") {
baseURI = "baseURI" in source ? source.baseURI : baseURI;
sourceUrl = "url" in source ? source.url : sourceUrl;
result = result();
}
if (result instanceof basis.Token) {
baseURI = "baseURI" in source ? source.baseURI : baseURI;
sourceUrl = "url" in source ? source.url : sourceUrl;
result = result.get();
}
if (Array.isArray(result)) {
if (clone) result = cloneDecl(result);
result = {
tokens: result
};
} else {
if (typeof result != "object" || !Array.isArray(result.tokens)) result = String(result);
}
if (typeof result == "string") result = makeDeclaration(result, baseURI, options, sourceUrl, source);
return result;
}
function l10nHandler(value) {
if (this.type != "markup" && this.token.type == "markup") {
buildTemplate.call(this.template);
}
}
function buildTemplate() {
var decl = getDeclFromSource(this.source, this.baseURI, false, {
isolate: this.getIsolatePrefix()
});
var destroyBuilder = this.destroyBuilder;
var funcs = this.builder(decl.tokens, this);
var deps = this.deps_;
var l10n = this.l10n_;
if (deps) {
this.deps_ = null;
for (var i = 0, dep; dep = deps[i]; i++) dep.bindingBridge.detach(dep, buildTemplate, this);
}
if (l10n) for (var i = 0, item; item = l10n[i]; i++) item.token.bindingBridge.detach(item.token, l10nHandler, item);
if (decl.deps && decl.deps.length) {
deps = decl.deps;
this.deps_ = deps;
for (var i = 0, dep; dep = deps[i]; i++) dep.bindingBridge.attach(dep, buildTemplate, this);
}
if (decl.l10n) {
l10n = decl.l10n;
this.l10n_ = {};
for (var i = 0, key; key = l10n[i]; i++) {
var l10nToken = basis.l10n.token(key);
l10nToken.bindingBridge.attach(l10nToken, l10nHandler, this.l10n_[key] = {
template: this,
token: l10nToken,
type: l10nToken.type
});
}
}
this.createInstance = funcs.createInstance;
this.clearInstance = funcs.destroyInstance;
this.getBinding = function() {
return {
names: funcs.keys
};
};
this.destroyBuilder = funcs.destroy;
this.instances_ = funcs.instances_;
this.decl_ = decl;
var declResources = decl.resources && decl.resources.length > 0 ? decl.resources : null;
if (declResources) for (var i = 0, res; res = declResources[i]; i++) startUseResource(res);
if (this.resources) for (var i = 0, res; res = this.resources[i]; i++) stopUseResource(res);
this.resources = declResources;
if (destroyBuilder) destroyBuilder(true);
}
var sourceByDocumentIdResolvers = {};
function getTemplateByDocumentId(id) {
var resolver = resolveSourceByDocumentId(id);
if (resolver.template) return resolver.template;
var host = document.getElementById(id);
var source = "";
if (host && host.tagName == "SCRIPT" && host.type == "text/basis-template") source = host.textContent || host.text; else if (!host) basis.dev.warn("Template script element with id `" + id + "` not found"); else basis.dev.warn('Template should be declared in