Statistics
| Branch: | Tag: | Revision:

root / snf-app / synnefo / ui / static / snf / js / ui / web / ui_model_views.js @ 483c9197

History | View | Annotate | Download (12.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 api = snf.api = snf.api || {};
9
    var models = snf.models = snf.models || {}
10
    var storage = snf.storage = snf.storage || {};
11
    var ui = snf.ui = snf.ui || {};
12
    var util = snf.util = snf.util || {};
13

    
14
    var views = snf.views = snf.views || {}
15

    
16
    // shortcuts
17
    var bb = root.Backbone;
18
    
19
    // Reusable collection view
20
    // Helpers handling list/edit functionality of a specific Collection object
21
    views.CollectionView = views.View.extend({
22

    
23
        collection: undefined,
24
        
25
        id_tpl: 'model-item-{0}',
26
        list_tpl: '#model-item-tpl',
27
        
28
        force_reset_before_fetch: true,
29
        auto_append_actions: true,
30

    
31
        initialize: function(options) {
32
            views.CollectionView.__super__.initialize.apply(this, arguments);
33
            _.bindAll(this);
34

    
35
            this.loading = this.$(".loading-models");
36
            this.content = this.$(".content");
37
            this.list = this.$(".model-list .items-list");
38
            this.list_cont = this.$(".model-list");
39
            this.form = this.$(".model-form");
40
            this.empty_msg = this.$(".items-empty-msg");
41
            
42
            this.init_collection_handlers();
43
            this.init_handlers();
44
            this.close_form();
45
            this.content.hide();
46
            this.loading.show();
47
            this.update_models();
48
            this.items = [];
49
            this.create_disabled = false;
50
            this.submiting = false;
51
        },
52
        
53
        update_models: function() {
54
            this.collection.fetch({success:this.handle_reset});
55
        },
56
        
57
        init_handlers: function() {
58
            this.$(".add-new").click(_.bind(this.show_form, this, undefined));
59

    
60
            this.form.find(".form-action.cancel").click(this.close_form);
61
            this.form.find(".form-action.submit").click(this.submit_form);
62
            
63
            var self = this;
64
            this.form.find("form").submit(function(e){
65
                e.preventDefault();
66
                self.submit_form();
67
                return false;
68
            });
69

    
70
            this.$(".quick-add").click(_.bind(this.show_form, this, undefined));
71
        },
72

    
73
        init_collection_handlers: function() {
74
            this.collection.bind("reset", this.handle_reset);
75
        },
76

    
77
        reset_handlers: function() {
78
            this.collection.unbind("reset", this.handle_reset);
79
        },
80
            
81
        set_items: function(models) {
82
            this.items = _.map(models, function(m) { return m.id });
83
        },
84

    
85
        handle_reset: function(collection, models) {
86
            this.loading.hide();
87
            this.content.show();
88

    
89
            if (this.force_reset_before_update) {
90
                this.reset_list();
91
            }
92

    
93
            this.update_list(this.collection.models);
94
            this.update_removed(this.items, this.collection.models);
95

    
96
            this.set_items(this.collection.models);
97
        },
98

    
99
        show_form: function(model) {
100
            if (this.create_disabled) { return };
101
            var create = (model === undefined || model.id === undefined ? true : false);
102
        
103
            if (create) {
104
                this.form.find(".new-title").show();
105
                this.form.find(".edit-title").hide();
106
            } else {
107
                this.form.find(".new-title").hide();
108
                this.form.find(".edit-title").show();
109
            }
110

    
111
            var model = model || new this.collection.model();
112
            this.list_cont.hide();
113

    
114
            this.reset_form(model);
115
            if (!snf.util.canReadFile()) {
116
                this.form.find(".fromfile-field").hide();
117
            }
118
            this.form.show();
119

    
120
            this.editing = true;
121
            this.editing_id = model.id;
122
            this.creating = create ? true : false;
123

    
124
            $(this.form.find("input").get(0)).focus();
125
            this.reset_form_errors();
126
        },
127

    
128
        reset_form: function(model) {
129
            if (!model) {
130
                this.form.find("input, textarea").val("");
131
                return;
132
            }
133

    
134
            this.update_form_from_model(model);
135
        },
136
        
137
        show_list_msg: function(type, msg) {
138
            this.list_messages = this.list_messages || this.$(".list-messages");
139
            var el = $('<div class="{0}">{1}</div>'.format(type, msg));
140
            this.list_messages.append(el);
141
            window.setTimeout(function(){
142
                el.fadeOut(300).delay(300).remove();
143
            }, this.message_timeout || 4000)
144
        },
145
        
146
        get_fields_map: function() {
147
            return {};
148
        },
149
        
150
        reset_form_errors: function() {
151
            this.form.find(".form-field").removeClass("error");
152
            this.form.find(".form-field .errors").empty();
153
            this.form.find(".form-messages").empty();
154
        },
155

    
156
        show_form_errors: function(errors) {
157
            this.reset_form_errors();
158
            var fields_map = this.get_fields_map();
159
            this.form_messages = this.form_messages || this.$(".form-messages");
160
            
161
            _.each(errors.errors, _.bind(function(error, key){
162
                var field = this.form.find(fields_map[key]).closest(".form-field");
163
                field.addClass("error");
164
                _.each(error, function(error_msg) {
165
                    var error_el = $('<div class="error">{0}</div>'.format(error_msg));
166
                    field.find(".errors").append(error_el);
167
                });
168
            }, this));
169
            
170
            var msg = errors[''];
171
            if (msg) {
172
                var el = $('<div class="error">{0}</div>'.format(msg));
173
                this.$(".form-messages").append(el);
174
            }
175
        },
176

    
177
        clean_form_errors: function() {
178

    
179
        },
180
        
181
        submit_form: function() {
182
            if (this.submiting) { return };
183
            var errlist = this.validate_data(this.get_form_data());
184
            if (errlist.empty()) {
185
                this.save_model(this.get_form_data());
186
            } else {
187
                this.show_form_errors(errlist);
188
            }
189
        },
190

    
191
        close_form: function() {
192
            this.editing = false;
193
            this.editing_id = undefined;
194
            this.creating = false;
195

    
196
            this.form.hide();
197
            this.list_cont.show();
198
            this.list_cont.find("h3").show();
199
        },
200

    
201
        create_model_element: function(model) {
202
            var el = this.$(this.list_tpl).clone();
203

    
204
            el.removeClass("hidden");
205
            el.addClass("model-item");
206
            el.attr("id", "item-content-" + this.id_tpl.format(model.id));
207

    
208
            if (this.auto_append_actions) {
209
                this.append_actions(el, model);
210
            }
211

    
212
            return el;
213
        },
214

    
215
        append_actions: function(el, model) {
216
            var actions = $('<div class="item-actions">' +
217
                            '<div class="item-action remove">remove</div>' + 
218
                            '<div class="item-action confirm-remove confirm">' +
219
                            '<span class="text">confirm</span>' + 
220
                            '<span class="cancel-remove cancel">cancel</span></div>' + 
221
                            '<div class="item-action edit">edit</div>' +
222
                            '</div>');
223
            el.append(actions);
224
        },
225

    
226
        bind_list_item_actions: function(el, model) {
227
            el.find(".item-actions .edit").click(_.bind(this.show_form, this, model));
228
            el.find(".item-actions .remove").click(_.bind(this.show_confirm_remove, this, el, model));
229
            el.find(".item-actions .confirm-remove .do-confirm").click(_.bind(this.delete_model, this, model)).hide();
230
            el.find(".item-actions .confirm-remove .cancel-remove").click(_.bind(this.cancel_confirm_remove, 
231
                                                                        this, el, model)).hide();
232
            
233
            // initialize download link
234
            snf.util.promptSaveFile(el.find(".item-actions .download"), model.get_filename(), model.get("content"))
235
        },
236

    
237
        show_confirm_remove: function(el, model) {
238
            var confirmed = confirm(this.confirm_delete_msg || "Are you sure you want to delete this entry ?");
239
            if (confirmed) {
240
                this.delete_model(model);
241
            }
242
            //el.closest(".model-item").addClass("pending-delete");
243
        },
244

    
245
        cancel_confirm_remove: function(el, model) {
246
            el.closest(".model-item").removeClass("pending-delete");
247
        },
248

    
249
        new_list_el: function(model) {
250
            var list_el = $("<li></li>");
251
            el = this.create_model_element(model);
252
            list_el.attr("id", this.id_tpl.format(model.id));
253
            list_el.addClass("model-item");
254
            this.update_list_item(el, model, true);
255
            list_el.append(el);
256
            this.bind_list_item_actions(list_el, model);
257
            return list_el;
258
        },
259

    
260
        item_el: function(id) {
261
            return this.$("#" + this.id_tpl.format(id));
262
        },
263

    
264
        item_exists: function(model) {
265
            return this.item_el(model.id).length > 0;
266
        },
267

    
268
        reset_list: function() {
269
            this.list.find("model-item").remove();
270
        },
271
        
272
        save_model: function(data) {
273
            this.form.find("form-action.submit").addClass("in-progress");
274
            this.submiting = true;
275
            var created = this.creating;
276
            var options = {
277
                success: _.bind(function(){
278
                    this.update_models();
279
                    this.close_form();
280
                    if (created) {
281
                        this.show_list_msg("success", this.create_success_msg || "Entry created");
282
                    } else {
283
                        this.show_list_msg("success", this.update_success_msg || "Entry updated");
284
                    }
285
                }, this),
286

    
287
                error: _.bind(function(data, xhr){
288
                    var resp_error = "";
289
                    // try to parse response
290
                    try {
291
                        json_resp = JSON.parse(xhr.responseText);
292
                        resp_error = json_resp.errors[json_resp.non_field_key].join("<br />");
293
                    } catch (err) {}
294

    
295
                    var form_error = resp_error != "" ? 
296
                                this.create_failed_msg + " ({0})".format(resp_error) : 
297
                                this.create_failed_msg;
298

    
299
                    this.show_form_errors({'': form_error || 'Entry submition failed'})
300
                }, this),
301

    
302
                complete: _.bind(function(){
303
                    this.submiting = false;
304
                    this.form.find("form-action.submit").addClass("in-progress");
305
                }, this),
306

    
307
                skip_api_error: true
308
            }
309

    
310
            if (this.editing_id && this.collection.get(this.editing_id)) {
311
                var model = this.collection.get(this.editing_id);
312
                model.save(data, options);
313
            } else {
314
                this.collection.create(data, options);
315
            }
316
        },
317

    
318
        delete_model: function(model) {
319
            this.item_el(model.id).addClass("in-progress").addClass("deleting");
320
            var self = this;
321
            model.destroy({success:this.update_models, error: function() {
322
                    self.show_list_msg("error", "Remove failed");
323
                    self.item_el(model.id).removeClass("in-progress").removeClass("deleting");
324
                }});
325
        },
326
        
327
        update_removed: function(ids, models) {
328
            var newids = _.map(models, function(m) { return m.id });
329
            _.each(_.difference(ids, newids), _.bind(function(id){
330
                this.item_el(id).remove();
331
            }, this));
332
        },
333

    
334
        update_list: function(models, reset) {
335
            var reset = reset || false;
336
            if (reset) { this.reset_list() };
337
            
338
            // handle removed items
339
            _.each(models, _.bind(function(model) {
340
                if (this.item_exists(model)) { 
341
                    this.update_list_item(this.item_el(model.id), model);
342
                    return;
343
                };
344
                var item_el = this.new_list_el(model);
345
                this.list.prepend(item_el);
346
                this.trigger("item:add", this.list, item_el, model);
347
            }, this));
348

    
349
            this.check_empty();
350
        },
351

    
352
        check_empty: function() {
353
            if (this.collection.length == 0) {
354
                this.empty_msg.show();
355
                this.list.find(".header").hide();
356
                this.el.addClass("empty");
357
            } else {
358
                this.empty_msg.hide();
359
                this.list.find(".header").show();
360
                this.el.removeClass("empty");
361
            }
362
        },
363

    
364
        reset: function (){}
365

    
366
    });
367
})(this);