root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_model_views.js @ 00a67605
History | View | Annotate | Download (14.7 kB)
1 |
// Copyright 2011 GRNET S.A. All rights reserved.
|
---|---|
2 |
//
|
3 |
// Redistribution and use in source and binary forms, with or
|
4 |
// without modification, are permitted provided that the following
|
5 |
// conditions are met:
|
6 |
//
|
7 |
// 1. Redistributions of source code must retain the above
|
8 |
// copyright notice, this list of conditions and the following
|
9 |
// disclaimer.
|
10 |
//
|
11 |
// 2. Redistributions in binary form must reproduce the above
|
12 |
// copyright notice, this list of conditions and the following
|
13 |
// disclaimer in the documentation and/or other materials
|
14 |
// provided with the distribution.
|
15 |
//
|
16 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 |
// POSSIBILITY OF SUCH DAMAGE.
|
28 |
//
|
29 |
// The views and conclusions contained in the software and
|
30 |
// documentation are those of the authors and should not be
|
31 |
// interpreted as representing official policies, either expressed
|
32 |
// or implied, of GRNET S.A.
|
33 |
//
|
34 |
|
35 |
;(function(root){
|
36 |
|
37 |
// root
|
38 |
var root = root;
|
39 |
|
40 |
// setup namepsaces
|
41 |
var snf = root.synnefo = root.synnefo || {};
|
42 |
var api = snf.api = snf.api || {};
|
43 |
var models = snf.models = snf.models || {}
|
44 |
var storage = snf.storage = snf.storage || {};
|
45 |
var ui = snf.ui = snf.ui || {};
|
46 |
var util = snf.util = snf.util || {};
|
47 |
|
48 |
var views = snf.views = snf.views || {}
|
49 |
|
50 |
// shortcuts
|
51 |
var bb = root.Backbone;
|
52 |
|
53 |
// Reusable collection view
|
54 |
// Helpers handling list/edit functionality of a specific Collection object
|
55 |
views.CollectionView = views.View.extend({ |
56 |
|
57 |
collection: undefined, |
58 |
|
59 |
id_tpl: 'model-item-{0}', |
60 |
list_tpl: '#model-item-tpl', |
61 |
|
62 |
force_reset_before_fetch: true, |
63 |
auto_append_actions: true, |
64 |
fetch_params: {},
|
65 |
|
66 |
initialize: function(options) { |
67 |
views.CollectionView.__super__.initialize.apply(this, arguments); |
68 |
_.bindAll(this);
|
69 |
|
70 |
this.loading = this.$(".loading-models"); |
71 |
this.content = this.$(".content"); |
72 |
this.list = this.$(".model-list .items-list"); |
73 |
this.list_cont = this.$(".model-list"); |
74 |
this.form = this.$(".model-form-cont"); |
75 |
this.empty_msg = this.$(".items-empty-msg"); |
76 |
|
77 |
this.init_collection_handlers();
|
78 |
this.init_handlers();
|
79 |
this.close_form();
|
80 |
this.content.hide();
|
81 |
this.loading.show();
|
82 |
this.update_models();
|
83 |
this.items = [];
|
84 |
this.create_disabled = false; |
85 |
this.submiting = false; |
86 |
}, |
87 |
|
88 |
update_models: function() { |
89 |
var params = {};
|
90 |
_.extend(params, this.fetch_params);
|
91 |
params['success'] = this.handle_reset; |
92 |
this.collection.fetch(params);
|
93 |
}, |
94 |
|
95 |
init_handlers: function() { |
96 |
this.$(".add-new").click(_.bind(this.show_form, this, undefined)); |
97 |
|
98 |
this.form.find(".form-action.cancel").click(this.close_form); |
99 |
this.form.find(".form-action.submit").click(this.submit_form); |
100 |
|
101 |
var self = this; |
102 |
this.form.find("form").submit(function(e){ |
103 |
e.preventDefault(); |
104 |
self.submit_form(); |
105 |
return false; |
106 |
}); |
107 |
|
108 |
this.$(".quick-add").click(_.bind(this.show_form, this, undefined)); |
109 |
}, |
110 |
|
111 |
init_collection_handlers: function() { |
112 |
this.collection.bind("reset", this.handle_reset); |
113 |
}, |
114 |
|
115 |
reset_handlers: function() { |
116 |
this.collection.unbind("reset", this.handle_reset); |
117 |
}, |
118 |
|
119 |
set_items: function(models) { |
120 |
this.items = _.map(models, function(m) { return m.id }); |
121 |
}, |
122 |
|
123 |
_get_models: function() { |
124 |
return this.collection.models; |
125 |
}, |
126 |
|
127 |
handle_reset: function(collection, models) { |
128 |
this.loading.hide();
|
129 |
this.content.show();
|
130 |
|
131 |
if (this.force_reset_before_update) { |
132 |
this.reset_list();
|
133 |
} |
134 |
|
135 |
this.update_list(this._get_models()); |
136 |
this.update_removed(this.items, this._get_models()); |
137 |
|
138 |
this.set_items(this._get_models()); |
139 |
}, |
140 |
|
141 |
show_form: function(model) { |
142 |
if (this.create_disabled) { return }; |
143 |
var create = (model === undefined || model.id === undefined ? true : false); |
144 |
|
145 |
if (create) {
|
146 |
this.form.find(".new-title").show(); |
147 |
this.form.find(".edit-title").hide(); |
148 |
} else {
|
149 |
this.form.find(".new-title").hide(); |
150 |
this.form.find(".edit-title").show(); |
151 |
} |
152 |
|
153 |
var model = model || new this.collection.model(); |
154 |
this.list_cont.hide();
|
155 |
|
156 |
this.reset_form(model);
|
157 |
if (!snf.util.canReadFile()) {
|
158 |
this.form.find(".fromfile-field").hide(); |
159 |
} |
160 |
this.form.show();
|
161 |
|
162 |
this.editing = true; |
163 |
this.editing_id = model.id;
|
164 |
this.creating = create ? true : false; |
165 |
|
166 |
$(this.form.find("input").get(0)).focus(); |
167 |
this.reset_form_errors();
|
168 |
}, |
169 |
|
170 |
reset_form: function(model) { |
171 |
if (!model.id) {
|
172 |
this.form.find("input, textarea").val(""); |
173 |
this.form.find("select").each(function() { |
174 |
$(this).get(0).selectedIndex = 0; |
175 |
}); |
176 |
return;
|
177 |
} |
178 |
|
179 |
this.update_form_from_model(model);
|
180 |
}, |
181 |
|
182 |
show_list_msg: function(type, msg) { |
183 |
this.list_messages = this.list_messages || this.$(".list-messages"); |
184 |
var el = $('<div class="{0}">{1}</div>'.format(type, msg)); |
185 |
this.list_messages.append(el);
|
186 |
window.setTimeout(function(){
|
187 |
el.fadeOut(300).delay(300).remove(); |
188 |
}, this.message_timeout || 4000) |
189 |
}, |
190 |
|
191 |
get_fields_map: function() { |
192 |
return {};
|
193 |
}, |
194 |
|
195 |
reset_form_errors: function() { |
196 |
this.form.find(".form-field").removeClass("error"); |
197 |
this.form.find(".form-field .errors").empty(); |
198 |
this.form.find(".form-messages").empty(); |
199 |
}, |
200 |
|
201 |
show_form_errors: function(errors) { |
202 |
this.reset_form_errors();
|
203 |
var fields_map = this.get_fields_map(); |
204 |
this.form_messages = this.form_messages || this.$(".form-messages"); |
205 |
|
206 |
_.each(errors.errors, _.bind(function(error, key){
|
207 |
var field = this.form.find(fields_map[key]).closest(".form-field"); |
208 |
field.addClass("error");
|
209 |
_.each(error, function(error_msg) {
|
210 |
var error_el = $('<div class="error">{0}</div>'.format(error_msg)); |
211 |
field.find(".errors").append(error_el);
|
212 |
}); |
213 |
}, this));
|
214 |
|
215 |
var msg = errors['']; |
216 |
if (msg) {
|
217 |
var el = $('<div class="error">{0}</div>'.format(msg)); |
218 |
this.$(".form-messages").append(el); |
219 |
} |
220 |
}, |
221 |
|
222 |
clean_form_errors: function() { |
223 |
|
224 |
}, |
225 |
|
226 |
get_save_params: function(data, options) { |
227 |
return options;
|
228 |
}, |
229 |
|
230 |
submit_form: function() { |
231 |
if (this.submiting) { return }; |
232 |
var errlist = this.validate_data(this.get_form_data()); |
233 |
if (errlist.empty()) {
|
234 |
this.save_model(this.get_form_data()); |
235 |
} else {
|
236 |
this.show_form_errors(errlist);
|
237 |
} |
238 |
}, |
239 |
|
240 |
close_form: function() { |
241 |
this.editing = false; |
242 |
this.editing_id = undefined; |
243 |
this.creating = false; |
244 |
|
245 |
this.form.hide();
|
246 |
this.list_cont.show();
|
247 |
this.list_cont.find("h3").show(); |
248 |
}, |
249 |
|
250 |
create_model_element: function(model) { |
251 |
var el = this.$(this.list_tpl).clone(); |
252 |
|
253 |
el.removeClass("hidden");
|
254 |
el.addClass("model-item");
|
255 |
el.attr("id", "item-content-" + this.id_tpl.format(model.id)); |
256 |
|
257 |
if (this.auto_append_actions) { |
258 |
this.append_actions(el, model);
|
259 |
} |
260 |
|
261 |
return el;
|
262 |
}, |
263 |
|
264 |
append_actions: function(el, model) { |
265 |
var actions = $('<div class="item-actions">' + |
266 |
'<div class="item-action remove">remove</div>' +
|
267 |
'<div class="item-action confirm-remove confirm">' +
|
268 |
'<span class="text">confirm</span>' +
|
269 |
'<span class="cancel-remove cancel">cancel</span></div>' +
|
270 |
'<div class="item-action edit">edit</div>' +
|
271 |
'</div>');
|
272 |
el.append(actions); |
273 |
}, |
274 |
|
275 |
bind_list_item_actions: function(el, model) { |
276 |
el.find(".item-actions .edit").click(_.bind(this.show_form, this, model)); |
277 |
el.find(".item-actions .remove").click(_.bind(this.show_confirm_remove, this, el, model)); |
278 |
el.find(".item-actions .confirm-remove .do-confirm").click(_.bind(this.delete_model, this, model)).hide(); |
279 |
el.find(".item-actions .confirm-remove .cancel-remove").click(_.bind(this.cancel_confirm_remove, |
280 |
this, el, model)).hide();
|
281 |
}, |
282 |
|
283 |
show_confirm_remove: function(el, model) { |
284 |
var confirmed = confirm(this.confirm_delete_msg || "Are you sure you want to delete this entry ?"); |
285 |
if (confirmed) {
|
286 |
this.delete_model(model);
|
287 |
} |
288 |
//el.closest(".model-item").addClass("pending-delete");
|
289 |
}, |
290 |
|
291 |
cancel_confirm_remove: function(el, model) { |
292 |
el.closest(".model-item").removeClass("pending-delete"); |
293 |
}, |
294 |
|
295 |
_list_el: "<li></li>", |
296 |
|
297 |
new_list_el: function(model) { |
298 |
var list_el = $(this._list_el); |
299 |
el = this.create_model_element(model);
|
300 |
list_el.attr("id", this.id_tpl.format(model.id)); |
301 |
list_el.addClass("model-item");
|
302 |
this.update_list_item(el, model, true); |
303 |
list_el.append(el); |
304 |
this.bind_list_item_actions(list_el, model);
|
305 |
return list_el;
|
306 |
}, |
307 |
|
308 |
item_el: function(id) { |
309 |
return this.$("#" + this.id_tpl.format(id)); |
310 |
}, |
311 |
|
312 |
item_exists: function(model) { |
313 |
return this.item_el(model.id).length > 0; |
314 |
}, |
315 |
|
316 |
reset_list: function() { |
317 |
this.list.find("model-item").remove(); |
318 |
}, |
319 |
|
320 |
save_model: function(data) { |
321 |
this.form.find("form-action.submit").addClass("in-progress"); |
322 |
this.submiting = true; |
323 |
var created = this.creating; |
324 |
var options = {
|
325 |
success: _.bind(function(){ |
326 |
this.update_models();
|
327 |
this.close_form();
|
328 |
if (created) {
|
329 |
this.show_list_msg("success", this.create_success_msg || "Entry created"); |
330 |
} else {
|
331 |
this.show_list_msg("success", this.update_success_msg || "Entry updated"); |
332 |
} |
333 |
}, this),
|
334 |
|
335 |
error: _.bind(function(data, xhr){ |
336 |
var resp_error = ""; |
337 |
// try to parse response
|
338 |
try {
|
339 |
json_resp = JSON.parse(xhr.responseText); |
340 |
resp_error = json_resp.errors[json_resp.non_field_key].join("<br />");
|
341 |
} catch (err) {}
|
342 |
|
343 |
var form_error = resp_error != "" ? |
344 |
this.create_failed_msg + " ({0})".format(resp_error) : |
345 |
this.create_failed_msg;
|
346 |
this.show_form_errors({'': form_error || this.submit_failed_msg || 'Entry submition failed'}) |
347 |
}, this),
|
348 |
|
349 |
complete: _.bind(function(){ |
350 |
this.submiting = false; |
351 |
this.form.find("form-action.submit").addClass("in-progress"); |
352 |
}, this),
|
353 |
|
354 |
skip_api_error: true |
355 |
} |
356 |
|
357 |
if (this.editing_id && this.collection.get(this.editing_id)) { |
358 |
var model = this.collection.get(this.editing_id); |
359 |
model.save(data, this.get_save_params(data, options));
|
360 |
} else {
|
361 |
this.collection.create(data, this.get_save_params(data, options)); |
362 |
} |
363 |
}, |
364 |
|
365 |
delete_model: function(model) { |
366 |
this.item_el(model.id).addClass("in-progress").addClass("deleting"); |
367 |
var self = this; |
368 |
model.destroy({success:this.update_models, error: function() { |
369 |
self.show_list_msg("error", "Remove failed"); |
370 |
self.item_el(model.id).removeClass("in-progress").removeClass("deleting"); |
371 |
}}); |
372 |
}, |
373 |
|
374 |
update_removed: function(ids, models) { |
375 |
var newids = _.map(models, function(m) { return m.id }); |
376 |
_.each(_.difference(ids, newids), _.bind(function(id){
|
377 |
this.item_el(id).remove();
|
378 |
}, this));
|
379 |
}, |
380 |
|
381 |
update_list: function(models, reset) { |
382 |
var reset = reset || false; |
383 |
if (reset) { this.reset_list() }; |
384 |
|
385 |
// handle removed items
|
386 |
_.each(models, _.bind(function(model) {
|
387 |
if (this.item_exists(model)) { |
388 |
this.update_list_item(this.item_el(model.id), model); |
389 |
return;
|
390 |
}; |
391 |
var item_el = this.new_list_el(model); |
392 |
this.list.prepend(item_el);
|
393 |
this.trigger("item:add", this.list, item_el, model); |
394 |
}, this));
|
395 |
|
396 |
this.check_empty();
|
397 |
}, |
398 |
|
399 |
check_empty: function() { |
400 |
if (this.collection.length == 0) { |
401 |
this.empty_msg.show();
|
402 |
this.list.find(".header").hide(); |
403 |
this.el.addClass("empty"); |
404 |
} else {
|
405 |
this.empty_msg.hide();
|
406 |
this.list.find(".header").show(); |
407 |
this.el.removeClass("empty"); |
408 |
} |
409 |
}, |
410 |
|
411 |
reset: function (){} |
412 |
|
413 |
}); |
414 |
})(this);
|