1 | var BaseCollection;
|
2 | var Collection = BaseCollection = require("./Collection");
|
3 | var _ = require("underscore")
|
4 |
|
5 | module.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 |
|
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 |
|
101 |
|
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 | });
|