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