UNPKG

7 kBJavaScriptView Raw
1var BaseCollection;
2var Collection = BaseCollection = require("./Collection");
3var _ = require("underscore")
4
5module.exports = Collection.extend("ExtendedCollection", {
6
7 constructor: function(models, options){
8 this._index = {};
9 this._groups = {};
10 this._eventMarkers = {};
11
12 Collection.apply(this, arguments);
13
14 var index = {};
15 var groups = {};
16
17 if(this.index) _.extend(index, this.index);
18 if(options && options.index) _.extend(index, options.index);
19 if(this.group) _.extend(groups, this.group);
20 if(options && options.group) _.extend(groups, options.group);
21 this.indexBy(index).groupBy(groups);
22 },
23
24 fire: function(event){
25 if(_.isObject(event)){
26 for(var key in event){
27 var args = ["trigger"];
28 args = args.concat(Array.isString(event)? event[key] : [event[key]]);
29 this.invoke.apply(this, args);
30 }
31 return this;
32 }
33 var args = Array.prototype.slice.call(arguments);
34 args.unshift("trigger");
35 this.invoke.apply(this, args);
36 return this;
37 },
38
39 // iterator - string, function or array of strings that compile index from many attributes and separators
40 indexBy: function(name, iterator){
41 if(_.isObject(name)){
42 for(var key in name) {
43 this.indexBy(key, name[key]);
44 }
45 return this;
46 }
47 var itr;
48 var index = this._index[name] = {};
49 if(typeof iterator == "undefined") iterator = name;
50
51 if(typeof iterator == "string")
52 itr = function(model){return model.get(iterator);};
53 else if(Array.isArray(iterator))
54 itr = function(model){
55 return iterator.map(function(stuff){
56 return model.get(stuff).toString();
57 }).join("_");
58 };
59 else itr = iterator;
60 var self = this;
61
62 var addModel = function(model){
63 var index = itr(model);
64 this[index] = model;
65 model.on("change", function(model){
66 var newIndex = itr(model);
67 if(newIndex !== index){
68 delete this[index];
69 this[newIndex] = model;
70 index = newIndex;
71 }
72 }, this);
73 };
74
75 var removeModel = function(model){
76 model.off(null, null, this);
77 delete this[itr(model)];
78 };
79
80 this
81 .on("add", addModel, index)
82 .on("remove",removeModel, index)
83 .on("reset", function(){
84 options.previousModels.forEach(removeModel);
85 self.each(addModel, this)
86 }, index )
87 .each(_.bind(addModel, index));
88 return this;
89 },
90
91 dropIndex: function(name){
92 if(typeof this._index[name] === "undefined") return this;
93 var self = this;
94 this.each(function(model){model.off(null, null, self._index[name]);});
95 this.off(null, null, this._index[name]);
96 delete this._index[name];
97 return this;
98 },
99
100 // iterator - string, function or array of strings that compile index from many attributes and separators
101 // also, iterator can return array of strings to add model to multiple groups
102 groupBy: function(name, iterator, Collection){
103 Collection = Collection || BaseCollection;
104
105 if(_.isObject(name)){
106 for(var key in name) {
107 this.groupBy(key, name[key]);
108 }
109 return this;
110 }
111 var itr;
112
113 if(typeof iterator == "string")
114 itr = function(model){return model.get(iterator);};
115 else itr = iterator;
116 var self = this;
117
118 var groups = this._groups[name] = {};
119
120 var add = function(index, model){
121 if(!groups[index]) {
122 groups[index] = new Collection();
123 groups[index].add(model);
124 self.trigger("group:"+name, index, groups[index]);
125 }
126 else{
127 groups[index].add(model);
128 }
129 };
130
131 var remove = function(index, model){
132 groups[index].remove(model);
133 if(groups[index].length === 0) {
134 groups[index].trigger("empty");
135 self.trigger("dropGroup:"+name, index);
136 delete groups[index];
137 }
138 };
139
140 var addModel = function(model){
141 var group = itr(model);
142 if(Array.isArray(group)){
143 group.forEach(function(gr){ add(gr, model); });
144 }
145 else add(group, model);
146
147 model.on("change", function(model){
148 var newIndex = itr(model);
149 if(!_.isEqual(newIndex, group)){
150 if(Array.isArray(group) && Array.isArray(newIndex)){
151 var added = _.difference( newIndex, group );
152 var removed = _.difference( group, newIndex );
153 added .forEach(function(idx){add (idx, model);});
154 removed .forEach(function(idx){remove(idx, model);});
155 }
156 else{
157 remove (group, model);
158 add (newIndex, model);
159 }
160 group = newIndex;
161 }
162 }, this);
163 };
164
165 var removeModel = function(model){
166 model.off(null, null, this);
167 var idx = itr(model);
168 if(Array.isArray(idx)) idx.forEach(function(i){remove(i, model);});
169 else remove(idx, model);
170 };
171
172 this
173 .on("add", addModel, groups)
174 .on("remove", removeModel, groups)
175 .on("reset", function(collection, options){
176 options.previousModels.forEach(removeModel);
177 self.each(addModel, this);
178 }, groups )
179 .each(_.bind(addModel, groups));
180 return this;
181 },
182
183 getGroup: function(name, index){
184 return this._groups[name] ? this._groups[name][index] : undefined;
185 },
186
187 dropGroup: function(name){
188 var self = this;
189 var groups = this._groups[name];
190 if(!grups) return this;
191 this.each(function(model){model.off(null, null, groups);});
192 this
193 .off("add", null, groups)
194 .off("reset", null, groups)
195 .off("remove", null, groups);
196 delete this._groups[name];
197 return this;
198 },
199
200 bindAll: function(evt, method, ctx){
201 var self = this;
202 var marker = this._eventMarkers[evt] = {};
203 var bind = function(model){ model.on (evt, method, ctx || model) };
204 var unbind = function(model){ model.off(evt, method, ctx || model) };
205 this
206 .on("add", bind, marker)
207 .on("remove", unbind, marker)
208 .on("reset", function(collection, options){
209 options.previousModels.forEach(unbind);
210 self.each(bind);
211 }, marker );
212 this.each(bind);
213 return this;
214 },
215
216 unbindAll: function(evt, method, ctx){
217 var marker = this._eventMarkers[evt];
218 if(!marker) return this;
219 this
220 .off("add", method || null, marker)
221 .off("reset", null, marker)
222 .off("remove", method || null, marker);
223 this.each(function(model){model.off(evt, method || null, ctx || model)});
224 delete this._eventMarkers[evt];
225 return this;
226 },
227
228 getBy: function(index, value){
229 return this._index[index] ? this._index[ index ][ value ] : undefined;
230 }
231
232});