root / snf-cyclades-app / synnefo / ui / static / snf / js / views_ext.js @ 365af933
History | View | Annotate | Download (14.8 kB)
1 |
;(function(root){
|
---|---|
2 |
|
3 |
// root
|
4 |
var root = root;
|
5 |
|
6 |
// setup namepsaces
|
7 |
var snf = root.synnefo = root.synnefo || {};
|
8 |
var models = snf.models = snf.models || {}
|
9 |
var storage = snf.storage = snf.storage || {};
|
10 |
var ui = snf.ui = snf.ui || {};
|
11 |
var util = snf.util || {};
|
12 |
var views = snf.views = snf.views || {}
|
13 |
|
14 |
// shortcuts
|
15 |
var bb = root.Backbone;
|
16 |
|
17 |
// logging
|
18 |
var logger = new snf.logging.logger("SNF-VIEWS"); |
19 |
var debug = _.bind(logger.debug, logger);
|
20 |
|
21 |
// Extended views module
|
22 |
// View objects to provide more sophisticated base objects for views
|
23 |
// that are bind to existing storage model/collection objects.
|
24 |
views.ext = {}; |
25 |
|
26 |
views.ext.View = views.View.extend({ |
27 |
rivets_view: false, |
28 |
rivets: undefined, |
29 |
container: undefined, |
30 |
classes:'', |
31 |
|
32 |
storage_handlers: {},
|
33 |
|
34 |
init: function() {}, |
35 |
post_init: function() {}, |
36 |
|
37 |
initialize: function(options) { |
38 |
this.container = options && options.container;
|
39 |
this._subviews = [];
|
40 |
if (this.tpl) { |
41 |
this.el = $(this.tpl).clone().removeClass("hidden").removeAttr('id'); |
42 |
} |
43 |
this.init.apply(this, arguments); |
44 |
this.post_init.apply(this, arguments); |
45 |
this.append_to_container();
|
46 |
$(this.el).addClass(this.classes); |
47 |
_.bindAll(this);
|
48 |
}, |
49 |
|
50 |
append_to_container: function() { |
51 |
if (!this.container) { return } |
52 |
var cont = $(this.container); |
53 |
cont.append(this.el);
|
54 |
}, |
55 |
|
56 |
create_view: function(view_cls, options) { |
57 |
var options = _.extend({}, options);
|
58 |
options.parent_view = this;
|
59 |
var view = new view_cls(options); |
60 |
return view;
|
61 |
}, |
62 |
|
63 |
add_subview: function(view) { |
64 |
view.parent_view = this;
|
65 |
this._subviews.push(view);
|
66 |
}, |
67 |
|
68 |
remove_view: function(view) { |
69 |
this._subviews = _.without(this._subviews, view); |
70 |
}, |
71 |
|
72 |
hide_subviews: function() { |
73 |
_.each(this._subviews, function(view) { |
74 |
view.hide(true);
|
75 |
}); |
76 |
}, |
77 |
|
78 |
show_subviews: function() { |
79 |
_.each(this._subviews, function(view) { |
80 |
view.show(true);
|
81 |
}); |
82 |
}, |
83 |
|
84 |
pre_hide: function() { |
85 |
this.rivets_unbind();
|
86 |
this.remove_handlers();
|
87 |
}, |
88 |
|
89 |
get_extra_rivet_models: function() {}, |
90 |
|
91 |
get_rivet_object: function() { |
92 |
return this.rivet_object; |
93 |
}, |
94 |
|
95 |
post_hide: function() { |
96 |
this.hide_subviews();
|
97 |
this.trigger("hide"); |
98 |
}, |
99 |
|
100 |
rivets_init: function() { |
101 |
if (!this.rivets_view) { return } |
102 |
var rivet_object = this.get_rivet_object(); |
103 |
rivet_object['view'] = this; |
104 |
if (this.el != $("body").get(0)) { |
105 |
this.rivets = rivets.bind(this.el, rivet_object); |
106 |
} else {
|
107 |
} |
108 |
}, |
109 |
|
110 |
rivets_update: function() { |
111 |
if (!this.rivets_view) { return } |
112 |
this.rivets.update();
|
113 |
}, |
114 |
|
115 |
rivets_bind: function() { |
116 |
if (!this.rivets_view) { return } |
117 |
if (!this.rivets) { this.rivets_init(); return } |
118 |
var rivet_object = this.get_rivet_object(); |
119 |
rivet_object['view'] = this; |
120 |
this.rivets.models = rivet_object;
|
121 |
//this.rivets.build();
|
122 |
this.rivets.bind();
|
123 |
}, |
124 |
|
125 |
rivets_unbind: function() { |
126 |
if (!this.rivets_view) { return } |
127 |
this.rivets.unbind();
|
128 |
}, |
129 |
|
130 |
pre_show: function() { |
131 |
this.set_handlers();
|
132 |
this.rivets_bind();
|
133 |
this.show_subviews();
|
134 |
}, |
135 |
|
136 |
resolve_storage_object: function(id) { |
137 |
var result;
|
138 |
if (this['resolve_' + id + '_storage_object']) { |
139 |
return this['resolve_' + id + '_storage_object'](); |
140 |
} |
141 |
result = synnefo.storage[id]; |
142 |
return result ? result : this.collection |
143 |
}, |
144 |
|
145 |
each_storage_handler: function(cb, context) { |
146 |
if (!context) { context = this } |
147 |
_.each(this.storage_handlers, function(handlers, object_name) { |
148 |
_.each(handlers, function(events, handler_name) {
|
149 |
_.each(events, function(event) {
|
150 |
object = this.resolve_storage_object(object_name);
|
151 |
handler = this['handle_' + handler_name]; |
152 |
if (!handler) {
|
153 |
throw "Handler " + handler_name + " does not exist"; |
154 |
} |
155 |
if (!object) {
|
156 |
throw "Storage object " + object_name + " does not exist"; |
157 |
} |
158 |
cb.call(context, object, event, handler); |
159 |
}, this);
|
160 |
}, this);
|
161 |
}, this);
|
162 |
}, |
163 |
|
164 |
get_handler: function(id) { |
165 |
}, |
166 |
|
167 |
set_handlers: function() { |
168 |
this.each_storage_handler(this.set_handler, this); |
169 |
}, |
170 |
|
171 |
remove_handlers: function() { |
172 |
this.each_storage_handler(this.remove_handler, this); |
173 |
}, |
174 |
|
175 |
set_handler: function(object, event, handler) { |
176 |
object.bind(event, handler); |
177 |
}, |
178 |
|
179 |
remove_handler: function(object, event, handler) { |
180 |
object.unbind(event, handler); |
181 |
} |
182 |
}); |
183 |
|
184 |
views.ext.PaneView = views.ext.View.extend({ |
185 |
collection_view_cls: null, |
186 |
collection_view_selector: '.collection', |
187 |
init: function() { |
188 |
var options = {};
|
189 |
options['el'] = $(this.$(this.collection_view_selector).get(0)); |
190 |
this.collection_view = this.create_view(this.collection_view_cls, options); |
191 |
this.add_subview(this.collection_view); |
192 |
}, |
193 |
}); |
194 |
|
195 |
views.ext.CollectionView = views.ext.View.extend({ |
196 |
collection: undefined, |
197 |
model_view_cls: undefined, |
198 |
animation_speed: 200, |
199 |
|
200 |
init: function() { |
201 |
var handlers = {};
|
202 |
handlers[this.collection_name] = {
|
203 |
'collection_change': ['update', 'sort'], |
204 |
'collection_reset': ['reset'], |
205 |
'model_change': ['change'], |
206 |
'model_add': ['add'], |
207 |
'model_remove': ['remove'] |
208 |
} |
209 |
this.storage_handlers = _.extend(handlers, this.storage_handlers) |
210 |
this._model_views = {};
|
211 |
this.list_el = $(this.$(".items-list").get(0)); |
212 |
this.empty_el = $(this.$(".empty-list").get(0)); |
213 |
if (this.create_view_cls) { |
214 |
this._create_view = new this.create_view_cls(); |
215 |
} |
216 |
this.$(".create-button a").click(_.bind(function(e) { |
217 |
e.preventDefault(); |
218 |
this.handle_create_click();
|
219 |
}, this));
|
220 |
}, |
221 |
|
222 |
handle_create_click: function() { |
223 |
if (this._create_view) { |
224 |
this._create_view.show();
|
225 |
} |
226 |
}, |
227 |
|
228 |
pre_show: function() { |
229 |
views.ext.CollectionView.__super__.pre_show.apply(this, arguments); |
230 |
this.update_models();
|
231 |
}, |
232 |
|
233 |
handle_collection_reset: function() { |
234 |
this.update_models();
|
235 |
}, |
236 |
|
237 |
handle_model_change: function(model) { |
238 |
var el, index, model, parent, view, anim;
|
239 |
view = this._model_views[model.id];
|
240 |
if (!view) { return } |
241 |
el = view.el; |
242 |
parent = this.parent_for_model(model);
|
243 |
if (!parent.find(el).length) {
|
244 |
index = this.collection.indexOf(model);
|
245 |
anim = true;
|
246 |
this.place_in_parent(parent, el, model, index, anim);
|
247 |
} |
248 |
}, |
249 |
|
250 |
handle_collection_change: function() { |
251 |
this.update_models();
|
252 |
}, |
253 |
|
254 |
handle_model_add: function(model, collection, options) { |
255 |
this.add_model(model);
|
256 |
}, |
257 |
|
258 |
handle_model_remove: function(model, collection, options) { |
259 |
this.remove_model(model);
|
260 |
}, |
261 |
|
262 |
show_empty: function() { |
263 |
this.empty_el.show();
|
264 |
}, |
265 |
|
266 |
hide_empty: function() { |
267 |
this.empty_el.hide();
|
268 |
}, |
269 |
|
270 |
check_empty: function() { |
271 |
if (this.collection.length == 0) { |
272 |
this.show_empty();
|
273 |
this.list_el.hide();
|
274 |
} else {
|
275 |
this.list_el.show();
|
276 |
this.hide_empty();
|
277 |
} |
278 |
}, |
279 |
|
280 |
parent_for_model: function(model) { |
281 |
return this.list_el; |
282 |
}, |
283 |
|
284 |
place_in_parent: function(parent, el, m, index, anim) { |
285 |
var place_func, place_func_context, position_found;
|
286 |
|
287 |
_.each(parent.find(".model-item"), function(el) { |
288 |
var el = $(el); |
289 |
var el_index = el.data('index'); |
290 |
if (!el_index || position_found) { return }; |
291 |
if (parseInt(el_index) < index) {
|
292 |
place_func = el.before; |
293 |
place_func_context = el; |
294 |
position_found = true;
|
295 |
} |
296 |
}); |
297 |
|
298 |
if (!position_found) {
|
299 |
place_func = parent.append; |
300 |
place_func_context = parent; |
301 |
} |
302 |
|
303 |
if (anim) {
|
304 |
var self = this; |
305 |
el.fadeOut(this.animation_speed, function() { |
306 |
place_func.call(place_func_context, el); |
307 |
el.fadeIn(self.animation_speed); |
308 |
}); |
309 |
} else {
|
310 |
place_func.call(place_func_context, el); |
311 |
} |
312 |
el.attr("data-index", index);
|
313 |
}, |
314 |
|
315 |
get_model_view_cls: function(m) { |
316 |
return this.model_view_cls |
317 |
}, |
318 |
|
319 |
add_model: function(m, index) { |
320 |
// if no available class for model exists, skip model add
|
321 |
var view_cls = this.get_model_view_cls(m); |
322 |
if (!view_cls) { return } |
323 |
|
324 |
// avoid duplicate entries
|
325 |
if (this._model_views[m.id]) { return } |
326 |
|
327 |
// handle empty collection
|
328 |
this.check_empty();
|
329 |
|
330 |
// initialize view
|
331 |
var view = this.create_view(this.get_model_view_cls(m), {model: m}); |
332 |
this.add_model_view(view, m, index);
|
333 |
}, |
334 |
|
335 |
add_model_view: function(view, model, index) { |
336 |
// append html element to the parent
|
337 |
var el = view.init_element();
|
338 |
// append to registry object
|
339 |
this._model_views[model.id] = view;
|
340 |
el.addClass("model-item");
|
341 |
// where to place ?
|
342 |
var parent = this.parent_for_model(model); |
343 |
// append
|
344 |
this.place_in_parent(parent, el, model, index);
|
345 |
// make it visible by default
|
346 |
this.add_subview(view);
|
347 |
view.show(true);
|
348 |
}, |
349 |
|
350 |
each_model_view: function(cb, context) { |
351 |
if (!context) { context = this }; |
352 |
_.each(this._model_views, function(view, model_id){ |
353 |
var model = this.collection.get(model_id); |
354 |
cb.call(this, model, view, model_id);
|
355 |
}, this);
|
356 |
}, |
357 |
|
358 |
remove_model: function(m) { |
359 |
console.log("REMOVING MODEL", m);
|
360 |
var model_view = this._model_views[m.id]; |
361 |
if (!model_view) {
|
362 |
console.error("no view found");
|
363 |
return;
|
364 |
} |
365 |
model_view.hide(); |
366 |
model_view.el.remove(); |
367 |
this.remove_view(model_view);
|
368 |
delete this._model_views[m.id]; |
369 |
this.check_empty();
|
370 |
}, |
371 |
|
372 |
update_models: function(m) { |
373 |
this.check_empty();
|
374 |
this.collection.each(function(model, index) { |
375 |
if (!(model.id in this._model_views)) { |
376 |
this.add_model(model, index);
|
377 |
} else {
|
378 |
if (model != this._model_views[model.id].model) { |
379 |
this._model_views[model.id].model = model;
|
380 |
this._model_views[model.id].rivets_unbind();
|
381 |
this._model_views[model.id].rivets_bind();
|
382 |
} |
383 |
this.handle_model_change(model);
|
384 |
} |
385 |
}, this);
|
386 |
|
387 |
this.each_model_view(function(model, view, model_id){ |
388 |
if (!model) {
|
389 |
model = {'id': model_id};
|
390 |
this.remove_model(model);
|
391 |
} |
392 |
}) |
393 |
} |
394 |
}); |
395 |
|
396 |
views.ext.ModelView = views.ext.View.extend({ |
397 |
rivets_view: true, |
398 |
|
399 |
initialize: function() { |
400 |
views.ext.ModelView.__super__.initialize.apply(this, arguments); |
401 |
var actions = this.model.get('actions'); |
402 |
if (actions) {
|
403 |
this.init_action_methods(this.model.get('actions')); |
404 |
this.bind("hide", function() { |
405 |
actions.reset_pending(); |
406 |
}); |
407 |
} |
408 |
}, |
409 |
|
410 |
set_confirm: function() {}, |
411 |
unset_confirm: function() {}, |
412 |
|
413 |
init_action_methods: function(actions) { |
414 |
_.each(actions.actions, function(action) {
|
415 |
var method;
|
416 |
method = 'set_{0}_confirm'.format(action);
|
417 |
if (this[method]) { return } |
418 |
this[method] = _.bind(function(model, ev) { |
419 |
if (ev) { ev.stopPropagation() }
|
420 |
var data = {};
|
421 |
this.set_confirm(action);
|
422 |
this.model.actions.set_pending_action(action);
|
423 |
}, this);
|
424 |
method = 'unset_{0}_confirm'.format(action);
|
425 |
if (this[method]) { return } |
426 |
this[method] = _.bind(function(model, ev) { |
427 |
if (ev) { ev.stopPropagation() }
|
428 |
var data = {};
|
429 |
this.unset_confirm(action);
|
430 |
this.model.actions.unset_pending_action(action);
|
431 |
}, this);
|
432 |
}, this);
|
433 |
}, |
434 |
|
435 |
get_rivet_object: function() { |
436 |
var model = {
|
437 |
model: this.model |
438 |
} |
439 |
return model
|
440 |
}, |
441 |
|
442 |
post_init_element: function() {}, |
443 |
|
444 |
init_element: function() { |
445 |
this.el.attr("id", "model-" + this.model.id); |
446 |
this.post_init_element();
|
447 |
this.update_layout();
|
448 |
return this.el; |
449 |
}, |
450 |
|
451 |
update_layout: function() {} |
452 |
|
453 |
}); |
454 |
|
455 |
views.ModelRenameView = views.ext.ModelView.extend({ |
456 |
tpl: '#rename-view-tpl', |
457 |
title_attr: 'name', |
458 |
|
459 |
init: function() { |
460 |
views.ModelRenameView.__super__.init.apply(this, arguments); |
461 |
this.name_cont = this.$(".model-name"); |
462 |
this.edit_cont = this.$(".edit"); |
463 |
|
464 |
this.edit_btn = this.$(".edit-btn"); |
465 |
this.value = this.$(".value"); |
466 |
this.input = this.$("input"); |
467 |
this.confirm = this.edit_cont.find(".confirm"); |
468 |
this.cancel = this.edit_cont.find(".cancel"); |
469 |
|
470 |
if (this.model.get('rename_disabled')) { |
471 |
this.edit_btn.remove();
|
472 |
} |
473 |
|
474 |
this.value.dblclick(_.bind(function(e) { |
475 |
this.set_edit();
|
476 |
}, this));
|
477 |
this.input.bind('keyup', _.bind(function(e) { |
478 |
// enter keypress
|
479 |
if (e.which == 13) { this.rename(); } |
480 |
// esc keypress
|
481 |
if (e.which == 27) { this.unset_edit(); } |
482 |
}, this));
|
483 |
// initial state
|
484 |
this.unset_edit();
|
485 |
}, |
486 |
|
487 |
post_hide: function() { |
488 |
this.unset_edit();
|
489 |
}, |
490 |
|
491 |
set_edit: function() { |
492 |
if (this.model.get('rename_disabled')) { return } |
493 |
var self = this; |
494 |
this.input.val(this.model.get('name')); |
495 |
window.setTimeout(function() {
|
496 |
self.input.focus(); |
497 |
}, 20);
|
498 |
this.name_cont.hide();
|
499 |
this.edit_cont.show();
|
500 |
}, |
501 |
|
502 |
unset_edit: function() { |
503 |
this.name_cont.show();
|
504 |
this.edit_cont.hide();
|
505 |
}, |
506 |
|
507 |
rename: function() { |
508 |
var value = _.trim(this.input.val()); |
509 |
if (value) {
|
510 |
this.model.rename(value);
|
511 |
this.unset_edit();
|
512 |
} |
513 |
} |
514 |
}); |
515 |
|
516 |
views.ext.ModelCreateView = views.ext.ModelView.extend({}); |
517 |
views.ext.ModelEditView = views.ext.ModelCreateView.extend({}); |
518 |
|
519 |
})(this);
|