dojox
Version:
Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.
285 lines (246 loc) • 8.18 kB
JavaScript
define(["dojo/_base/declare", "dojo/_base/array", "dojo/_base/html", "dojo/_base/lang", "dojo/dom-class",
"dojo/Stateful", "dojo/Evented", "dojo/when"],
function(declare, arr, html, lang, domClass, Stateful, Evented, when){
return declare("dojox.calendar.StoreManager", [Stateful, Evented], {
// summary:
// This mixin contains the store management.
// owner: Object
// The owner of the store manager: a view or a calendar widget.
owner: null,
// store: dojo.store.Store
// The store that contains the events to display.
store: null,
_ownerItemsProperty: null,
_getParentStoreManager: function(){
if(this.owner && this.owner.owner){
return this.owner.owner.get("storeManager");
}
return null;
},
_initItems: function(items){
// tags:
// private
this.set("items", items);
return items;
},
_itemsSetter: function(value){
this.items = value;
this.emit("dataLoaded", value);
},
_computeVisibleItems: function(renderData){
// summary:
// Computes the data items that are in the displayed interval.
// renderData: Object
// The renderData that contains the start and end time of the displayed interval.
// tags:
// protected
var startTime = renderData.startTime;
var endTime = renderData.endTime;
var res = null;
var items = this.owner[this._ownerItemsProperty];
if(items){
res = arr.filter(items, function(item){
return this.owner.isOverlapping(renderData, item.startTime, item.endTime, startTime, endTime);
}, this);
}
return res;
},
_updateItems: function(object, previousIndex, newIndex){
// as soon as we add a item or remove one layout might change,
// let's make that the default
// TODO: what about items in non visible area...
// tags:
// private
var layoutCanChange = true;
var oldItem = null;
var newItem = this.owner.itemToRenderItem(object, this.store);
// keep a reference on the store data item.
newItem._item = object;
// get back the items from the owner that can contain the item created interactively.
this.items = this.owner[this._ownerItemsProperty];
// set the item as in the store
if(previousIndex!==-1){
if(newIndex!==previousIndex){
// this is a remove or a move
this.items.splice(previousIndex, 1);
if(this.owner.setItemSelected && this.owner.isItemSelected(newItem)){
this.owner.setItemSelected(newItem, false);
this.owner.dispatchChange(newItem, this.get("selectedItem"), null, null);
}
}else{
// this is a put, previous and new index identical
// check what changed
oldItem = this.items[previousIndex];
var cal = this.owner.dateModule;
layoutCanChange = cal.compare(newItem.startTime, oldItem.startTime) !== 0 ||
cal.compare(newItem.endTime, oldItem.endTime) !== 0;
// we want to keep the same item object and mixin new values
// into old object
lang.mixin(oldItem, newItem);
}
}else if(newIndex!==-1){
// this is a add
var l, i;
var tempId = object.temporaryId;
if(tempId){
// this item had a temporary id that was changed
l = this.items ? this.items.length : 0;
for(i=l-1; i>=0; i--){
if(this.items[i].id === tempId){
this.items[i] = newItem;
break;
}
}
// clean to temp id state and reset the item with new id to its current state.
var stateObj = this._getItemStoreStateObj({id: tempId});
this._cleanItemStoreState(tempId);
this._setItemStoreState(newItem, stateObj ? stateObj.state : null);
}
var s = this._getItemStoreStateObj(newItem);
if(s && s.state === "storing"){
// if the item is at the correct index (creation)
// we must fix it. Should not occur but ensure integrity.
if(this.items && this.items[newIndex] && this.items[newIndex].id !== newItem.id){
l = this.items.length;
for(i=l-1; i>=0; i--){
if(this.items[i].id === newItem.id){
this.items.splice(i, 1);
break;
}
}
this.items.splice(newIndex, 0, newItem);
}
// update with the latest values from the store.
lang.mixin(s.renderItem, newItem);
}else{
this.items.splice(newIndex, 0, newItem);
}
this.set("items", this.items);
}
this._setItemStoreState(newItem, "stored");
if(!this.owner._isEditing){
if(layoutCanChange){
this.emit("layoutInvalidated");
}else{
// just update the item
this.emit("renderersInvalidated", oldItem);
}
}
},
_storeSetter: function(value){
var r;
var owner = this.owner;
if(this._observeHandler){
this._observeHandler.remove();
this._observeHandler = null;
}
if(value){
var results = value.query(owner.query, owner.queryOptions);
if(results.observe){
// user asked us to observe the store
this._observeHandler = results.observe(lang.hitch(this, this._updateItems), true);
}
results = results.map(lang.hitch(this, function(item){
var renderItem = owner.itemToRenderItem(item, value);
if(renderItem.id == null){
console.err("The data item " + item.summary + " must have an unique identifier from the store.getIdentity(). The calendar will NOT work properly.");
}
// keep a reference on the store data item.
renderItem._item = item;
return renderItem;
}));
r = when(results, lang.hitch(this, this._initItems));
}else{
// we remove the store
r = this._initItems([]);
}
this.store = value;
return r;
},
_getItemStoreStateObj: function(/*Object*/item){
// tags
// private
var parentManager = this._getParentStoreManager();
if(parentManager){
return parentManager._getItemStoreStateObj(item);
}
var store = this.get("store");
if(store != null && this._itemStoreState != null){
var id = item.id === undefined ? store.getIdentity(item) : item.id;
return this._itemStoreState[id];
}
return null;
},
getItemStoreState: function(item){
// summary:
// Returns the creation state of an item.
// This state is changing during the interactive creation of an item.
// Valid values are:
// - "unstored": The event is being interactively created. It is not in the store yet.
// - "storing": The creation gesture has ended, the event is being added to the store.
// - "stored": The event is not in the two previous states, and is assumed to be in the store
// (not checking because of performance reasons, use store API for testing existence in store).
// item: Object
// The item.
// returns: String
var parentManager = this._getParentStoreManager();
if(parentManager){
return parentManager.getItemStoreState(item);
}
if(this._itemStoreState == null){
return "stored";
}
var store = this.get("store");
var id = item.id === undefined ? store.getIdentity(item) : item.id;
var s = this._itemStoreState[id];
if(store != null && s !== undefined){
return s.state;
}
return "stored";
},
_cleanItemStoreState: function(id){
var parentManager = this._getParentStoreManager();
if(parentManager){
return parentManager._cleanItemStoreState(id);
}
if(!this._itemStoreState){
return;
}
var s = this._itemStoreState[id];
if(s){
delete this._itemStoreState[id];
return true;
}
return false;
},
_setItemStoreState: function(/*Object*/item, /*String*/state){
// tags
// private
var parentManager = this._getParentStoreManager();
if(parentManager){
parentManager._setItemStoreState(item, state);
return;
}
if(this._itemStoreState === undefined){
this._itemStoreState = {};
}
var store = this.get("store");
var id = item.id === undefined ? store.getIdentity(item) : item.id;
var s = this._itemStoreState[id];
if(state === "stored" || state == null){
if(s !== undefined){
delete this._itemStoreState[id];
}
return;
}
if(store){
this._itemStoreState[id] = {
id: id,
item: item,
renderItem: this.owner.itemToRenderItem(item, store),
state: state
};
}
}
});
});