ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

kube.js (66525B)


      1 (function ($) {
      2     /*
      3         Kube. CSS & JS Framework
      4         Version 6.5.2
      5         Updated: February 2, 2017
      6 
      7         http://imperavi.com/kube/
      8 
      9         Copyright (c) 2009-2017, Imperavi LLC.
     10         License: MIT
     11     */
     12     if (typeof jQuery === 'undefined') {
     13         throw new Error('Kube\'s requires jQuery')
     14     }
     15 
     16     ;(function ($) {
     17         var version = $.fn.jquery.split('.');
     18         if (version[0] == 1 && version[1] < 8) {
     19             throw new Error('Kube\'s requires at least jQuery v1.8');
     20         }
     21     })(jQuery);
     22 
     23     ;(function () {
     24         // Inherits
     25         Function.prototype.inherits = function (parent) {
     26             var F = function () {
     27             };
     28             F.prototype = parent.prototype;
     29             var f = new F();
     30 
     31             for (var prop in this.prototype) f[prop] = this.prototype[prop];
     32             this.prototype = f;
     33             this.prototype.super = parent.prototype;
     34         };
     35 
     36         // Core Class
     37         var Kube = function (element, options) {
     38             options = (typeof options === 'object') ? options : {};
     39 
     40             this.$element = $(element);
     41             this.opts = $.extend(true, this.defaults, $.fn[this.namespace].options, this.$element.data(), options);
     42             this.$target = (typeof this.opts.target === 'string') ? $(this.opts.target) : null;
     43         };
     44 
     45         // Core Functionality
     46         Kube.prototype = {
     47             getInstance: function () {
     48                 return this.$element.data('fn.' + this.namespace);
     49             },
     50             hasTarget: function () {
     51                 return !(this.$target === null);
     52             },
     53             callback: function (type) {
     54                 var args = [].slice.call(arguments).splice(1);
     55 
     56                 // on element callback
     57                 if (this.$element) {
     58                     args = this._fireCallback($._data(this.$element[0], 'events'), type, this.namespace, args);
     59                 }
     60 
     61                 // on target callback
     62                 if (this.$target) {
     63                     args = this._fireCallback($._data(this.$target[0], 'events'), type, this.namespace, args);
     64                 }
     65 
     66                 // opts callback
     67                 if (this.opts && this.opts.callbacks && $.isFunction(this.opts.callbacks[type])) {
     68                     return this.opts.callbacks[type].apply(this, args);
     69                 }
     70 
     71                 return args;
     72             },
     73             _fireCallback: function (events, type, eventNamespace, args) {
     74                 if (events && typeof events[type] !== 'undefined') {
     75                     var len = events[type].length;
     76                     for (var i = 0; i < len; i++) {
     77                         var namespace = events[type][i].namespace;
     78                         if (namespace === eventNamespace) {
     79                             var value = events[type][i].handler.apply(this, args);
     80                         }
     81                     }
     82                 }
     83 
     84                 return (typeof value === 'undefined') ? args : value;
     85             }
     86         };
     87 
     88         // Scope
     89         window.MaterialisKube = Kube;
     90 
     91     })();
     92     /**
     93      * @library Kube Plugin
     94      * @author Imperavi LLC
     95      * @license MIT
     96      */
     97 
     98     var Kube = window.MaterialisKube;
     99     (function (Kube) {
    100         Kube.Plugin = {
    101             create: function (classname, pluginname) {
    102                 pluginname = (typeof pluginname === 'undefined') ? classname.toLowerCase() : pluginname;
    103 
    104                 $.fn[pluginname] = function (method, options) {
    105                     var args = Array.prototype.slice.call(arguments, 1);
    106                     var name = 'fn.' + pluginname;
    107                     var val = [];
    108 
    109                     this.each(function () {
    110                         var $this = $(this), data = $this.data(name);
    111                         options = (typeof method === 'object') ? method : options;
    112 
    113                         if (!data) {
    114                             // Initialization
    115                             $this.data(name, {});
    116                             $this.data(name, (data = new Kube[classname](this, options)));
    117                         }
    118 
    119                         // Call methods
    120                         if (typeof method === 'string') {
    121                             if ($.isFunction(data[method])) {
    122                                 var methodVal = data[method].apply(data, args);
    123                                 if (methodVal !== undefined) {
    124                                     val.push(methodVal);
    125                                 }
    126                             }
    127                             else {
    128                                 $.error('No such method "' + method + '" for ' + classname);
    129                             }
    130                         }
    131 
    132                     });
    133 
    134                     return (val.length === 0 || val.length === 1) ? ((val.length === 0) ? this : val[0]) : val;
    135                 };
    136 
    137                 $.fn[pluginname].options = {};
    138 
    139                 return this;
    140             },
    141             autoload: function (pluginname) {
    142                 var arr = pluginname.split(',');
    143                 var len = arr.length;
    144 
    145                 for (var i = 0; i < len; i++) {
    146                     var name = arr[i].toLowerCase().split(',').map(function (s) {
    147                         return s.trim()
    148                     }).join(',');
    149                     this.autoloadQueue.push(name);
    150                 }
    151 
    152                 return this;
    153             },
    154             autoloadQueue: [],
    155             startAutoload: function () {
    156                 if (!window.MutationObserver || this.autoloadQueue.length === 0) {
    157                     return;
    158                 }
    159 
    160                 var self = this;
    161                 var observer = new MutationObserver(function (mutations) {
    162                     mutations.forEach(function (mutation) {
    163                         var newNodes = mutation.addedNodes;
    164                         if (newNodes.length === 0 || (newNodes.length === 1 && newNodes.nodeType === 3)) {
    165                             return;
    166                         }
    167 
    168                         self.startAutoloadOnce();
    169                     });
    170                 });
    171 
    172                 // pass in the target node, as well as the observer options
    173                 observer.observe(document, {
    174                     subtree: true,
    175                     childList: true
    176                 });
    177             },
    178             startAutoloadOnce: function () {
    179                 var self = this;
    180                 var $nodes = $('[data-component]').not('[data-loaded]');
    181                 $nodes.each(function () {
    182                     var $el = $(this);
    183                     var pluginname = $el.data('component');
    184 
    185                     if (self.autoloadQueue.indexOf(pluginname) !== -1) {
    186                         $el.attr('data-loaded', true);
    187                         $el[pluginname]();
    188                     }
    189                 });
    190 
    191             },
    192             watch: function () {
    193                 Kube.Plugin.startAutoloadOnce();
    194                 Kube.Plugin.startAutoload();
    195             }
    196         };
    197 
    198         $(window).on('load', function () {
    199             Kube.Plugin.watch();
    200         });
    201 
    202     }(Kube));
    203     /**
    204      * @library Kube Animation
    205      * @author Imperavi LLC
    206      * @license MIT
    207      */
    208     (function (Kube) {
    209         Kube.Animation = function (element, effect, callback) {
    210             this.namespace = 'animation';
    211             this.defaults = {};
    212 
    213             // Parent Constructor
    214             Kube.apply(this, arguments);
    215 
    216             // Initialization
    217             this.effect = effect;
    218             this.completeCallback = (typeof callback === 'undefined') ? false : callback;
    219             this.prefixes = ['', '-moz-', '-o-animation-', '-webkit-'];
    220             this.queue = [];
    221 
    222             this.start();
    223         };
    224 
    225         Kube.Animation.prototype = {
    226             start: function () {
    227                 if (this.isSlideEffect()) this.setElementHeight();
    228 
    229                 this.addToQueue();
    230                 this.clean();
    231                 this.animate();
    232             },
    233             addToQueue: function () {
    234                 this.queue.push(this.effect);
    235             },
    236             setElementHeight: function () {
    237                 this.$element.height(this.$element.height());
    238             },
    239             removeElementHeight: function () {
    240                 this.$element.css('height', '');
    241             },
    242             isSlideEffect: function () {
    243                 return (this.effect === 'slideDown' || this.effect === 'slideUp');
    244             },
    245             isHideableEffect: function () {
    246                 var effects = ['fadeOut', 'slideUp', 'flipOut', 'zoomOut', 'slideOutUp', 'slideOutRight', 'slideOutLeft'];
    247 
    248                 return ($.inArray(this.effect, effects) !== -1);
    249             },
    250             isToggleEffect: function () {
    251                 return (this.effect === 'show' || this.effect === 'hide');
    252             },
    253             storeHideClasses: function () {
    254                 if (this.$element.hasClass('hide-sm')) this.$element.data('hide-sm-class', true);
    255                 else if (this.$element.hasClass('hide-md')) this.$element.data('hide-md-class', true);
    256             },
    257             revertHideClasses: function () {
    258                 if (this.$element.data('hide-sm-class')) this.$element.addClass('hide-sm').removeData('hide-sm-class');
    259                 else if (this.$element.data('hide-md-class')) this.$element.addClass('hide-md').removeData('hide-md-class');
    260                 else this.$element.addClass('hide');
    261             },
    262             removeHideClass: function () {
    263                 if (this.$element.data('hide-sm-class')) this.$element.removeClass('hide-sm');
    264                 else if (this.$element.data('hide-md-class')) this.$element.removeClass('hide-md');
    265                 else this.$element.removeClass('hide');
    266             },
    267             animate: function () {
    268                 this.storeHideClasses();
    269                 if (this.isToggleEffect()) {
    270                     return this.makeSimpleEffects();
    271                 }
    272 
    273                 this.$element.addClass('kubeanimated');
    274                 this.$element.addClass(this.queue[0]);
    275                 this.removeHideClass();
    276 
    277                 var _callback = (this.queue.length > 1) ? null : this.completeCallback;
    278                 this.complete('AnimationEnd', $.proxy(this.makeComplete, this), _callback);
    279             },
    280             makeSimpleEffects: function () {
    281                 if (this.effect === 'show') this.removeHideClass();
    282                 else if (this.effect === 'hide') this.revertHideClasses();
    283 
    284                 if (typeof this.completeCallback === 'function') this.completeCallback(this);
    285             },
    286             makeComplete: function () {
    287                 if (this.$element.hasClass(this.queue[0])) {
    288                     this.clean();
    289                     this.queue.shift();
    290 
    291                     if (this.queue.length) this.animate();
    292                 }
    293             },
    294             complete: function (type, make, callback) {
    295                 var events = type.split(' ').map(function (type) {
    296                     return type.toLowerCase() + ' webkit' + type + ' o' + type + ' MS' + type;
    297                 });
    298 
    299                 this.$element.one(events.join(' '), $.proxy(function () {
    300                     if (typeof make === 'function') make();
    301                     if (this.isHideableEffect()) this.revertHideClasses();
    302                     if (this.isSlideEffect()) this.removeElementHeight();
    303                     if (typeof callback === 'function') callback(this);
    304 
    305                     this.$element.off(event);
    306 
    307                 }, this));
    308             },
    309             clean: function () {
    310                 this.$element.removeClass('kubeanimated').removeClass(this.queue[0]);
    311             }
    312         };
    313 
    314         // Inheritance
    315         Kube.Animation.inherits(Kube);
    316 
    317     }(Kube));
    318 
    319 // Plugin
    320     (function ($) {
    321         $.fn.animation = function (effect, callback) {
    322             var name = 'fn.animation';
    323 
    324             return this.each(function () {
    325                 var $this = $(this), data = $this.data(name);
    326 
    327                 $this.data(name, {});
    328                 $this.data(name, (data = new Kube.Animation(this, effect, callback)));
    329             });
    330         };
    331 
    332         $.fn.animation.options = {};
    333 
    334     })(jQuery);
    335     /**
    336      * @library Kube Detect
    337      * @author Imperavi LLC
    338      * @license MIT
    339      */
    340     (function (Kube) {
    341         Kube.Detect = function () {
    342         };
    343 
    344         Kube.Detect.prototype = {
    345             isMobile: function () {
    346                 return /(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent);
    347             },
    348             isDesktop: function () {
    349                 return !/(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent);
    350             },
    351             isMobileScreen: function () {
    352                 return ($(window).width() <= 768);
    353             },
    354             isTabletScreen: function () {
    355                 return ($(window).width() >= 768 && $(window).width() <= 1024);
    356             },
    357             isDesktopScreen: function () {
    358                 return ($(window).width() > 1024);
    359             }
    360         };
    361 
    362 
    363     }(Kube));
    364     /**
    365      * @library Kube FormData
    366      * @author Imperavi LLC
    367      * @license MIT
    368      */
    369     (function (Kube) {
    370         Kube.FormData = function (app) {
    371             this.opts = app.opts;
    372         };
    373 
    374         Kube.FormData.prototype = {
    375             set: function (data) {
    376                 this.data = data;
    377             },
    378             get: function (formdata) {
    379                 this.formdata = formdata;
    380 
    381                 if (this.opts.appendForms) this.appendForms();
    382                 if (this.opts.appendFields) this.appendFields();
    383 
    384                 return this.data;
    385             },
    386             appendFields: function () {
    387                 var $fields = $(this.opts.appendFields);
    388                 if ($fields.length === 0) {
    389                     return;
    390                 }
    391 
    392                 var self = this;
    393                 var str = '';
    394 
    395                 if (this.formdata) {
    396                     $fields.each(function () {
    397                         self.data.append($(this).attr('name'), $(this).val());
    398                     });
    399                 }
    400                 else {
    401                     $fields.each(function () {
    402                         str += '&' + $(this).attr('name') + '=' + $(this).val();
    403                     });
    404 
    405                     this.data = (this.data === '') ? str.replace(/^&/, '') : this.data + str;
    406                 }
    407             },
    408             appendForms: function () {
    409                 var $forms = $(this.opts.appendForms);
    410                 if ($forms.length === 0) {
    411                     return;
    412                 }
    413 
    414                 if (this.formdata) {
    415                     var self = this;
    416                     var formsData = $(this.opts.appendForms).serializeArray();
    417                     $.each(formsData, function (i, s) {
    418                         self.data.append(s.name, s.value);
    419                     });
    420                 }
    421                 else {
    422                     var str = $forms.serialize();
    423 
    424                     this.data = (this.data === '') ? str : this.data + '&' + str;
    425                 }
    426             }
    427         };
    428 
    429 
    430     }(Kube));
    431     /**
    432      * @library Kube Response
    433      * @author Imperavi LLC
    434      * @license MIT
    435      */
    436     (function (Kube) {
    437         Kube.Response = function (app) {
    438         };
    439 
    440         Kube.Response.prototype = {
    441             parse: function (str) {
    442                 if (str === '') return false;
    443 
    444                 var obj = {};
    445 
    446                 try {
    447                     obj = JSON.parse(str);
    448                 } catch (e) {
    449                     return false;
    450                 }
    451 
    452                 if (obj[0] !== undefined) {
    453                     for (var item in obj) {
    454                         this.parseItem(obj[item]);
    455                     }
    456                 }
    457                 else {
    458                     this.parseItem(obj);
    459                 }
    460 
    461                 return obj;
    462             },
    463             parseItem: function (item) {
    464                 if (item.type === 'value') {
    465                     $.each(item.data, $.proxy(function (key, val) {
    466                         val = (val === null || val === false) ? 0 : val;
    467                         val = (val === true) ? 1 : val;
    468 
    469                         $(key).val(val);
    470 
    471                     }, this));
    472                 }
    473                 else if (item.type === 'html') {
    474                     $.each(item.data, $.proxy(function (key, val) {
    475                         val = (val === null || val === false) ? '' : val;
    476 
    477                         $(key).html(this.stripslashes(val));
    478 
    479                     }, this));
    480                 }
    481                 else if (item.type === 'addClass') {
    482                     $.each(item.data, function (key, val) {
    483                         $(key).addClass(val);
    484                     });
    485                 }
    486                 else if (item.type === 'removeClass') {
    487                     $.each(item.data, function (key, val) {
    488                         $(key).removeClass(val);
    489                     });
    490                 }
    491                 else if (item.type === 'command') {
    492                     $.each(item.data, function (key, val) {
    493                         $(val)[key]();
    494                     });
    495                 }
    496                 else if (item.type === 'animation') {
    497                     $.each(item.data, function (key, data) {
    498                         data.opts = (typeof data.opts === 'undefined') ? {} : data.opts;
    499 
    500                         $(key).animation(data.name, data.opts);
    501                     });
    502                 }
    503                 else if (item.type === 'location') {
    504                     top.location.href = item.data;
    505                 }
    506                 else if (item.type === 'notify') {
    507                     $.notify(item.data);
    508                 }
    509 
    510                 return item;
    511             },
    512             stripslashes: function (str) {
    513                 return (str + '').replace(/\0/g, '0').replace(/\\([\\'"])/g, '$1');
    514             }
    515         };
    516 
    517 
    518     }(Kube));
    519     /**
    520      * @library Kube Utils
    521      * @author Imperavi LLC
    522      * @license MIT
    523      */
    524     (function (Kube) {
    525         Kube.Utils = function () {
    526         };
    527 
    528         Kube.Utils.prototype = {
    529             disableBodyScroll: function () {
    530                 var $body = $('html');
    531                 var windowWidth = window.innerWidth;
    532 
    533                 if (!windowWidth) {
    534                     var documentElementRect = document.documentElement.getBoundingClientRect();
    535                     windowWidth = documentElementRect.right - Math.abs(documentElementRect.left);
    536                 }
    537 
    538                 var isOverflowing = document.body.clientWidth < windowWidth;
    539                 var scrollbarWidth = this.measureScrollbar();
    540 
    541                 $body.css('overflow', 'hidden');
    542                 if (isOverflowing) $body.css('padding-right', scrollbarWidth);
    543             },
    544             measureScrollbar: function () {
    545                 var $body = $('body');
    546                 var scrollDiv = document.createElement('div');
    547                 scrollDiv.className = 'scrollbar-measure';
    548 
    549                 $body.append(scrollDiv);
    550                 var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
    551                 $body[0].removeChild(scrollDiv);
    552                 return scrollbarWidth;
    553             },
    554             enableBodyScroll: function () {
    555                 $('html').css({'overflow': '', 'padding-right': ''});
    556             }
    557         };
    558 
    559 
    560     }(Kube));
    561     /**
    562      * @library Kube Message
    563      * @author Imperavi LLC
    564      * @license MIT
    565      */
    566     (function (Kube) {
    567         Kube.Message = function (element, options) {
    568             this.namespace = 'message';
    569             this.defaults = {
    570                 closeSelector: '.close',
    571                 closeEvent: 'click',
    572                 animationOpen: 'fadeIn',
    573                 animationClose: 'fadeOut',
    574                 callbacks: ['open', 'opened', 'close', 'closed']
    575             };
    576 
    577             // Parent Constructor
    578             Kube.apply(this, arguments);
    579 
    580             // Initialization
    581             this.start();
    582         };
    583 
    584         // Functionality
    585         Kube.Message.prototype = {
    586             start: function () {
    587                 this.$close = this.$element.find(this.opts.closeSelector);
    588                 this.$close.on(this.opts.closeEvent + '.' + this.namespace, $.proxy(this.close, this));
    589                 this.$element.addClass('open');
    590             },
    591             stop: function () {
    592                 this.$close.off('.' + this.namespace);
    593                 this.$element.removeClass('open');
    594             },
    595             open: function (e) {
    596                 if (e) e.preventDefault();
    597 
    598                 if (!this.isOpened()) {
    599                     this.callback('open');
    600                     this.$element.animation(this.opts.animationOpen, $.proxy(this.onOpened, this));
    601                 }
    602             },
    603             isOpened: function () {
    604                 return this.$element.hasClass('open');
    605             },
    606             onOpened: function () {
    607                 this.callback('opened');
    608                 this.$element.addClass('open');
    609             },
    610             close: function (e) {
    611                 if (e) e.preventDefault();
    612 
    613                 if (this.isOpened()) {
    614                     this.callback('close');
    615                     this.$element.animation(this.opts.animationClose, $.proxy(this.onClosed, this));
    616                 }
    617             },
    618             onClosed: function () {
    619                 this.callback('closed');
    620                 this.$element.removeClass('open');
    621             }
    622         };
    623 
    624         // Inheritance
    625         Kube.Message.inherits(Kube);
    626 
    627         // Plugin
    628         Kube.Plugin.create('Message');
    629         Kube.Plugin.autoload('Message');
    630 
    631     }(Kube));
    632     /**
    633      * @library Kube Sticky
    634      * @author Imperavi LLC
    635      * @license MIT
    636      */
    637     (function (Kube) {
    638         Kube.Sticky = function (element, options) {
    639             this.namespace = 'sticky';
    640             this.defaults = {
    641                 classname: 'fixed',
    642                 offset: 0, // pixels
    643                 callbacks: ['fixed', 'unfixed']
    644             };
    645 
    646             // Parent Constructor
    647             Kube.apply(this, arguments);
    648 
    649             // Initialization
    650             this.start();
    651         };
    652 
    653         // Functionality
    654         Kube.Sticky.prototype = {
    655             start: function () {
    656                 this.offsetTop = this.getOffsetTop();
    657 
    658                 this.load();
    659                 $(window).scroll($.proxy(this.load, this));
    660             },
    661             getOffsetTop: function () {
    662                 return this.$element.offset().top;
    663             },
    664             load: function () {
    665                 return (this.isFix()) ? this.fixed() : this.unfixed();
    666             },
    667             isFix: function () {
    668                 return ($(window).scrollTop() > (this.offsetTop + this.opts.offset));
    669             },
    670             fixed: function () {
    671                 this.$element.addClass(this.opts.classname).css('top', this.opts.offset + 'px');
    672                 this.callback('fixed');
    673             },
    674             unfixed: function () {
    675                 this.$element.removeClass(this.opts.classname).css('top', '');
    676                 this.callback('unfixed');
    677             }
    678         };
    679 
    680         // Inheritance
    681         Kube.Sticky.inherits(Kube);
    682 
    683         // Plugin
    684         Kube.Plugin.create('Sticky');
    685         Kube.Plugin.autoload('Sticky');
    686 
    687     }(Kube));
    688     /**
    689      * @library Kube Toggleme
    690      * @author Imperavi LLC
    691      * @license MIT
    692      */
    693     (function (Kube) {
    694         Kube.Toggleme = function (element, options) {
    695             this.namespace = 'toggleme';
    696             this.defaults = {
    697                 toggleEvent: 'click',
    698                 target: null,
    699                 text: '',
    700                 animationOpen: 'slideDown',
    701                 animationClose: 'slideUp',
    702                 callbacks: ['open', 'opened', 'close', 'closed']
    703             };
    704 
    705             // Parent Constructor
    706             Kube.apply(this, arguments);
    707 
    708             // Initialization
    709             this.start();
    710         };
    711 
    712         // Functionality
    713         Kube.Toggleme.prototype = {
    714             start: function () {
    715                 if (!this.hasTarget()) return;
    716 
    717                 this.$element.on(this.opts.toggleEvent + '.' + this.namespace, $.proxy(this.toggle, this));
    718             },
    719             stop: function () {
    720                 this.$element.off('.' + this.namespace);
    721                 this.revertText();
    722             },
    723             toggle: function (e) {
    724                 if (this.isOpened()) this.close(e);
    725                 else this.open(e);
    726             },
    727             open: function (e) {
    728                 if (e) e.preventDefault();
    729 
    730                 if (!this.isOpened()) {
    731                     this.storeText();
    732                     this.callback('open');
    733                     this.$target.animation('slideDown', $.proxy(this.onOpened, this));
    734 
    735                     // changes the text of $element with a less delay to smooth
    736                     setTimeout($.proxy(this.replaceText, this), 100);
    737                 }
    738             },
    739             close: function (e) {
    740                 if (e) e.preventDefault();
    741 
    742                 if (this.isOpened()) {
    743                     this.callback('close');
    744                     this.$target.animation('slideUp', $.proxy(this.onClosed, this));
    745                 }
    746             },
    747             isOpened: function () {
    748                 return (this.$target.hasClass('open'));
    749             },
    750             onOpened: function () {
    751                 this.$target.addClass('open');
    752                 this.callback('opened');
    753             },
    754             onClosed: function () {
    755                 this.$target.removeClass('open');
    756                 this.revertText();
    757                 this.callback('closed');
    758             },
    759             storeText: function () {
    760                 this.$element.data('replacement-text', this.$element.html());
    761             },
    762             revertText: function () {
    763                 var text = this.$element.data('replacement-text');
    764                 if (text) this.$element.html(text);
    765 
    766                 this.$element.removeData('replacement-text');
    767             },
    768             replaceText: function () {
    769                 if (this.opts.text !== '') {
    770                     this.$element.html(this.opts.text);
    771                 }
    772             }
    773         };
    774 
    775         // Inheritance
    776         Kube.Toggleme.inherits(Kube);
    777 
    778         // Plugin
    779         Kube.Plugin.create('Toggleme');
    780         Kube.Plugin.autoload('Toggleme');
    781 
    782     }(Kube));
    783     /**
    784      * @library Kube Offcanvas
    785      * @author Imperavi LLC
    786      * @license MIT
    787      */
    788     (function (Kube) {
    789         Kube.Offcanvas = function (element, options) {
    790             this.namespace = 'offcanvas';
    791             this.defaults = {
    792                 target: null, // selector
    793                 push: true, // boolean
    794                 width: '250px', // string
    795                 direction: 'left', // string: left or right
    796                 toggleEvent: 'click',
    797                 clickOutside: true, // boolean
    798                 animationOpen: 'slideInLeft',
    799                 animationClose: 'slideOutLeft',
    800                 callbacks: ['open', 'opened', 'close', 'closed']
    801             };
    802 
    803             // Parent Constructor
    804             Kube.apply(this, arguments);
    805 
    806             // Services
    807             this.utils = new Kube.Utils();
    808             this.detect = new Kube.Detect();
    809 
    810             // Initialization
    811             this.start();
    812         };
    813 
    814         // Functionality
    815         Kube.Offcanvas.prototype = {
    816             start: function () {
    817                 if (!this.hasTarget()) return;
    818 
    819                 this.buildTargetWidth();
    820                 this.buildAnimationDirection();
    821 
    822                 this.$close = this.getCloseLink();
    823                 this.$element.on(this.opts.toggleEvent + '.' + this.namespace, $.proxy(this.toggle, this));
    824                 this.$target.addClass('offcanvas');
    825                 this.$target.trigger('kube.offcanvas.ready');
    826             },
    827             stop: function () {
    828                 this.closeAll();
    829 
    830                 this.$element.off('.' + this.namespace);
    831                 this.$close.off('.' + this.namespace);
    832                 $(document).off('.' + this.namespace);
    833             },
    834             toggle: function (e) {
    835                 if (this.isOpened()) this.close(e);
    836                 else this.open(e);
    837             },
    838             buildTargetWidth: function () {
    839                 this.opts.width = ($(window).width() < parseInt(this.opts.width)) ? '100%' : this.opts.width;
    840             },
    841             buildAnimationDirection: function () {
    842                 if (this.opts.direction === 'right') {
    843                     this.opts.animationOpen = 'slideInRight';
    844                     this.opts.animationClose = 'slideOutRight';
    845                 }
    846             },
    847             getCloseLink: function () {
    848                 return this.$target.find('.close');
    849             },
    850             open: function (e) {
    851                 if (e) e.preventDefault();
    852 
    853                 if (!this.isOpened()) {
    854                     this.closeAll();
    855                     this.callback('open');
    856 
    857                     this.$target.addClass('offcanvas-' + this.opts.direction);
    858                     this.$target.css('width', Math.min(parseInt(this.opts.width), window.innerWidth - 100));
    859                     this.$target.css('right', '-' + Math.min(parseInt(this.opts.width), window.innerWidth - 100));
    860 
    861                     this.pushBody();
    862 
    863                     this.$target.trigger('kube.offcanvas.open');
    864                     this.$target.animation(this.opts.animationOpen, $.proxy(this.onOpened, this));
    865 
    866 
    867                 }
    868             },
    869             closeAll: function () {
    870                 var $elms = $(document).find('.offcanvas');
    871                 if ($elms.length !== 0) {
    872                     $elms.each(function () {
    873                         var $el = $(this);
    874 
    875                         if ($el.hasClass('open')) {
    876                             $el.css('width', '').animation('hide');
    877                             $el.removeClass('open offcanvas-left offcanvas-right');
    878                         }
    879 
    880                     });
    881 
    882                     $(document).off('.' + this.namespace);
    883                     $('body').css('left', '');
    884                 }
    885             },
    886             close: function (e) {
    887                 if (e) {
    888                     var $el = $(e.target);
    889                     var tagName = $el[0].tagName;
    890                     var isTag = (tagName === 'A' || tagName === 'BUTTON' || tagName === 'I' || $el.parents('a').length);
    891                     if (isTag && $el.closest('.offcanvas').length !== 0 && !$el.hasClass('close')) {
    892                         return;
    893                     }
    894 
    895                     e.preventDefault();
    896                 }
    897 
    898                 if (this.isOpened()) {
    899                     this.utils.enableBodyScroll();
    900                     this.callback('close');
    901                     this.pullBody();
    902                     this.$target.trigger('kube.offcanvas.close');
    903                     this.$target.animation(this.opts.animationClose, $.proxy(this.onClosed, this));
    904                 }
    905             },
    906             isOpened: function () {
    907                 return (this.$target.hasClass('open'));
    908             },
    909             onOpened: function () {
    910                 if (this.opts.clickOutside) $(document).on('click.' + this.namespace + ' tap.' + this.namespace, $.proxy(this.close, this));
    911                 if (this.detect.isMobileScreen()) $('html').addClass('no-scroll');
    912 
    913                 $(document).on('keyup.' + this.namespace, $.proxy(this.handleKeyboard, this));
    914                 this.$close.on('click.' + this.namespace, $.proxy(this.close, this));
    915 
    916                 this.utils.disableBodyScroll();
    917                 this.$target.addClass('open');
    918                 this.callback('opened');
    919 
    920 
    921             },
    922             onClosed: function () {
    923                 if (this.detect.isMobileScreen()) $('html').removeClass('no-scroll');
    924 
    925                 this.$target.css('width', '').removeClass('offcanvas-' + this.opts.direction);
    926 
    927                 this.$close.off('.' + this.namespace);
    928                 $(document).off('.' + this.namespace);
    929 
    930                 this.$target.removeClass('open');
    931                 this.callback('closed');
    932 
    933                 this.$target.trigger('kube.offcanvas.closed');
    934             },
    935             handleKeyboard: function (e) {
    936                 if (e.which === 27) this.close();
    937             },
    938             pullBody: function () {
    939                 if (this.opts.push) {
    940                     $('body').animate({left: 0}, 350, function () {
    941                         $(this).removeClass('offcanvas-push-body');
    942                     });
    943                 }
    944             },
    945             pushBody: function () {
    946                 if (this.opts.push) {
    947                     var properties = (this.opts.direction === 'left') ? {'left': this.opts.width} : {'left': '-' + this.opts.width};
    948                     $('body').addClass('offcanvas-push-body').animate(properties, 200);
    949                 }
    950             }
    951         };
    952 
    953         // Inheritance
    954         Kube.Offcanvas.inherits(Kube);
    955 
    956         // Plugin
    957         Kube.Plugin.create('Offcanvas');
    958         Kube.Plugin.autoload('Offcanvas');
    959 
    960     }(Kube));
    961     /**
    962      * @library Kube Collapse
    963      * @author Imperavi LLC
    964      * @license MIT
    965      */
    966     (function (Kube) {
    967         Kube.Collapse = function (element, options) {
    968             this.namespace = 'collapse';
    969             this.defaults = {
    970                 target: null,
    971                 toggle: true,
    972                 active: false, // string (hash = tab id selector)
    973                 toggleClass: 'collapse-toggle',
    974                 boxClass: 'collapse-box',
    975                 callbacks: ['open', 'opened', 'close', 'closed'],
    976 
    977                 // private
    978                 hashes: [],
    979                 currentHash: false,
    980                 currentItem: false
    981             };
    982 
    983             // Parent Constructor
    984             Kube.apply(this, arguments);
    985 
    986             // Initialization
    987             this.start();
    988         };
    989 
    990         // Functionality
    991         Kube.Collapse.prototype = {
    992             start: function () {
    993                 // items
    994                 this.$items = this.getItems();
    995                 this.$items.each($.proxy(this.loadItems, this));
    996 
    997                 // boxes
    998                 this.$boxes = this.getBoxes();
    999 
   1000                 // active
   1001                 this.setActiveItem();
   1002             },
   1003             getItems: function () {
   1004                 return this.$element.find('.' + this.opts.toggleClass);
   1005             },
   1006             getBoxes: function () {
   1007                 return this.$element.find('.' + this.opts.boxClass);
   1008             },
   1009             loadItems: function (i, el) {
   1010                 var item = this.getItem(el);
   1011 
   1012                 // set item identificator
   1013                 item.$el.attr('rel', item.hash);
   1014 
   1015                 // active
   1016                 if (!$(item.hash).hasClass('hide')) {
   1017                     this.opts.currentItem = item;
   1018                     this.opts.active = item.hash;
   1019 
   1020                     item.$el.addClass('active');
   1021                 }
   1022 
   1023                 // event
   1024                 item.$el.on('click.collapse', $.proxy(this.toggle, this));
   1025 
   1026             },
   1027             setActiveItem: function () {
   1028                 if (this.opts.active !== false) {
   1029                     this.opts.currentItem = this.getItemBy(this.opts.active);
   1030                     this.opts.active = this.opts.currentItem.hash;
   1031                 }
   1032 
   1033                 if (this.opts.currentItem !== false) {
   1034                     this.addActive(this.opts.currentItem);
   1035                     this.opts.currentItem.$box.removeClass('hide');
   1036                 }
   1037             },
   1038             addActive: function (item) {
   1039                 item.$box.removeClass('hide').addClass('open');
   1040                 item.$el.addClass('active');
   1041 
   1042                 if (item.$caret !== false) item.$caret.removeClass('down').addClass('up');
   1043                 if (item.$parent !== false) item.$parent.addClass('active');
   1044 
   1045                 this.opts.currentItem = item;
   1046             },
   1047             removeActive: function (item) {
   1048                 item.$box.removeClass('open');
   1049                 item.$el.removeClass('active');
   1050 
   1051                 if (item.$caret !== false) item.$caret.addClass('down').removeClass('up');
   1052                 if (item.$parent !== false) item.$parent.removeClass('active');
   1053 
   1054                 this.opts.currentItem = false;
   1055             },
   1056             toggle: function (e) {
   1057                 if (e) e.preventDefault();
   1058 
   1059                 var target = $(e.target).closest('.' + this.opts.toggleClass).get(0) || e.target;
   1060                 var item = this.getItem(target);
   1061 
   1062                 if (this.isOpened(item.hash)) this.close(item.hash);
   1063                 else this.open(e)
   1064             },
   1065             openAll: function () {
   1066                 this.$items.addClass('active');
   1067                 this.$boxes.addClass('open').removeClass('hide');
   1068             },
   1069             open: function (e, push) {
   1070                 if (typeof e === 'undefined') return;
   1071                 if (typeof e === 'object') e.preventDefault();
   1072 
   1073                 var target = $(e.target).closest('.' + this.opts.toggleClass).get(0) || e.target;
   1074                 var item = (typeof e === 'object') ? this.getItem(target) : this.getItemBy(e);
   1075 
   1076                 if (item.$box.hasClass('open')) {
   1077                     return;
   1078                 }
   1079 
   1080                 if (this.opts.toggle) this.closeAll();
   1081 
   1082                 this.callback('open', item);
   1083                 this.addActive(item);
   1084 
   1085                 item.$box.animation('slideDown', $.proxy(this.onOpened, this));
   1086             },
   1087             onOpened: function () {
   1088                 this.callback('opened', this.opts.currentItem);
   1089             },
   1090             closeAll: function () {
   1091                 this.$items.removeClass('active').closest('li').removeClass('active');
   1092                 this.$boxes.removeClass('open').addClass('hide');
   1093             },
   1094             close: function (num) {
   1095                 var item = this.getItemBy(num);
   1096 
   1097                 this.callback('close', item);
   1098 
   1099                 this.opts.currentItem = item;
   1100 
   1101                 item.$box.animation('slideUp', $.proxy(this.onClosed, this));
   1102             },
   1103             onClosed: function () {
   1104                 var item = this.opts.currentItem;
   1105 
   1106                 this.removeActive(item);
   1107                 this.callback('closed', item);
   1108             },
   1109             isOpened: function (hash) {
   1110                 return $(hash).hasClass('open');
   1111             },
   1112             getItem: function (element) {
   1113                 var item = {};
   1114 
   1115                 item.$el = $(element);
   1116                 item.hash = item.$el.attr('href');
   1117                 item.$box = $(item.hash);
   1118 
   1119                 var $parent = item.$el.parent();
   1120                 item.$parent = ($parent[0].tagName === 'LI') ? $parent : false;
   1121 
   1122                 var $caret = item.$el.find('.caret');
   1123                 item.$caret = ($caret.length !== 0) ? $caret : false;
   1124 
   1125                 return item;
   1126             },
   1127             getItemBy: function (num) {
   1128                 var element = (typeof num === 'number') ? this.$items.eq(num - 1) : this.$element.find('[rel="' + num + '"]');
   1129 
   1130                 return this.getItem(element);
   1131             }
   1132         };
   1133 
   1134         // Inheritance
   1135         Kube.Collapse.inherits(Kube);
   1136 
   1137         // Plugin
   1138         Kube.Plugin.create('Collapse');
   1139         Kube.Plugin.autoload('Collapse');
   1140 
   1141     }(Kube));
   1142     /**
   1143      * @library Kube Dropdown
   1144      * @author Imperavi LLC
   1145      * @license MIT
   1146      */
   1147     (function (Kube) {
   1148         Kube.Dropdown = function (element, options) {
   1149             this.namespace = 'dropdown';
   1150             this.defaults = {
   1151                 target: null,
   1152                 toggleEvent: 'click',
   1153                 height: false, // integer
   1154                 width: false, // integer
   1155                 animationOpen: 'slideDown',
   1156                 animationClose: 'slideUp',
   1157                 caretUp: false,
   1158                 callbacks: ['open', 'opened', 'close', 'closed']
   1159             };
   1160 
   1161             // Parent Constructor
   1162             Kube.apply(this, arguments);
   1163 
   1164             // Services
   1165             this.utils = new Kube.Utils();
   1166             this.detect = new Kube.Detect();
   1167 
   1168             // Initialization
   1169             this.start();
   1170         };
   1171 
   1172         // Functionality
   1173         Kube.Dropdown.prototype = {
   1174             start: function () {
   1175                 this.buildClose();
   1176                 this.buildCaret();
   1177 
   1178                 if (this.detect.isMobile()) this.buildMobileAnimation();
   1179 
   1180                 this.$target.addClass('hide');
   1181                 this.$element.on(this.opts.toggleEvent + '.' + this.namespace, $.proxy(this.toggle, this));
   1182 
   1183             },
   1184             stop: function () {
   1185                 this.$element.off('.' + this.namespace);
   1186                 this.$target.removeClass('open').addClass('hide');
   1187                 this.disableEvents();
   1188             },
   1189             buildMobileAnimation: function () {
   1190                 this.opts.animationOpen = 'fadeIn';
   1191                 this.opts.animationClose = 'fadeOut';
   1192             },
   1193             buildClose: function () {
   1194                 this.$close = this.$target.find('.close');
   1195             },
   1196             buildCaret: function () {
   1197                 this.$caret = this.getCaret();
   1198                 this.buildCaretPosition();
   1199             },
   1200             buildCaretPosition: function () {
   1201                 var height = this.$element.offset().top + this.$element.innerHeight() + this.$target.innerHeight();
   1202 
   1203                 if ($(document).height() > height) {
   1204                     return;
   1205                 }
   1206 
   1207                 this.opts.caretUp = true;
   1208                 this.$caret.addClass('up');
   1209             },
   1210             getCaret: function () {
   1211                 return this.$element.find('.caret');
   1212             },
   1213             toggleCaretOpen: function () {
   1214                 if (this.opts.caretUp) this.$caret.removeClass('up').addClass('down');
   1215                 else this.$caret.removeClass('down').addClass('up');
   1216             },
   1217             toggleCaretClose: function () {
   1218                 if (this.opts.caretUp) this.$caret.removeClass('down').addClass('up');
   1219                 else this.$caret.removeClass('up').addClass('down');
   1220             },
   1221             toggle: function (e) {
   1222                 if (this.isOpened()) this.close(e);
   1223                 else this.open(e);
   1224             },
   1225             open: function (e) {
   1226                 if (e) e.preventDefault();
   1227 
   1228                 this.callback('open');
   1229                 $('.dropdown').removeClass('open').addClass('hide');
   1230 
   1231                 if (this.opts.height) this.$target.css('min-height', this.opts.height + 'px');
   1232                 if (this.opts.width) this.$target.width(this.opts.width);
   1233 
   1234                 this.setPosition();
   1235                 this.toggleCaretOpen();
   1236 
   1237                 this.$target.animation(this.opts.animationOpen, $.proxy(this.onOpened, this));
   1238             },
   1239             close: function (e) {
   1240                 if (!this.isOpened()) {
   1241                     return;
   1242                 }
   1243 
   1244                 if (e) {
   1245                     if (this.shouldNotBeClosed(e.target)) {
   1246                         return;
   1247                     }
   1248 
   1249                     e.preventDefault();
   1250                 }
   1251 
   1252                 this.utils.enableBodyScroll();
   1253                 this.callback('close');
   1254                 this.toggleCaretClose();
   1255 
   1256                 this.$target.animation(this.opts.animationClose, $.proxy(this.onClosed, this));
   1257             },
   1258             onClosed: function () {
   1259                 this.$target.removeClass('open');
   1260                 this.disableEvents();
   1261                 this.callback('closed');
   1262             },
   1263             onOpened: function () {
   1264                 this.$target.addClass('open');
   1265                 this.enableEvents();
   1266                 this.callback('opened');
   1267             },
   1268             isOpened: function () {
   1269                 return (this.$target.hasClass('open'));
   1270             },
   1271             enableEvents: function () {
   1272                 if (this.detect.isDesktop()) {
   1273                     this.$target.on('mouseover.' + this.namespace, $.proxy(this.utils.disableBodyScroll, this.utils))
   1274                         .on('mouseout.' + this.namespace, $.proxy(this.utils.enableBodyScroll, this.utils));
   1275                 }
   1276 
   1277                 $(document).on('scroll.' + this.namespace, $.proxy(this.setPosition, this));
   1278                 $(window).on('resize.' + this.namespace, $.proxy(this.setPosition, this));
   1279                 $(document).on('click.' + this.namespace + ' touchstart.' + this.namespace, $.proxy(this.close, this));
   1280                 $(document).on('keydown.' + this.namespace, $.proxy(this.handleKeyboard, this));
   1281                 this.$target.find('[data-action="dropdown-close"]').on('click.' + this.namespace, $.proxy(this.close, this));
   1282             },
   1283             disableEvents: function () {
   1284                 this.$target.off('.' + this.namespace);
   1285                 $(document).off('.' + this.namespace);
   1286                 $(window).off('.' + this.namespace);
   1287             },
   1288             handleKeyboard: function (e) {
   1289                 if (e.which === 27) this.close(e);
   1290             },
   1291             shouldNotBeClosed: function (el) {
   1292                 if ($(el).attr('data-action') === 'dropdown-close' || el === this.$close[0]) {
   1293                     return false;
   1294                 }
   1295                 else if ($(el).closest('.dropdown').length === 0) {
   1296                     return false;
   1297                 }
   1298 
   1299                 return true;
   1300             },
   1301             isNavigationFixed: function () {
   1302                 return (this.$element.closest('.fixed').length !== 0);
   1303             },
   1304             getPlacement: function (height) {
   1305                 return ($(document).height() < height) ? 'top' : 'bottom';
   1306             },
   1307             getOffset: function (position) {
   1308                 return (this.isNavigationFixed()) ? this.$element.position() : this.$element.offset();
   1309             },
   1310             getPosition: function () {
   1311                 return (this.isNavigationFixed()) ? 'fixed' : 'absolute';
   1312             },
   1313             setPosition: function () {
   1314                 if (this.detect.isMobile()) {
   1315                     this.$target.addClass('dropdown-mobile');
   1316                     return;
   1317                 }
   1318 
   1319                 var position = this.getPosition();
   1320                 var coords = this.getOffset(position);
   1321                 var height = this.$target.innerHeight();
   1322                 var width = this.$target.innerWidth();
   1323                 var placement = this.getPlacement(coords.top + height + this.$element.innerHeight());
   1324                 var leftFix = ($(window).width() < (coords.left + width)) ? (width - this.$element.innerWidth()) : 0;
   1325                 var top, left = coords.left - leftFix;
   1326 
   1327                 if (placement === 'bottom') {
   1328                     if (!this.isOpened()) this.$caret.removeClass('up').addClass('down');
   1329 
   1330                     this.opts.caretUp = false;
   1331                     top = coords.top + this.$element.outerHeight() + 1;
   1332                 }
   1333                 else {
   1334                     this.opts.animationOpen = 'show';
   1335                     this.opts.animationClose = 'hide';
   1336 
   1337                     if (!this.isOpened()) this.$caret.addClass('up').removeClass('down');
   1338 
   1339                     this.opts.caretUp = true;
   1340                     top = coords.top - height - 1;
   1341                 }
   1342 
   1343                 this.$target.css({position: position, top: top + 'px', left: left + 'px'});
   1344             }
   1345         };
   1346 
   1347         // Inheritance
   1348         Kube.Dropdown.inherits(Kube);
   1349 
   1350         // Plugin
   1351         Kube.Plugin.create('Dropdown');
   1352         Kube.Plugin.autoload('Dropdown');
   1353 
   1354     }(Kube));
   1355     /**
   1356      * @library Kube Tabs
   1357      * @author Imperavi LLC
   1358      * @license MIT
   1359      */
   1360     (function (Kube) {
   1361         Kube.Tabs = function (element, options) {
   1362             this.namespace = 'tabs';
   1363             this.defaults = {
   1364                 equals: false,
   1365                 active: false, // string (hash = tab id selector)
   1366                 live: false, // class selector
   1367                 hash: true, //boolean
   1368                 callbacks: ['init', 'next', 'prev', 'open', 'opened', 'close', 'closed']
   1369             };
   1370 
   1371             // Parent Constructor
   1372             Kube.apply(this, arguments);
   1373 
   1374             // Initialization
   1375             this.start();
   1376         };
   1377 
   1378         // Functionality
   1379         Kube.Tabs.prototype = {
   1380             start: function () {
   1381                 if (this.opts.live !== false) this.buildLiveTabs();
   1382 
   1383                 this.tabsCollection = [];
   1384                 this.hashesCollection = [];
   1385                 this.currentHash = [];
   1386                 this.currentItem = false;
   1387 
   1388                 // items
   1389                 this.$items = this.getItems();
   1390                 this.$items.each($.proxy(this.loadItems, this));
   1391 
   1392                 // tabs
   1393                 this.$tabs = this.getTabs();
   1394 
   1395                 // location hash
   1396                 this.currentHash = this.getLocationHash();
   1397 
   1398                 // close all
   1399                 this.closeAll();
   1400 
   1401                 // active & height
   1402                 this.setActiveItem();
   1403                 this.setItemHeight();
   1404 
   1405                 // callback
   1406                 this.callback('init');
   1407 
   1408             },
   1409             getTabs: function () {
   1410                 return $(this.tabsCollection).map(function () {
   1411                     return this.toArray();
   1412                 });
   1413             },
   1414             getItems: function () {
   1415                 return this.$element.find('a');
   1416             },
   1417             loadItems: function (i, el) {
   1418                 var item = this.getItem(el);
   1419 
   1420                 // set item identificator
   1421                 item.$el.attr('rel', item.hash);
   1422 
   1423                 // collect item
   1424                 this.collectItem(item);
   1425 
   1426                 // active
   1427                 if (item.$parent.hasClass('active')) {
   1428                     this.currentItem = item;
   1429                     this.opts.active = item.hash;
   1430                 }
   1431 
   1432                 // event
   1433                 item.$el.on('click.tabs', $.proxy(this.open, this));
   1434 
   1435             },
   1436             collectItem: function (item) {
   1437                 this.tabsCollection.push(item.$tab);
   1438                 this.hashesCollection.push(item.hash);
   1439             },
   1440             buildLiveTabs: function () {
   1441                 var $layers = $(this.opts.live);
   1442 
   1443                 if ($layers.length === 0) {
   1444                     return;
   1445                 }
   1446 
   1447                 this.$liveTabsList = $('<ul />');
   1448                 $layers.each($.proxy(this.buildLiveItem, this));
   1449 
   1450                 this.$element.html('').append(this.$liveTabsList);
   1451 
   1452             },
   1453             buildLiveItem: function (i, tab) {
   1454                 var $tab = $(tab);
   1455                 var $li = $('<li />');
   1456                 var $a = $('<a />');
   1457                 var index = i + 1;
   1458 
   1459                 $tab.attr('id', this.getLiveItemId($tab, index));
   1460 
   1461                 var hash = '#' + $tab.attr('id');
   1462                 var title = this.getLiveItemTitle($tab);
   1463 
   1464                 $a.attr('href', hash).attr('rel', hash).text(title);
   1465                 $li.append($a);
   1466 
   1467                 this.$liveTabsList.append($li);
   1468             },
   1469             getLiveItemId: function ($tab, index) {
   1470                 return (typeof $tab.attr('id') === 'undefined') ? this.opts.live.replace('.', '') + index : $tab.attr('id');
   1471             },
   1472             getLiveItemTitle: function ($tab) {
   1473                 return (typeof $tab.attr('data-title') === 'undefined') ? $tab.attr('id') : $tab.attr('data-title');
   1474             },
   1475             setActiveItem: function () {
   1476                 if (this.currentHash) {
   1477                     this.currentItem = this.getItemBy(this.currentHash);
   1478                     this.opts.active = this.currentHash;
   1479                 }
   1480                 else if (this.opts.active === false) {
   1481                     this.currentItem = this.getItem(this.$items.first());
   1482                     this.opts.active = this.currentItem.hash;
   1483                 }
   1484 
   1485                 this.addActive(this.currentItem);
   1486             },
   1487             addActive: function (item) {
   1488                 item.$parent.addClass('active');
   1489                 item.$tab.removeClass('hide').addClass('open');
   1490 
   1491                 this.currentItem = item;
   1492             },
   1493             removeActive: function (item) {
   1494                 item.$parent.removeClass('active');
   1495                 item.$tab.addClass('hide').removeClass('open');
   1496 
   1497                 this.currentItem = false;
   1498             },
   1499             next: function (e) {
   1500                 if (e) e.preventDefault();
   1501 
   1502                 var item = this.getItem(this.fetchElement('next'));
   1503 
   1504                 this.open(item.hash);
   1505                 this.callback('next', item);
   1506 
   1507             },
   1508             prev: function (e) {
   1509                 if (e) e.preventDefault();
   1510 
   1511                 var item = this.getItem(this.fetchElement('prev'));
   1512 
   1513                 this.open(item.hash);
   1514                 this.callback('prev', item);
   1515             },
   1516             fetchElement: function (type) {
   1517                 var element;
   1518                 if (this.currentItem !== false) {
   1519                     // prev or next
   1520                     element = this.currentItem.$parent[type]().find('a');
   1521 
   1522                     if (element.length === 0) {
   1523                         return;
   1524                     }
   1525                 }
   1526                 else {
   1527                     // first
   1528                     element = this.$items[0];
   1529                 }
   1530 
   1531                 return element;
   1532             },
   1533             open: function (e, push) {
   1534                 if (typeof e === 'undefined') return;
   1535                 if (typeof e === 'object') e.preventDefault();
   1536 
   1537                 var item = (typeof e === 'object') ? this.getItem(e.target) : this.getItemBy(e);
   1538                 this.closeAll();
   1539 
   1540                 this.callback('open', item);
   1541                 this.addActive(item);
   1542 
   1543                 // push state (doesn't need to push at the start)
   1544                 this.pushStateOpen(push, item);
   1545                 this.callback('opened', item);
   1546             },
   1547             pushStateOpen: function (push, item) {
   1548                 if (push !== false && this.opts.hash !== false) {
   1549                     history.pushState(false, false, item.hash);
   1550                 }
   1551             },
   1552             close: function (num) {
   1553                 var item = this.getItemBy(num);
   1554 
   1555                 if (!item.$parent.hasClass('active')) {
   1556                     return;
   1557                 }
   1558 
   1559                 this.callback('close', item);
   1560                 this.removeActive(item);
   1561                 this.pushStateClose();
   1562                 this.callback('closed', item);
   1563 
   1564             },
   1565             pushStateClose: function () {
   1566                 if (this.opts.hash !== false) {
   1567                     history.pushState(false, false, ' ');
   1568                 }
   1569             },
   1570             closeAll: function () {
   1571                 this.$tabs.removeClass('open').addClass('hide');
   1572                 this.$items.parent().removeClass('active');
   1573             },
   1574             getItem: function (element) {
   1575                 var item = {};
   1576 
   1577                 item.$el = $(element);
   1578                 item.hash = item.$el.attr('href');
   1579                 item.$parent = item.$el.parent();
   1580                 item.$tab = $(item.hash);
   1581 
   1582                 return item;
   1583             },
   1584             getItemBy: function (num) {
   1585                 var element = (typeof num === 'number') ? this.$items.eq(num - 1) : this.$element.find('[rel="' + num + '"]');
   1586 
   1587                 return this.getItem(element);
   1588             },
   1589             getLocationHash: function () {
   1590                 if (this.opts.hash === false) {
   1591                     return false;
   1592                 }
   1593 
   1594                 return (this.isHash()) ? top.location.hash : false;
   1595             },
   1596             isHash: function () {
   1597                 return !(top.location.hash === '' || $.inArray(top.location.hash, this.hashesCollection) === -1);
   1598             },
   1599             setItemHeight: function () {
   1600                 if (this.opts.equals) {
   1601                     var minHeight = this.getItemMaxHeight() + 'px';
   1602                     this.$tabs.css('min-height', minHeight);
   1603                 }
   1604             },
   1605             getItemMaxHeight: function () {
   1606                 var max = 0;
   1607                 this.$tabs.each(function () {
   1608                     var h = $(this).height();
   1609                     max = h > max ? h : max;
   1610                 });
   1611 
   1612                 return max;
   1613             }
   1614         };
   1615 
   1616         // Inheritance
   1617         Kube.Tabs.inherits(Kube);
   1618 
   1619         // Plugin
   1620         Kube.Plugin.create('Tabs');
   1621         Kube.Plugin.autoload('Tabs');
   1622 
   1623     }(Kube));
   1624     /**
   1625      * @library Kube Modal
   1626      * @author Imperavi LLC
   1627      * @license MIT
   1628      */
   1629     (function ($) {
   1630         $.modalcurrent = null;
   1631         $.modalwindow = function (options) {
   1632             var opts = $.extend({}, options, {show: true});
   1633             var $element = $('<span />');
   1634 
   1635             $element.modal(opts);
   1636         };
   1637 
   1638     })(jQuery);
   1639 
   1640     (function (Kube) {
   1641         Kube.Modal = function (element, options) {
   1642             this.namespace = 'modal';
   1643             this.defaults = {
   1644                 target: null,
   1645                 show: false,
   1646                 url: false,
   1647                 header: false,
   1648                 width: '600px', // string
   1649                 height: false, // or string
   1650                 maxHeight: false,
   1651                 position: 'center', // top or center
   1652                 overlay: true,
   1653                 appendForms: false,
   1654                 appendFields: false,
   1655                 animationOpen: 'show',
   1656                 animationClose: 'hide',
   1657                 callbacks: ['open', 'opened', 'close', 'closed']
   1658             };
   1659 
   1660             // Parent Constructor
   1661             Kube.apply(this, arguments);
   1662 
   1663             // Services
   1664             this.utils = new Kube.Utils();
   1665             this.detect = new Kube.Detect();
   1666 
   1667             // Initialization
   1668             this.start();
   1669         };
   1670 
   1671         // Functionality
   1672         Kube.Modal.prototype = {
   1673             start: function () {
   1674                 if (!this.hasTarget()) {
   1675                     return;
   1676                 }
   1677 
   1678                 if (this.opts.show) this.load();
   1679                 else this.$element.on('click.' + this.namespace, $.proxy(this.load, this));
   1680             },
   1681             buildModal: function () {
   1682                 this.$modal = this.$target.find('.modal');
   1683                 this.$header = this.$target.find('.modal-header');
   1684                 this.$close = this.$target.find('.close');
   1685                 this.$body = this.$target.find('.modal-body');
   1686             },
   1687             buildOverlay: function () {
   1688                 if (this.opts.overlay === false) {
   1689                     return;
   1690                 }
   1691 
   1692                 if ($('#modal-overlay').length !== 0) {
   1693                     this.$overlay = $('#modal-overlay');
   1694                 }
   1695                 else {
   1696                     this.$overlay = $('<div id="modal-overlay">').addClass('hide');
   1697                     $('body').prepend(this.$overlay);
   1698                 }
   1699 
   1700                 this.$overlay.addClass('overlay');
   1701             },
   1702             buildHeader: function () {
   1703                 if (this.opts.header) this.$header.html(this.opts.header);
   1704             },
   1705             load: function (e) {
   1706                 this.buildModal();
   1707                 this.buildOverlay();
   1708                 this.buildHeader();
   1709 
   1710                 if (this.opts.url) this.buildContent();
   1711                 else this.open(e);
   1712             },
   1713             open: function (e) {
   1714                 if (e) e.preventDefault();
   1715 
   1716                 if (this.isOpened()) {
   1717                     return;
   1718                 }
   1719 
   1720                 if (this.detect.isMobile()) this.opts.width = '96%';
   1721                 if (this.opts.overlay) this.$overlay.removeClass('hide');
   1722 
   1723                 this.$target.removeClass('hide');
   1724                 this.$modal.removeClass('hide');
   1725 
   1726                 this.enableEvents();
   1727                 this.findActions();
   1728 
   1729                 this.resize();
   1730                 $(window).on('resize.' + this.namespace, $.proxy(this.resize, this));
   1731 
   1732                 if (this.detect.isDesktop()) this.utils.disableBodyScroll();
   1733 
   1734                 // enter
   1735                 this.$modal.find('input[type=text],input[type=url],input[type=email]').on('keydown.' + this.namespace, $.proxy(this.handleEnter, this));
   1736 
   1737                 this.callback('open');
   1738                 this.$modal.animation(this.opts.animationOpen, $.proxy(this.onOpened, this));
   1739             },
   1740             close: function (e) {
   1741                 if (!this.$modal || !this.isOpened()) {
   1742                     return;
   1743                 }
   1744 
   1745                 if (e) {
   1746                     if (this.shouldNotBeClosed(e.target)) {
   1747                         return;
   1748                     }
   1749 
   1750                     e.preventDefault();
   1751                 }
   1752 
   1753                 this.callback('close');
   1754                 this.disableEvents();
   1755 
   1756                 this.$modal.animation(this.opts.animationClose, $.proxy(this.onClosed, this));
   1757 
   1758                 if (this.opts.overlay) this.$overlay.animation(this.opts.animationClose);
   1759             },
   1760             onOpened: function () {
   1761                 this.$modal.addClass('open');
   1762                 this.callback('opened');
   1763 
   1764                 $.modalcurrent = this;
   1765             },
   1766             onClosed: function () {
   1767                 this.callback('closed');
   1768 
   1769                 this.$target.addClass('hide');
   1770                 this.$modal.removeClass('open');
   1771 
   1772                 if (this.detect.isDesktop()) this.utils.enableBodyScroll();
   1773 
   1774                 this.$body.css('height', '');
   1775                 $.modalcurrent = null;
   1776             },
   1777             isOpened: function () {
   1778                 return (this.$modal.hasClass('open'));
   1779             },
   1780             getData: function () {
   1781                 var formdata = new Kube.FormData(this);
   1782                 formdata.set('');
   1783 
   1784                 return formdata.get();
   1785             },
   1786             buildContent: function () {
   1787                 $.ajax({
   1788                     url: this.opts.url + '?' + new Date().getTime(),
   1789                     cache: false,
   1790                     type: 'post',
   1791                     data: this.getData(),
   1792                     success: $.proxy(function (data) {
   1793                         this.$body.html(data);
   1794                         this.open();
   1795 
   1796                     }, this)
   1797                 });
   1798             },
   1799             buildWidth: function () {
   1800                 var width = this.opts.width;
   1801                 var top = '2%';
   1802                 var bottom = '2%';
   1803                 var percent = width.match(/%$/);
   1804 
   1805                 if ((parseInt(this.opts.width) > $(window).width()) && !percent) {
   1806                     width = '96%';
   1807                 }
   1808                 else if (!percent) {
   1809                     top = '16px';
   1810                     bottom = '16px';
   1811                 }
   1812 
   1813                 this.$modal.css({'width': width, 'margin-top': top, 'margin-bottom': bottom});
   1814 
   1815             },
   1816             buildPosition: function () {
   1817                 if (this.opts.position !== 'center') {
   1818                     return;
   1819                 }
   1820 
   1821                 var windowHeight = $(window).height();
   1822                 var height = this.$modal.outerHeight();
   1823                 var top = (windowHeight / 2 - height / 2) + 'px';
   1824 
   1825                 if (this.detect.isMobile()) top = '2%';
   1826                 else if (height > windowHeight) top = '16px';
   1827 
   1828                 this.$modal.css('margin-top', top);
   1829             },
   1830             buildHeight: function () {
   1831                 var windowHeight = $(window).height();
   1832 
   1833                 if (this.opts.maxHeight) {
   1834                     var padding = parseInt(this.$body.css('padding-top')) + parseInt(this.$body.css('padding-bottom'));
   1835                     var margin = parseInt(this.$modal.css('margin-top')) + parseInt(this.$modal.css('margin-bottom'));
   1836                     var height = windowHeight - this.$header.innerHeight() - padding - margin;
   1837 
   1838                     this.$body.height(height);
   1839                 }
   1840                 else if (this.opts.height !== false) {
   1841                     this.$body.css('height', this.opts.height);
   1842                 }
   1843 
   1844                 var modalHeight = this.$modal.outerHeight();
   1845                 if (modalHeight > windowHeight) {
   1846                     this.opts.animationOpen = 'show';
   1847                     this.opts.animationClose = 'hide';
   1848                 }
   1849             },
   1850             resize: function () {
   1851                 this.buildWidth();
   1852                 this.buildPosition();
   1853                 this.buildHeight();
   1854             },
   1855             enableEvents: function () {
   1856                 this.$close.on('click.' + this.namespace, $.proxy(this.close, this));
   1857                 $(document).on('keyup.' + this.namespace, $.proxy(this.handleEscape, this));
   1858                 this.$target.on('click.' + this.namespace, $.proxy(this.close, this));
   1859             },
   1860             disableEvents: function () {
   1861                 this.$close.off('.' + this.namespace);
   1862                 $(document).off('.' + this.namespace);
   1863                 this.$target.off('.' + this.namespace);
   1864                 $(window).off('.' + this.namespace);
   1865             },
   1866             findActions: function () {
   1867                 this.$body.find('[data-action="modal-close"]').on('mousedown.' + this.namespace, $.proxy(this.close, this));
   1868             },
   1869             setHeader: function (header) {
   1870                 this.$header.html(header);
   1871             },
   1872             setContent: function (content) {
   1873                 this.$body.html(content);
   1874             },
   1875             setWidth: function (width) {
   1876                 this.opts.width = width;
   1877                 this.resize();
   1878             },
   1879             getModal: function () {
   1880                 return this.$modal;
   1881             },
   1882             getBody: function () {
   1883                 return this.$body;
   1884             },
   1885             getHeader: function () {
   1886                 return this.$header;
   1887             },
   1888             handleEnter: function (e) {
   1889                 if (e.which === 13) {
   1890                     e.preventDefault();
   1891                     this.close(false);
   1892                 }
   1893             },
   1894             handleEscape: function (e) {
   1895                 return (e.which === 27) ? this.close(false) : true;
   1896             },
   1897             shouldNotBeClosed: function (el) {
   1898                 if ($(el).attr('data-action') === 'modal-close' || el === this.$close[0]) {
   1899                     return false;
   1900                 }
   1901                 else if ($(el).closest('.modal').length === 0) {
   1902                     return false;
   1903                 }
   1904 
   1905                 return true;
   1906             }
   1907         };
   1908 
   1909         // Inheritance
   1910         Kube.Modal.inherits(Kube);
   1911 
   1912         // Plugin
   1913         Kube.Plugin.create('Modal');
   1914         Kube.Plugin.autoload('Modal');
   1915 
   1916     }(Kube));
   1917 
   1918 })(jQuery)