define(function module(require) { "use strict";
var pushIfMissing = require('./utils').pushIfMissing;
var removeIfExisting = require('./utils').removeIfExisting;
var identity = require('./utils').identity;
/**
*
* @class View
* @classdesc This is the class representing a view.
*/
var View = Object.subclass('View', {
initialize: function() {
this.items = [];
this.downstream = [];
this.enterCallbacks = [];
this.exitCallbacks = [];
this.layersByItem = new Map();
},
safeAdd: function(item) {
var wasNewItem = pushIfMissing(this.items, item);
if(wasNewItem) {
console.log('added to selection', item);
this.enterCallbacks.forEach(function(enterCallback) { enterCallback(item); });
this.downstream.forEach(function(ea) { ea.newItemFromUpstream(item); });
}
},
safeRemove: function(item) {
var gotRemoved = removeIfExisting(this.items, item);
if(gotRemoved) {
console.log('removed from selection', item);
this.exitCallbacks.forEach(function(exitCallback) { exitCallback(item); });
this.downstream.forEach(function(ea) { ea.destroyItemFromUpstream(item); });
}
},
// Get persistent version of the current state of the Selection.
/**
* Returns an Array of the objects that are currently in the set. This Array does not update automatically.
* @function View#now
* @return {Array}
*/
now: function() {
var arr = [];
this.items.forEach(function(item) {
arr.push(item);
});
return arr;
},
/**
* Returns the current number of objects the set contains.
* @function View#size
* @return {Number}
*/
size: function() { return this.now().length; },
/**
* Takes a callback that consumes a single parameter. This callback is called whenever an object is added to the reactive set with that very object.
* @function View#enter
* @param {View~enterCallback} callback - this is executed everytime an object is added to the set
* @return {View} The callee of this method.
*/
enter: function(callback) {
this.enterCallbacks.push(callback);
this.now().forEach(function(item) { callback(item); });
return this;
},
/**
* Similar to \texttt{enter}, but the callback is called everytime an object is removed from the set.
* @function View#exit
* @param {View~exitCallback} callback
* @return {View} The callee of this method.
*/
exit: function(callback) {
this.exitCallbacks.push(callback);
return this;
},
/**
* Define partial behavior attached to each object while it is contained in the set.
* @function View#layer
* @param {Object} partialBehavior - the mixin to be applied.
* @returns {View} The callee of this method.
*/
// TODO: is this currently limited to 1 layer per item-view combination?
layer: function(partialBehavior) {
var layersByItem = this.layersByItem;
this.enter(function(item) {
// lazy initialization
if(!layersByItem.has(item)) {
layersByItem.set(item, new Layer().refineObject(item, partialBehavior));
}
var layerForItem = layersByItem.get(item);
if(!layerForItem.isGlobal()) {
layerForItem.beGlobal();
}
});
this.exit(function(item) {
var layerForItem = layersByItem.get(item);
if(layerForItem && layerForItem.isGlobal()) {
layerForItem.beNotGlobal();
}
});
return this;
}
});
/**
* This callback is call whenever an item is added to this {@link View}.
* @callback View~enterCallback
* @param {Object} item - the item that was just added to the {@link View}.
*/
/**
* This callback is call whenever an item is removed from this {@link View}.
* @callback View~exitCallback
* @param {Object} item - the item that was just removed from the {@link View}.
*/
return View;
});