Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / ui / web / ui_model_views.js @ c72a830d

History | View | Annotate | Download (12.3 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.submiting = false;
50
        },
51
        
52
        update_models: function() {
53
            this.collection.fetch({success:this.handle_reset});
54
        },
55
        
56
        init_handlers: function() {
57
            this.$(".add-new").click(_.bind(this.show_form, this, undefined));
58

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

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

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

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

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

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

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

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

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

    
109
            var model = model || new this.collection.model();
110
            this.list_cont.hide();
111

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

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

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

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

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

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

    
175
        clean_form_errors: function() {
176

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

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

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

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

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

    
206
            if (this.auto_append_actions) {
207
                this.append_actions(el, model);
208
            }
209

    
210
            return el;
211
        },
212

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

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

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

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

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

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

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

    
266
        reset_list: function() {
267
            this.list.find("model-item").remove();
268
        },
269
        
270
        save_model: function(data) {
271
            this.form.find("form-action.submit").addClass("in-progress");
272
            this.submiting = true;
273

    
274
            var options = {
275
                success: _.bind(function(){
276
                    this.update_models();
277
                    this.close_form();
278
                    this.show_list_msg("success", this.create_success_msg || "Entry created");
279
                }, this),
280

    
281
                error: _.bind(function(data, xhr){
282
                    var resp_error = "";
283
                    // try to parse response
284
                    try {
285
                        json_resp = JSON.parse(xhr.responseText);
286
                        resp_error = json_resp.errors[json_resp.non_field_key].join("<br />");
287
                    } catch (err) {}
288

    
289
                    var form_error = resp_error != "" ? 
290
                                this.create_failed_msg + " ({0})".format(resp_error) : 
291
                                this.create_failed_msg;
292

    
293
                    this.show_form_errors({'': form_error || 'Entry submition failed'})
294
                }, this),
295

    
296
                complete: _.bind(function(){
297
                    this.submiting = false;
298
                    this.form.find("form-action.submit").addClass("in-progress");
299
                }, this),
300

    
301
                skip_api_error: true
302
            }
303

    
304
            if (this.editing_id && this.collection.get(this.editing_id)) {
305
                var model = this.collection.get(this.editing_id);
306
                model.save(data, options);
307
            } else {
308
                this.collection.create(data, options);
309
            }
310
        },
311

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

    
328
        update_list: function(models, reset) {
329
            var reset = reset || false;
330
            if (reset) { this.reset_list() };
331
            
332
            // handle removed items
333
            _.each(models, _.bind(function(model) {
334
                if (this.item_exists(model)) { 
335
                    this.update_list_item(this.item_el(model.id), model);
336
                    return;
337
                };
338
                this.list.append(this.new_list_el(model))
339
            }, this));
340

    
341
            this.check_empty();
342
        },
343

    
344
        check_empty: function() {
345
            if (this.collection.length == 0) {
346
                this.empty_msg.show();
347
                this.list.find(".header").hide();
348
                this.el.addClass("empty");
349
            } else {
350
                this.empty_msg.hide();
351
                this.list.find(".header").show();
352
                this.el.removeClass("empty");
353
            }
354
        },
355

    
356
        reset: function (){}
357

    
358
    });
359
})(this);