Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (17.9 kB)

1
;(function(root){
2
    
3
    var root = root;
4
    var snf = root.synnefo = root.synnefo || {};
5
    
6
    snf.i18n = {};
7

    
8
    // Logging namespace
9
    var logging = snf.logging = snf.logging || {};
10

    
11
    // logger object
12
    var logger = logging.logger = function(ns, level){
13
        var levels = ["debug", "info", "error"];
14
        var con = window.console;
15
        
16
        this.level = level || synnefo.logging.level;
17
        this.ns = ns || "";
18

    
19
        this._log = function(lvl) {
20
            if (lvl >= this.level && con) {
21
                var args = Array.prototype.slice.call(arguments[1]);
22
                var level_name = levels[lvl];
23
                    
24
                if (this.ns) {
25
                    args = ["["+this.ns+"] "].concat(args);
26
                }
27

    
28
                log = con.log
29
                if (con[level_name])
30
                    log = con[level_name]
31

    
32
                try {
33
                    con && log.apply(con, Array.prototype.slice.call(args));
34
                } catch (err) {}
35
            }
36
        }
37

    
38
        this.debug = function() {
39
            var args = [0]; args.push.call(args, arguments);
40
            this._log.apply(this, args);
41
        }
42

    
43
        this.info = function() {
44
            var args = [1]; args.push.call(args, arguments);
45
            this._log.apply(this, args);
46
        }
47

    
48
        this.error = function() {
49
            var args = [2]; args.push.call(args, arguments);
50
            this._log.apply(this, args);
51
        }
52

    
53
    };
54
    
55
    synnefo.collect_user_data = function() {
56
        var data = {}
57
        
58
        try {
59
            data.client = {'browser': $.browser, 'screen': $.extend({}, screen), 'client': $.client}
60
        } catch (err) { data.client = err }
61
        try {
62
            data.calls = synnefo.api.requests;
63
        } catch (err) { data.calls = err }
64
        try {
65
            data.errors = synnefo.api.errors;
66
        } catch (err) { data.errors = err }
67
        try {
68
            data.data = {};
69
        } catch (err) { data.data = err }
70
        try {
71
            data.data.vms = synnefo.storage.vms.toJSON();
72
        } catch (err) { data.data.vms = err }
73
        try {
74
            data.data.networks = synnefo.storage.networks.toJSON();
75
        } catch (err) { data.data.networks = err }
76
        //try {
77
            //data.data.images = synnefo.storage.images.toJSON();
78
        //} catch (err) { data.data.images = err }
79
        //try {
80
            //data.data.flavors = synnefo.storage.flavors.toJSON();
81
        //} catch (err) { data.data.flavors = err }
82
        try {
83
            data.date = new Date;
84
        } catch (err) { data.date = err }
85

    
86
        return data;
87
    }
88

    
89
    // default logger level (debug)
90
    synnefo.logging.level = 0;
91

    
92
    // generic logger
93
    synnefo.log = new logger({'ns':'SNF'});
94

    
95
    // synnefo config options
96
    synnefo.config = synnefo.config || {};
97
    synnefo.config.api_url = "/api/v1.1";
98
    
99
    // Util namespace
100
    synnefo.util = synnefo.util || {};
101

    
102
    // Extensions and Utility functions
103
    synnefo.util.ISODateString = function(d){
104
        function pad(n){
105
            return n<10 ? '0'+n : n
106
        }
107
         return d.getUTCFullYear()+'-'
108
         + pad(d.getUTCMonth()+1)+'-'
109
         + pad(d.getUTCDate())+'T'
110
         + pad(d.getUTCHours())+':'
111
         + pad(d.getUTCMinutes())+':'
112
         + pad(d.getUTCSeconds())+'Z'
113
    }
114

    
115
 
116
    synnefo.util.parseUri = function(sourceUri) {
117
        var uriPartNames = ["source","protocol","authority","domain","port","path","directoryPath","fileName","query","anchor"];
118
        var uriParts = new RegExp("^(?:([^:/?#.]+):)?(?://)?(([^:/?#]*)(?::(\\d*))?)?((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[\\?#]|$)))*/?)?([^?#/]*))?(?:\\?([^#]*))?(?:#(.*))?").exec(sourceUri);
119
        var uri = {};
120
        
121
        for(var i = 0; i < 10; i++){
122
            uri[uriPartNames[i]] = (uriParts[i] ? uriParts[i] : "");
123
        }
124
    
125
        // Always end directoryPath with a trailing backslash if a path was present in the source URI
126
        // Note that a trailing backslash is NOT automatically inserted within or appended to the "path" key
127
        if(uri.directoryPath.length > 0){
128
            uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/");
129
        }
130
        
131
        return uri;
132
    }
133

    
134
    synnefo.util.equalHeights = function() {
135
        var max_height = 0;
136
        var selectors = _.toArray(arguments);
137
            
138
        _.each(selectors, function(s){
139
            console.log($(s).height());
140
        })
141
        // TODO: implement me
142
    }
143

    
144
    synnefo.util.ClipHelper = function(wrapper, text, settings) {
145
        settings = settings || {};
146
        this.el = $('<div class="clip-copy"></div>');
147
        wrapper.append(this.el);
148
        this.clip = $(this.el).zclip(_.extend({
149
            path: synnefo.config.js_url + "lib/ZeroClipboard.swf",
150
            copy: text
151
        }, settings));
152
    }
153

    
154
    synnefo.util.truncate = function(string, size, append, words) {
155
        if (string === undefined) { return "" };
156
        if (string.length <= size) {
157
            return string;
158
        }
159

    
160
        if (append === undefined) {
161
            append = "...";
162
        }
163
        
164
        if (!append) { append = "" };
165
        // TODO: implement word truncate
166
        if (words === undefined) {
167
            words = false;
168
        }
169
        
170
        len = size - append.length;
171
        return string.substring(0, len) + append;
172
    }
173

    
174
    synnefo.util.readablizeBytes = function(bytes) {
175
        var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB'];
176
        var e = Math.floor(Math.log(bytes)/Math.log(1024));
177
        return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
178
    }
179
    
180
    synnefo.i18n.API_ERROR_MESSAGES = {
181
        'timeout': {
182
            'message': 'TIMEOUT', 
183
            'allow_report': false,
184
            'type': 'Network'
185
        },
186
        
187
        'error': {
188
            'message': 'API error'
189
        }, 
190

    
191
        'abort': {},
192
        'parserror': {},
193
        '413': {
194
            'title': "Account warning"
195
        }
196
    }
197
    
198
    synnefo.util.array_diff = function(arr1, arr2) {
199
        var removed = [];
200
        var added = [];
201

    
202
        _.each(arr1, function(v) {
203
            if (arr2.indexOf(v) == -1) {
204
                removed[removed.length] = v;
205
            }
206
        })
207

    
208

    
209
        _.each(arr2, function(v) {
210
            if (arr1.indexOf(v) == -1) {
211
                added[added.length] = v;
212
            }
213
        })
214

    
215
        return {del: removed, add: added};
216
    }
217

    
218
    synnefo.util.open_window = function(url, name, specs) {
219
        // default specs
220
        var opts = _.extend({
221
            scrollbars: 'no',
222
            menubar: 'no',
223
            toolbar: 'no',
224
            status: 'no',
225
            top: 'no',
226
            left: 'no',
227
            height: screen.height,
228
            width: screen.width,
229
            fullscreen: 'yes',
230
            channelmode: 'yes',
231
            directories: 'no',
232
            left: 0,
233
            location: 'no',
234
            top: 0
235
        }, opts)
236
        
237
        window.open(url, name, opts);
238
    }
239
    
240
    synnefo.util.readFileContents = function(f, cb) {
241
        var reader = new FileReader();
242
        var start = 0;
243
        var stop = f.size - 1;
244

    
245
        reader.onloadend = function(e) {
246
            return cb(e.target.result);
247
        }
248
        
249
        var data = reader.readAsText(f);
250
    },
251
    
252
    synnefo.util.generateKey = function(passphrase, length) {
253
        var passphrase = passphrase || "";
254
        var length = length || 1024;
255
        var key = cryptico.generateRSAKey(passphrase, length);
256

    
257
        _.extend(key.prototype, {
258
            download: function() {
259
            }
260
        });
261

    
262
        return key;
263
    }
264
    
265
    synnefo.util.publicKeyTypesMap = {
266
        "ecdsa-sha2-nistp256": "ecdsa",
267
        "ssh-dss" : "dsa",
268
        "ssh-rsa": "rsa"
269
    }
270

    
271
    synnefo.util.validatePublicKey = function(key) {
272
        var b64 = _(key).trim().split("\n").join("").split("\r\n").join("");
273
        var type = "rsa";
274

    
275
        // in case key starts with something like ssh-rsa
276
        if (b64.split(" ").length > 1) {
277
            var parts = key.split(" ");
278
            
279
            // identify key type
280
            type_key = parts[0];
281
            if (parseInt(type_key) >= 768) {
282
                type = "rsa1";
283
                
284
                if (parts[1] == 65537) {
285
                    if (parts.length == 3) {
286
                        return [parts[0], parts[1], parts[2]].join(" ")
287
                    }
288
                }
289
                // invalid rsa1 key
290
                throw "Invalid rsa1 key";
291
            }
292
            
293
            b64 = parts[1];
294
            if (!synnefo.util.publicKeyTypesMap[type_key]) { throw "Invalid rsa key (cannot identify encryption)" }
295

    
296
            try {
297
                var data = $.base64.decode(b64);
298
                return [parts[0], parts[1]].join(" ");
299
            } catch (err) {
300
                throw "Invalid key content";
301
            }
302

    
303
            throw "Invalid key content";
304
        }
305
        
306
        // no type defined check rsa
307
        if (_(b64).startsWith("AAAAB3NzaC1yc2EA")) {
308
            try {
309
                var data = $.base64.decode(b64);
310
                return ["ssh-rsa", b64].join(" ");
311
            } catch (err) {
312
                throw "Invalid content for rsa key";
313
            }
314
        }
315

    
316
        if (_(b64).startsWith("AAAAE2Vj")) {
317
            try {
318
                var data = $.base64.decode(b64);
319
                return ["ecdsa-sha2-nistp256", b64].join(" ");
320
            } catch (err) {
321
                throw "Invalid content for ecdsa key";
322
            }
323
        }
324

    
325
        if (_(b64).startsWith("AAAAB3N")) {
326
            try {
327
                var data = $.base64.decode(b64);
328
                return ["ssh-dss", b64].join(" ");
329
            } catch (err) {
330
                throw "Invalid content for dss key (" + err + ")";
331
            }
332
        }
333

    
334
        throw "Invalid key content";
335
    }
336
    
337
    // detect flash `like a boss`
338
    // http://stackoverflow.com/questions/998245/how-can-i-detect-if-flash-is-installed-and-if-not-display-a-hidden-div-that-inf/3336320#3336320 
339
    synnefo.util.hasFlash = function() {
340
        var hasFlash = false;
341
        try {
342
            var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
343
            if (fo) hasFlash = true;
344
        } catch(e) {
345
          if(navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) hasFlash = true;
346
        }
347
        return hasFlash;
348
    }
349

    
350
    synnefo.util.promptSaveFile = function(selector, filename, data, options) {
351
        if (!synnefo.util.hasFlash()) { return };
352
        try {
353
            return $(selector).downloadify(_.extend({
354
                filename: function(){ return filename },
355
                data: function(){ return data },
356
                onComplete: function(){},
357
                onCancel: function(){},
358
                onError: function(){
359
                    console.log("ERROR", arguments);
360
                },
361
                swf: synnefo.config.media_url + 'js/lib/media/downloadify.swf',
362
                downloadImage: synnefo.config.images_url + 'download.png',
363
                transparent: true,
364
                append: false,
365
                height:20,
366
                width: 20,
367
                dataType: 'string'
368
          }, options));
369
        } catch (err) {
370
            return false;
371
        }
372
    }
373

    
374
    synnefo.util.canReadFile = function() {
375
        if ($.browser.msie) { return false };
376
        if (window.FileReader && window.File) {
377
            var f = File.prototype.__proto__;
378
            if (f.slice || f.webkitSlice || f.mozSlice) {
379
                return true
380
            }
381
        }
382
        return false;
383
    }
384

    
385
    synnefo.util.errorList = function() {
386
        
387
        this.initialize = function() {
388
            this.errors = {};
389
        }
390

    
391
        this.add = function(key, msg) {
392
            this.errors[key] = this.errors[key] || [];
393
            this.errors[key].push(msg);
394
        }
395

    
396
        this.get = function(key) {
397
            return this.errors[key];
398
        }
399

    
400
        this.empty = function() {
401
            return _.isEmpty(this.errors);
402
        }
403

    
404
        this.initialize();
405
    }
406

    
407
    synnefo.util.stacktrace = function() {
408
        try {
409
            var obj = {};
410
            if (window.Error && Error.captureStackTrace) {
411
                Error.captureStackTrace(obj, synnefo.util.stacktrace);
412
                return obj.stack;
413
            } else {
414
                return printStackTrace().join("<br /><br />");
415
            }
416
        } catch (err) {}
417
        return "";
418
    },
419
    
420
    synnefo.util.array_combinations = function(arr) {
421
        if (arr.length == 1) {
422
            return arr[0];
423
        } else {
424
            var result = [];
425

    
426
            // recur with the rest of array
427
            var allCasesOfRest = synnefo.util.array_combinations(arr.slice(1));  
428
            for (var i = 0; i < allCasesOfRest.length; i++) {
429
                for (var j = 0; j < arr[0].length; j++) {
430
                    result.push(arr[0][j] + "-" + allCasesOfRest[i]);
431
                }
432
            }
433
            return result;
434
        }
435
    }
436

    
437
    synnefo.util.parse_api_error = function() {
438
        if (arguments.length == 1) { arguments = arguments[0] };
439

    
440
        var xhr = arguments[0];
441
        var error_message = arguments[1];
442
        var error_thrown = arguments[2];
443
        var ajax_settings = _.last(arguments) || {};
444
        var call_settings = ajax_settings.error_params || {};
445
        var json_data = undefined;
446

    
447
        var critical = ajax_settings.critical === undefined ? true : ajax_settings.critical;
448

    
449
        if (xhr.responseText) {
450
            try {
451
                json_data = JSON.parse(xhr.responseText)
452
            } catch (err) {}
453
        }
454
        
455
        module = "API"
456

    
457
        try {
458
            path = synnefo.util.parseUri(ajax_settings.url).path.split("/");
459
            path.splice(0,3)
460
            module = path.join("/");
461
        } catch (err) {
462
            console.error("cannot identify api error module");
463
        }
464
        
465
        defaults = {
466
            'message': 'Api error',
467
            'type': 'API',
468
            'allow_report': true,
469
            'fatal_error': ajax_settings.critical || false,
470
            'non_critical': !critical
471
        }
472

    
473
        var code = -1;
474
        try {
475
            code = xhr.status || "undefined";
476
        } catch (err) {console.error(err);}
477
        var details = "";
478
        
479
        if ([413].indexOf(code) > -1) {
480
            defaults.non_critical = true;
481
            defaults.allow_report = false;
482
            defaults.allow_reload = false;
483
            error_message = "limit_error";
484
        }
485

    
486
        if (critical) {
487
            defaults.allow_report = true;
488
        }
489
        
490
        if (json_data) {
491
            $.each(json_data, function(key, obj) {
492
                code = obj.code;
493
                details = obj.details.replace("\n","<br>");
494
                error_message = obj.message;
495
            })
496
        }
497

    
498
        extra = {'URL': ajax_settings.url};
499
        options = {};
500
        options = _.extend(options, {'details': details, 'message': error_message, 'ns': module, 'extra_details': extra});
501
        options = _.extend(options, call_settings);
502
        options = _.extend(options, synnefo.i18n.API_ERROR_MESSAGES[error_message] || {});
503
        options = _.extend(options, synnefo.i18n.API_ERROR_MESSAGES[code] || {});
504
        
505
        if (window.ERROR_OVERRIDES && window.ERROR_OVERRIDES[options.message]) {
506
            options.message = window.ERROR_OVERRIDES[options.message];
507
        }
508
        
509
        if (code && window.ERROR_OVERRIDES && window.ERROR_OVERRIDES[code]) {
510
            options.message = window.ERROR_OVERRIDES[code];
511
        }
512

    
513
        options = _.extend(defaults, options);
514
        options.code = code;
515

    
516
        return options;
517
    }
518

    
519

    
520
    // Backbone extensions
521
    //
522
    // super method
523
    Backbone.Model.prototype._super = Backbone.Collection.prototype._super = Backbone.View.prototype._super = function(funcName){
524
        return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
525
    }
526

    
527
    // simple string format helper 
528
    // http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
529
    String.prototype.format = function() {
530
        var formatted = this;
531
        for (var i = 0; i < arguments.length; i++) {
532
            var regexp = new RegExp('\\{'+i+'\\}', 'gi');
533
            formatted = formatted.replace(regexp, arguments[i]);
534
        }
535
        return formatted;
536
    };
537

    
538

    
539
    $.fn.setCursorPosition = function(pos) {
540
        if ($(this).get(0).setSelectionRange) {
541
          $(this).get(0).setSelectionRange(pos, pos);
542
        } else if ($(this).get(0).createTextRange) {
543
          var range = $(this).get(0).createTextRange();
544
          range.collapse(true);
545
          range.moveEnd('character', pos);
546
          range.moveStart('character', pos);
547
          range.select();
548
        }
549
    }
550

    
551
    // trim prototype for IE
552
    if(typeof String.prototype.trim !== 'function') {
553
        String.prototype.trim = function() {
554
            return this.replace(/^\s+|\s+$/g, '');
555
        }
556
    }
557

    
558
    // http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area 
559
    $.fn.setCursorPosition = function(pos) {
560
        // not all browsers support setSelectionRange
561
        // put it in try/catch, fallback to no text selection
562
        try {
563
            if ($(this).get(0).setSelectionRange) {
564
              $(this).get(0).setSelectionRange(pos, pos);
565
            } else if ($(this).get(0).createTextRange) {
566
              var range = $(this).get(0).createTextRange();
567
              range.collapse(true);
568
              range.moveEnd('character', pos);
569
              range.moveStart('character', pos);
570
              range.select();
571
            }
572
        } catch (err) {
573
        }
574
    }
575

    
576
    // indexOf prototype for IE
577
    if (!Array.prototype.indexOf) {
578
      Array.prototype.indexOf = function(elt /*, from*/) {
579
        var len = this.length;
580
        var from = Number(arguments[1]) || 0;
581
        from = (from < 0)
582
             ? Math.ceil(from)
583
             : Math.floor(from);
584
        if (from < 0)
585
          from += len;
586

    
587
        for (; from < len; from++) {
588
          if (from in this &&
589
              this[from] === elt)
590
            return from;
591
        }
592
        return -1;
593
      };
594
    }
595

    
596
})(this);