Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / views.js @ f9f43e09

History | View | Annotate | Download (11.6 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 models = snf.models = snf.models || {}
43
    var storage = snf.storage = snf.storage || {};
44
    var views = snf.views = snf.views || {}
45
    var util = snf.util = snf.util || {}
46

    
47
    // shortcuts
48
    var bb = root.Backbone;
49
    
50
    // logging
51
    var logger = new snf.logging.logger("SNF-VIEWS");
52
    var debug = _.bind(logger.debug, logger);
53
    
54
    // Base view object
55
    views.View = bb.View.extend({
56
        // the main element of the view
57
        // view delegates show, visible, hide etc to this element
58
        view_id: false,
59

    
60
        el: '#app',
61
        data_from: false,
62
        selectors: {},
63
        
64
        pre_show: function() {},
65
        post_show: function() {},
66
        pre_hide: function() {},
67
        post_hide: function() {},
68

    
69
        initialize: function(options) {
70
            views.View.__super__.initialize.apply(this, [options]);
71
            this.log = new snf.logging.logger("SNF-VIEWS:" + this.view_id);
72
            this.parent_view = options && options.parent_view;
73
        },
74

    
75
        // is the view visible ?
76
        visible: function(){
77
            return $(this.el).is(":visible");
78
        },
79
        
80
        // hide view
81
        hide: function(force) {
82
            if (!this.visible() && !force) { return this };
83
            this.pre_hide();
84
            var ret = $(this.el).hide();
85
            this.post_hide();
86
            return ret;
87
        },
88
        
89
        // show view
90
        show: function(force) {
91
            if (this.visible() && !force) { return this };
92
            this.pre_show();
93
            $(this.el).show();
94
            if (this.show_view) { this.show_view.apply(this, arguments)};
95
            this.post_show();
96
        },
97

    
98
        sel: function(id) {
99
            return this.$(this.selectors[id]);
100
        },
101

    
102
        // animations
103
        fadeIn: function(time, callback) {
104
            $(this.el).fadeIn(time, callback);
105
            return this.show();
106
        },
107

    
108
        fadeOut: function(time, callback) {
109
            $(this.el).fadeOut(time, callback);
110
            return this.hide();
111
        }
112
    });
113
    
114
    
115
    // overlays registry
116
    views._overlay_index = [];
117

    
118
    // overlay view helper
119
    views.Overlay = views.View.extend({
120
        view_id: 'overlay',
121
        tpl_selector: '#generic-overlay-tpl',
122
        css_class: 'overlay',
123
        oneInstance: true,
124
        fixed: false,
125

    
126
        initialize: function(options, selector) {
127
            this.defaults = {
128
                load: false,
129
                closeOnClick: false,
130
                closeOnEsc: false,
131
                mask: {
132
                    color: "#444",
133
                    loadSpeed: snf.config.overlay_speed || 0,
134
                    opacity: 0.7
135
                },
136
                speed: snf.config.overlay_speed || 200
137
            }
138
            
139
            this.tpl_selector = selector || this.tpl_selector;
140
            views.Overlay.__super__.initialize.apply(this);
141
            views._overlay_index.push(this);
142

    
143
            this.options = _.extend(this.defaults, options);
144
            this.options.clone = this.options.clone == undefined ? true : this.options.clone;
145
            this.options.fixed = this.fixed;
146

    
147
            this.options.onOpen = this.options.onOpen || function() {};
148
            this.options.onClose = this.options.onClose || function() {};
149
            this.options.beforeOpen = this.options.beforeOpen || function() {};
150
            this.options.beforeClose = this.options.beforeClose || function() {};
151
            this.el = this.create_element();
152
            this.el.hide();
153
        
154
            var ajax_params = _.clone(this.options);
155

    
156
            ajax_params.onBeforeLoad = _.bind(this._beforeOpen, this);
157
            ajax_params.onLoad = _.bind(this._onOpen, this);
158
            ajax_params.onBeforeClose = _.bind(this._beforeClose, this);
159
            ajax_params.onClose = _.bind(this._onClose, this);
160
            ajax_params.oneInstance = this.oneInstance;
161
            // create overlay
162
            // TODO: does this return overlay object ?? (to avoid the next code line)
163
            $(this.el).overlay(ajax_params);
164

    
165
            this.overlay = $(this.el).overlay();
166
            this.append_css = this.options ? this.options.css_class ? this.options.css_class : "" : "";
167

    
168
            this.is_visible = false;
169
            return this;
170
        },
171

    
172
        create_element: function() {
173
            var el = undefined;
174
            if (this.options.clone) {
175
                el = $(this.tpl_selector).clone();
176
            } else {
177
                el = $(this.tpl_selector);
178
            }
179
            
180
            // append content
181
            if (this.content_selector) {
182
                var content = $(this.content_selector).clone();
183
                content.addClass("content");
184
                
185
                if ($(el).find(".content").length) {
186
                    $(el).find(".content").replaceWith(content);
187
                }
188
                content.removeClass("hidden");
189
            }
190

    
191
            if (this.overlay_id) {
192
            }
193

    
194
            $(el).addClass("overlay");
195
            if (this.css_class) {
196
                $(el).addClass(this.css_class);
197
            }
198
            
199
            if (this.options.clone) {
200
                $("body").append(el);
201
            }
202

    
203
            return el;
204
        },
205

    
206
        set_title: function(title) {
207
            if (title || this.title) {
208
                $(this.el).find(".overlay-header .title").html(title || this.title)
209
            }
210
        },
211

    
212
        set_subtitle: function(subtitle) {
213
            if (subtitle || this.subtitle) {
214
                $(this.el).find(".overlay-header .subtitle").html(subtitle || this.subtitle)
215
            }
216
        },
217

    
218
        _beforeOpen: function() {
219
            this.is_visible = true;
220
            if (this.append_css) {
221
                $(this.el).addClass(this.append_css);
222
            }
223

    
224
            this.set_title();
225
            this.set_subtitle();
226
            
227
            this.beforeOpen.apply(this, arguments);
228
            this.options.beforeOpen.apply(this, arguments);
229
        },
230

    
231
        _onOpen: function() {
232
            // clear previously bound click events
233
            $(this.el).find(".closeme").unbind("click");
234

    
235
            if ($(this.el).find(".closeme").length) {
236
                $(this.el).find(".closeme").click(_.bind(function(){
237
                    this.hide();
238
                }, this))
239
            }
240
            this.onOpen.apply(this, arguments);
241
            this.options.onOpen.apply(this, arguments);
242
        },
243

    
244
        _beforeClose: function() {
245
            this.is_visible = false;
246
            this.beforeClose.apply(this, arguments);
247
            this.options.beforeClose.apply(this, arguments);
248
        },
249

    
250
        _onClose: function() {
251
            if (this.append_css) {
252
                $(this.el).removeClass(this.append_css);
253
            }
254
            this.onClose.apply(this, arguments);
255
            this.options.onClose.apply(this, arguments);
256
        },
257

    
258
        beforeOpen: function () {},
259
        onOpen: function () {},
260
        beforeClose: function () {},
261
        onClose: function () {},
262

    
263
        show: function() {
264
            // close opened overlays
265
            var hidden = false;
266
            _.each(views._overlay_index, function(ovr){
267
                if (ovr == this) { return };
268
                if (ovr.visible()) {
269
                    hidden = true;
270
                    ovr.hide();
271
                }
272
            })
273

    
274
            // do we need to wait for other overlays to close ???
275
            if (hidden) { delay = 300; } else { delay = 0; }
276

    
277
            this.is_visible = true;
278
            window.setTimeout(_.bind(function(){ this.overlay.load(); this.trigger('show') }, this), delay)
279
            return this;
280
        },
281

    
282
        hide: function() {
283
            if (!this.overlay.isOpened()) {
284
                // if its not opened events wont trigger
285
                this._onClose()
286
            } else {
287
                this.overlay.close();
288
            }
289
            return this;
290
        }
291
    });
292

    
293
    
294
    // overlay view helper
295
    views.VMOverlay = views.Overlay.extend({
296

    
297
        initialize: function() {
298
            views.VMOverlay.__super__.initialize.apply(this);
299
            this.vm = undefined;
300
            this.view_id_tpl = this.view_id;
301

    
302
            _.bindAll(this, "_handle_vm_change", "_handle_vm_remove");
303
        },
304

    
305
        set_vm: function(vm) {
306
            if (this.vm) { this.unbind_vm_handlers };
307
            this.vm = vm;
308
            this.view_id = this.view_id + "_" + vm.id;
309
            this.bind_vm_handlers();
310
        },
311

    
312
        bind_vm_handlers: function() {
313
            this.log.debug("binding handlers");
314
            this.vm.bind("change", this._handle_vm_change);
315
            storage.vms.bind("remove", this._handle_vm_remove);
316
        },
317
        
318
        unbind_vm_handlers: function() {
319
            this.log.debug("unbinding handlers", this.vm);
320
            if (!this.vm) { return };
321
            this.vm.unbind("change", this._handle_vm_change);
322
            storage.vms.unbind("remove", this._handle_vm_remove);
323
        },
324
        
325
        _update_vm_details: function() { 
326
            if (!this.vm) { console.error("invalid view state"); return }
327
            this.set_subtitle(this.vm.escape("name") + snf.ui.helpers.vm_icon_tag(this.vm, "small"));
328
            this.update_vm_details() 
329
        },
330

    
331
        update_vm_details: function() {},
332
        handle_vm_remove: function() {},
333
        handle_vm_change: function () {},
334
        
335
        _handle_vm_remove: function(vm, collection) {
336
            if (this.vm && vm.id == this.vm.id) {
337
                this.hide();
338
            }
339
            this.handle_vm_remove();
340
        },
341
        
342
        _handle_vm_change: function(vm) {
343
            this._update_vm_details();
344
            this.handle_vm_change(vm);
345
        },
346
        
347
        beforeClose: function() {
348
            this.unbind_vm_handlers();
349
            this.vm = undefined;
350
        },
351

    
352
        show: function(vm) {
353
            this.set_vm(vm);
354
            views.VMOverlay.__super__.show.apply(this, arguments);
355
            this._update_vm_details();
356
        }
357

    
358
    });
359

    
360
    snf.config.update_hidden_views = true;
361

    
362
})(this);