/**
 * StickyScroll
 * written by Rick Harris - @iamrickharris
 * 
 * Requires jQuery 1.4+
 * 
 * Make elements stick to the top of your page as you scroll
 *
 * See README for details
 *
*/

(function($) {
  $.fn.stickyScroll = function(options) {
  
    var methods = {
      
      init : function(options) {
        
        var settings;
        
        if (options.mode !== 'auto' && options.mode !== 'manual') {
          if (options.container) {
            options.mode = 'auto';
          }
          if (options.bottomBoundary) {
            options.mode = 'manual';
          }
        }
        
        settings = $.extend({
          mode: 'auto', // 'auto' or 'manual'
          container: $('body'),
          topBoundary: null,
          bottomBoundary: null
        }, options);
        
        function bottomBoundary() {
          return $(document).height() - settings.container.offset().top
            - settings.container.attr('offsetHeight');
        }

        function topBoundary() {
          return settings.container.offset().top
        }

        function elHeight(el) {
          return $(el).attr('offsetHeight');
        }
        
        // make sure user input is a jQuery object
        settings.container = $(settings.container);
        if(!settings.container.length) {
          if(console) {
            console.log('StickyScroll: the element ' + options.container +
              ' does not exist, we\'re throwing in the towel');
          }
          return;
        }

        // calculate automatic bottomBoundary
        if(settings.mode === 'auto') {
          settings.topBoundary = topBoundary();
          settings.bottomBoundary = bottomBoundary();
        }

        return this.each(function(index) {

          var el = $(this),
            win = $(window),
            id = Date.now() + index,
            height = elHeight(el);
            
          el.data('sticky-id', id);
          
          win.bind('scroll.stickyscroll-' + id, function() {
            var top = $(document).scrollTop(),
              bottom = $(document).height() - top - height;

            if(bottom <= settings.bottomBoundary) {
              el.offset({
                top: $(document).height() - settings.bottomBoundary - height
              })
              .removeClass('sticky-active')
              .removeClass('sticky-inactive')
              .addClass('sticky-stopped');
            }
            else if(top > settings.topBoundary) {
              el.offset({
                top: $(window).scrollTop()
              })
              .removeClass('sticky-stopped')
              .removeClass('sticky-inactive')
              .addClass('sticky-active');
            }
            else if(top < settings.topBoundary) {
              el.css({
                position: '',
                top: '',
                bottom: ''
              })
              .removeClass('sticky-stopped')
              .removeClass('sticky-active')
              .addClass('sticky-inactive');
            }
          });
          
          win.bind('resize.stickyscroll-' + id, function() {
            if (settings.mode === 'auto') {
              settings.topBoundary = topBoundary();
              settings.bottomBoundary = bottomBoundary();
            }
            height = elHeight(el);
            $(this).scroll();
          })
          
          el.addClass('sticky-processed');
          
          // start it off
          win.scroll();

        });
        
      },
      
      reset : function() {
        return this.each(function() {
          var el = $(this),
            id = el.data('sticky-id');
            
          el.css({
            position: '',
            top: '',
            bottom: ''
          })
          .removeClass('sticky-stopped')
          .removeClass('sticky-active')
          .removeClass('sticky-inactive')
          .removeClass('sticky-processed');
          
          $(window).unbind('.stickyscroll-' + id);
        });
      }
      
    };
    
    // if options is a valid method, execute it
    if (methods[options]) {
      return methods[options].apply(this,
        Array.prototype.slice.call(arguments, 1));
    }
    // or, if options is a config object, or no options are passed, init
    else if (typeof options === 'object' || !options) {
      return methods.init.apply(this, arguments);
    }
    
    else if(console) {
      console.log('Method' + options +
        ' does not exist on jQuery.stickyScroll');
    }

  };
})(jQuery);;
/*
 * qTip2 - Pretty powerful tooltips - v2.2.1
 * http://qtip2.com
 *
 * Copyright (c) 2014 
 * Released under the MIT licenses
 * http://jquery.org/license
 *
 * Date: Sat Sep 6 2014 11:12 GMT+0100+0100
 * Plugins: None
 * Styles: core
 */
/*global window: false, jQuery: false, console: false, define: false */

/* Cache window, document, undefined */
(function (window, document, undefined) {

    // Uses AMD or browser globals to create a jQuery plugin.
    (function (factory) {
        "use strict";
        if (typeof define === 'function' && define.amd) {
            define(['jquery'], factory);
        }
        else if (jQuery && !jQuery.fn.qtip) {
            factory(jQuery);
        }
    }
    (function ($) {
        "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
        ;// Munge the primitives - Paul Irish tip
        var TRUE = true,
        FALSE = false,
        NULL = null,

        // Common variables
        X = 'x', Y = 'y',
        WIDTH = 'width',
        HEIGHT = 'height',

        // Positioning sides
        TOP = 'top',
        LEFT = 'left',
        BOTTOM = 'bottom',
        RIGHT = 'right',
        CENTER = 'center',

        // Position adjustment types
        FLIP = 'flip',
        FLIPINVERT = 'flipinvert',
        SHIFT = 'shift',

        // Shortcut vars
        QTIP, PROTOTYPE, CORNER, CHECKS,
        PLUGINS = {},
        NAMESPACE = 'qtip',
        ATTR_HAS = 'data-hasqtip',
        ATTR_ID = 'data-qtip-id',
        WIDGET = ['ui-widget', 'ui-tooltip'],
        SELECTOR = '.' + NAMESPACE,
        INACTIVE_EVENTS = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '),

        CLASS_FIXED = NAMESPACE + '-fixed',
        CLASS_DEFAULT = NAMESPACE + '-default',
        CLASS_FOCUS = NAMESPACE + '-focus',
        CLASS_HOVER = NAMESPACE + '-hover',
        CLASS_DISABLED = NAMESPACE + '-disabled',

        replaceSuffix = '_replacedByqTip',
        oldtitle = 'oldtitle',
        trackingBound,

        // Browser detection
        BROWSER = {
            /*
             * IE version detection
             *
             * Adapted from: http://ajaxian.com/archives/attack-of-the-ie-conditional-comment
             * Credit to James Padolsey for the original implemntation!
             */
            ie: (function () {
                for (
                    var v = 4, i = document.createElement("div") ;
                    (i.innerHTML = "<!--[if gt IE " + v + "]><i></i><![endif]-->") && i.getElementsByTagName("i")[0];
                    v += 1
                ) { }
                return v > 4 ? v : NaN;
            }()),

            /*
             * iOS version detection
             */
            iOS: parseFloat(
                ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1])
                .replace('undefined', '3_2').replace('_', '.').replace('_', '')
            ) || FALSE
        };
        ; function QTip(target, options, id, attr) {
            // Elements and ID
            this.id = id;
            this.target = target;
            this.tooltip = NULL;
            this.elements = { target: target };

            // Internal constructs
            this._id = NAMESPACE + '-' + id;
            this.timers = { img: {} };
            this.options = options;
            this.plugins = {};

            // Cache object
            this.cache = {
                event: {},
                target: $(),
                disabled: FALSE,
                attr: attr,
                onTooltip: FALSE,
                lastClass: ''
            };

            // Set the initial flags
            this.rendered = this.destroyed = this.disabled = this.waiting =
                this.hiddenDuringWait = this.positioning = this.triggering = FALSE;
        }
        PROTOTYPE = QTip.prototype;

        PROTOTYPE._when = function (deferreds) {
            return $.when.apply($, deferreds);
        };

        PROTOTYPE.render = function (show) {
            if (this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit

            var self = this,
                options = this.options,
                cache = this.cache,
                elements = this.elements,
                text = options.content.text,
                title = options.content.title,
                button = options.content.button,
                posOptions = options.position,
                namespace = '.' + this._id + ' ',
                deferreds = [],
                tooltip;

            // Add ARIA attributes to target
            $.attr(this.target[0], 'aria-describedby', this._id);

            // Create public position object that tracks current position corners
            cache.posClass = this._createPosClass(
                (this.position = { my: posOptions.my, at: posOptions.at }).my
            );

            // Create tooltip element
            this.tooltip = elements.tooltip = tooltip = $('<div/>', {
                'id': this._id,
                'class': [NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass].join(' '),
                'width': options.style.width || '',
                'height': options.style.height || '',
                'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,

                /* ARIA specific attributes */
                'role': 'alert',
                'aria-live': 'polite',
                'aria-atomic': FALSE,
                'aria-describedby': this._id + '-content',
                'aria-hidden': TRUE
            })
            .toggleClass(CLASS_DISABLED, this.disabled)
            .attr(ATTR_ID, this.id)
            .data(NAMESPACE, this)
            .appendTo(posOptions.container)
            .append(
                // Create content element
                elements.content = $('<div />', {
                    'class': NAMESPACE + '-content',
                    'id': this._id + '-content',
                    'aria-atomic': TRUE
                })
            );

            // Set rendered flag and prevent redundant reposition calls for now
            this.rendered = -1;
            this.positioning = TRUE;

            // Create title...
            if (title) {
                this._createTitle();

                // Update title only if its not a callback (called in toggle if so)
                if (!$.isFunction(title)) {
                    deferreds.push(this._updateTitle(title, FALSE));
                }
            }

            // Create button
            if (button) { this._createButton(); }

            // Set proper rendered flag and update content if not a callback function (called in toggle)
            if (!$.isFunction(text)) {
                deferreds.push(this._updateContent(text, FALSE));
            }
            this.rendered = TRUE;

            // Setup widget classes
            this._setWidget();

            // Initialize 'render' plugins
            $.each(PLUGINS, function (name) {
                var instance;
                if (this.initialize === 'render' && (instance = this(self))) {
                    self.plugins[name] = instance;
                }
            });

            // Unassign initial events and assign proper events
            this._unassignEvents();
            this._assignEvents();

            // When deferreds have completed
            this._when(deferreds).then(function () {
                // tooltiprender event
                self._trigger('render');

                // Reset flags
                self.positioning = FALSE;

                // Show tooltip if not hidden during wait period
                if (!self.hiddenDuringWait && (options.show.ready || show)) {
                    self.toggle(TRUE, cache.event, FALSE);
                }
                self.hiddenDuringWait = FALSE;
            });

            // Expose API
            QTIP.api[this.id] = this;

            return this;
        };

        PROTOTYPE.destroy = function (immediate) {
            // Set flag the signify destroy is taking place to plugins
            // and ensure it only gets destroyed once!
            if (this.destroyed) { return this.target; }

            function process() {
                if (this.destroyed) { return; }
                this.destroyed = TRUE;

                var target = this.target,
                    title = target.attr(oldtitle),
                    timer;

                // Destroy tooltip if rendered
                if (this.rendered) {
                    this.tooltip.stop(1, 0).find('*').remove().end().remove();
                }

                // Destroy all plugins
                $.each(this.plugins, function (name) {
                    this.destroy && this.destroy();
                });

                // Clear timers
                for (timer in this.timers) {
                    clearTimeout(this.timers[timer]);
                }

                // Remove api object and ARIA attributes
                target.removeData(NAMESPACE)
                    .removeAttr(ATTR_ID)
                    .removeAttr(ATTR_HAS)
                    .removeAttr('aria-describedby');

                // Reset old title attribute if removed
                if (this.options.suppress && title) {
                    target.attr('title', title).removeAttr(oldtitle);
                }

                // Remove qTip events associated with this API
                this._unassignEvents();

                // Remove ID from used id objects, and delete object references
                // for better garbage collection and leak protection
                this.options = this.elements = this.cache = this.timers =
                    this.plugins = this.mouse = NULL;

                // Delete epoxsed API object
                delete QTIP.api[this.id];
            }

            // If an immediate destory is needed
            if ((immediate !== TRUE || this.triggering === 'hide') && this.rendered) {
                this.tooltip.one('tooltiphidden', $.proxy(process, this));
                !this.triggering && this.hide();
            }

                // If we're not in the process of hiding... process
            else { process.call(this); }

            return this.target;
        };
        ; function invalidOpt(a) {
            return a === NULL || $.type(a) !== 'object';
        }

        function invalidContent(c) {
            return !($.isFunction(c) || (c && c.attr) || c.length || ($.type(c) === 'object' && (c.jquery || c.then)));
        }

        // Option object sanitizer
        function sanitizeOptions(opts) {
            var content, text, ajax, once;

            if (invalidOpt(opts)) { return FALSE; }

            if (invalidOpt(opts.metadata)) {
                opts.metadata = { type: opts.metadata };
            }

            if ('content' in opts) {
                content = opts.content;

                if (invalidOpt(content) || content.jquery || content.done) {
                    content = opts.content = {
                        text: (text = invalidContent(content) ? FALSE : content)
                    };
                }
                else { text = content.text; }

                // DEPRECATED - Old content.ajax plugin functionality
                // Converts it into the proper Deferred syntax
                if ('ajax' in content) {
                    ajax = content.ajax;
                    once = ajax && ajax.once !== FALSE;
                    delete content.ajax;

                    content.text = function (event, api) {
                        var loading = text || $(this).attr(api.options.content.attr) || 'Loading...',

                        deferred = $.ajax(
                            $.extend({}, ajax, { context: api })
                        )
                        .then(ajax.success, NULL, ajax.error)
                        .then(function (content) {
                            if (content && once) { api.set('content.text', content); }
                            return content;
                        },
                        function (xhr, status, error) {
                            if (api.destroyed || xhr.status === 0) { return; }
                            api.set('content.text', status + ': ' + error);
                        });

                        return !once ? (api.set('content.text', loading), deferred) : loading;
                    };
                }

                if ('title' in content) {
                    if ($.isPlainObject(content.title)) {
                        content.button = content.title.button;
                        content.title = content.title.text;
                    }

                    if (invalidContent(content.title || FALSE)) {
                        content.title = FALSE;
                    }
                }
            }

            if ('position' in opts && invalidOpt(opts.position)) {
                opts.position = { my: opts.position, at: opts.position };
            }

            if ('show' in opts && invalidOpt(opts.show)) {
                opts.show = opts.show.jquery ? { target: opts.show } :
                    opts.show === TRUE ? { ready: TRUE } : { event: opts.show };
            }

            if ('hide' in opts && invalidOpt(opts.hide)) {
                opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide };
            }

            if ('style' in opts && invalidOpt(opts.style)) {
                opts.style = { classes: opts.style };
            }

            // Sanitize plugin options
            $.each(PLUGINS, function () {
                this.sanitize && this.sanitize(opts);
            });

            return opts;
        }

        // Setup builtin .set() option checks
        CHECKS = PROTOTYPE.checks = {
            builtin: {
                // Core checks
                '^id$': function (obj, o, v, prev) {
                    var id = v === TRUE ? QTIP.nextid : v,
                        new_id = NAMESPACE + '-' + id;

                    if (id !== FALSE && id.length > 0 && !$('#' + new_id).length) {
                        this._id = new_id;

                        if (this.rendered) {
                            this.tooltip[0].id = this._id;
                            this.elements.content[0].id = this._id + '-content';
                            this.elements.title[0].id = this._id + '-title';
                        }
                    }
                    else { obj[o] = prev; }
                },
                '^prerender': function (obj, o, v) {
                    v && !this.rendered && this.render(this.options.show.ready);
                },

                // Content checks
                '^content.text$': function (obj, o, v) {
                    this._updateContent(v);
                },
                '^content.attr$': function (obj, o, v, prev) {
                    if (this.options.content.text === this.target.attr(prev)) {
                        this._updateContent(this.target.attr(v));
                    }
                },
                '^content.title$': function (obj, o, v) {
                    // Remove title if content is null
                    if (!v) { return this._removeTitle(); }

                    // If title isn't already created, create it now and update
                    v && !this.elements.title && this._createTitle();
                    this._updateTitle(v);
                },
                '^content.button$': function (obj, o, v) {
                    this._updateButton(v);
                },
                '^content.title.(text|button)$': function (obj, o, v) {
                    this.set('content.' + o, v); // Backwards title.text/button compat
                },

                // Position checks
                '^position.(my|at)$': function (obj, o, v) {
                    'string' === typeof v && (this.position[o] = obj[o] = new CORNER(v, o === 'at'));
                },
                '^position.container$': function (obj, o, v) {
                    this.rendered && this.tooltip.appendTo(v);
                },

                // Show checks
                '^show.ready$': function (obj, o, v) {
                    v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE));
                },

                // Style checks
                '^style.classes$': function (obj, o, v, p) {
                    this.rendered && this.tooltip.removeClass(p).addClass(v);
                },
                '^style.(width|height)': function (obj, o, v) {
                    this.rendered && this.tooltip.css(o, v);
                },
                '^style.widget|content.title': function () {
                    this.rendered && this._setWidget();
                },
                '^style.def': function (obj, o, v) {
                    this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v);
                },

                // Events check
                '^events.(render|show|move|hide|focus|blur)$': function (obj, o, v) {
                    this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip' + o, v);
                },

                // Properties which require event reassignment
                '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function () {
                    if (!this.rendered) { return; }

                    // Set tracking flag
                    var posOptions = this.options.position;
                    this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);

                    // Reassign events
                    this._unassignEvents();
                    this._assignEvents();
                }
            }
        };

        // Dot notation converter
        function convertNotation(options, notation) {
            var i = 0, obj, option = options,

            // Split notation into array
            levels = notation.split('.');

            // Loop through
            while (option = option[levels[i++]]) {
                if (i < levels.length) { obj = option; }
            }

            return [obj || options, levels.pop()];
        }

        PROTOTYPE.get = function (notation) {
            if (this.destroyed) { return this; }

            var o = convertNotation(this.options, notation.toLowerCase()),
                result = o[0][o[1]];

            return result.precedance ? result.string() : result;
        };

        function setCallback(notation, args) {
            var category, rule, match;

            for (category in this.checks) {
                for (rule in this.checks[category]) {
                    if (match = (new RegExp(rule, 'i')).exec(notation)) {
                        args.push(match);

                        if (category === 'builtin' || this.plugins[category]) {
                            this.checks[category][rule].apply(
                                this.plugins[category] || this, args
                            );
                        }
                    }
                }
            }
        }

        var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,
            rrender = /^prerender|show\.ready/i;

        PROTOTYPE.set = function (option, value) {
            if (this.destroyed) { return this; }

            var rendered = this.rendered,
                reposition = FALSE,
                options = this.options,
                checks = this.checks,
                name;

            // Convert singular option/value pair into object form
            if ('string' === typeof option) {
                name = option; option = {}; option[name] = value;
            }
            else { option = $.extend({}, option); }

            // Set all of the defined options to their new values
            $.each(option, function (notation, value) {
                if (rendered && rrender.test(notation)) {
                    delete option[notation]; return;
                }

                // Set new obj value
                var obj = convertNotation(options, notation.toLowerCase()), previous;
                previous = obj[0][obj[1]];
                obj[0][obj[1]] = value && value.nodeType ? $(value) : value;

                // Also check if we need to reposition
                reposition = rmove.test(notation) || reposition;

                // Set the new params for the callback
                option[notation] = [obj[0], obj[1], value, previous];
            });

            // Re-sanitize options
            sanitizeOptions(options);

            /*
             * Execute any valid callbacks for the set options
             * Also set positioning flag so we don't get loads of redundant repositioning calls.
             */
            this.positioning = TRUE;
            $.each(option, $.proxy(setCallback, this));
            this.positioning = FALSE;

            // Update position if needed
            if (this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) {
                this.reposition(options.position.target === 'mouse' ? NULL : this.cache.event);
            }

            return this;
        };
        ; PROTOTYPE._update = function (content, element, reposition) {
            var self = this,
                cache = this.cache;

            // Make sure tooltip is rendered and content is defined. If not return
            if (!this.rendered || !content) { return FALSE; }

            // Use function to parse content
            if ($.isFunction(content)) {
                content = content.call(this.elements.target, cache.event, this) || '';
            }

            // Handle deferred content
            if ($.isFunction(content.then)) {
                cache.waiting = TRUE;
                return content.then(function (c) {
                    cache.waiting = FALSE;
                    return self._update(c, element);
                }, NULL, function (e) {
                    return self._update(e, element);
                });
            }

            // If content is null... return false
            if (content === FALSE || (!content && content !== '')) { return FALSE; }

            // Append new content if its a DOM array and show it if hidden
            if (content.jquery && content.length > 0) {
                element.empty().append(
                    content.css({ display: 'block', visibility: 'visible' })
                );
            }

                // Content is a regular string, insert the new content
            else { element.html(content); }

            // Wait for content to be loaded, and reposition
            return this._waitForContent(element).then(function (images) {
                if (self.rendered && self.tooltip[0].offsetWidth > 0) {
                    self.reposition(cache.event, !images.length);
                }
            });
        };

        PROTOTYPE._waitForContent = function (element) {
            var cache = this.cache;

            // Set flag
            cache.waiting = TRUE;

            // If imagesLoaded is included, ensure images have loaded and return promise
            return ($.fn.imagesLoaded ? element.imagesLoaded() : $.Deferred().resolve([]))
                .done(function () { cache.waiting = FALSE; })
                .promise();
        };

        PROTOTYPE._updateContent = function (content, reposition) {
            this._update(content, this.elements.content, reposition);
        };

        PROTOTYPE._updateTitle = function (content, reposition) {
            if (this._update(content, this.elements.title, reposition) === FALSE) {
                this._removeTitle(FALSE);
            }
        };

        PROTOTYPE._createTitle = function () {
            var elements = this.elements,
                id = this._id + '-title';

            // Destroy previous title element, if present
            if (elements.titlebar) { this._removeTitle(); }

            // Create title bar and title elements
            elements.titlebar = $('<div />', {
                'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '')
            })
            .append(
                elements.title = $('<div />', {
                    'id': id,
                    'class': NAMESPACE + '-title',
                    'aria-atomic': TRUE
                })
            )
            .insertBefore(elements.content)

            // Button-specific events
            .delegate('.qtip-close', 'mousedown keydown mouseup keyup mouseout', function (event) {
                $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
            })
            .delegate('.qtip-close', 'mouseover mouseout', function (event) {
                $(this).toggleClass('ui-state-hover', event.type === 'mouseover');
            });

            // Create button if enabled
            if (this.options.content.button) { this._createButton(); }
        };

        PROTOTYPE._removeTitle = function (reposition) {
            var elements = this.elements;

            if (elements.title) {
                elements.titlebar.remove();
                elements.titlebar = elements.title = elements.button = NULL;

                // Reposition if enabled
                if (reposition !== FALSE) { this.reposition(); }
            }
        };
        ; PROTOTYPE._createPosClass = function (my) {
            return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev();
        };

        PROTOTYPE.reposition = function (event, effect) {
            if (!this.rendered || this.positioning || this.destroyed) { return this; }

            // Set positioning flag
            this.positioning = TRUE;

            var cache = this.cache,
                tooltip = this.tooltip,
                posOptions = this.options.position,
                target = posOptions.target,
                my = posOptions.my,
                at = posOptions.at,
                viewport = posOptions.viewport,
                container = posOptions.container,
                adjust = posOptions.adjust,
                method = adjust.method.split(' '),
                tooltipWidth = tooltip.outerWidth(FALSE),
                tooltipHeight = tooltip.outerHeight(FALSE),
                targetWidth = 0,
                targetHeight = 0,
                type = tooltip.css('position'),
                position = { left: 0, top: 0 },
                visible = tooltip[0].offsetWidth > 0,
                isScroll = event && event.type === 'scroll',
                win = $(window),
                doc = container[0].ownerDocument,
                mouse = this.mouse,
                pluginCalculations, offset, adjusted, newClass;

            // Check if absolute position was passed
            if ($.isArray(target) && target.length === 2) {
                // Force left top and set position
                at = { x: LEFT, y: TOP };
                position = { left: target[0], top: target[1] };
            }

                // Check if mouse was the target
            else if (target === 'mouse') {
                // Force left top to allow flipping
                at = { x: LEFT, y: TOP };

                // Use the mouse origin that caused the show event, if distance hiding is enabled
                if ((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) {
                    event = cache.origin;
                }

                    // Use cached event for resize/scroll events
                else if (!event || (event && (event.type === 'resize' || event.type === 'scroll'))) {
                    event = cache.event;
                }

                    // Otherwise, use the cached mouse coordinates if available
                else if (mouse && mouse.pageX) {
                    event = mouse;
                }

                // Calculate body and container offset and take them into account below
                if (type !== 'static') { position = container.offset(); }
                if (doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) {
                    offset = $(document.body).offset();
                }

                // Use event coordinates for position
                position = {
                    left: event.pageX - position.left + (offset && offset.left || 0),
                    top: event.pageY - position.top + (offset && offset.top || 0)
                };

                // Scroll events are a pain, some browsers
                if (adjust.mouse && isScroll && mouse) {
                    position.left -= (mouse.scrollX || 0) - win.scrollLeft();
                    position.top -= (mouse.scrollY || 0) - win.scrollTop();
                }
            }

                // Target wasn't mouse or absolute...
            else {
                // Check if event targetting is being used
                if (target === 'event') {
                    if (event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
                        cache.target = $(event.target);
                    }
                    else if (!event.target) {
                        cache.target = this.elements.target;
                    }
                }
                else if (target !== 'event') {
                    cache.target = $(target.jquery ? target : this.elements.target);
                }
                target = cache.target;

                // Parse the target into a jQuery object and make sure there's an element present
                target = $(target).eq(0);
                if (target.length === 0) { return this; }

                    // Check if window or document is the target
                else if (target[0] === document || target[0] === window) {
                    targetWidth = BROWSER.iOS ? window.innerWidth : target.width();
                    targetHeight = BROWSER.iOS ? window.innerHeight : target.height();

                    if (target[0] === window) {
                        position = {
                            top: (viewport || target).scrollTop(),
                            left: (viewport || target).scrollLeft()
                        };
                    }
                }

                    // Check if the target is an <AREA> element
                else if (PLUGINS.imagemap && target.is('area')) {
                    pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE);
                }

                    // Check if the target is an SVG element
                else if (PLUGINS.svg && target && target[0].ownerSVGElement) {
                    pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE);
                }

                    // Otherwise use regular jQuery methods
                else {
                    targetWidth = target.outerWidth(FALSE);
                    targetHeight = target.outerHeight(FALSE);
                    position = target.offset();
                }

                // Parse returned plugin values into proper variables
                if (pluginCalculations) {
                    targetWidth = pluginCalculations.width;
                    targetHeight = pluginCalculations.height;
                    offset = pluginCalculations.offset;
                    position = pluginCalculations.position;
                }

                // Adjust position to take into account offset parents
                position = this.reposition.offset(target, position, container);

                // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2)
                if ((BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1) ||
                    (BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33) ||
                    (!BROWSER.iOS && type === 'fixed')
                ) {
                    position.left -= win.scrollLeft();
                    position.top -= win.scrollTop();
                }

                // Adjust position relative to target
                if (!pluginCalculations || (pluginCalculations && pluginCalculations.adjustable !== FALSE)) {
                    position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0;
                    position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0;
                }
            }

            // Adjust position relative to tooltip
            position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0);
            position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0);

            // Use viewport adjustment plugin if enabled
            if (PLUGINS.viewport) {
                adjusted = position.adjusted = PLUGINS.viewport(
                    this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight
                );

                // Apply offsets supplied by positioning plugin (if used)
                if (offset && adjusted.left) { position.left += offset.left; }
                if (offset && adjusted.top) { position.top += offset.top; }

                // Apply any new 'my' position
                if (adjusted.my) { this.position.my = adjusted.my; }
            }

                // Viewport adjustment is disabled, set values to zero
            else { position.adjusted = { left: 0, top: 0 }; }

            // Set tooltip position class if it's changed
            if (cache.posClass !== (newClass = this._createPosClass(this.position.my))) {
                tooltip.removeClass(cache.posClass).addClass((cache.posClass = newClass));
            }

            // tooltipmove event
            if (!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; }
            delete position.adjusted;

            // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
            if (effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
                tooltip.css(position);
            }

                // Use custom function if provided
            else if ($.isFunction(posOptions.effect)) {
                posOptions.effect.call(tooltip, this, $.extend({}, position));
                tooltip.queue(function (next) {
                    // Reset attributes to avoid cross-browser rendering bugs
                    $(this).css({ opacity: '', height: '' });
                    if (BROWSER.ie) { this.style.removeAttribute('filter'); }

                    next();
                });
            }

            // Set positioning flag
            this.positioning = FALSE;

            return this;
        };

        // Custom (more correct for qTip!) offset calculator
        PROTOTYPE.reposition.offset = function (elem, pos, container) {
            if (!container[0]) { return pos; }

            var ownerDocument = $(elem[0].ownerDocument),
                quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat',
                parent = container[0],
                scrolled, position, parentOffset, overflow;

            function scroll(e, i) {
                pos.left += i * e.scrollLeft();
                pos.top += i * e.scrollTop();
            }

            // Compensate for non-static containers offset
            do {
                if ((position = $.css(parent, 'position')) !== 'static') {
                    if (position === 'fixed') {
                        parentOffset = parent.getBoundingClientRect();
                        scroll(ownerDocument, -1);
                    }
                    else {
                        parentOffset = $(parent).position();
                        parentOffset.left += (parseFloat($.css(parent, 'borderLeftWidth')) || 0);
                        parentOffset.top += (parseFloat($.css(parent, 'borderTopWidth')) || 0);
                    }

                    pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0);
                    pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0);

                    // If this is the first parent element with an overflow of "scroll" or "auto", store it
                    if (!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); }
                }
            }
            while ((parent = parent.offsetParent));

            // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode)
            if (scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) {
                scroll(scrolled, 1);
            }

            return pos;
        };

        // Corner class
        var C = (CORNER = PROTOTYPE.reposition.Corner = function (corner, forceY) {
            corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase();
            this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
            this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
            this.forceY = !!forceY;

            var f = corner.charAt(0);
            this.precedance = (f === 't' || f === 'b' ? Y : X);
        }).prototype;

        C.invert = function (z, center) {
            this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z];
        };

        C.string = function (join) {
            var x = this.x, y = this.y;

            var result = x !== y ?
                (x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ?
                    [y, x] : [x, y]
                ) :
            [x];

            return join !== false ? result.join(' ') : result;
        };

        C.abbrev = function () {
            var result = this.string(false);
            return result[0].charAt(0) + (result[1] && result[1].charAt(0) || '');
        };

        C.clone = function () {
            return new CORNER(this.string(), this.forceY);
        };

        ;
        PROTOTYPE.toggle = function (state, event) {
            var cache = this.cache,
                options = this.options,
                tooltip = this.tooltip;

            // Try to prevent flickering when tooltip overlaps show element
            if (event) {
                if ((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) &&
                    options.show.target.add(event.target).length === options.show.target.length &&
                    tooltip.has(event.relatedTarget).length) {
                    return this;
                }

                // Cache event
                cache.event = $.event.fix(event);
            }

            // If we're currently waiting and we've just hidden... stop it
            this.waiting && !state && (this.hiddenDuringWait = TRUE);

            // Render the tooltip if showing and it isn't already
            if (!this.rendered) { return state ? this.render(1) : this; }
            else if (this.destroyed || this.disabled) { return this; }

            var type = state ? 'show' : 'hide',
                opts = this.options[type],
                otherOpts = this.options[!state ? 'show' : 'hide'],
                posOptions = this.options.position,
                contentOptions = this.options.content,
                width = this.tooltip.css('width'),
                visible = this.tooltip.is(':visible'),
                animate = state || opts.target.length === 1,
                sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
                identicalState, allow, showEvent, delay, after;

            // Detect state if valid one isn't provided
            if ((typeof state).search('boolean|number')) { state = !visible; }

            // Check if the tooltip is in an identical state to the new would-be state
            identicalState = !tooltip.is(':animated') && visible === state && sameTarget;

            // Fire tooltip(show/hide) event and check if destroyed
            allow = !identicalState ? !!this._trigger(type, [90]) : NULL;

            // Check to make sure the tooltip wasn't destroyed in the callback
            if (this.destroyed) { return this; }

            // If the user didn't stop the method prematurely and we're showing the tooltip, focus it
            if (allow !== FALSE && state) { this.focus(event); }

            // If the state hasn't changed or the user stopped it, return early
            if (!allow || identicalState) { return this; }

            // Set ARIA hidden attribute
            $.attr(tooltip[0], 'aria-hidden', !!!state);

            // Execute state specific properties
            if (state) {
                // Store show origin coordinates
                this.mouse && (cache.origin = $.event.fix(this.mouse));

                // Update tooltip content & title if it's a dynamic function
                if ($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
                if ($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }

                // Cache mousemove events for positioning purposes (if not already tracking)
                if (!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
                    $(document).bind('mousemove.' + NAMESPACE, this._storeMouse);
                    trackingBound = TRUE;
                }

                // Update the tooltip position (set width first to prevent viewport/max-width issues)
                if (!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
                this.reposition(event, arguments[2]);
                if (!width) { tooltip.css('width', ''); }

                // Hide other tooltips if tooltip is solo
                if (!!opts.solo) {
                    (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
                        .not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo'));
                }
            }
            else {
                // Clear show timer if we're hiding
                clearTimeout(this.timers.show);

                // Remove cached origin on hide
                delete cache.origin;

                // Remove mouse tracking event if not needed (all tracking qTips are hidden)
                if (trackingBound && !$(SELECTOR + '[tracking="true"]:visible', opts.solo).not(tooltip).length) {
                    $(document).unbind('mousemove.' + NAMESPACE);
                    trackingBound = FALSE;
                }

                // Blur the tooltip
                this.blur(event);
            }

            // Define post-animation, state specific properties
            after = $.proxy(function () {
                if (state) {
                    // Prevent antialias from disappearing in IE by removing filter
                    if (BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }

                    // Remove overflow setting to prevent tip bugs
                    tooltip.css('overflow', '');

                    // Autofocus elements if enabled
                    if ('string' === typeof opts.autofocus) {
                        $(this.options.show.autofocus, tooltip).focus();
                    }

                    // If set, hide tooltip when inactive for delay period
                    this.options.show.target.trigger('qtip-' + this.id + '-inactive');
                }
                else {
                    // Reset CSS states
                    tooltip.css({
                        display: '',
                        visibility: '',
                        opacity: '',
                        left: '',
                        top: ''
                    });
                }

                // tooltipvisible/tooltiphidden events
                this._trigger(state ? 'visible' : 'hidden');
            }, this);

            // If no effect type is supplied, use a simple toggle
            if (opts.effect === FALSE || animate === FALSE) {
                tooltip[type]();
                after();
            }

                // Use custom function if provided
            else if ($.isFunction(opts.effect)) {
                tooltip.stop(1, 1);
                opts.effect.call(tooltip, this);
                tooltip.queue('fx', function (n) {
                    after(); n();
                });
            }

                // Use basic fade function by default
            else { tooltip.fadeTo(90, state ? 1 : 0, after); }

            // If inactive hide method is set, active it
            if (state) { opts.target.trigger('qtip-' + this.id + '-inactive'); }

            return this;
        };

        PROTOTYPE.show = function (event) { return this.toggle(TRUE, event); };

        PROTOTYPE.hide = function (event) { return this.toggle(FALSE, event); };
        ; PROTOTYPE.focus = function (event) {
            if (!this.rendered || this.destroyed) { return this; }

            var qtips = $(SELECTOR),
                tooltip = this.tooltip,
                curIndex = parseInt(tooltip[0].style.zIndex, 10),
                newIndex = QTIP.zindex + qtips.length,
                focusedElem;

            // Only update the z-index if it has changed and tooltip is not already focused
            if (!tooltip.hasClass(CLASS_FOCUS)) {
                // tooltipfocus event
                if (this._trigger('focus', [newIndex], event)) {
                    // Only update z-index's if they've changed
                    if (curIndex !== newIndex) {
                        // Reduce our z-index's and keep them properly ordered
                        qtips.each(function () {
                            if (this.style.zIndex > curIndex) {
                                this.style.zIndex = this.style.zIndex - 1;
                            }
                        });

                        // Fire blur event for focused tooltip
                        qtips.filter('.' + CLASS_FOCUS).qtip('blur', event);
                    }

                    // Set the new z-index
                    tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
                }
            }

            return this;
        };

        PROTOTYPE.blur = function (event) {
            if (!this.rendered || this.destroyed) { return this; }

            // Set focused status to FALSE
            this.tooltip.removeClass(CLASS_FOCUS);

            // tooltipblur event
            this._trigger('blur', [this.tooltip.css('zIndex')], event);

            return this;
        };
        ; PROTOTYPE.disable = function (state) {
            if (this.destroyed) { return this; }

            // If 'toggle' is passed, toggle the current state
            if (state === 'toggle') {
                state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled);
            }

                // Disable if no state passed
            else if ('boolean' !== typeof state) {
                state = TRUE;
            }

            if (this.rendered) {
                this.tooltip.toggleClass(CLASS_DISABLED, state)
                    .attr('aria-disabled', state);
            }

            this.disabled = !!state;

            return this;
        };

        PROTOTYPE.enable = function () { return this.disable(FALSE); };
        ; PROTOTYPE._createButton = function () {
            var self = this,
                elements = this.elements,
                tooltip = elements.tooltip,
                button = this.options.content.button,
                isString = typeof button === 'string',
                close = isString ? button : 'Close tooltip';

            if (elements.button) { elements.button.remove(); }

            // Use custom button if one was supplied by user, else use default
            if (button.jquery) {
                elements.button = button;
            }
            else {
                elements.button = $('<a />', {
                    'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE + '-icon'),
                    'title': close,
                    'aria-label': close
                })
                .prepend(
                    $('<span />', {
                        'class': 'ui-icon ui-icon-close',
                        'html': '&times;'
                    })
                );
            }

            // Create button and setup attributes
            elements.button.appendTo(elements.titlebar || tooltip)
                .attr('role', 'button')
                .click(function (event) {
                    if (!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); }
                    return FALSE;
                });
        };

        PROTOTYPE._updateButton = function (button) {
            // Make sure tooltip is rendered and if not, return
            if (!this.rendered) { return FALSE; }

            var elem = this.elements.button;
            if (button) { this._createButton(); }
            else { elem.remove(); }
        };
        ;// Widget class creator
        function createWidgetClass(cls) {
            return WIDGET.concat('').join(cls ? '-' + cls + ' ' : ' ');
        }

        // Widget class setter method
        PROTOTYPE._setWidget = function () {
            var on = this.options.style.widget,
                elements = this.elements,
                tooltip = elements.tooltip,
                disabled = tooltip.hasClass(CLASS_DISABLED);

            tooltip.removeClass(CLASS_DISABLED);
            CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled';
            tooltip.toggleClass(CLASS_DISABLED, disabled);

            tooltip.toggleClass('ui-helper-reset ' + createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on);

            if (elements.content) {
                elements.content.toggleClass(createWidgetClass('content'), on);
            }
            if (elements.titlebar) {
                elements.titlebar.toggleClass(createWidgetClass('header'), on);
            }
            if (elements.button) {
                elements.button.toggleClass(NAMESPACE + '-icon', !on);
            }
        };
        ; function delay(callback, duration) {
            // If tooltip has displayed, start hide timer
            if (duration > 0) {
                return setTimeout(
                    $.proxy(callback, this), duration
                );
            }
            else { callback.call(this); }
        }

        function showMethod(event) {
            if (this.tooltip.hasClass(CLASS_DISABLED)) { return; }

            // Clear hide timers
            clearTimeout(this.timers.show);
            clearTimeout(this.timers.hide);

            // Start show timer
            this.timers.show = delay.call(this,
                function () { this.toggle(TRUE, event); },
                this.options.show.delay
            );
        }

        function hideMethod(event) {
            if (this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; }

            // Check if new target was actually the tooltip element
            var relatedTarget = $(event.relatedTarget),
                ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0],
                ontoTarget = relatedTarget[0] === this.options.show.target[0];

            // Clear timers and stop animation queue
            clearTimeout(this.timers.show);
            clearTimeout(this.timers.hide);

            // Prevent hiding if tooltip is fixed and event target is the tooltip.
            // Or if mouse positioning is enabled and cursor momentarily overlaps
            if (this !== relatedTarget[0] &&
                (this.options.position.target === 'mouse' && ontoTooltip) ||
                (this.options.hide.fixed && (
                    (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget))
                )) {
                try {
                    event.preventDefault();
                    event.stopImmediatePropagation();
                } catch (e) { }

                return;
            }

            // If tooltip has displayed, start hide timer
            this.timers.hide = delay.call(this,
                function () { this.toggle(FALSE, event); },
                this.options.hide.delay,
                this
            );
        }

        function inactiveMethod(event) {
            if (this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; }

            // Clear timer
            clearTimeout(this.timers.inactive);

            this.timers.inactive = delay.call(this,
                function () { this.hide(event); },
                this.options.hide.inactive
            );
        }

        function repositionMethod(event) {
            if (this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); }
        }

        // Store mouse coordinates
        PROTOTYPE._storeMouse = function (event) {
            (this.mouse = $.event.fix(event)).type = 'mousemove';
            return this;
        };

        // Bind events
        PROTOTYPE._bind = function (targets, events, method, suffix, context) {
            if (!targets || !method || !events.length) { return; }
            var ns = '.' + this._id + (suffix ? '-' + suffix : '');
            $(targets).bind(
                (events.split ? events : events.join(ns + ' ')) + ns,
                $.proxy(method, context || this)
            );
            return this;
        };
        PROTOTYPE._unbind = function (targets, suffix) {
            targets && $(targets).unbind('.' + this._id + (suffix ? '-' + suffix : ''));
            return this;
        };

        // Global delegation helper
        function delegate(selector, events, method) {
            $(document.body).delegate(selector,
                (events.split ? events : events.join('.' + NAMESPACE + ' ')) + '.' + NAMESPACE,
                function () {
                    var api = QTIP.api[$.attr(this, ATTR_ID)];
                    api && !api.disabled && method.apply(api, arguments);
                }
            );
        }
        // Event trigger
        PROTOTYPE._trigger = function (type, args, event) {
            var callback = $.Event('tooltip' + type);
            callback.originalEvent = (event && $.extend({}, event)) || this.cache.event || NULL;

            this.triggering = type;
            this.tooltip.trigger(callback, [this].concat(args || []));
            this.triggering = FALSE;

            return !callback.isDefaultPrevented();
        };

        PROTOTYPE._bindEvents = function (showEvents, hideEvents, showTargets, hideTargets, showMethod, hideMethod) {
            // Get tasrgets that lye within both
            var similarTargets = showTargets.filter(hideTargets).add(hideTargets.filter(showTargets)),
                toggleEvents = [];

            // If hide and show targets are the same...
            if (similarTargets.length) {

                // Filter identical show/hide events
                $.each(hideEvents, function (i, type) {
                    var showIndex = $.inArray(type, showEvents);

                    // Both events are identical, remove from both hide and show events
                    // and append to toggleEvents
                    showIndex > -1 && toggleEvents.push(showEvents.splice(showIndex, 1)[0]);
                });

                // Toggle events are special case of identical show/hide events, which happen in sequence
                if (toggleEvents.length) {
                    // Bind toggle events to the similar targets
                    this._bind(similarTargets, toggleEvents, function (event) {
                        var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false;
                        (state ? hideMethod : showMethod).call(this, event);
                    });

                    // Remove the similar targets from the regular show/hide bindings
                    showTargets = showTargets.not(similarTargets);
                    hideTargets = hideTargets.not(similarTargets);
                }
            }

            // Apply show/hide/toggle events
            this._bind(showTargets, showEvents, showMethod);
            this._bind(hideTargets, hideEvents, hideMethod);
        };

        PROTOTYPE._assignInitialEvents = function (event) {
            var options = this.options,
                showTarget = options.show.target,
                hideTarget = options.hide.target,
                showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
                hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];

            // Catch remove/removeqtip events on target element to destroy redundant tooltips
            this._bind(this.elements.target, ['remove', 'removeqtip'], function (event) {
                this.destroy(true);
            }, 'destroy');

            /*
             * Make sure hoverIntent functions properly by using mouseleave as a hide event if
             * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
             */
            if (/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) {
                hideEvents.push('mouseleave');
            }

            /*
             * Also make sure initial mouse targetting works correctly by caching mousemove coords
             * on show targets before the tooltip has rendered. Also set onTarget when triggered to
             * keep mouse tracking working.
             */
            this._bind(showTarget, 'mousemove', function (event) {
                this._storeMouse(event);
                this.cache.onTarget = TRUE;
            });

            // Define hoverIntent function
            function hoverIntent(event) {
                // Only continue if tooltip isn't disabled
                if (this.disabled || this.destroyed) { return FALSE; }

                // Cache the event data
                this.cache.event = event && $.event.fix(event);
                this.cache.target = event && $(event.target);

                // Start the event sequence
                clearTimeout(this.timers.show);
                this.timers.show = delay.call(this,
                    function () { this.render(typeof event === 'object' || options.show.ready); },
                    options.prerender ? 0 : options.show.delay
                );
            }

            // Filter and bind events
            this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function () {
                if (!this.timers) { return FALSE; }
                clearTimeout(this.timers.show);
            });

            // Prerendering is enabled, create tooltip now
            if (options.show.ready || options.prerender) { hoverIntent.call(this, event); }
        };

        // Event assignment method
        PROTOTYPE._assignEvents = function () {
            var self = this,
                options = this.options,
                posOptions = options.position,

                tooltip = this.tooltip,
                showTarget = options.show.target,
                hideTarget = options.hide.target,
                containerTarget = posOptions.container,
                viewportTarget = posOptions.viewport,
                documentTarget = $(document),
                bodyTarget = $(document.body),
                windowTarget = $(window),

                showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
                hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];


            // Assign passed event callbacks
            $.each(options.events, function (name, callback) {
                self._bind(tooltip, name === 'toggle' ? ['tooltipshow', 'tooltiphide'] : ['tooltip' + name], callback, null, tooltip);
            });

            // Hide tooltips when leaving current window/frame (but not select/option elements)
            if (/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') {
                this._bind(documentTarget, ['mouseout', 'blur'], function (event) {
                    if (!/select|option/.test(event.target.nodeName) && !event.relatedTarget) {
                        this.hide(event);
                    }
                });
            }

            // Enable hide.fixed by adding appropriate class
            if (options.hide.fixed) {
                hideTarget = hideTarget.add(tooltip.addClass(CLASS_FIXED));
            }

                /*
                 * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
                 * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
                 */
            else if (/mouse(over|enter)/i.test(options.show.event)) {
                this._bind(hideTarget, 'mouseleave', function () {
                    clearTimeout(this.timers.show);
                });
            }

            // Hide tooltip on document mousedown if unfocus events are enabled
            if (('' + options.hide.event).indexOf('unfocus') > -1) {
                this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function (event) {
                    var elem = $(event.target),
                        enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0,
                        isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0;

                    if (elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor &&
                        !this.target.has(elem[0]).length && enabled
                    ) {
                        this.hide(event);
                    }
                });
            }

            // Check if the tooltip hides when inactive
            if ('number' === typeof options.hide.inactive) {
                // Bind inactive method to show target(s) as a custom event
                this._bind(showTarget, 'qtip-' + this.id + '-inactive', inactiveMethod, 'inactive');

                // Define events which reset the 'inactive' event handler
                this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod);
            }

            // Filter and bind events
            this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod);

            // Mouse movement bindings
            this._bind(showTarget.add(tooltip), 'mousemove', function (event) {
                // Check if the tooltip hides when mouse is moved a certain distance
                if ('number' === typeof options.hide.distance) {
                    var origin = this.cache.origin || {},
                        limit = this.options.hide.distance,
                        abs = Math.abs;

                    // Check if the movement has gone beyond the limit, and hide it if so
                    if (abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
                        this.hide(event);
                    }
                }

                // Cache mousemove coords on show targets
                this._storeMouse(event);
            });

            // Mouse positioning events
            if (posOptions.target === 'mouse') {
                // If mouse adjustment is on...
                if (posOptions.adjust.mouse) {
                    // Apply a mouseleave event so we don't get problems with overlapping
                    if (options.hide.event) {
                        // Track if we're on the target or not
                        this._bind(showTarget, ['mouseenter', 'mouseleave'], function (event) {
                            if (!this.cache) { return FALSE; }
                            this.cache.onTarget = event.type === 'mouseenter';
                        });
                    }

                    // Update tooltip position on mousemove
                    this._bind(documentTarget, 'mousemove', function (event) {
                        // Update the tooltip position only if the tooltip is visible and adjustment is enabled
                        if (this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) {
                            this.reposition(event);
                        }
                    });
                }
            }

            // Adjust positions of the tooltip on window resize if enabled
            if (posOptions.adjust.resize || viewportTarget.length) {
                this._bind($.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod);
            }

            // Adjust tooltip position on scroll of the window or viewport element if present
            if (posOptions.adjust.scroll) {
                this._bind(windowTarget.add(posOptions.container), 'scroll', repositionMethod);
            }
        };

        // Un-assignment method
        PROTOTYPE._unassignEvents = function () {
            var options = this.options,
                showTargets = options.show.target,
                hideTargets = options.hide.target,
                targets = $.grep([
                    this.elements.target[0],
                    this.rendered && this.tooltip[0],
                    options.position.container[0],
                    options.position.viewport[0],
                    options.position.container.closest('html')[0], // unfocus
                    window,
                    document
                ], function (i) {
                    return typeof i === 'object';
                });

            // Add show and hide targets if they're valid
            if (showTargets && showTargets.toArray) {
                targets = targets.concat(showTargets.toArray());
            }
            if (hideTargets && hideTargets.toArray) {
                targets = targets.concat(hideTargets.toArray());
            }

            // Unbind the events
            this._unbind(targets)
                ._unbind(targets, 'destroy')
                ._unbind(targets, 'inactive');
        };

        // Apply common event handlers using delegate (avoids excessive .bind calls!)
        $(function () {
            delegate(SELECTOR, ['mouseenter', 'mouseleave'], function (event) {
                var state = event.type === 'mouseenter',
                    tooltip = $(event.currentTarget),
                    target = $(event.relatedTarget || event.target),
                    options = this.options;

                // On mouseenter...
                if (state) {
                    // Focus the tooltip on mouseenter (z-index stacking)
                    this.focus(event);

                    // Clear hide timer on tooltip hover to prevent it from closing
                    tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide);
                }

                    // On mouseleave...
                else {
                    // When mouse tracking is enabled, hide when we leave the tooltip and not onto the show target (if a hide event is set)
                    if (options.position.target === 'mouse' && options.position.adjust.mouse &&
                        options.hide.event && options.show.target && !target.closest(options.show.target[0]).length) {
                        this.hide(event);
                    }
                }

                // Add hover class
                tooltip.toggleClass(CLASS_HOVER, state);
            });

            // Define events which reset the 'inactive' event handler
            delegate('[' + ATTR_ID + ']', INACTIVE_EVENTS, inactiveMethod);
        });
        ;// Initialization method
        function init(elem, id, opts) {
            var obj, posOptions, attr, config, title,

            // Setup element references
            docBody = $(document.body),

            // Use document body instead of document element if needed
            newTarget = elem[0] === document ? docBody : elem,

            // Grab metadata from element if plugin is present
            metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL,

            // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise
            metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL,

            // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method,
            html5 = elem.data(opts.metadata.name || 'qtipopts');

            // If we don't get an object returned attempt to parse it manualyl without parseJSON
            try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; } catch (e) { }

            // Merge in and sanitize metadata
            config = $.extend(TRUE, {}, QTIP.defaults, opts,
                typeof html5 === 'object' ? sanitizeOptions(html5) : NULL,
                sanitizeOptions(metadata5 || metadata));

            // Re-grab our positioning options now we've merged our metadata and set id to passed value
            posOptions = config.position;
            config.id = id;

            // Setup missing content if none is detected
            if ('boolean' === typeof config.content.text) {
                attr = elem.attr(config.content.attr);

                // Grab from supplied attribute if available
                if (config.content.attr !== FALSE && attr) { config.content.text = attr; }

                    // No valid content was found, abort render
                else { return FALSE; }
            }

            // Setup target options
            if (!posOptions.container.length) { posOptions.container = docBody; }
            if (posOptions.target === FALSE) { posOptions.target = newTarget; }
            if (config.show.target === FALSE) { config.show.target = newTarget; }
            if (config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); }
            if (config.hide.target === FALSE) { config.hide.target = newTarget; }
            if (config.position.viewport === TRUE) { config.position.viewport = posOptions.container; }

            // Ensure we only use a single container
            posOptions.container = posOptions.container.eq(0);

            // Convert position corner values into x and y strings
            posOptions.at = new CORNER(posOptions.at, TRUE);
            posOptions.my = new CORNER(posOptions.my);

            // Destroy previous tooltip if overwrite is enabled, or skip element if not
            if (elem.data(NAMESPACE)) {
                if (config.overwrite) {
                    elem.qtip('destroy', true);
                }
                else if (config.overwrite === FALSE) {
                    return FALSE;
                }
            }

            // Add has-qtip attribute
            elem.attr(ATTR_HAS, id);

            // Remove title attribute and store it if present
            if (config.suppress && (title = elem.attr('title'))) {
                // Final attr call fixes event delegatiom and IE default tooltip showing problem
                elem.removeAttr('title').attr(oldtitle, title).attr('title', '');
            }

            // Initialize the tooltip and add API reference
            obj = new QTip(elem, config, id, !!attr);
            elem.data(NAMESPACE, obj);

            return obj;
        }

        // jQuery $.fn extension method
        QTIP = $.fn.qtip = function (options, notation, newValue) {
            var command = ('' + options).toLowerCase(), // Parse command
                returned = NULL,
                args = $.makeArray(arguments).slice(1),
                event = args[args.length - 1],
                opts = this[0] ? $.data(this[0], NAMESPACE) : NULL;

            // Check for API request
            if ((!arguments.length && opts) || command === 'api') {
                return opts;
            }

                // Execute API command if present
            else if ('string' === typeof options) {
                this.each(function () {
                    var api = $.data(this, NAMESPACE);
                    if (!api) { return TRUE; }

                    // Cache the event if possible
                    if (event && event.timeStamp) { api.cache.event = event; }

                    // Check for specific API commands
                    if (notation && (command === 'option' || command === 'options')) {
                        if (newValue !== undefined || $.isPlainObject(notation)) {
                            api.set(notation, newValue);
                        }
                        else {
                            returned = api.get(notation);
                            return FALSE;
                        }
                    }

                        // Execute API command
                    else if (api[command]) {
                        api[command].apply(api, args);
                    }
                });

                return returned !== NULL ? returned : this;
            }

                // No API commands. validate provided options and setup qTips
            else if ('object' === typeof options || !arguments.length) {
                // Sanitize options first
                opts = sanitizeOptions($.extend(TRUE, {}, options));

                return this.each(function (i) {
                    var api, id;

                    // Find next available ID, or use custom ID if provided
                    id = $.isArray(opts.id) ? opts.id[i] : opts.id;
                    id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id;

                    // Initialize the qTip and re-grab newly sanitized options
                    api = init($(this), id, opts);
                    if (api === FALSE) { return TRUE; }
                    else { QTIP.api[id] = api; }

                    // Initialize plugins
                    $.each(PLUGINS, function () {
                        if (this.initialize === 'initialize') { this(api); }
                    });

                    // Assign initial pre-render events
                    api._assignInitialEvents(event);
                });
            }
        };

        // Expose class
        $.qtip = QTip;

        // Populated in render method
        QTIP.api = {};
        ; $.each({
            /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
            attr: function (attr, val) {
                if (this.length) {
                    var self = this[0],
                        title = 'title',
                        api = $.data(self, 'qtip');

                    if (attr === title && api && 'object' === typeof api && api.options.suppress) {
                        if (arguments.length < 2) {
                            return $.attr(self, oldtitle);
                        }

                        // If qTip is rendered and title was originally used as content, update it
                        if (api && api.options.content.attr === title && api.cache.attr) {
                            api.set('content.text', val);
                        }

                        // Use the regular attr method to set, then cache the result
                        return this.attr(oldtitle, val);
                    }
                }

                return $.fn['attr' + replaceSuffix].apply(this, arguments);
            },

            /* Allow clone to correctly retrieve cached title attributes */
            clone: function (keepData) {
                var titles = $([]), title = 'title',

                // Clone our element using the real clone method
                elems = $.fn['clone' + replaceSuffix].apply(this, arguments);

                // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false
                if (!keepData) {
                    elems.filter('[' + oldtitle + ']').attr('title', function () {
                        return $.attr(this, oldtitle);
                    })
                    .removeAttr(oldtitle);
                }

                return elems;
            }
        }, function (name, func) {
            if (!func || $.fn[name + replaceSuffix]) { return TRUE; }

            var old = $.fn[name + replaceSuffix] = $.fn[name];
            $.fn[name] = function () {
                return func.apply(this, arguments) || old.apply(this, arguments);
            };
        });

        /* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar).
         * This snippet is taken directly from jQuery UI source code found here:
         *     http://code.jquery.com/ui/jquery-ui-git.js
         */
        if (!$.ui) {
            $['cleanData' + replaceSuffix] = $.cleanData;
            $.cleanData = function (elems) {
                for (var i = 0, elem; (elem = $(elems[i])).length; i++) {
                    if (elem.attr(ATTR_HAS)) {
                        try { elem.triggerHandler('removeqtip'); }
                        catch (e) { }
                    }
                }
                $['cleanData' + replaceSuffix].apply(this, arguments);
            };
        }
        ;// qTip version
        QTIP.version = '2.2.1';

        // Base ID for all qTips
        QTIP.nextid = 0;

        // Inactive events array
        QTIP.inactiveEvents = INACTIVE_EVENTS;

        // Base z-index for all qTips
        QTIP.zindex = 15000;

        // Define configuration defaults
        QTIP.defaults = {
            prerender: FALSE,
            id: FALSE,
            overwrite: TRUE,
            suppress: TRUE,
            content: {
                text: TRUE,
                attr: 'title',
                title: FALSE,
                button: FALSE
            },
            position: {
                my: 'top left',
                at: 'bottom right',
                target: FALSE,
                container: FALSE,
                viewport: FALSE,
                adjust: {
                    x: 0, y: 0,
                    mouse: TRUE,
                    scroll: TRUE,
                    resize: TRUE,
                    method: 'flipinvert flipinvert'
                },
                effect: function (api, pos, viewport) {
                    $(this).animate(pos, {
                        duration: 200,
                        queue: FALSE
                    });
                }
            },
            show: {
                target: FALSE,
                event: 'mouseenter',
                effect: TRUE,
                delay: 90,
                solo: FALSE,
                ready: FALSE,
                autofocus: FALSE
            },
            hide: {
                target: FALSE,
                event: 'mouseleave',
                effect: TRUE,
                delay: 0,
                fixed: FALSE,
                inactive: FALSE,
                leave: 'window',
                distance: FALSE
            },
            style: {
                classes: '',
                widget: FALSE,
                width: FALSE,
                height: FALSE,
                def: TRUE
            },
            events: {
                render: NULL,
                move: NULL,
                show: NULL,
                hide: NULL,
                toggle: NULL,
                visible: NULL,
                hidden: NULL,
                focus: NULL,
                blur: NULL
            }
        };
        ;
    }));
}(window, document));;
(function ($) {
    $(document).ready(function () {
        initFormMessage($);
    });
})(jQuery);

var initFormMessage = function ($) {
    var $wFormMessage = $('#form-message-content');
    if ($wFormMessage != undefined) {

        setTimeout(function () {
            $wFormMessage.fadeOut('slow', function () {
                $wFormMessage.parent().parent().remove();
            });
        }, 7500);
    }
};;
/*
*   wConfirmClick
*   Sebastian Stefaniuk (Webitects.com)
*   Sept 2012, updated Feb 2015
*
*   02-26-2015  v1.1.0.0    Added off/on for single event binding; Plus, added namespace to click event
*   09-01-2012  v1.0.0.0    Initial version
*   
*   Simple jquery plugin that throws a custom confirmation message using jQuery dialog
*   when clicking on selected object. If confirmation is false, click is ignored.
*
*/
(function ($) {

    $.fn.wConfirmClick = function (opts) {
        var settings = $.extend({
            onConfirm: null,
            message: null,
            title: 'Warning'
        }, opts);

        var s4 = function () {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        }

        var guid = function () {
            var g = (s4() + s4() + "-" + s4() + "-4" + s4().substr(0, 3) + "-" + s4() + "-" + s4() + s4() + s4()).toLowerCase();
            return g;
        }

        this.each(function () {
            var $this = $(this);
            opts = opts === undefined ? {} : opts;

            $this.off('click.confirmClick').on('click.confirmClick', function (e) {
                var id = guid();

                var title = $this.data('title');
                var message = $this.data('message');

                opts.title = title !== undefined && title.length > 0 ? title : opts.title;
                opts.message = message !== undefined && message.length > 0 ? message : opts.message;

                $('body').find(':eq(0)').before('<div id="' + id + '">' + opts.message + '</div>');
                var $messageDiv = $('#' + id);

                if ($messageDiv.length > 0) {
                    $messageDiv.dialog({
                        title: opts.title == undefined ? 'Warning' : opts.title,
                        width: 400,
                        resizable: false,
                        modal: true,
                        buttons: {
                            'Proceed': function () {
                                $(this).dialog('close');
                                $messageDiv.remove();

                                if (opts.onConfirm == null) {
                                    if ($this.attr('href') != undefined)
                                        window.location = $this.attr('href');
                                    else if ($this.is("input[type='submit']"))
                                        $this.closest('form').submit();
                                }
                                else if(typeof opts.onConfirm === 'function') {
                                    var r = opts.onConfirm($this);
                                    if (r) {
                                        if ($this.attr('href') != undefined)
                                            window.location = $this.attr('href');
                                        else if ($this.is("input[type='submit']"))
                                            $this.closest('form').submit();
                                    }
                                }
                            },
                            Cancel: function () {
                                $(this).dialog('close');
                                $messageDiv.remove();
                            }
                        }
                    });

                    e.preventDefault();
                    return false;
                }
                else {
                    var c = confirm(message);
                    if (!c) {
                        e.preventDefault();
                        return false;
                    }
                }
            });
        });

        return this;
    };

})(jQuery);;
var initConfirmMessage = function ($, message, customClass) {
    if (customClass == 'error') ConfirmMessage.Error(message);
    else ConfirmMessage.Confirm(message);
};

var ConfirmMessage = (function ($) {

    var _wrapperClass = 'form-message';

    var _template = function (message, customClass) {
        var html = '';
        html += '<div class="' + _wrapperClass + '">';
        html += '   <div class="' + _wrapperClass + '-wrapper">';
        html += '       <div class="' + _wrapperClass + '-content ' + (customClass ? customClass : '') + '">' + message + '</div>';
        html += '   </div>';
        html += '</div>';
        return html;
    }

    var _showMessage = function (html) {
        $('body').find('.' + _wrapperClass).remove();

        var $msg = $(html);
        $('body').prepend($msg);
        setTimeout(function () {
            $msg.fadeOut('slow', function () {
                $msg.remove();
            });
        }, 4000);
    }

    var classes = {
        confirm: 'confirm',
        error: 'error',
        warn: 'warn'
    };

    return {
        Confirm: function (message) {
            var templ = _template(message, classes.confirm);
            _showMessage(templ);
        },
        Error: function (message) {
            var templ = _template(message, classes.error);
            _showMessage(templ);
        },
        Warn: function (message) {
            var templ = _template(message, classes.warn);
            _showMessage(templ);
        }
    }

})(jQuery);;
(function ($) {
    $(function () {

        /*
        *   Small class that holds a Highcharts chart reference
        *   and its related height/width values.
        */
        var highchartDimensions = (function () {
            var cls = function (chart) {
                var _chart = chart,
                    _height = chart.chartHeight,
                    _width = chart.chartWidth,
                    $parent = $(chart.container).parent(),
                    _resizeWidth = $parent.data('printresize') || 670;

                this.height = _height;
                this.width = _width;
                this.resizeWidth = _resizeWidth;
                this.chart = _chart;
                this.$parent = $parent;
            }

            return cls;
        })();

        // variable that holds all instances of highchartDimensions when printing
        var highchartDims;

        // Note: beforePrint gets called when triggering the browser's Print function.
        // afterPrint gets called when the Print function is cancelled/completed.
        
        var beforePrint = function () {
            // populate array of highchartDimensions
            highchartDims = [];
            if (window.Highcharts) {
                var i = 0, max = window.Highcharts.charts.length;
                for (i = 0; i < max; i += 1) {
                    var chart = window.Highcharts.charts[i];
                    if (chart !== undefined)
                        highchartDims.push(new highchartDimensions(window.Highcharts.charts[i]));
                }
            }

            var i = 0, max = highchartDims.length;
            for (i = 0; i < max; i += 1) {
                var chart = highchartDims[i].chart,
                    chartHeight = highchartDims[i].height;
                chart.setSize(highchartDims[i].resizeWidth, chartHeight, false);
            }
        };

        var afterPrint = function() {
            var i = 0, max = highchartDims.length;
            for (i = 0; i < max; i += 1) {
                var chart = highchartDims[i].chart,
                    chartHeight = highchartDims[i].height,
                    chartWidth = highchartDims[i].width;
                chart.setSize(chartWidth, chartHeight, false);
                chart.hasUserSize = null;    // This makes chart responsive
            }
        };

        if (window.matchMedia) {
            var mediaQueryList = window.matchMedia('print');
            mediaQueryList.addListener(function(mql) {
                if (mql.matches) {
                    beforePrint();
                } else {
                    afterPrint();
                }
            });
        }

        window.onbeforeprint = beforePrint;
        window.onafterprint = afterPrint;

    });
})(jQuery);;
(function ($) {
    "use strict";

    window.webitects = window.webitects || {};
    var w = window.webitects;

    // Wrap tables with a div
    $(function () {
        if (!$(".table-scroller").length)
            $("table").wrap("<div class='table-scroller' />")
        
        $(".table-scroller")
            .css("overflow", "auto")
            .css("-webkit-overflow-scrolling", "touch");
    });

})(jQuery);;
/*  webitects namespace
*
*   v 1.4.2.0   01-08-2014  Updated webitects.hashEvent
*   v 1.3.2.0   01-06-2013  Added webitects.imageScaler(); Added webitects.call(); Updated webitects.localStorage
*   v 1.1.0.0   01-03-2013  Updated webitects.templater
*   v 1.0.0.0   12-30-2013  Initial version
*
*/

window.webitects = window.webitects || {};
var w = window.webitects;

/* jQuery Storage API Plugin 1.5.0 https://github.com/julien-maurel/jQuery-Storage-API */
!function (e) { function t(t) { var r, n, i, o = arguments.length, s = window[t], a = arguments, u = a[1]; if (2 > o) throw Error("Minimum 2 arguments must be given"); if (e.isArray(u)) { n = {}; for (var g in u) { r = u[g]; try { n[r] = JSON.parse(s.getItem(r)) } catch (m) { n[r] = s.getItem(r) } } return n } if (2 != o) { try { n = JSON.parse(s.getItem(u)) } catch (m) { throw new ReferenceError(u + " is not defined in this storage") } for (var g = 2; o - 1 > g; g++) if (n = n[a[g]], void 0 === n) throw new ReferenceError([].slice.call(a, 1, g + 1).join(".") + " is not defined in this storage"); if (e.isArray(a[g])) { i = n, n = {}; for (var f in a[g]) n[a[g][f]] = i[a[g][f]]; return n } return n[a[g]] } try { return JSON.parse(s.getItem(u)) } catch (m) { return s.getItem(u) } } function n(t) { var r, n, i = arguments.length, o = window[t], s = arguments, a = s[1], u = s[2], g = {}; if (2 > i || !e.isPlainObject(a) && 3 > i) throw Error("Minimum 3 arguments must be given or second parameter must be an object"); if (e.isPlainObject(a)) { for (var m in a) r = a[m], e.isPlainObject(r) ? o.setItem(m, JSON.stringify(r)) : o.setItem(m, r); return a } if (3 == i) return "object" == typeof u ? o.setItem(a, JSON.stringify(u)) : o.setItem(a, u), u; try { n = o.getItem(a), null != n && (g = JSON.parse(n)) } catch (f) { } n = g; for (var m = 2; i - 2 > m; m++) r = s[m], n[r] && e.isPlainObject(n[r]) || (n[r] = {}), n = n[r]; return n[s[m]] = s[m + 1], o.setItem(a, JSON.stringify(g)), g } function i(t) { var r, n, i = arguments.length, o = window[t], s = arguments, a = s[1]; if (2 > i) throw Error("Minimum 2 arguments must be given"); if (e.isArray(a)) { for (var u in a) o.removeItem(a[u]); return !0 } if (2 == i) return o.removeItem(a), !0; try { r = n = JSON.parse(o.getItem(a)) } catch (g) { throw new ReferenceError(a + " is not defined in this storage") } for (var u = 2; i - 1 > u; u++) if (n = n[s[u]], void 0 === n) throw new ReferenceError([].slice.call(s, 1, u).join(".") + " is not defined in this storage"); if (e.isArray(s[u])) for (var m in s[u]) delete n[s[u][m]]; else delete n[s[u]]; return o.setItem(a, JSON.stringify(r)), !0 } function o(t, r) { var n = u(t); for (var o in n) i(t, n[o]); if (r) for (var o in e.namespaceStorages) g(o) } function s(r) { var n = arguments.length, i = arguments, o = (window[r], i[1]); if (1 == n) return 0 == u(r).length; if (e.isArray(o)) { for (var a = 0; a < o.length; a++) if (!s(r, o[a])) return !1; return !0 } try { var g = t.apply(this, arguments); e.isArray(i[n - 1]) || (g = { totest: g }); for (var a in g) if (!(e.isPlainObject(g[a]) && e.isEmptyObject(g[a]) || e.isArray(g[a]) && !g[a].length) && g[a]) return !1; return !0 } catch (m) { return !0 } } function a(r) { var n = arguments.length, i = arguments, o = (window[r], i[1]); if (2 > n) throw Error("Minimum 2 arguments must be given"); if (e.isArray(o)) { for (var s = 0; s < o.length; s++) if (!a(r, o[s])) return !1; return !0 } try { var u = t.apply(this, arguments); e.isArray(i[n - 1]) || (u = { totest: u }); for (var s in u) if (void 0 === u[s] || null === u[s]) return !1; return !0 } catch (g) { return !1 } } function u(r) { var n = arguments.length, i = window[r], o = arguments, s = (o[1], []), a = {}; if (a = n > 1 ? t.apply(this, o) : i, a._cookie) for (var u in e.cookie()) "" != u && s.push(u.replace(a._prefix, "")); else for (var g in a) s.push(g); return s } function g(t) { if (!t || "string" != typeof t) throw Error("First parameter must be a string"); window.localStorage.getItem(t) || window.localStorage.setItem(t, "{}"), window.sessionStorage.getItem(t) || window.sessionStorage.setItem(t, "{}"); var r = { localStorage: e.extend({}, e.localStorage, { _ns: t }), sessionStorage: e.extend({}, e.sessionStorage, { _ns: t }) }; return e.cookie && (window.cookieStorage.getItem(t) || window.cookieStorage.setItem(t, "{}"), r.cookieStorage = e.extend({}, e.cookieStorage, { _ns: t })), e.namespaceStorages[t] = r, r } var m = "ls_", f = "ss_", c = { _type: "", _ns: "", _callMethod: function (e, t) { var r = [this._type]; return this._ns && r.push(this._ns), [].push.apply(r, t), e.apply(this, r) }, get: function () { return this._callMethod(t, arguments) }, set: function () { var t = arguments.length, i = arguments, o = i[0]; if (1 > t || !e.isPlainObject(o) && 2 > t) throw Error("Minimum 2 arguments must be given or first parameter must be an object"); if (e.isPlainObject(o) && this._ns) { for (var s in o) n(this._type, this._ns, s, o[s]); return o } return r = this._callMethod(n, i), this._ns ? r[o] : r }, remove: function () { if (arguments.length < 1) throw Error("Minimum 1 argument must be given"); return this._callMethod(i, arguments) }, removeAll: function (e) { return this._ns ? (n(this._type, this._ns, {}), !0) : o(this._type, e) }, isEmpty: function () { return this._callMethod(s, arguments) }, isSet: function () { if (arguments.length < 1) throw Error("Minimum 1 argument must be given"); return this._callMethod(a, arguments) }, keys: function () { return this._callMethod(u, arguments) } }; if (e.cookie) { window.name || (window.name = Math.floor(1e8 * Math.random())); var l = { _cookie: !0, _prefix: "", _expires: null, setItem: function (t, r) { e.cookie(this._prefix + t, r, { expires: this._expires }) }, getItem: function (t) { return e.cookie(this._prefix + t) }, removeItem: function (t) { return e.removeCookie(this._prefix + t) }, clear: function () { for (var t in e.cookie()) "" != t && (!this._prefix && -1 === t.indexOf(m) && -1 === t.indexOf(f) || this._prefix && 0 === t.indexOf(this._prefix)) && e.removeCookie(t) }, setExpires: function (e) { return this._expires = e, this } }; window.localStorage || (window.localStorage = e.extend({}, l, { _prefix: m, _expires: 3650 }), window.sessionStorage = e.extend({}, l, { _prefix: f + window.name + "_" })), window.cookieStorage = e.extend({}, l), e.cookieStorage = e.extend({}, c, { _type: "cookieStorage", setExpires: function (e) { return window.cookieStorage.setExpires(e), this } }) } e.initNamespaceStorage = function (e) { return g(e) }, e.localStorage = e.extend({}, c, { _type: "localStorage" }), e.sessionStorage = e.extend({}, c, { _type: "sessionStorage" }), e.namespaceStorages = {}, e.removeAllStorages = function (t) { e.localStorage.removeAll(t), e.sessionStorage.removeAll(t), e.cookieStorage && e.cookieStorage.removeAll(t), t || (e.namespaceStorages = {}) } } (jQuery);

// jquery.ba-hashchange.min.js: http://benalman.com/projects/jquery-hashchange-plugin/
(function (e, t, n) { "$:nomunge"; function c(e) { e = e || location.href; return "#" + e.replace(/^[^#]*#?(.*)$/, "$1") } var r = navigator.userAgent.toLowerCase(); var i = /msie/.test(r); var s = "hashchange", o = document, u, a = e.event.special, f = o.documentMode, l = "on" + s in t && (f === n || f > 7); e.fn[s] = function (e) { return e ? this.bind(s, e) : this.trigger(s) }; e.fn[s].delay = 50; a[s] = e.extend(a[s], { setup: function () { if (l) { return false } e(u.start) }, teardown: function () { if (l) { return false } e(u.stop) } }); u = function () { function d() { var n = c(), r = p(a); if (n !== a) { h(a = n, r); e(t).trigger(s) } else if (r !== a) { location.href = location.href.replace(/#.*/, "") + r } u = setTimeout(d, e.fn[s].delay) } var r = {}, u, a = c(), f = function (e) { return e }, h = f, p = f; r.start = function () { u || d() }; r.stop = function () { u && clearTimeout(u); u = n }; i && !l && function () { var t, n; r.start = function () { if (!t) { n = e.fn[s].src; n = n && n + c(); t = e('<iframe tabindex="-1" title="empty"/>').hide().one("load", function () { n || h(c()); d() }).attr("src", n || "javascript:0").insertAfter("body")[0].contentWindow; o.onpropertychange = function () { try { if (event.propertyName === "title") { t.document.title = o.title } } catch (e) { } } } }; r.stop = f; p = function () { return c(t.location.href) }; h = function (n, r) { var i = t.document, u = e.fn[s].domain; if (n !== r) { i.title = o.title; i.open(); u && i.write('<script>document.domain="' + u + '"</script>'); i.close(); t.location.hash = n } } } (); return r } () })(jQuery, this);
http://localhost:60548/jurisdictional-boundaries
/* jQuery.cookie https://github.com/carhartl/jquery-cookie */
(function (e) { if (typeof define === "function" && define.amd) { define(["jquery"], e) } else { e(jQuery) } })(function (e) { function n(e) { return e } function r(e) { return decodeURIComponent(e.replace(t, " ")) } function i(e) { if (e.indexOf('"') === 0) { e = e.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\") } try { return s.json ? JSON.parse(e) : e } catch (t) { } } var t = /\+/g; var s = e.cookie = function (t, o, u) { if (o !== undefined) { u = e.extend({}, s.defaults, u); if (typeof u.expires === "number") { var a = u.expires, f = u.expires = new Date; f.setDate(f.getDate() + a) } o = s.json ? JSON.stringify(o) : String(o); return document.cookie = [s.raw ? t : encodeURIComponent(t), "=", s.raw ? o : encodeURIComponent(o), u.expires ? "; expires=" + u.expires.toUTCString() : "", u.path ? "; path=" + u.path : "", u.domain ? "; domain=" + u.domain : "", u.secure ? "; secure" : ""].join("") } var l = s.raw ? n : r; var c = document.cookie.split("; "); var h = t ? undefined : {}; for (var p = 0, d = c.length; p < d; p++) { var v = c[p].split("="); var m = l(v.shift()); var g = l(v.join("=")); if (t && t === m) { h = i(g); break } if (!t) { h[m] = i(g) } } return h }; s.defaults = {}; e.removeCookie = function (t, n) { if (e.cookie(t) !== undefined) { e.cookie(t, "", e.extend({}, n, { expires: -1 })); return true } return false } })

/* DateFormat */
var dateFormat = function () { var e = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, t = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, a = /[^-+\dA-Z]/g, m = function (e, t) { for (e += "", t = t || 2; e.length < t;) e = "0" + e; return e }; return function (d, y, n) { var r = dateFormat; if (1 != arguments.length || "[object String]" != Object.prototype.toString.call(d) || /\d/.test(d) || (y = d, d = void 0), d = d ? new Date(d) : new Date, isNaN(d)) throw SyntaxError("invalid date"); y = (r.masks[y] || y || r.masks["default"]) + "", "UTC:" == y.slice(0, 4) && (y = y.slice(4), n = !0); var s = n ? "getUTC" : "get", i = d[s + "Date"](), o = d[s + "Day"](), u = d[s + "Month"](), M = d[s + "FullYear"](), l = d[s + "Hours"](), T = d[s + "Minutes"](), h = d[s + "Seconds"](), c = d[s + "Milliseconds"](), g = n ? 0 : d.getTimezoneOffset(), D = { d: i, dd: m(i), ddd: r.i18n.dayNames[o], dddd: r.i18n.dayNames[o + 7], m: u + 1, mm: m(u + 1), mmm: r.i18n.monthNames[u], mmmm: r.i18n.monthNames[u + 12], yy: (M + "").slice(2), yyyy: M, h: l % 12 || 12, hh: m(l % 12 || 12), H: l, HH: m(l), M: T, MM: m(T), s: h, ss: m(h), l: m(c, 3), L: m(c > 99 ? Math.round(c / 10) : c), t: 12 > l ? "a" : "p", tt: 12 > l ? "am" : "pm", T: 12 > l ? "A" : "P", TT: 12 > l ? "AM" : "PM", Z: n ? "UTC" : ((d + "").match(t) || [""]).pop().replace(a, ""), o: (g > 0 ? "-" : "+") + m(100 * Math.floor(Math.abs(g) / 60) + Math.abs(g) % 60, 4), S: ["th", "st", "nd", "rd"][i % 10 > 3 ? 0 : (i % 100 - i % 10 != 10) * i % 10] }; return y.replace(e, function (e) { return e in D ? D[e] : e.slice(1, e.length - 1) }) } }(); dateFormat.masks = { "default": "ddd mmm dd yyyy HH:MM:ss", shortDate: "m/d/yy", mediumDate: "mmm d, yyyy", longDate: "mmmm d, yyyy", fullDate: "dddd, mmmm d, yyyy", shortTime: "h:MM TT", mediumTime: "h:MM:ss TT", longTime: "h:MM:ss TT Z", isoDate: "yyyy-mm-dd", isoTime: "HH:MM:ss", isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" }, dateFormat.i18n = { dayNames: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] }, Date.prototype.format = function (e, t) { return dateFormat(this, e, t) }

/* w.array.list */
Array.prototype.add = function (e) { this.push(e) }; Array.prototype.addRange = function (e) { var t = this; if (e !== undefined) { e.foreach(function (e) { t.add(e) }) } return t.clone() }; Array.prototype.any = function () { return this.length > 0 }; Array.prototype.clone = function () { var e = []; this.foreach(function (t) { e.add(t) }); return e }; Array.prototype.distinct = function (e) { var t = []; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function") { if (t.findIndex(function (t) { return t === e(n[r]) }) == -1) t.add(e(n[r])) } else if (typeof e === "undefined") { if (t.findIndex(function (e) { return e === n[r] }) == -1) t.add(n[r]) } else if (n[r] === e) { if (t.findIndex(function (e) { return e === n[r] }) == -1) t.add(n[r]) } } return t }; Array.prototype.find = function (e) { var t; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function" && e(n[r]) || n[r] === e) { t = n[r]; break } } return t }; Array.prototype.findAll = function (e) { var t = []; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function" && e(n[r]) || n[r] === e) t.add(n[r]) } return t }; Array.prototype.findIndex = function (e) { var t = -1; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function" && e(n[r]) || n[r] === e) { t = r; break } } return t }; Array.prototype.first = function () { var e; if (this.length > 0) e = this[0]; return e }; Array.prototype.foreach = function (e) { for (var t = 0; t < this.length; t++) { if (typeof e === "function") { var n = e(this[t], t); if (n) break } } }; Array.prototype.insertAt = function (e, t) { if (t >= 0 && t <= this.length) { var n = []; var r = this; if (t === 0) { n.add(e); n.addRange(r) } else if (t === r.length) { n = r.clone(); n.add(e) } else { n.addRange(r.take(t, 0)); n.add(e); n.addRange(r.take(r.length - t, t)) } for (var i = 0; i < n.length; i++) r[i] = n[i] } else throw "Array.prototype.insertAt(): Cannot insert at " + t + ". Array length = " + this.length }; Array.prototype.last = function () { var e; if (this.length > 0) e = this[this.length - 1]; return e }; Array.prototype.longCount = function (e) { var t = 0; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function" && e(n[r]) || n[r] === e) t++ } return t }; Array.prototype.max = function (e) { var t; var n = this; var r = Number.MIN_VALUE; for (var i = 0; i < n.length; i++) { if (typeof e === "function" && e(n[i]) > r) r = e(n[i]); else if (typeof e === "undefined" && n[i] > r) r = n[i] } return r }; Array.prototype.min = function (e) { var t; var n = this; var r = Number.MAX_VALUE; for (var i = 0; i < n.length; i++) { if (typeof e === "function" && e(n[i]) < r) r = e(n[i]); else if (typeof e === "undefined" && n[i] < r) r = n[i] } return r }; Array.prototype.orderBy = function (e) { var t = Array.Utility.getCompare(this, arguments); this.sort(t); return this.clone() }; Array.prototype.removeAll = function (e) { var t = []; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function" && e(n[r]) || n[r] === e) { } else t.add(n[r]) } return t }; Array.prototype.removeAt = function (e) { var t = []; var n = this; for (var r = 0; r < n.length; r++) if (r !== e) t.push(n[r]); return t }; Array.prototype.select = function (e) { var t = []; var n = this; for (var r = 0; r < n.length; r++) { if (typeof e === "function") { t.add(e(n[r])) } else if (typeof e === "string") { t.add(n[r][e]) } } return t }; Array.prototype.take = function (e, t) { var n = []; var r = this; t = t !== undefined ? t : 0; for (var i = t; i < e + t; i++) { if (i < r.length) n.add(r[i]); else break } return n }; Array.prototype.type = undefined; Array.Utility = { getCompare: function () { var e = [], t = [], n = []; var r; var i = arguments.length > 1 ? arguments[1] : []; var s = arguments[0]; for (var o = 0; o < i.length; o++) { var u = i[o].del; var a = i[o].sort !== undefined ? i[o].sort : Array.SortType.ASC; var f; if (typeof u === "function") { f = function (e, t, n, r) { var i = Array.Utility.getSortValue(n(e)); var s = Array.Utility.getSortValue(n(t)); if (i < s) return r === Array.SortType.ASC ? -1 : 1; if (i > s) return r === Array.SortType.ASC ? 1 : -1; return 0 } } else if (typeof u === "string") { f = function (e, t, n, r) { var i = Array.Utility.getSortValue(e[n]); var s = Array.Utility.getSortValue(t[n]); if (i < s) return r === Array.SortType.ASC ? -1 : 1; if (i > s) return r === Array.SortType.ASC ? 1 : -1; return 0 } } if (f !== undefined) { e.push(f); t.push(u); n.push(a) } } if (e.length > 0) { r = function (r, i) { var s = 0; for (var o = 0; o < e.length; o++) { s = e[o](r, i, t[o], n[o]); if (s !== 0) break } return s } } else { var a; if (i.length === 1) { if (typeof i[0] === "number") a = i[0]; else a = Array.SortType.ASC } else a = Array.SortType.ASC; if (a === Array.SortType.DESC) { r = function (e, t) { if (e > t) return -1; if (e < t) return 1; return 0 } } else { r = function (e, t) { if (e < t) return -1; if (e > t) return 1; return 0 } } } return r }, getSortValue: function (e) { if (typeof e === "number") return parseFloat(e); else if (typeof e === "string") return e.toString(); return e } }; Array.SortType = { ASC: 1, DESC: 2 }

webitects.call = function (method, args) {
    if (typeof method === 'function') {
        var _args = [];
        if (arguments.length > 1)
            for (var i = 1; i < arguments.length; i++)
                _args.push(arguments[i]);
        return method.apply(this, _args || []);
    }
}

/*  utility
*
*   v 1.0.0.0   12-30-2013  Initial version.
*/
webitects.utility = (function ($) {

    return {
        s4: function () {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        },

        guid: function () {
            var g = (webitects.utility.s4() + webitects.utility.s4() + "-" + webitects.utility.s4() + "-4" + webitects.utility.s4().substr(0, 3) + "-" + webitects.utility.s4() + "-" + webitects.utility.s4() + webitects.utility.s4() + webitects.utility.s4()).toLowerCase();
            return g;
        },

        stripHtmlTags: function (html) {
            if (html.length > 0)
                html = html.replace(/<([^>]*)>/g, '');
            return html;
        },

        isIE: function () {
            var nav = navigator.userAgent.toLowerCase();
            return /msie/.test(nav);
        },

        isTouchDevice: function () {
            if (webitects.utility.isIE()) {
                if (navigator.msMaxTouchPoints !== undefined)
                    return navigator.msMaxTouchPoints > 0;
                else
                    return !!('ontouchstart' in window) || !!('onmsgesturechange' in window);
            }
            else
                return !!('ontouchstart' in window) || !!('onmsgesturechange' in window);
        },

        jsonDateToDate: function (str) {
            return new Date(parseInt(str.substring(6)));
        },

        nl2br: function (str) {
            var breakTag = '<br />';
            return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
        },

        toSlug: function (str) {
            if (str.length > 0) {
                str = str.length > 80 ? str.substring(0, 80) : str;
                str = str.toLowerCase();
                str = str.replace(/(?:[^a-z0-9 ])/g, '');
                str = str.replace(/(\s+)/g, '-');
                return str;
            }
            return str;
        }
    }

})(jQuery);

/*  console
*   Sebastian Stefaniuk - webitects.com
*
*   v 1.0.0.0   01-10-2014  Initial version
*/
webitects.console = (function ($, w) {

    (function (e, t) { if (typeof exports === "object") { module.exports = t() } else if (typeof define === "function" && define.amd) { define(t) } else { e.printStackTrace = t() } })(this, function () { function e(t) { t = t || { guess: true }; var n = t.e || null, r = !!t.guess; var i = new e.implementation, s = i.run(n); return r ? i.guessAnonymousFunctions(s) : s } e.implementation = function () { }; e.implementation.prototype = { run: function (e, t) { e = e || this.createException(); t = t || this.mode(e); if (t === "other") { return this.other(arguments.callee) } else { return this[t](e) } }, createException: function () { try { this.undef() } catch (e) { return e } }, mode: function (e) { if (e["arguments"] && e.stack) { return "chrome" } else if (e.stack && e.sourceURL) { return "safari" } else if (e.stack && e.number) { return "ie" } else if (e.stack && e.fileName) { return "firefox" } else if (e.message && e["opera#sourceloc"]) { if (!e.stacktrace) { return "opera9" } if (e.message.indexOf("\n") > -1 && e.message.split("\n").length > e.stacktrace.split("\n").length) { return "opera9" } return "opera10a" } else if (e.message && e.stack && e.stacktrace) { if (e.stacktrace.indexOf("called from line") < 0) { return "opera10b" } return "opera11" } else if (e.stack && !e.fileName) { return "chrome" } return "other" }, instrumentFunction: function (t, n, r) { t = t || window; var i = t[n]; t[n] = function () { r.call(this, e().slice(4)); return t[n]._instrumented.apply(this, arguments) }; t[n]._instrumented = i }, deinstrumentFunction: function (e, t) { if (e[t].constructor === Function && e[t]._instrumented && e[t]._instrumented.constructor === Function) { e[t] = e[t]._instrumented } }, chrome: function (e) { return (e.stack + "\n").replace(/^[\s\S]+?\s+at\s+/, " at ").replace(/^\s+(at eval )?at\s+/gm, "").replace(/^([^\(]+?)([\n$])/gm, "{anonymous}() ($1)$2").replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, "{anonymous}() ($1)").replace(/^(.+) \((.+)\)$/gm, "$1@$2").split("\n").slice(0, -1) }, safari: function (e) { return e.stack.replace(/\[native code\]\n/m, "").replace(/^(?=\w+Error\:).*$\n/m, "").replace(/^@/gm, "{anonymous}()@").split("\n") }, ie: function (e) { return e.stack.replace(/^\s*at\s+(.*)$/gm, "$1").replace(/^Anonymous function\s+/gm, "{anonymous}() ").replace(/^(.+)\s+\((.+)\)$/gm, "$1@$2").split("\n").slice(1) }, firefox: function (e) { return e.stack.replace(/(?:\n@:0)?\s+$/m, "").replace(/^(?:\((\S*)\))?@/gm, "{anonymous}($1)@").split("\n") }, opera11: function (e) { var t = "{anonymous}", n = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/; var r = e.stacktrace.split("\n"), i = []; for (var s = 0, o = r.length; s < o; s += 2) { var u = n.exec(r[s]); if (u) { var a = u[4] + ":" + u[1] + ":" + u[2]; var f = u[3] || "global code"; f = f.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, t); i.push(f + "@" + a + " -- " + r[s + 1].replace(/^\s+/, "")) } } return i }, opera10b: function (e) { var t = /^(.*)@(.+):(\d+)$/; var n = e.stacktrace.split("\n"), r = []; for (var i = 0, s = n.length; i < s; i++) { var o = t.exec(n[i]); if (o) { var u = o[1] ? o[1] + "()" : "global code"; r.push(u + "@" + o[2] + ":" + o[3]) } } return r }, opera10a: function (e) { var t = "{anonymous}", n = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; var r = e.stacktrace.split("\n"), i = []; for (var s = 0, o = r.length; s < o; s += 2) { var u = n.exec(r[s]); if (u) { var a = u[3] || t; i.push(a + "()@" + u[2] + ":" + u[1] + " -- " + r[s + 1].replace(/^\s+/, "")) } } return i }, opera9: function (e) { var t = "{anonymous}", n = /Line (\d+).*script (?:in )?(\S+)/i; var r = e.message.split("\n"), i = []; for (var s = 2, o = r.length; s < o; s += 2) { var u = n.exec(r[s]); if (u) { i.push(t + "()@" + u[2] + ":" + u[1] + " -- " + r[s + 1].replace(/^\s+/, "")) } } return i }, other: function (e) { var t = "{anonymous}", n = /function\s*([\w\-$]+)?\s*\(/i, r = [], i, s, o = 10; while (e && e["arguments"] && r.length < o) { i = n.test(e.toString()) ? RegExp.$1 || t : t; s = Array.prototype.slice.call(e["arguments"] || []); r[r.length] = i + "(" + this.stringifyArguments(s) + ")"; e = e.caller } return r }, stringifyArguments: function (e) { var t = []; var n = Array.prototype.slice; for (var r = 0; r < e.length; ++r) { var i = e[r]; if (i === undefined) { t[r] = "undefined" } else if (i === null) { t[r] = "null" } else if (i.constructor) { if (i.constructor === Array) { if (i.length < 3) { t[r] = "[" + this.stringifyArguments(i) + "]" } else { t[r] = "[" + this.stringifyArguments(n.call(i, 0, 1)) + "..." + this.stringifyArguments(n.call(i, -1)) + "]" } } else if (i.constructor === Object) { t[r] = "#object" } else if (i.constructor === Function) { t[r] = "#function" } else if (i.constructor === String) { t[r] = '"' + i + '"' } else if (i.constructor === Number) { t[r] = i } } } return t.join(",") }, sourceCache: {}, ajax: function (e) { var t = this.createXMLHTTPObject(); if (t) { try { t.open("GET", e, false); t.send(null); return t.responseText } catch (n) { } } return "" }, createXMLHTTPObject: function () { var e, t = [function () { return new XMLHttpRequest }, function () { return new ActiveXObject("Msxml2.XMLHTTP") }, function () { return new ActiveXObject("Msxml3.XMLHTTP") }, function () { return new ActiveXObject("Microsoft.XMLHTTP") } ]; for (var n = 0; n < t.length; n++) { try { e = t[n](); this.createXMLHTTPObject = t[n]; return e } catch (r) { } } }, isSameDomain: function (e) { return typeof location !== "undefined" && e.indexOf(location.hostname) !== -1 }, getSource: function (e) { if (!(e in this.sourceCache)) { this.sourceCache[e] = this.ajax(e).split("\n") } return this.sourceCache[e] }, guessAnonymousFunctions: function (e) { for (var t = 0; t < e.length; ++t) { var n = /\{anonymous\}\(.*\)@(.*)/, r = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/, i = e[t], s = n.exec(i); if (s) { var o = r.exec(s[1]); if (o) { var u = o[1], a = o[2], f = o[3] || 0; if (u && this.isSameDomain(u) && a) { var l = this.guessAnonymousFunction(u, a, f); e[t] = i.replace("{anonymous}", l) } } } } return e }, guessAnonymousFunction: function (e, t, n) { var r; try { r = this.findFunctionName(this.getSource(e), t) } catch (i) { r = "getSource failed with url: " + e + ", exception: " + i.toString() } return r }, findFunctionName: function (e, t) { var n = /function\s+([^(]*?)\s*\(([^)]*)\)/; var r = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/; var i = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/; var s = "", o, u = Math.min(t, 20), a, f; for (var l = 0; l < u; ++l) { o = e[t - l - 1]; f = o.indexOf("//"); if (f >= 0) { o = o.substr(0, f) } if (o) { s = o + s; a = r.exec(s); if (a && a[1]) { return a[1] } a = n.exec(s); if (a && a[1]) { return a[1] } a = i.exec(s); if (a && a[1]) { return a[1] } } } return "(?)" } }; return e })

    var $console,
        $menu,
        $pane,
        $ribbon,
        $trigger;

    var vars = {
        bottomMargin: 0,
        classes: {
            itemError: 'error',
            open: 'open'
        },
        console: {
            height: 250,
            maxHeight: 0.5
        },
        enabled: false,
        oldError: function () { },
        oldLog: function () { },
        options: {},
        shown: true,
        templates: {
            console: {},
            line: {},
            lineItem: {}
        },
        windowH: 0,
        windowW: 0
    }

    var fn = {
        enable: function (options) {
            var defaults = {
                shown: true
            }

            options = $.extend(defaults, options);
            vars.options = options;
            vars.shown = options.shown;

            vars.templates.console = new w.templater({ html:
                function () {
                    var html = '';
                    html += '<div class="webitects-console">';
                    html += '    <div class="console-menu">';
                    html += '        <div class="console-ribbon"><a class="console-trigger ' + (vars.shown ? vars.classes.open : '') + '">w.console</a></div>';
                    html += '    </div>';
                    html += '    <div class="console-pane"></div>';
                    html += '</div>';
                    return html;
                }
            });

            vars.templates.line = new w.templater({ html:
                function () {
                    var html = '';
                    html += '<div class="console-line">';
                    html += '    <ul class="console-items">{{items}}</ul>';
                    html += '    <div class="clear"></div>';
                    html += '</div>';
                    return html;
                }
            });

            vars.templates.lineItem = new w.templater({ html: '<li>{{content}}</li>' });
            vars.bottomMargin = $('body').css('margin-bottom');

            $console = $(vars.templates.console.html());
            $('body').prepend($console);

            $menu = $console.find('.console-menu');
            $pane = $console.find('.console-pane');
            $ribbon = $console.find('.console-ribbon');
            $trigger = $console.find('.console-trigger');

            $(window).resize(fn.onResize);
            $(window).trigger('resize');
            window.onerror = fn.onError;

            vars.oldLog = console.log;
            vars.oldError = console.error;

            console.log = function (args) {
                var _args = [];
                if (arguments.length > 0)
                    for (var i = 0; i < arguments.length; i++)
                        _args.push(arguments[i]);
                fn.displayMessage.apply(this, _args || []);
            }

            console.error = function (args) {
                var _args = [];
                if (arguments.length > 0)
                    for (var i = 0; i < arguments.length; i++)
                        _args.push(arguments[i]);
                fn.displayMessage.apply(this, _args || []);
                $pane.find('.console-item').first().addClass(vars.classes.itemError);
            }

            $trigger.bind(w.events.tap, function (e) {
                if (fn.isOpen()) fn.hideConsole();
                else fn.showConsole();
                e.preventDefault();
                return false;
            });
        },

        disable: function () {
            $(window).unbind('resize');
        },

        displayMessage: function (args) {
            var html = '';
            for (var i = 0; i < arguments.length; i++) {
                var content = fn.getDisplay(arguments[i]);
                vars.templates.lineItem.fields.content = content;
                html += vars.templates.lineItem.html();
                vars.templates.lineItem.reset();
            }

            vars.templates.line.fields.items = html;
            $pane.prepend($('<span class="console-item">' + vars.templates.line.html() + '<span>'));
        },

        getDisplay: function (item) {
            var display;
            if (typeof item === 'object') {
                display = fn.objectMarkup({ Object: item });
            }
            else if (typeof item === 'undefined') {
                display = '<pre class="undefined">undefined</pre>';
            }
            else {
                display = '<pre>' + item.toString() + '</pre>';
            }
            return display;
        },

        hideConsole: function () {
            $console.animate({ 'top': (vars.windowH - $ribbon.outerHeight(true)) + 'px' }, 125);
            $('body').animate({ 'margin-bottom': vars.bottomMargin }, 125);
            $trigger.removeClass(vars.classes.open);
        },

        isOpen: function () {
            return $trigger.attr('class').indexOf(vars.classes.open) > -1;
        },

        objectMarkup: function (obj) {
            var keys = Object.keys(obj);
            var html = '<ul class="console-object">';
            for (var i = 0; i < keys.length; i++) {
                html += '<li>';
                if (typeof obj[keys[i]] === 'object') {
                    html += '<pre><strong>' + keys[i] + '</strong>:</pre>';
                    html += fn.objectMarkup(obj[keys[i]]);
                }
                else
                    html += '<pre><strong>' + keys[i] + '</strong>: ' + obj[keys[i]] + '</pre>';
                html += '</li>';
            }
            html += '</ul>';
            return html;
        },

        showConsole: function () {
            $console.animate({ 'top': (vars.windowH - $console.outerHeight(true)) + 'px' }, 275);
            $('body').animate({ 'margin-bottom': $console.outerHeight(true) + 'px' }, 275);
            $trigger.addClass(vars.classes.open);
        },

        onError: function (e) {
            var _args = [];
            if (arguments.length > 0)
                for (var i = 0; i < arguments.length; i++)
                    _args.push(arguments[i]);
            fn.displayMessage.apply(this, _args || []);
            $pane.find('.console-item').first().addClass(vars.classes.itemError);
        },

        onResize: function (e) {
            vars.windowH = window.innerHeight ? window.innerHeight : $(window).height();
            vars.windowW = window.innerWidth ? window.innerWidth : $(window).width();

            var paneHeight = vars.console.height;
            paneHeight = paneHeight > (vars.console.maxHeight * vars.windowH) ? vars.console.maxHeight * vars.windowH : paneHeight;

            $console.css('width', vars.windowW + 'px');
            $pane.css('height', parseInt(paneHeight) + 'px');

            if (fn.isOpen()) {
                $console.css('top', (vars.windowH - $console.outerHeight(true)) + 'px');
                $('body').css('margin-bottom', $console.outerHeight(true) + 'px');
            }
            else {
                $console.css('top', (vars.windowH - $ribbon.outerHeight(true)) + 'px');
                $('body').css('margin-bottom', vars.bottomMargin);
            }
        }
    }

    return {
        enable: function (options) {
            fn.enable(options);
        },

        disable: function () {
            fn.disable();
        }
    }

})(jQuery, webitects);

/*  connection
*   Sebastian Stefaniuk - Webitects.com
*
*   v 1.0.1.0   04-07-2014 - Updated connection check to not use image.
*   v 1.0.0.0   12-30-2013 - Initial version in the webitects namespace.
*/
webitects.connection = (function ($) {

    var _isIE = function () {
        var nav = navigator.userAgent.toLowerCase();
        return /msie/.test(nav);
    }

    var vars = {
        checking: false,
        connected: true,
        evtPrefix: 'w.connection.',
        img: undefined,
        imgSrc: '/ping',
        isIE: _isIE(),
        time: 60 * 1000
    };

    var fn = {
        init: function () {
            // flip it off and on to trigger event
            fn.triggerEvent(false);
            fn.triggerEvent(true);

            fn.checkConnection();

            setInterval(function () {
                fn.checkConnection();
            }, vars.time);
        },

        checkConnection: function () {
            if (!vars.checking) {
                vars.checking = true;

                $.ajax({
                    url: '/ping',
                    type: 'Head',
                    success: function (obj) {
                        fn.triggerEvent(true);
                    },
                    error: function (status) {
                        fn.triggerEvent(false);
                    },
                    complete: function () {
                        vars.checking = false;
                    }
                });
            }
        },

        triggerEvent: function (connected) {
            var e = null;
            var eventName = connected ? webitects.connection.events.ONLINE : webitects.connection.events.OFFLINE;

            if (vars.connected !== connected) {
                $(document).trigger(eventName);
                vars.connected = connected;
            }

            vars.checking = false;
        },

        isOnline: function () {
            return vars.connected;
        }
    };

    return {
        init: function () {
            fn.init();
        },

        online: function () {
            return fn.isOnline();
        },

        events: {
            ONLINE: vars.evtPrefix + 'online',
            OFFLINE: vars.evtPrefix + 'offline'
        }
    }

})(jQuery);

window.onload = function () {
    webitects.connection.init();
}

/*  dataLoader
*   Sebastian Stefaniuk - Webitects.com
*
*   v 3.0.0.0   12-30-2013 - Major update: part of global webitects (webitects) namespace.
*   v 2.0.0.1   12-18-2013 - Bug fix when forcing to reload object. Caused stack overflow.
*   v 2.0.0.0   12-13-2013 - Major update. Incompatible with earlier version.
*   v 1.2.1.0   12-03-2013 - Added updateData() method.
*   v 1.1.0.0   11-06-2013
*
*   Loads an external source and, if available, stores the result in local storage.
*
*   Dependencies:
*   jquery.js 
*   jquery.storageapi.js (https://github.com/julien-maurel/jQuery-Storage-API)
*
*   var dataLoader = new DataLoader(url, options);
*
*   Param               Type                Default                Description
*   ------------------------------------------------------------------------------------------------------------------------------------
*   options.url              string              undefined              Location of resource
*   options.data             json object         {}                     Parameters to be sent to resource url
*   options.type             string              'Get'                  Type of resource method
*   options.useLocal         bool                true                   Indicates whether to use local storage
*   options.events.loaded    func                undefined              Called after resource is loaded
*   options.events.error     func                undefined              Called after error during ajax load
*   options.events.loading   func                undefined              Called when loading of resource starts
*   options.events.complete  func                undefined              Called when ajax completed        
*
*
*   Methods
*   ------------------------------------------------------------------------------------------------------------------------------------
*   dataLoader.bind(event, func)        Bind this loader instance to an event.
*   dataLoader.load(maxAge)             Loads the resource. Param maxAge is uint, indicates number of seconds the resource can be old.
*                                       If age is older than maxAge, resource will be reloaded. Otherwise it will be read from storage.
*   dataLoader.clearItem()              Clears item saved to local storage.
*   dataLoader.age                 Gets age of item saved in local storage in seconds.
*   dataLoader.getItem()                Gets items saved to local storage.
*   dataLoader.updateData(param, value) Updates data item sent with ajax call.
*
*/
webitects.dataLoader = (function ($) {

    var $document = $(document),
        connection = webitects.connection,
        storage = $.localStorage;

    var cls = function (options) {
        var defaults = {
            data: {},
            events: {
                complete: function () { },
                error: function (errors) { if (typeof console !== 'undefined') console.error(errors); },
                loaded: function (obj) { },
                loading: function () { }
            },
            key: undefined,
            retry: {
                enabled: false,
                timeout: 5000
            },
            type: 'Get',
            url: undefined,
            useLocal: true
        };

        options = $.extend(defaults, options);

        var _loading = false;
        var _online;
        var _data = options.data;
        var _type = options.type;
        var _useLocal = options.useLocal;
        var _key = options.key !== undefined ? options.key : options.url;
        var _url = options.url;
        var _this = this;

        var $this = $(this);

        var isOnline = function () {
            _online = connection.online();
            return _online;
        }

        var getAgeFromLocalStorage = function (key) {
            var age;

            if (storage.isSet(key)) {
                var obj = storage.get(key);
                storeItem = webitects.dataLoader.StorageObject.parse(obj);
                age = storeItem.age;
            }

            return age;
        }

        var getFromLocalStorage = function (key) {
            var value;

            if (storage.isSet(key)) {
                var obj = storage.get(key);
                storeItem = webitects.dataLoader.StorageObject.parse(obj);
                value = storeItem;
            }

            return value;
        }

        var getRawFromLocalStorage = function (key) {
            var obj;

            if (storage.isSet(key)) {
                obj = storage.get(key);
            }

            return obj;
        }

        var saveToLocalStorage = function (args) {
            var _defaults = {
                key: undefined,
                maxAge: undefined,
                overwrite: true,
                updated: undefined,
                value: {}
            };

            args = $.extend(_defaults, args);
            args.key = args.key === undefined ? options.url : args.key;

            var storeItem = new webitects.dataLoader.StorageObject({
                maxAge: args.maxAge,
                updated: args.updated,
                value: args.value
            });

            var storeObj = storeItem.json;

            try {
                if (args.overwrite) {
                    storage.remove(args.key);
                    storage.set(args.key, storeObj);
                }
                else {
                    if (storage.isEmpty(args.key))
                        storage.set(args.key, storeObj);
                }
            }
            catch (err) {
                // if error occurs trying to save the object to local,
                // revert to not use local storage. QuotaExceededError: DOM Exception 22.
                _useLocal = false;
            }
        }

        var call = function (method, args) {
            if (typeof method === 'function')
                method(args);
        }

        //////////////////////////////////
        // Public methods
        //////////////////////////////////
        this.bind = function (evt, f) {
            $this.bind(evt, f);
        }

        this.load = function (maxAge) {
            if (!_loading) {
                _loading = true;
                call(options.events.loading);

                var lastUpdated = webitects.localStorage.getUpdated(_key);
                var obj = getFromLocalStorage(_key);
                var age = getAgeFromLocalStorage(_key);
                var now = webitects.dataLoader.Now;
                var forceReload = false;
                var online = isOnline();

                if (maxAge !== undefined)
                    forceReload = true;
                if (obj === undefined && maxAge !== undefined)
                    forceReload = true;

                if ((!online || forceReload) && _useLocal) {
                    if (obj === undefined) {
                        if (isOnline()) {
                            // if object in local storage is not available and
                            // navigator is online, try to re-load object.
                            _loading = false;
                            _this.load();
                        }
                        else
                            call(options.events.error, ['Offline; Using local storage; Object is undefined.']);
                    }
                    else {
                        if (age !== undefined && obj.maxAge !== undefined && age >= obj.maxAge) {
                            _loading = false;
                            _this.load();
                        }
                        else if (obj.updated !== undefined && lastUpdated !== undefined && lastUpdated >= obj.updated) {
                            _loading = false;
                            _this.load();
                        }
                        else {
                            call(options.events.loaded, obj.value);
                        }
                    }

                    _loading = false;
                    call(options.events.complete);
                }
                else {
                    $.ajax({
                        cache: false,
                        url: _url,
                        data: _data,
                        type: _type,
                        success: function (obj) {
                            var errors = obj.Errors;
                            var model = obj.Model;
                            var updated = obj.Updated;
                            var valid = obj.Valid;

                            if (valid) {
                                if (_useLocal) {
                                    var args = {
                                        key: _key,
                                        maxAge: maxAge,
                                        overwrite: true,
                                        updated: updated,
                                        value: obj
                                    };

                                    saveToLocalStorage(args);
                                }

                                call(options.events.loaded, obj);
                            }
                            else {
                                call(options.events.error, errors);
                                if (options.retry.enabled)
                                    _this.retry(maxAge);
                            }
                        },
                        error: function (status) {
                            if (_useLocal) {
                                var obj = getFromLocalStorage(_key);
                                if (obj === undefined) {
                                    call(options.events.error, ['Using local storage, but object is undefined.']);
                                    if (options.retry.enabled)
                                        _this.retry(maxAge);
                                }
                                else
                                    call(options.events.loaded, obj.value);
                            }
                            else {
                                call(options.events.error, ['Error occurred while loading object.']);
                                if (options.retry.enabled)
                                    _this.retry(maxAge);
                            }
                        },
                        complete: function () {
                            _loading = false;
                            call(options.events.complete);
                        }
                    });
                }
            }
        }

        this.clearItem = function () {
            storage.remove(_key);
        }

        this.getItem = function () {
            var obj = getFromLocalStorage(_key);
            return obj;
        }

        this.getAge = function () {
            var rawAge = getAgeFromLocalStorage(_key);
            var now = webitects.dataLoader.Now;
            var age = now - rawAge;
            return age;
        }

        this.retry = function (maxAge) {
            setTimeout(function () { _this.load(maxAge); }, options.retry.timeout);
        }

        this.updateData = function (param, value) {
            var d = _data !== undefined ? _data : {};
            d[param] = value;
            _data = d;
        }
    }

    ///////////////////////////////
    // internal private object
    ///////////////////////////////
    var _StorageObject = (function () {
        var so = function (options) {
            var defaults = {
                age: undefined,
                maxAge: undefined,
                updated: undefined,
                value: {}
            };

            options = $.extend(defaults, options);

            var _age = options.age,
                _maxAge = options.maxAge,
                _updated = options.updated,
                _value = options.value;


            // if age was not passed into constructor,
            // set age to be current seconds since epoch
            _age = _age ? _age : webitects.dataLoader.Now;

            this.age = _age;
            this.maxAge = _maxAge;
            this.updated = _updated;
            this.value = _value;

            this.json = {
                age: _age,
                maxAge: _maxAge,
                updated: _updated,
                value: _value
            };
        }

        so.parse = function (obj) {
            var lived = webitects.dataLoader.Now - obj.age;

            var args = {
                age: lived,
                maxAge: obj.maxAge,
                updated: obj.updated,
                value: obj.value
            }
            return new webitects.dataLoader.StorageObject(args);
        }

        return so;
    });

    cls.StorageObject = _StorageObject();

    cls.Now = parseInt(new Date().getTime() / 1000);

    return cls;

})(jQuery);

/*  events
*
*   v 1.0.0.0   12-30-2013  Initial version.
*/
webitects.events = (function ($) {
    return {
        tap: webitects.utility.isTouchDevice() ? 'tap' : 'click'
    }
})(jQuery);

/*  w.file-cache.js
*   Author: Sebastian Stefaniuk, Webitects.com
*
*   Description:
*   Loads a local server-available file into cache and stores it in local storage as a data url.
*
*   v 1.0.1.0   12-30-2013  Updated fn.isSupported(); Added documentation section.
*   v 1.0.0.0   12-27-2013  Initial version.
*   
*   Requires HTML5 compatiblity, as well as JS FileReader, Blob, and LocalStorage. jQuery 1.8+
*
*   Methods
*
*   Name            Arguments
*   --------------------------------------------------------------------------------------------------------------------------------------------------------
*   init            options {
*                       keyPrefix: ''                                   Prefix for local storage key
*                   }
*   cachedFile      key (string)                                        Gets cached file by local storage key. Returns data url.
*   inCache         key (string)                                        Checks if file by local storage key is cached in local storage. Returns bool.
*   isSupported                                                         Checks if this plugin is supported by browser. Returns bool.
*   loader                                                              Returns a new file loader object. Used internally.
*   loadFile        options {
*                       key: '',                                        Local storage key for file to load
*                       maxAge: undefined,                              Maximum age of file in local storage. Will try to reload file on page load if age >= maxAge
*                       loaderOptions: {
*                           contentType: 'application/octet-stream',    Content type of file to be loaded.
*                           url: '',                                    Location of file (should be on same server as location of your website)
*                           events: {
*                               complete: function () { },              Called when ajax request is completed.
*                               error: function (error) { },            Called when an error occurred, either via ajax or when saving to local storage.
*                               loaded: function (result) { },          Called when file has been successfully loaded AND saved into local storage. Result = data url.
*                               loading: function () { },               Called when first trying to load the file.
*                               progress: function (e, perc) { }        Called everytime ajax request makes a loading progress. e = progressEvent, perc = percentage loaded as float.
*                           }
*                       }
*                   }
*   now                                                                 Returns current date in seconds. Returns int.
*   remove          key (string)                                        Removes file from local storage by key.
*   storageObject                                                       Returns a local storage object. Used internally.
*
*/
webitects.fileCache = (function ($) {

    var vars = {
        keyPrefix: '',
        options: {},
        storage: $.localStorage
    };

    var fn = {
        /***************************
        * Initializers
        ***************************/
        init: function (options) {
            var defaults = {
                keyPrefix: ''
            };

            options = $.extend(defaults, options);
            vars.options = options;
            vars.keyPrefix = options.keyPrefix;
        },

        /***************************
        * Functional
        ***************************/
        call: function (method, args) {
            if (typeof method === 'function') {
                var _args = [];
                if (arguments.length > 0)
                    for (var i = 1; i < arguments.length; i++)
                        _args.push(arguments[i]);
                method.apply(this, _args || []);
            }
        },

        loadFile: function (options) {
            var defaults = {
                key: '',
                maxAge: undefined,
                loaderOptions: {}
            };

            options = $.extend(defaults, options);

            var fileObject = fn.localStorage.retrieve(options.key);

            if (fileObject === undefined) {
                var loader = new webitects.fileCache.loader(options.loaderOptions);
                loader.load({
                    onLoaded: function (blob) {
                        fn.localStorage.save({
                            key: options.key,
                            maxAge: options.maxAge,
                            overwrite: true,
                            value: blob,
                            events: {
                                error: options.loaderOptions.events.error,
                                saved: options.loaderOptions.events.loaded
                            }
                        });
                    }
                });
            }
            else {
                if (fileObject.maxAge !== undefined && fileObject.age >= fileObject.maxAge) {
                    fn.localStorage.remove(options.key);
                    fn.loadFile(options);
                }
                else
                    fn.call(options.loaderOptions.events.loaded, fileObject.value);
            }
        },

        localStorage: {
            isSupported: function () {
                if (typeof window.FileReader === 'undefined') return false;
                if (typeof window.Blob === 'undefined') return false;
                if (typeof window.localStorage === 'undefined') return false;
                return true;
            },

            remove: function (key) {
                key = vars.keyPrefix + key;
                if (vars.storage.isSet(key))
                    vars.storage.remove(key);
            },

            retrieve: function (key) {
                var value;
                key = vars.keyPrefix + key;

                if (vars.storage.isSet(key)) {
                    var obj = vars.storage.get(key);
                    storeItem = webitects.fileCache.storageObject.parse(obj);
                    value = storeItem;
                }

                return value;
            },

            save: function (args) {
                var _defaults = {
                    key: '',
                    maxAge: undefined,
                    overwrite: true,
                    value: {},
                    events: {
                        error: function (error) { },
                        saved: function (fileObject) { }
                    }
                };

                args = $.extend(_defaults, args);
                args.key = vars.keyPrefix + args.key;

                var storeItem = new webitects.fileCache.storageObject({
                    maxAge: args.maxAge,
                    value: args.value
                });

                var storeObj = storeItem.json;

                try {
                    if (args.overwrite) {
                        vars.storage.remove(args.key);
                        vars.storage.set(args.key, storeObj);
                    }
                    else {
                        if (storage.isEmpty(args.key))
                            vars.storage.set(args.key, storeObj);
                    }
                    fn.call(args.events.saved, args.value);
                }
                catch (err) {
                    fn.call(args.events.error, err);
                }
            }
        }
    }

    /***************************
    * Inner classes
    ***************************/
    var _loader = (function () {
        var cls = function (options) {
            var loading = false;
            var defaults = {
                contentType: 'application/octet-stream',
                url: '',
                events: {
                    complete: function () { },
                    error: function (error) { },
                    loaded: function (dataResult) { },
                    loading: function () { },
                    progress: function (progressEvent, percentageLoaded) { }
                }
            };

            options = $.extend(defaults, options);

            this.load = function (loadOptions) {
                if (!loading) {
                    loading = true;
                    fn.call(options.events.loading);

                    var xhr = new XMLHttpRequest(),
                        blob,
                        fileReader = new FileReader();

                    xhr.open('GET', options.url, true);
                    xhr.responseType = 'arraybuffer';

                    xhr.addEventListener('load', function () {
                        if (xhr.status === 200) {
                            blob = new Blob([xhr.response], { type: options.contentType });

                            fileReader.onload = function (e) {
                                var result = e.target.result;
                                fn.call(options.events.complete);
                                fn.call(loadOptions.onLoaded, result);
                                loading = false;
                            };

                            fileReader.readAsDataURL(blob);
                        }
                        else {
                            fn.call(options.events.error, xhr);
                            fn.call(options.events.complete);
                            loading = false;
                        }
                    }, false);

                    xhr.addEventListener('progress', function (e) {
                        var perc = parseFloat((e.loaded / e.total * 100).toFixed(4));
                        fn.call(options.events.progress, e, perc);
                    }, false);

                    xhr.send();
                }
            }

            this.loading = function () {
                return loading;
            }
        }

        return cls;
    });

    var _storageObject = (function () {
        var so = function (options) {
            var defaults = {
                age: undefined,
                maxAge: undefined,
                value: {}
            };

            options = $.extend(defaults, options);

            var _age = options.age,
                _maxAge = options.maxAge,
                _value = options.value;


            // if age was not passed into constructor,
            // set age to be current seconds since epoch
            _age = _age ? _age : webitects.fileCache.now;

            this.age = _age;
            this.maxAge = _maxAge;
            this.value = _value;

            this.json = {
                age: _age,
                maxAge: _maxAge,
                value: _value
            };
        }

        so.parse = function (obj) {
            var lived = webitects.fileCache.now - obj.age;

            var args = {
                age: lived,
                maxAge: obj.maxAge,
                value: obj.value
            }

            return new webitects.fileCache.storageObject(args);
        }

        return so;
    });

    /***************************
    * Public methods
    ***************************/
    return {
        init: function (options) {
            fn.init(options);
        },

        cachedFile: function (key) {
            if (webitects.fileCache.inCache(key))
                return fn.localStorage.retrieve(key).value;
            else
                return undefined;
        },

        inCache: function (key) {
            if (fn.localStorage.isSupported()) {
                var cachedFileObject = fn.localStorage.retrieve(key);
                if (cachedFileObject !== undefined) {
                    var maxAge = cachedFileObject.maxAge;
                    var age = cachedFileObject.age;
                    if (age >= maxAge)
                        return false;
                    else
                        return true;
                }
                else
                    return false;
            }
            else
                return false;
        },

        isSupported: function () {
            return fn.localStorage.isSupported();
        },

        loader: new _loader(),

        loadFile: function (options) {
            fn.loadFile(options);
        },

        now: parseInt(new Date().getTime() / 1000),

        remove: function (key) {
            fn.localStorage.remove(key);
        },

        storageObject: new _storageObject()
    }

})(jQuery);

/*  hashEvent
*   Sebastian Stefaniuk - Webitects.com
*
*   v 1.1.0.0   01-08-2014  Added hashEvent.hashObject() inner class.
*   v 1.0.0.0   12-30-2013  Initial version in the webitects namespace.
*/
webitects.hashEvent = (function ($) {

    var $document = $(document);
    var $window = $(window);

    var data;
    var hash;
    var opts;

    var init = function (o) {
        opts = o;

        $window.hashchange(onHashChange);
        $document.hashchange(onHashChange);

        hash = location.hash;
        var hasHash = /^#/.test(hash);
        if (hasHash) {
            data = hashToKeyValues(hash);
            $document.trigger(webitects.hashEvent.events.NEW, data);
        }
        else
            $document.trigger(webitects.hashEvent.events.NONE);
    }

    var onHashChange = function (e) {
        hash = location.hash;
        var hasHash = /^#/.test(hash);
        if (hasHash) {
            data = hashToKeyValues(hash);
            $document.trigger(webitects.hashEvent.events.NEW, data);
        }
        else
            $document.trigger(webitects.hashEvent.events.NONE);
    }

    var hashToKeyValues = function (h) {
        var data = [];

        if (h != undefined) {
            h = h.replace(/^#/, '');
            try {
                var kv = h.split('&');
                if (kv.length > 0) {
                    for (var i = 0; i < kv.length; i++) {
                        var p = kv[i].split('=');

                        if ((p[0] != '' && p[0] != undefined) && (p[1] != undefined))
                            data.push([p[0], p[1]]);
                    }
                }
            }
            catch (err) { }
        }
        else
            throw "webitects.hashEvent @hashToKeyValues: hash is undefined";

        return data;
    }

    var _evtPrefix = 'w.hash-event.';

    var _hashObject = (function () {
        var ho = function (options) {
            var vars = {
                nvp: options.nvp,
                obj: new webitects.list()
            }

            var fn = {
                init: function () {
                    for (var i = 0; i < vars.nvp.length; i++)
                        vars.obj.add({ name: vars.nvp[i][0], value: vars.nvp[i][1] });
                }
            }

            this.toString = function () {
                var str = [];
                vars.obj.foreach(function (o) {
                    str.push(o.name + '=' + o.value);
                });
                return str.join('&');
            }

            this.param = function (args) {
                var _args = arguments;
                if (_args.length === 2) {
                    var o = vars.obj.find(function (o) { return o.name === _args[0]; });
                    _args[1] = _args[1] === undefined ? '' : _args[1];
                    if (o !== undefined) o.value = _args[1];
                    else vars.obj.add({ name: _args[0], value: _args[1] });
                }
                else if (_args.length === 1) {
                    var o = vars.obj.find(function (o) { return o.name === _args[0]; });
                    return o !== undefined ? o.value : o;
                }
            }

            this.hashObject = function () {
                return vars.obj;
            }

            fn.init();
        }

        return ho;
    });

    return {
        init: function (options) {
            init(options);
        },

        addToHash: function (obj) {
            hash += (webitects.hashEvent.hasHash() ? '&' : '#') + (obj.key + '=' + obj.value);
            location.hash = hash;
        },

        events: {
            NEW: _evtPrefix + 'new',
            NONE: _evtPrefix + 'none'
        },

        getData: function () {
            return data;
        },

        getHashValue: function () {
            value = '';
            if (webitects.hashEvent.hasHash())
                value = hash.substring(1);
            return value;
        },

        getValueByParam: function (param) {
            var val = undefined;

            if (data !== undefined) {
                for (var i = 0; i < data.length; i++)
                    if (data[i][0] == param)
                        val = data[i][1];
            }

            return val;
        },

        hasHash: function () {
            if (hash !== undefined)
                return /^#/.test(hash);
            else
                return false;
        },

        hashObject: new _hashObject(),

        nvpToHash: function (nvp) {
            if (nvp !== undefined) {
                var delim = '';
                var h = '#';

                for (var i = 0; i < nvp.length; i++) {
                    h += delim + nvp[i][0] + '=' + nvp[i][1];
                    delim = '&';
                }

                location.hash = h;
            }
        },

        nvpToHashString: function (nvp) {
            if (nvp !== undefined) {
                var delim = '';
                var h = '#';

                for (var i = 0; i < nvp.length; i++) {
                    h += delim + nvp[i][0] + '=' + nvp[i][1];
                    delim = '&';
                }

                return h;
            }
        },

        getHash: function () {
            return hash;
        },

        setHash: function (h) {
            hash = h;
            location.hash = h;
        },

        updateHashValue: function (param, value) {
            if (webitects.hashEvent.hasHash()) {
                var nvp = webitects.hashEvent.getData();
                if (nvp !== undefined) {
                    var hasParam = false;
                    var index = -1;

                    for (var i = 0; i < nvp.length; i++) {
                        if (nvp[i][0] == param) {
                            index = i;
                            hasParam = true;
                        }
                    }

                    if (hasParam)
                        nvp[index][1] = value;
                    else
                        nvp.push([param, value]);

                    webitects.hashEvent.nvpToHash(nvp);
                }
            }
        },

        updateHashValues: function (nvp) {
            if (webitects.hashEvent.hasHash() && nvp !== undefined) {
                var _nvp = webitects.hashEvent.getData();
                if (_nvp != undefined) {
                    for (var i = 0; i < nvp.length; i++) {
                        for (var j = 0; j < _nvp.length; j++) {
                            if (_nvp[j][0] == nvp[i][0])
                                _nvp[j][1] = nvp[i][1];
                        }
                    }

                    webitects.hashEvent.nvpToHash(_nvp);
                }
            }
        }
    }

})(jQuery);

/*  image scaler
*   Sebastian Stefaniuk, webitects.com
*
*   v 1.0.0.0   01-06-2013  Initial version
*/
webitects.imageScaler = (function ($) {

    var cls = function (options) {
        var _parentHeight = options.parentHeight;
        var _parentWidth = options.parentWidth;
        var _imageHeight = options.imageHeight;
        var _imageWidth = options.imageWidth;
        var _steps = options.steps ? options.steps : 4;
        var _scaleX;
        var _scaleY;
        var _heights;
        var _widths;
        var _outOfView;

        var init = function () {
            _heights = [];
            _widths = [];
            _outOfView = true;

            _scaleX = _imageWidth / _parentWidth;
            _scaleY = _imageHeight / _parentHeight;

            if (_scaleX >= _scaleY) {
                scaleByX();
            }
            else {
                scaleByY();
            }
        }

        var scaleByY = function () {
            var minH = parseInt(_imageHeight / _scaleY);
            var minW = parseInt(_imageWidth / _scaleY);

            if (minH > _imageHeight) {
                minH = _imageHeight;
                minW = _imageWidth;
                _outOfView = false;
            }

            calculateSteps(minW, minH);
        }

        var scaleByX = function () {
            var minW = parseInt(_imageWidth / _scaleX);
            var minH = parseInt(_imageHeight / _scaleX);

            if (minW > _imageWidth) {
                minW = _imageWidth;
                minH = _imageHeight;
                _outOfView = false;
            }

            calculateSteps(minW, minH);
        }

        var calculateSteps = function (minW, minH) {
            var imgDiffWidth = _imageWidth - minW;
            var imgDiffHeight = _imageHeight - minH;
            var stepWidth = parseInt(imgDiffWidth / _steps);
            var stepHeight = parseInt(imgDiffHeight / _steps);

            for (var i = 0; i < _steps; i++) {
                if (i === 0) {
                    _widths.push(minW);
                    _heights.push(minH);
                }
                else if (i === _steps.length - 1) {
                    _widths.push(_imageWidth);
                    _heights.push(_imageHeight);
                }
                else {
                    _widths.push(minW + (stepWidth * i));
                    _heights.push(minH + (stepHeight * i));
                }
            }
        }

        this.getHeight = function () {
            return _heights[0];
        }

        this.getHeights = function () {
            return _heights;
        }

        this.getWidth = function () {
            return _widths[0];
        }

        this.getWidths = function () {
            return _widths;
        }

        this.isOutOfView = function () {
            return _outOfView;
        }

        init();
    }

    return cls;

})(jQuery);

/*  list
*   Sebastian Stefaniuk, webitects.com
*
*   v 1.3.1.0   12-16-2013  Allow foreach() method to break loop if predicate function returns true;
*   v 1.3.0.0   12-06-2013  Added distinct() method => returns a new webitects.list object with items selected in delegate of method.
*   v 1.2.0.0   12-03-2013  Added longCount() method; Bug fix in take() method.
*   v 1.0.1.0   11-27-2013  Allow nullable type to be added to list of non-null type.
*   v 1.0.0.0   11-10-2013  Initial version
*
*/

webitects.list = (function () {

    var cls = function (array) {
        var _array = [];
        var _this = this;
        var _type;

        var _init = function () {
            if (array !== undefined && array instanceof Array) {
                for (var i = 0; i < array.length; i++)
                    _this.add(array[i]);
            }
        }

        this.add = function (item) {
            if ((_array.length == 0 && item !== null) || (_type === undefined && item !== null))
                _type = typeof item;

            if (_type !== typeof item && item !== null)
                throw 'Item is of type ' + (typeof item) + ', but webitects.list is composed of ' + _type + ' type objects.';
            else
                _array.push(item);
        }

        this.addRange = function (list) {
            var _list = _this;
            if (list !== undefined) {
                list.foreach(function (item) {
                    _list.add(item);
                });
            }
            return _list
        }

        this.any = function () {
            return _array.length > 0;
        }

        this.clear = function () {
            _array = [];
        }

        this.count = function () {
            return _array.length;
        }

        this.distinct = function (predicate) {
            var _list = new webitects.list();
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate)) {
                    if (typeof predicate === 'function' && predicate(_array[i])) {
                        if (_list.findIndex(function (x) { return x == predicate(_array[i]); }) == -1)
                            _list.add(predicate(_array[i]));
                    }
                    else if (_array[i] === predicate) {
                        if (_list.findIndex(function (x) { return x == _array[i]; }) == -1)
                            _list.add(_array[i]);
                    }
                }
            }
            return _list;
        }

        this.first = function () {
            var item
            if (_array.length > 0)
                item = _array[0];
            return item;
        }

        this.foreach = function (predicate) {
            for (var i = 0; i < _array.length; i++) {
                if (typeof predicate === 'function') {
                    var doBreak = predicate(_array[i], i);
                    if (doBreak)
                        break;
                }
            }
        }

        this.itemAt = function (index) {
            return _array[index];
        }

        this.last = function () {
            var item
            if (_array.length > 0)
                item = _array[_array.length - 1];
            return item;
        }

        this.longCount = function (predicate) {
            var count = 0;
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate))
                    count++;
            }
            return count;
        }

        this.removeAll = function (predicate) {
            var _list = new webitects.list();
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate)) { }
                else
                    _list.add(_array[i]);
            }
            return _list;
        }

        this.removeAt = function (index) {
            var newArray = [];
            for (var i = 0; i < _array.length; i++)
                if (i !== index)
                    newArray.push(_array[i]);
            _array = newArray;
        }

        this.take = function (numOfItems, skip) {
            var list = new webitects.list();
            skip = skip !== undefined ? skip : 0;
            for (var i = skip; i < numOfItems + skip; i++) {
                if (i < _array.length) list.add(_array[i]);
                else break;
            }
            return list;
        }

        ////////////////////////////////
        // Find functions
        ////////////////////////////////
        this.find = function (predicate) {
            var item;
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate)) {
                    item = _array[i];
                    break;
                }
            }
            return item;
        }

        this.findAll = function (predicate) {
            var _list = new webitects.list();
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate))
                    _list.add(_array[i]);
            }
            return _list;
        }

        this.findIndex = function (predicate) {
            var index = -1;
            for (var i = 0; i < _array.length; i++) {
                if ((typeof predicate === 'function' && predicate(_array[i])) || (_array[i] === predicate)) {
                    index = i;
                    break;
                }
            }
            return index;
        }

        ////////////////////////////////
        // Order functions
        ////////////////////////////////
        this.orderBy = function (args) {
            var _list = new webitects.list(),
                _newArray = [],
                _compareFunc = getCompare(arguments);

            for (var i = 0; i < _array.length; i++)
                _newArray.push(_array[i]);

            _newArray.sort(_compareFunc);

            for (var i = 0; i < _newArray.length; i++)
                _list.add(_newArray[i]);

            return _list;
        }

        ////////////////////////////////
        // "To" functions
        ////////////////////////////////
        this.toArray = function () {
            return _array;
        }

        ////////////////////////////////
        // Private functions
        ////////////////////////////////
        var getCompare = function () {
            var compares = [],
                predicates = [],
                types = [];

            var compareFunc;
            var args = arguments.length > 0 ? arguments[0] : [];

            for (var i = 0; i < args.length; i++) {
                var predicate = args[i].pred;
                var compareType = args[i].type !== undefined ? args[i].type : webitects.list.CompareType.ASC;
                var compare;

                if (typeof predicate === 'function') {
                    compare = function (a, b, pred, type) {
                        var x = getSortValue(pred(a));
                        var y = getSortValue(pred(b));
                        if (x < y) return type === webitects.list.CompareType.ASC ? -1 : 1;
                        if (x > y) return type === webitects.list.CompareType.ASC ? 1 : -1;
                        return 0;
                    };
                }
                else if (typeof predicate === 'string') {
                    compare = function (a, b, pred, type) {
                        var x = getSortValue(a[pred]);
                        var y = getSortValue(b[pred]);
                        if (x < y) return type === webitects.list.CompareType.ASC ? -1 : 1;
                        if (x > y) return type === webitects.list.CompareType.ASC ? 1 : -1;
                        return 0;
                    };
                }

                if (compare !== undefined) {
                    compares.push(compare);
                    predicates.push(predicate);
                    types.push(compareType);
                }
            }

            if (compares.length > 0) {
                compareFunc = function (x, y) {
                    var val = 0;
                    for (var i = 0; i < compares.length; i++) {
                        val = compares[i](x, y, predicates[i], types[i]);
                        if (val !== 0)
                            break;
                    }
                    return val;
                }
            }
            else {
                var compareType;

                if (arguments.length == 1) {
                    if (typeof arguments[0] === 'Number') compareType = arguments[0];
                    else compareType = webitects.list.CompareType.ASC;
                }
                else
                    compareType = webitects.list.CompareType.ASC;

                if (compareType === webitects.list.CompareType.DESC) {
                    compareFunc = function (a, b) {
                        if (a > b) return -1;
                        if (a < b) return 1;
                        return 0;
                    };
                }
                else {
                    compareFunc = function (a, b) {
                        if (a < b) return -1;
                        if (a > b) return 1;
                        return 0;
                    };
                }
            }

            return compareFunc;
        }

        var getSortValue = function (item) {
            if (typeof item === 'number') return parseFloat(item);
            else if (typeof item === 'string') return item.toString();
            return item;
        }

        _init();
    }

    var _compareType = function () {
        return {
            ASC: 1,
            DESC: 2
        };
    }

    cls.CompareType = _compareType();

    return cls;

})();

/*  localStorage
*   Sebastian Stefaniuk - Webitects.com
*
*   v 1.4.0.0   01-06-2013  Added isSupported(), remove(), retrieve(), and save() methods.
*   v 1.0.0.0   12-30-2013  Initial version in the webitects namespace.
*/
webitects.localStorage = (function ($) {

    var _localStorageContext,
        _localStorageKeys,
        _localStoragePrefix,
        _reloadUrl,
        _storage = $.localStorage;

    var _parseContext = function (context) {
        var keysList = new webitects.list(context.keys);
        var keys = {};
        keysList.foreach(function (k) {
            keys[k.Key] = k.KeyEnum;
        });

        _localStorageContext = context;
        _localStorageKeys = keys;
    }

    var _storageObject = (function () {
        var so = function (options) {
            var defaults = {
                age: undefined,
                maxAge: undefined,
                value: {}
            };

            options = $.extend(defaults, options);

            var _age = options.age,
                _maxAge = options.maxAge,
                _value = options.value;


            // if age was not passed into constructor,
            // set age to be current seconds since epoch
            _age = _age ? _age : webitects.localStorage.now;

            this.age = _age;
            this.maxAge = _maxAge;
            this.value = _value;

            this.json = {
                age: _age,
                maxAge: _maxAge,
                value: _value
            };
        }

        so.parse = function (obj) {
            var lived = webitects.localStorage.now - obj.age;

            var args = {
                age: lived,
                maxAge: obj.maxAge,
                value: obj.value
            }

            return new webitects.localStorage.storageObject(args);
        }

        return so;
    });

    return {
        context: function (args) {
            if (arguments.length > 0) {
                var serialized = $('<div/>').html(arguments[0]).text();
                _localStorageContext = JSON.parse(serialized);
                _parseContext(_localStorageContext);
            }
            else
                return _localStorageContext;
        },

        keys: function (args) {
            if (arguments.length > 0)
                _localStorageKeys = arguments[0];
            else
                return _localStorageKeys;
        },

        getUpdated: function (key) {
            var prefix = _localStoragePrefix;
            var updated = undefined;
            var _key;

            if (_localStorageKeys !== undefined) {
                var keyValue;
                if (typeof key === 'string')
                    keyValue = _localStorageKeys[key.replace(_localStoragePrefix, '')];
                else
                    keyValue = key;

                _localStorageContext.storage.foreach(function (s) {
                    var keySubbed = s.key.replace(_localStoragePrefix, '');
                    if (_localStorageKeys[keySubbed] === keyValue) {
                        updated = s.updated;
                    }
                });
            }

            return updated;
        },

        isSupported: function () {
            if (typeof window.localStorage === 'undefined') return false;
            return true;
        },

        now: parseInt(new Date().getTime() / 1000),

        prefix: function (args) {
            if (arguments.length > 0) _localStoragePrefix = arguments[0];
            else return _localStoragePrefix;
        },

        reload: function (options) {
            var defaults = {
                onDifferent: function () { },
                onSame: function () { }
            };

            options = $.extend(defaults, options);

            var loader = new webitects.dataLoader({
                type: 'Post',
                url: _reloadUrl,
                useLocal: false,
                events: {
                    loaded: function (obj) {
                        var isDifferent = false;
                        var context = obj.Model;
                        var keys = new webitects.list(context.keys);
                        var contextStorage = new webitects.list(context.storage);

                        keys.foreach(function (key) {
                            contextStorage.foreach(function (s) {
                                if ((_localStoragePrefix + key.Key) === s.key) {
                                    var storageItem = _localStorageContext.storage.find(function (x) { return x.key === s.key; });
                                    if (storageItem === undefined || storageItem.updated !== s.updated)
                                        isDifferent = true;
                                }
                            });
                        });

                        _parseContext(context);

                        if (isDifferent)
                            options.onDifferent();
                        else
                            options.onSame();
                    },
                    error: function (errors) {
                        options.onSame();
                    }
                }
            });

            loader.load();
        },

        reloadUrl: function (args) {
            if (arguments.length > 0) _reloadUrl = arguments[0];
            else return _reloadUrl;
        },

        remove: function (key) {
            key = webitects.localStorage.prefix() + key;
            if (_storage.isSet(key))
                _storage.remove(key);
        },

        retrieve: function (key) {
            var value;
            key = webitects.localStorage.prefix() + key;

            if (_storage.isSet(key)) {
                var obj = _storage.get(key);
                storeItem = webitects.localStorage.storageObject.parse(obj);
                value = storeItem;
            }

            return value;
        },

        save: function (args) {
            var _defaults = {
                key: '',
                maxAge: undefined,
                overwrite: true,
                value: {},
                events: {
                    error: function (error) { },
                    saved: function (fileObject) { }
                }
            };

            args = $.extend(_defaults, args);
            args.key = webitects.localStorage.prefix() + args.key;

            var storeItem = new webitects.localStorage.storageObject({
                maxAge: args.maxAge,
                value: args.value
            });

            var storeObj = storeItem.json;

            try {
                if (args.overwrite) {
                    _storage.remove(args.key);
                    _storage.set(args.key, storeObj);
                }
                else {
                    if (_storage.isEmpty(args.key))
                        _storage.set(args.key, storeObj);
                }
                webitects.call(args.events.saved, storeObj);
            }
            catch (err) {
                webitects.call(args.events.error, err);
            }
        },

        storageObject: new _storageObject()
    }

})(jQuery);

/*  templater
*   Sebastian Stefaniuk, Webitects.com
*
*   v 1.1.1.0   01-03-2014  Added reset() method; Updated html() method.
*   v 1.0.0.0   12-30-2013  Initial version; Added to webitects (webitects) namespace.
*
*   Use the templater to easily create an html/string template that let's you populate
*   its variables via json objects. Example:
*
*   <div>
*       <h3>{{title}}</h3>
*       <p>{{subTitle}}</p>
*   </div>
*
*   This will generate Templater.fields as { title: '', subTitle: '' }
*   A template variable is surrounded with {{ and }} brackets.
*
*   Set variables like so:
*   template.fields.title = 'Something';
*   template.fields.subTitle = 'Another thing';
*
*   Generate the resulting markup: var html = template.html();
*   Multiple occurences of the same variable inside the template are ok. They will all get the same value.
*
*   To create a template from a jquery selector, instantiate the Templater object as:
*   var template = new Templater('#selector');
*
*   To create a template from a simple html string:
*   var template = new Templater({ html: '<h3>{{title}}</h3>' });
*
*   From a more complex html string:
*   var template = new Templater({
*       html: function() {
*           var s = '';
*           s += '<div>';
*           s += '    <h3>{{title}}</h3>';
*           s += '    <p>{{subTitle}}</p>';
*           s += '</div>';           
*           return s;
*       }
*   });
*
*   Variable            Type            Default
*   -------------------------------------------------------------------------------------------------------------------------
*   fields              json object     {} (or whichever variables are defined within {{ and }} brackets inside the template
*
*   Method              Return type     Description
*   -------------------------------------------------------------------------------------------------------------------------
*   html()              string          Returns the contents of the template with all variables replaced with fields values.
*                                       Variables that are empty are returned as empty.
*   reset()             void            Clears all field values to an empty string.
*
*/
webitects.templater = (function ($) {

    var cls = function (options) {
        var useTempl = typeof options === "string";
        var $this = $(this);
        var $templ = useTempl ? $(options) : {};

        if (useTempl && $templ.length != 1)
            throw 'from Templater: invalid template element.';

        if (!useTempl && (!options || (options.html === undefined || options.html == '')))
            throw 'from Templater: invalid template html. Must define html as new Templater({ html: \'...\' })';

        var _fields = {};
        var _html = useTempl ? $templ.html() : (typeof options.html === 'function' ? options.html() : options.html);
        var _fieldMatches = _html.match(/\{\{(.*?)\}\}/g);

        if (_fieldMatches)
            for (var i = 0; i < _fieldMatches.length; i++)
                _fieldMatches[i] = _fieldMatches[i].replace(/\{\{|\}\}/g, '');

        this.fields = _fields;

        this.html = function () {
            var html = _html;

            if (_fieldMatches)
                for (var i = 0; i < _fieldMatches.length; i++)
                    html = html.replace('\{\{' + _fieldMatches[i] + '\}\}', _fields[_fieldMatches[i]] !== undefined ? _fields[_fieldMatches[i]] : '');

            return html;
        }

        this.reset = function () {
            if (_fieldMatches)
                for (var i = 0; i < _fieldMatches.length; i++)
                    _fields[_fieldMatches[i]] = undefined;
        }
    }

    return cls;

})(jQuery);;
var URI = function () { var e = new RegExp("^" + "(?:" + "([^:/?#.]+)" + ":)?" + "(?://" + "(?:([^/?#]*)@)?" + "([\\w\\d\\-\\u0100-\\uffff.%]*)" + "(?::([0-9]+))?" + ")?" + "([^?#]+)?" + "(?:\\?([^#]*))?" + "(?:#(.*))?" + "$"); return function (t) { var n; n = t.match(e); return { scheme: n[1], user_info: n[2], domain: n[3], port: n[4], path: n[5], query_data: n[6], hash: n[7] } } }()

var utility = (function ($) {
    'use strict';

    return {
        attrFriendly: function (s, preserveCR) {
            preserveCR = preserveCR ? '&#13;' : '\n';
            return ('' + s) /* Forces the conversion to string. */
                .replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */
                .replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */
                .replace(/"/g, '&quot;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                /*
                You may add other replacements here for HTML only 
                (but it's not necessary).
                Or for XML, only if the named entities are defined in its DTD.
                */
                .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
                .replace(/[\r\n]/g, preserveCR);
            ;
        },

        formatThousands: function (n) {
            var f = '';
            if (n !== undefined) {
                n = parseFloat(n);

                var s = n.toString();
                var dec = '';

                if (s.lastIndexOf('.') > -1) {
                    dec = s.substring(s.lastIndexOf('.'));
                    s = s.substring(0, s.lastIndexOf('.'));
                }

                s = s.split('').reverse().join('');

                if (n < 1000)
                    f = s;
                else if (n >= 1000 && n < 1000000)
                    f = s.substring(0, 3) + ',' + s.substring(3);
                else if (n >= 1000000)
                    f = s.substring(0, 3) + ',' + s.substring(3, 6) + ',' + s.substring(6);
            }

            return f.split('').reverse().join('') + dec;
        },

        getBooleanIcon: function (booleanValue, iconElement, iconTrue, iconFalse) {
            iconElement = iconElement === undefined ? 'i' : iconElement;
            iconTrue = iconTrue === undefined ? 'check-circle' : iconTrue;
            iconFalse = iconFalse === undefined ? 'ban' : iconFalse;
            return '<{0} class="fa fa-{1}" data-orderby="{2}"></{0}>'.format(iconElement, booleanValue ? iconTrue : iconFalse, booleanValue ? 1 : 0);
        },

        s4: function () {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        },

        guid: function () {
            var g = (DataUtil.s4() + DataUtil.s4() + "-" + DataUtil.s4() + "-4" + DataUtil.s4().substr(0, 3) + "-" + DataUtil.s4() + "-" + DataUtil.s4() + DataUtil.s4() + DataUtil.s4()).toLowerCase();
            return g;
        },

        round: function(num, length) {
            return Math.round(num * Math.pow(10, length)) / Math.pow(10, length);
        },

        stripHtmlTags: function (html) {
            if (html.length > 0)
                html = html.replace(/<([^>]*)>/g, '');
            return html;
        },

        urlFriendly: function (str) {
            if (str.length > 0) {
                str = str.length > 80 ? str.substring(0, 80) : str;
                str = str.toLowerCase();
                str = str.replace(/(?:[^a-z0-9 ])/g, '');
                str = str.replace(/(\s+)/g, '-');
                return str;
            }
            return str;
        }
    }

})(jQuery);;
(function ($) {
    $(function () {

        $('.disabled').bind('click', function (e) {
            e.preventDefault();
            return false;
        });

    });
})(jQuery);;
var wFixedHeader = (function ($, w) {

    var cls = function (options) {
        var defaults = {
            table: undefined,
            topOffset: 55,
            wrapper: undefined
        };

        options = $.extend(true, defaults, options);

        var $header,
            $window = $(window),
            _this = this;

        var vars = {
            classes: {
                fixedHeader: 'fixed-header'
            },
            guid: ''
        }

        var fn = {
            init: function () {
                if (options.table.length > 0) {
                    vars.guid = options.table.data('wfh-id');
                    vars.guid = vars.guid === undefined || vars.guid.length === 0 ? w.utility.guid() : vars.guid;
                    options.table.attr('data-wfh-id', vars.guid);

                    // assign unique ids for each td/th in thead
                    options.table.find('thead td,th').each(function () {
                        var guid = $(this).data('wfh-id');
                        guid = guid === undefined || guid.length === 0 ? w.utility.guid() : guid;
                        $(this).attr('data-wfh-id', guid.substring(0, 8));
                    });

                    fn.buildHeader();

                    $window.bind('scroll', fn.onScroll);
                    $window.trigger('scroll');
                }
            },

            buildHeader: function () {
                $header = options.table.prev('.' + vars.classes.fixedHeader + '[data-wfh-id="' + vars.guid + '"]');

                if ($header.length === 0) {
                    var $thead = options.table.find('thead').clone(true);
                    options.table.before('<table class="data ' + vars.classes.fixedHeader + '" data-wfh-id="' + vars.guid + '"></table>');
                    options.table.prev('table').append($thead);
                    $header = options.table.prev('.' + vars.classes.fixedHeader + '[data-wfh-id="' + vars.guid + '"]');
                }

                $header.css({
                    'position': 'absolute',
                    'visibility': 'hidden'
                });

                if (options.wrapper !== undefined) {
                    $header.css('width', options.wrapper.outerWidth() + 'px');
                }
                else {
                    $header.css('width', options.table.width() + 'px');
                }
            },

            onScroll: function () {
                if ($header.length > 0) {
                    var minTop = options.table.offset().top;
                    var maxTop = options.table.offset().top + options.table.outerHeight(true);
                    var scroll = $window.scrollTop();
                    var height = $header.outerHeight(true);

                    if (scroll > minTop - options.topOffset && scroll < maxTop - options.topOffset - height) {
                        $header.css({
                            'visibility': 'visible',
                            'top': (scroll + options.topOffset) + 'px'
                        });
                    }
                    else if (scroll < minTop - options.topOffset) {
                        $header.css({
                            'visibility': 'hidden',
                            'top': minTop + 'px'
                        });
                    }
                }
            }
        }

        fn.init();

        this.reset = function () {
            if($header !== undefined)
                $header.remove();
            fn.buildHeader();
        }

        this.getCellByGuid = function (guid) {
            if ($header !== undefined) {
                return $header.find('td,th[data-wfh-id="' + guid + '"]');
            }
            else
                return undefined;
        }

        this.matchTableHeader = function () {
            options.table.find('thead td,th').each(function () {
                var $tableCell = $(this);
                var guid = $(this).data('wfh-id');
                var $headerCell = _this.getCellByGuid(guid);

                if ($headerCell !== undefined) {
                    // match class names
                    var htmlClass = $tableCell.attr('class');
                    $headerCell.attr('class', htmlClass === undefined ? '' : htmlClass);
                }
            });
        }
    }

    return cls;

})(jQuery, webitects);;
// Add ECMA262-5 method binding if not supported natively
//
if (!('bind' in Function.prototype)) {
    Function.prototype.bind = function (owner) {
        var that = this;
        if (arguments.length <= 1) {
            return function () {
                return that.apply(owner, arguments);
            };
        } else {
            var args = Array.prototype.slice.call(arguments, 1);
            return function () {
                return that.apply(owner, arguments.length === 0 ? args : args.concat(Array.prototype.slice.call(arguments)));
            };
        }
    };
}

// Add ECMA262-5 string trim if not supported natively
//
if (!('trim' in String.prototype)) {
    String.prototype.trim = function () {
        return this.replace(/^\s+/, '').replace(/\s+$/, '');
    };
}

// Add ECMA262-5 Array methods if not supported natively
//
if (!('indexOf' in Array.prototype)) {
    Array.prototype.indexOf = function (find, i /*opt*/) {
        if (i === undefined) i = 0;
        if (i < 0) i += this.length;
        if (i < 0) i = 0;
        for (var n = this.length; i < n; i++)
            if (i in this && this[i] === find)
                return i;
        return -1;
    };
}

if (!('lastIndexOf' in Array.prototype)) {
    Array.prototype.lastIndexOf = function (find, i /*opt*/) {
        if (i === undefined) i = this.length - 1;
        if (i < 0) i += this.length;
        if (i > this.length - 1) i = this.length - 1;
        for (i++; i-- > 0; ) /* i++ because from-argument is sadly inclusive */
            if (i in this && this[i] === find)
                return i;
        return -1;
    };
}

if (!('forEach' in Array.prototype)) {
    Array.prototype.forEach = function (action, that /*opt*/) {
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this)
                action.call(that, this[i], i, this);
    };
}

if (!('map' in Array.prototype)) {
    Array.prototype.map = function (mapper, that /*opt*/) {
        var other = new Array(this.length);
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this)
                other[i] = mapper.call(that, this[i], i, this);
        return other;
    };
}

if (!('filter' in Array.prototype)) {
    Array.prototype.filter = function (filter, that /*opt*/) {
        var other = [], v;
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this && filter.call(that, v = this[i], i, this))
                other.push(v);
        return other;
    };
}

if (!('every' in Array.prototype)) {
    Array.prototype.every = function (tester, that /*opt*/) {
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this && !tester.call(that, this[i], i, this))
                return false;
        return true;
    };
}

if (!('some' in Array.prototype)) {
    Array.prototype.some = function (tester, that /*opt*/) {
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this && tester.call(that, this[i], i, this))
                return true;
        return false;
    };
}

if (!String.prototype.format) {
    String.prototype.format = function () {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined'
              ? args[number]
              : match
            ;
        });
    };
};
/*
*
*   wMoreLess
*   Author: Sebastian Stefaniuk (Webitects)
*   
*   06-02-2016  v 1.1.0.0 Updated truncating logic to ignore html tags
*   12-09-2013  v 1.0.0.0 Initial version
*
*   Truncates a text if it exceeds a certain amount of chars to a shortened version
*   and appends more/less toggle button to show the rest of the text.
*
*/

(function ($) {
    $.wMoreLess = function (element, opts) {
        var defaults = $.extend({
            more: 'More',
            less: 'Less',
            chars: 100,
            delim: '&hellip; ',
            wordCut: false
        }, opts);

        var classes = {
            moreLess: 'more-less',
            original: 'more-less-original',
            toggled: 'more-less-toggled',
            toggler: 'more-less-toggler',
            truncated: 'more-less-truncated'
        }

        var plugin = this;
        plugin.settings = {};

        var $element = $(element), element = element,
            $original,
            $truncated,
            $wrapper;

        var _original,
            _originalJustText,
            _settings,
            _truncated;

        /***********************
        * Initializers
        ***********************/
        var _init = function () {
            _settings = plugin.settings;
            _original = $element.html();
            _originalJustText = $('<div />').html(_original).text();
            $element.addClass(classes.moreLess);
            $element.html($('<div class="' + classes.original + '" style="display:none;">' + _original + '</div><div class="' + classes.truncated + '"></div>'));

            $original = $element.find('.' + classes.original);
            $truncated = $element.find('.' + classes.truncated);
            
            if (_originalJustText.length > _settings.chars) {
                var i,
                    max = _original.length,
                    charCount = 0,
                    cutAtIdx = -1,
                    readyToCut = false,
                    stopCount = false;

                for (i = 0; i < max; i += 1) {
                    var c = _original[i];
                    
                    if (c === '<') {
                        stopCount = true;
                    }
                    else if (c === '&') { // &lt; &rt; &amp; etc... we still want to count it as one character
                        stopCount = true;
                        charCount++;
                    }

                    if (!stopCount) {
                        charCount++;

                        if (charCount >= _settings.chars) {
                            readyToCut = true;
                        }

                        // cutoff the text, no matter if the cutoff is in the middle of a word
                        if (readyToCut && _settings.wordCut) {
                            cutAtIdx = i;
                            break;
                        }
                        else if (readyToCut && !_settings.wordCut && c === ' ') {
                            cutAtIdx = i - 1;
                            break;
                        }
                    }

                    if (c === '>' || c === ';') {
                        stopCount = false;
                    }
                }
                
                _truncated = _original.substring(0, cutAtIdx + 1) + _settings.delim;;
                
                var div = document.createElement('div');
                div.innerHTML = _truncated;
                _truncated = div.innerHTML;

                $truncated.html(_truncated);
                _initTogglers();
            }
            else {
                $original.show();
                $truncated.hide();
            }
        }

        var _initTogglers = function () {
            var $lastChildTrun = $element.find('.' + classes.truncated).find('p,h3,h4,span').last(),
                $lastChildOrig = $element.find('.' + classes.original).find('p,h3,h4,span').last(),
                $toggleMore = $('<a href="#" class="' + classes.toggler + '">' + _settings.more + '</a>'),
                $toggleLess = $('<a href="#" class="' + classes.toggler + '">' + _settings.less + '</a>');

            // append "More" button
            if($lastChildTrun.length && $lastChildTrun.is('p,h3,h4,span'))
                $lastChildTrun.append(' ').append($toggleMore);
            else
                $element.find('.' + classes.truncated).append(' ').append($toggleMore);

            // append "Less" button
            if ($lastChildOrig.length && $lastChildOrig.is('p,h3,h4,span'))
                $lastChildOrig.append(' ').append($toggleLess);
            else
                $element.find('.' + classes.original).append(' ').append($toggleLess);

            // setup click events
            $toggleLess.on('click', function (e) {
                $original.hide();
                $truncated.show();
                e.preventDefault();
                return false;
            });

            $toggleMore.on('click', function (e) {
                $original.show();
                $truncated.hide();
                e.preventDefault();
                return false;
            });
        }

        /***********************
        * Public methods
        ***********************/
        plugin.init = function () {
            plugin.settings = $.extend({}, defaults, opts);
            _init();
        };

        plugin.reset = function (text) {
            if (text === undefined)
                text = _original;

            _original = '';
            _truncated = '';

            $element.removeClass(classes.moreLess);
            $element.html(text);

            _init();
        }

        plugin.init();
    };

    $.fn.wMoreLess = function (opts) {
        return this.each(function () {
            if ($(this).data('wMoreLess') == undefined) {
                var plugin = new $.wMoreLess(this, opts);
                $(this).data('wMoreLess', plugin);
            }
        });
    };

})(jQuery);;
(function ($) {
    $(document).tooltip({
        content: function () {
            if (!$(this).is('iframe') && ($(this).is("[data-more]") || $(this).is('[title]'))) {
                var txt = $(this).attr('title');
                var more = $(this).attr('data-more');
                var tip = '';

                if (typeof txt === 'string' && txt.length > 0)
                    tip += '<h6>' + txt + '</h6>';
                if (typeof more === 'string' && more.length > 0)
                    tip += '<p>' + more + '</p>';
                if (typeof more === 'undefined' && typeof txt === 'undefined')
                    tip += '<p>' + $(this).text() + '</p>';

                return tip;
            }
        },
        //hide: { effect: "hide" },
        items: "[data-more],[title]",
        show: { delay: 750, effect: "show" },
        open: function(event, ui) {
            if (typeof(event.originalEvent) === 'undefined')
                return false;
        
            var $id = $(ui.tooltip).attr('id');
        
            // close any lingering tooltips
            $('div.ui-tooltip').not('#' + $id).remove();
        
            // ajax function to pull in data and add it to the tooltip goes here
        },
        close: function(event, ui) {
            ui.tooltip.hover(function()
            {
                $(this).stop(true).fadeTo(400, 1); 
            },
            function()
            {
                $(this).fadeOut('400', function()
                {
                    $(this).remove();
                });
            });
        }
    });
})(jQuery);;
(function ($) {
    $(function () {

        function processSecondaryNav() {
            // add or remove "More" button depending on container/list heights
            $(".collapsible-nav").each(function () {
                var container = $(this);
                var list = $(this).find('ul');
                var tog = "<a class='collapsible-nav-toggle' href='#'><i class='fa fa-bars'></i> More</a>";
                
                if (list.height() > container.height()) {
                    if (container.find('.collapsible-nav-toggle').length === 0)
                        container.prepend(tog);
                } else {
                    container.find('.collapsible-nav-toggle').remove();
                    container.removeClass("open");
                }
            });

            $(".collapsible-nav-toggle").off("click.more touchstart.more");
            $(".collapsible-nav-toggle").on("click.more touchstart.more", function (e) {
                $(this).closest('.collapsible-nav').toggleClass("open");
                e.preventDefault();
                return false;
            });

            // collapse nav if item clicked/tapped
            $(".collapsible-nav li a").off("click.more touchstart.more");
            $(".collapsible-nav li a").on("click.more touchstart.more", function (e) {
                $(this).closest('.collapsible-nav').removeClass("open");
                e.preventDefault();
                return false;
            });
        }

        processSecondaryNav();

        $(window).on("resize", function () {
            processSecondaryNav();
        });
    });
})(jQuery);;
var dynamicDataTable = (function ($, w) {

    var classes = {
        plugin: 'dynamic-table'
    };

    var cls = function (options) {
        var _this = this,
            _data,
            _fixedHeader,
            _headings;

        var defaults = {
            dataTables: {
                'bAutoWidth': false,
                'bFilter': false,
                'bInfo': false,
                'bPaginate': false,
                'bLengthChange': false,
                'aaData': {},
                'fnDrawCallback': function (oSettings) { }
            },
            events: {
                hide: function (ht) { },
                show: function (ht) { },
                sort: function (ht, column, direction) { }
            },
            fixedHeader: {
                enabled: false,
                topOffset: 0
            },
            table: {
                data: [],
                headings: [],
                selector: '',
                width: '100%'
            },
            useDataTable: true
        };

        options = $.extend(true, defaults, options);

        var $element;

        /**************************
        * Initializers
        **************************/
        var _init = function () {
            $element = $(options.table.selector);

            _buildTable(options.table);
            _initDataTable();
        }

        var _initDataTable = function () {
            if (options.useDataTable) {
                $element.css('width', options.table.width);
                $element.dataTable().fnDestroy();
                $element.dataTable(options.dataTables);

                $element.find('thead th').bind('click', function (e) {
                    var $cell = $(this);

                    // get aria-sort from table
                    var i = $(this).filter(function () { return $(this).text() === $cell.text(); }).index();
                    if (i > -1 && _fixedHeader !== undefined) {
                        var guid = $cell.data('wfh-id');
                        if (guid !== undefined) {
                            var $headerCell = $element.find('td,th[data-wfh-id="' + guid + '"]');
                            if ($headerCell.length > 0)
                                $cell.attr('aria-sort', $headerCell.attr('aria-sort'));
                        }
                    }

                    var thClass = $(this).attr('class');
                    var sort = $(this).attr('aria-sort');
                    var col = w.utility.stripHtmlTags($(this).html());
                    
                    // check for sorting_asc/sorting_desc if aria-sort not there
                    if (sort === undefined && thClass !== undefined && thClass.indexOf('sorting_') > -1) {
                        if (thClass.indexOf('sorting_asc') > -1) sort = 'ascending';
                        else if (thClass.indexOf('sorting_desc') > -1) sort = 'descending';
                    }

                    if (typeof options.events.sort === 'function')
                        options.events.sort(_this, col, sort);
                });
            }

            if (options.fixedHeader.enabled) {
                _fixedHeader = new wFixedHeader({
                    table: $element,
                    topOffset: options.fixedHeader.topOffset,
                    wrapper: $element.closest('.' + classes.plugin)
                });
            }
        }

        /**************************
        * Functional
        **************************/
        var _buildTable = function (tableOptions) {
            if (tableOptions.headings !== undefined && tableOptions.headings.length > 0) {
                _headings = tableOptions.headings;
                _buildHeadings(tableOptions.headings);
            }

            if (tableOptions.data !== undefined && tableOptions.data.length > 0) {
                _data = tableOptions.data;
                _buildData(tableOptions.data);
            }
        }

        var _buildHeadings = function (headings) {
            var thead = '<tr>';

            headings.foreach(function (h, i) {
                var thTemp = new w.templater({ html: '<th{{attr}}>{{content}}</th>' });

                if (typeof h === 'object') {
                    thTemp.fields.attr = _getAttribs(h.attr);
                    thTemp.fields.content = h.name;
                }
                else {
                    thTemp.fields.content = h;
                }

                thead += thTemp.html();
            });

            thead += '</tr>';
            $element.find('thead').html($(thead));
            $element.wrap('<div class="' + classes.plugin + '"></div>');
        }

        var _buildData = function (data) {
            var tbody = [];

            data.foreach(function (d) {
                var row = d;
                var rowTemp = new w.templater({ html: '<tr {{htmlClass}}>' });

                if (d.data !== undefined)
                    row = d.data;

                if (d.rowClass !== undefined)
                    rowTemp.fields.htmlClass = 'class="' + d.rowClass + '"';

                tbody.push(rowTemp.html());
                row.foreach(function (r) {
                    var colTemp = new w.templater({ html: '<{{td}} {{htmlClass}} {{attr}}>{{label}}{{sort}}</{{td}}>' });

                    if (r.sort !== undefined)
                        colTemp.fields.sort = '<input type="hidden" data-sort="' + r.sort + '" value="' + r.value + '">';

                    if (r.htmlClass !== undefined)
                        colTemp.fields.htmlClass = 'class="' + r.htmlClass + '"';

                    colTemp.fields.attr = _getAttribs(r.attr);
                    colTemp.fields.label = r.label;
                    colTemp.fields.td = r.isHeader !== undefined && r.isHeader ? 'th' : 'td';
                    tbody.push(colTemp.html());
                });
                tbody.push('</tr>');
            });

            $element.find('tbody').html($(tbody.join('')));
        }

        var _getAttribs = function (attributes) {
            var attribs = '';
            if (attributes !== undefined) {
                var delim = ' ';
                attributes.foreach(function (a) {
                    attribs += delim + a[0] + '="' + $('<div/>').text(a[1]).html() + '"';
                });
            }
            return attribs;
        }

        var _isDataTable = function () {
            if (options.useDataTable) {
                var settings = $.fn.dataTableSettings;
                for (var i = 0, iLen = settings.length; i < iLen; i++)
                    if (settings[i].nTable == $element[0])
                        return true;
            }
            return false;
        }

        /**************************
        * Public methods
        **************************/
        this.cleanTable = function () {
            if (_isDataTable())
                $element.fnDestroy();
            $element.find('thead').empty();
            $element.find('tbody').empty();
        }

        this.data = function () {
            return _data;
        }

        this.headings = function () {
            return _headings;
        }

        this.resetData = function (data) {
            _data = data;
            _buildData(data);
        }

        this.resetDataTable = function (optionsArray) {
            if (optionsArray !== undefined && optionsArray.length > 0) {
                for (var i = 0; i < optionsArray.length; i++)
                    options.dataTables[optionsArray[i][0]] = optionsArray[i][1];
            }

            _initDataTable();
        }

        this.resetHeadings = function (headings) {
            _headings = headings;
            _buildHeadings(headings);
        }

        _init();
    }

    return cls;

})(jQuery, webitects);;
var dataTableExporter = (function ($, w) {

    var cls = function (options) {
        var _this = this;
        var defaults = {
            events: {
                beforeExport: function (exporter, form) { },
                dataFilter: function (exporter, data) { return data; },
                onExport: function (exporter) { }
            },
            exportOpts: {
                citationLink: '',
                filename: 'export.xlsx',
                title: 'Export',
                url: setup.url.exportOpts.data
            },
            selectors: {
                downloadLink: '',
                table: ''
            }
        };

        options = $.extend(true, defaults, options);

        var $downloadForm,
            $downloadLink,
            $exportData,
            $table,
            $wrapper;

        var vars = {
            classes: {
                wrapper: 'dt-exporter'
            },
            options: {},
            processing: false
        };

        var fn = {
            init: function () {
                vars.options = options;

                $downloadLink = $(vars.options.selectors.downloadLink);
                $table = $(vars.options.selectors.table);

                $downloadLink.wrap($('<div class="' + vars.classes.wrapper + '"></div>'));
                $wrapper = $downloadLink.closest('.' + vars.classes.wrapper);
                $wrapper.append($('<form action="' + vars.options.exportOpts.url + '" method="post"></form>'));
                $downloadForm = $wrapper.find('form');

                $downloadForm.append($('<input type="hidden" name="data" value="" />'));
                $downloadForm.append($('<input type="hidden" name="title" value="' + vars.options.exportOpts.title + '" />'));
                $downloadForm.append($('<input type="hidden" name="filename" value="' + vars.options.exportOpts.filename + '" />'));
                $downloadForm.append($('<input type="hidden" name="citationLink" value="' + vars.options.exportOpts.citationLink + '" />'));
                $exportData = $downloadForm.find("input[name='data']");

                fn.initDownloadLink();
            },

            initDownloadLink: function () {
                $downloadLink.bind('click', function (e) {
                    if (!vars.processing) {
                        w.call(vars.options.events.beforeExport, _this, $downloadForm);
                        vars.processing = true;

                        var data = fn.processDataForExport();
                        data = w.call(vars.options.events.dataFilter, _this, data);

                        $exportData.val(data);
                        $downloadForm.submit();

                        vars.processing = false;
                    }

                    e.preventDefault;
                    return false;
                });
            },

            processDataForExport: function () {
                var delim = '';
                var rowDelim = '';
                var json = [];
                json.push('[');

                // build header
                $table.find('thead tr').each(function (i) {
                    delim = '';
                    json.push(rowDelim + '[');
                    $(this).find('th').each(function () {
                        var $this = $(this);
                        if ($this.css('display') != 'none') {
                            json.push(delim + fn.cellToObject($this));
                            delim = ',';
                        }
                    });
                    json.push(']');
                    rowDelim = ',';
                });

                // build body
                $table.find('tbody tr').each(function (i) {
                    delim = '';
                    json.push(rowDelim + '[');
                    $(this).find('td,th').each(function () {
                        var $this = $(this);
                        if ($this.css('display') != 'none') {
                            json.push(delim + fn.cellToObject($this));
                            delim = ',';
                        }
                    });
                    json.push(']');
                });

                json.push(']');
                return json.join('');
            },

            cellToObject: function ($cell) {
                var overrideText = $cell.data('export-override-text');
                var text = ($cell.html() + '').toString();
                var colspan = $cell.attr('colspan') !== undefined ? $cell.attr('colspan') : 1;
                var rowspan = $cell.attr('rowspan') !== undefined ? $cell.attr('rowspan') : 1;

                if (overrideText !== undefined)
                    text = overrideText.toString();

                // escape quotes
                text = text.replace(/\"/g, '\\\"').trim();

                return JSON.stringify({
                    colspan: colspan,
                    rowspan: rowspan,
                    th: $cell.is('th'),
                    value: utility.stripHtmlTags(text)
                });
            }
        };

        fn.init();

        this.title = function (title) {
            if (arguments.length === 1) {
                vars.options.exportOpts.title = title;
                $downloadForm.find("input[name='title']").val(title);
            }
            else
                return vars.options.exportOpts.title;
        }

        this.filename = function (filename) {
            if (arguments.length === 1) {
                vars.options.exportOpts.filename = filename;
                $downloadForm.find("input[name='filename']").val(filename);
            }
            else
                return vars.options.exportOpts.filename;
        }

        this.citationLink = function (link) {
            if (arguments.length === 1) {
                vars.options.exportOpts.citationLink = link;
                $downloadForm.find("input[name='citationLink']").val(link);
            }
            else
                return vars.options.exportOpts.citationLink;
        }
    }

    return cls;

})(jQuery, webitects);;
(function ($) {
    $.wCheckbox = function (element, opts) {
        var defaults = $.extend({
            change: function (checkbox, value) { }
        }, opts);

        var plugin = this;
        var settings;
        plugin.settings = {};

        var $checkbox,
            $document = $(document),
            $element = $(element), element = element,
            $hidden;

        var vars = {
            classes: {
                link: 'wcheckbox-link',
                hidden: 'wcheckbox-hidden',
                wrapper: 'wcheckbox'
            },
            evtPrefix: 'wcheckbox.',
            firstCall: true,
            html: {
                checked: '<i class="fa fa-check-square"></i>',
                unchecked: '<i class="fa fa-square-o"></i>'
            },
            value: undefined
        }

        var eventName = {
            focus: vars.evtPrefix + 'focus'
        }

        plugin.init = function () {
            plugin.settings = $.extend(true, defaults, opts);
            settings = plugin.settings;

            fn.initCheckbox();
        }

        var fn = {
            /***********************
            * Initializers
            ***********************/
            initCheckbox: function () {
                var attribs = fn.getAttribs($element);
                var hasLabel = $element.parent().is('label');
                var isChecked = $element.prop('checked');
                var html = isChecked ? vars.html.checked : vars.html.unchecked;
                var labelHtml = hasLabel ? $element.parent().html().replace(/<input (.*type="checkbox")([^>]*)>/g, '') : '';
                var name = $element.attr('name') !== undefined ? 'name="' + $element.attr('name') + '"' : '';
                
                vars.value = $element.val();

                var cbHtml = '';
                cbHtml += '<div' + attribs + '>';
                cbHtml += '<input class="' + vars.classes.hidden + '" type="hidden" ' + name + ' value="' + vars.value + '" />';
                cbHtml += '<a class="' + vars.classes.link + '" href="#">' + html + labelHtml + '</a>';
                cbHtml += '</div>';
                
                $checkbox = $(cbHtml);

                if (hasLabel) {
                    $element.parent().replaceWith($checkbox);
                }
                else {
                    $element.replaceWith($checkbox);
                }

                $checkbox.addClass(vars.classes.wrapper);
                $hidden = $checkbox.find('.' + vars.classes.hidden);

                $checkbox.uniqueId();                

                fn.overrideJQueryFunc('val', function (value) {
                    if (arguments.length === 1) {
                        vars.value = value;
                        $checkbox.attr('value', value);
                        $hidden.val(value);
                    }
                    else {
                        return vars.value;
                    }
                });

                fn.overrideJQueryFunc('checked', function (value, fireChangeEvent) {
                    if (arguments.length >= 1)                       
                        fn.check(value);

                    if (arguments.length === 2) {
                        if(fireChangeEvent)
                            $element.trigger('change');
                    }
                });

                $element = $checkbox;
                $element.prop('checked', fn.isChecked());
                $element.val(vars.value);
                fn.initClick();
            },

            initClick: function () {
                $element.bind('click', function (e) {
                    fn.check(!fn.isChecked());
                    $element.trigger('change');

                    e.preventDefault();
                    return false;
                });
            },

            /***********************
            * Event handler
            ***********************/            

            /***********************
            * Functional
            ***********************/
            check: function(doCheck) {
                if (doCheck) {
                    $element.attr('checked', '');
                    $element.find('.' + vars.classes.link).find('i').replaceWith($(vars.html.checked));
                }
                else {
                    $element.removeAttr('checked');
                    $element.find('.' + vars.classes.link).find('i').replaceWith($(vars.html.unchecked));
                }

                $element.prop('checked', fn.isChecked());
            },

            isChecked: function() {
                return $element.is('[checked]');
            },

            getAttribs: function ($e) {
                var attribs = $e[0].attributes;
                var attribsHtml = '';
                var delim = ' ';

                for (var i = 0; i < attribs.length; i++) {
                    attribsHtml += delim + attribs[i].name + '="' + $('<div/>').text(attribs[i].value).html() + '"';
                };

                return attribsHtml;
            },

            overrideJQueryFunc: function (method, action) {
                var originalVal = $.fn[method];
                var thisNode = $checkbox;
                $.fn[method] = function () {
                    if (this[0] == thisNode[0]) {
                        return action.apply(this, arguments);
                    } else {
                        return originalVal.apply(this, arguments);
                    }
                };
            }
        }

        plugin.init();

        plugin.value = function () {
            return vars.value;
        }
    };

    $.fn.wCheckbox = function (opts) {
        return this.each(function () {
            if ($(this).data('wCheckbox') == undefined) {
                var plugin = new $.wCheckbox(this, opts);
                $(this).data('wCheckbox', plugin);
            }
        });
    };

})(jQuery);;
(function ($) {
    $.wDropdown = function (element, opts) {
        var defaults = $.extend(true, {
            change: function (dropdown, value, htmlValue) { }
        }, opts);

        var plugin = this;
        var settings;
        plugin.settings = {};

        var $document = $(document),
            $dropdown,
            $element = $(element), element = element,
            $input,
            $list,
            $listUl,
            $selected;

        var vars = {
            classes: {
                currentItem: 'current',
                item: 'item',
                list: 'menu',
                selected: 'selected',
                wrapper: 'wdropdown'
            },
            events: [],
            firstCall: true,
            html: {
                arrowDown: '<i class="fa fa-caret-down"></i>'
            },
            value: undefined
        }

        plugin.init = function () {
            // get original events from $element
            var events = $._data($element.get(0), 'events');
            if (events !== undefined) {
                $.each($._data($element.get(0), 'events'), function () {
                    $.each(this, function () {
                        vars.events.push(this);
                    });
                });
            }

            plugin.settings = $.extend(true, defaults, opts);
            settings = plugin.settings;

            fn.initDropdown();
            fn.initSelect();
            fn.initTabIndex();
            fn.initKeys();
        }

        var fn = {
            /***********************
            * Initializers
            ***********************/
            initDropdown: function () {
                var dropdownAttribs = fn.getAttribs($element);

                var width = 0;

                $input = $('<input type="hidden" ' + ($element.attr('name') !== undefined ? 'name="' + $element.attr('name') + '"' : '') + ' value="' + $element.val() + '" />');

                $dropdown = $('<div' + dropdownAttribs + '>' + $element.html() + '</div>');
                $element.replaceWith($dropdown);

                $dropdown.addClass(vars.classes.wrapper);
                $dropdown.empty();
                $dropdown.append($input);

                fn.setUniqueId($dropdown);

                $dropdown.append($('<a class="' + vars.classes.selected + '">' + vars.html.arrowDown + '</a><div class="' + vars.classes.list + '"><ul></ul></div>'));
                $list = $dropdown.find('.' + vars.classes.list);
                $listUl = $list.find('ul');
                $selected = $dropdown.find('.' + vars.classes.selected);

                $listUl.append($element.html());
                var $selectedOpt;

                $listUl.find('option').each(function () {
                    $opt = $(this);

                    var attribs = fn.getAttribs($opt);
                    var $li = $('<li' + attribs + '><a>' + $opt.html() + '</a></il>');
                    $opt.replaceWith($li);
                    $li.addClass(vars.classes.item);

                    if ($opt.prop('selected'))
                        $selectedOpt = $li;
                });

                $list.hide();

                // select pre-select option or first option
                if ($selectedOpt !== undefined)
                    fn.setSelected($selectedOpt);
                else if ($listUl.find('.' + vars.classes.item).length > 0)
                    fn.setSelected($listUl.find('.' + vars.classes.item).first());

                // jquery val override
                fn.overrideJQueryFunc('val', function (value) {
                    if (arguments.length >= 1) {
                        var $li = $listUl.find('li[data-value="' + value + '"]');
                        var triggerChange = arguments.length === 2 ? arguments[1] : true;
                        if ($li.length > 0)
                            fn.setSelected($li, triggerChange);
                    }
                    else
                        return vars.value;
                });

                // assign original events to dropdown
                for (var i = 0; i < vars.events.length; i++)
                    $dropdown.on(vars.events[i].type, vars.events[i].handler);

                $dropdown.on('blur', fn.onBlur);
                $dropdown.on('focus', fn.onFocus);
            },

            initSelect: function () {
                $selected.bind('click', function (e) {
                    $list.toggle();
                    $dropdown.focus();
                    e.preventDefault();
                    return false;
                });

                $('html').bind('click', function (e) {
                    if (fn.isOpen())
                        $list.toggle();
                    $dropdown.blur();
                });

                $listUl.find('.' + vars.classes.item).each(function () {
                    $(this).on('mouseover', function () {
                        $listUl.find('.' + vars.classes.item).removeClass(vars.classes.currentItem);
                    });

                    var clickEvent = fn.isIE() ? 'mousedown' : 'click';

                    $(this).on(clickEvent, function (e) {
                        $opt = $(this);
                        fn.setSelected($opt);
                        $list.toggle();

                        e.preventDefault();
                        return false;
                    });
                });
            },

            initTabIndex: function () {
                var $prevInput = $dropdown.prev('input,select,textarea,.wdropdown');
                var inputArray = $('input[type!="hidden"],select,textarea,.wdropdown,[tabindex]').toArray();
                var idx = 0;

                for (var i = 0; i < inputArray.length; i++) {
                    if ($dropdown[0] === inputArray[i]) {
                        idx = i;
                        break;
                    }
                }

                if (inputArray.length > 0 && idx > 0) {
                    var $prevInput = $(inputArray[idx - 1]);
                    $prevInput.on('keydown', function (e) {
                        var keyCode = e.keyCode || e.which;
                        if (keyCode === 9) {
                            $dropdown.focus();
                            return false;
                        }
                    });

                    $dropdown.attr('tabindex', idx);
                }
                else
                    $dropdown.attr('tabindex', idx);
            },

            initKeys: function () {
                $dropdown.on('keydown', function (e) {
                    var $opt = fn.getOption();
                    var keyCode = e.keyCode || e.which;

                    if (keyCode === 9) {
                        // TAB
                        var inputArray = $('input[type!="hidden"],select,textarea,.wdropdown,[tabindex]').toArray();
                        var idx = 0;

                        for (var i = 0; i < inputArray.length; i++) {
                            if ($dropdown[0] === inputArray[i]) {
                                idx = i;
                                break;
                            }
                        }

                        if (idx < inputArray.length)
                            $(inputArray[idx + 1]).focus();
                        $dropdown.blur();

                        return false;
                    }
                    else if (keyCode === 13) {
                        // ENTER                        
                        fn.setSelected($opt);
                        $list.hide();
                        return false;
                    }
                    else if (keyCode === 27) {
                        // ESC
                        fn.setSelected($opt);
                        $dropdown.blur();
                    }
                    else if (keyCode === 38 || keyCode === 40) {
                        // UP or DOWN
                        var optsCount = $listUl.find('.' + vars.classes.item).length;
                        var triggerChange = !fn.isOpen();
                        if (optsCount > 0 && $opt !== undefined) {
                            var idx = $opt.index();

                            if (keyCode === 38) {
                                // UP
                                idx = idx - 1;
                                if (idx < 0) fn.setSelected($listUl.find('.' + vars.classes.item).last(), triggerChange);
                                else fn.setSelected($opt.prev(), triggerChange);
                            }
                            else if (keyCode === 40) {
                                // DOWN
                                idx = idx + 1;
                                if (idx >= optsCount) fn.setSelected($listUl.find('.' + vars.classes.item).first(), triggerChange);
                                else fn.setSelected($opt.next(), triggerChange);
                            }
                        }
                        return false;
                    }
                });
            },

            /***********************
            * Event handler
            ***********************/
            onBlur: function (e) {
                $dropdown.removeClass('focus');
                $list.hide();
            },

            onFocus: function (e) {
                $dropdown.addClass('focus');
            },

            /***********************
            * Functional
            ***********************/
            getAttribs: function ($e) {
                var attribs = $e[0].attributes;
                var attribsHtml = '';
                var delim = ' ';

                for (var i = 0; i < attribs.length; i++) {
                    var name = attribs[i].name;
                    var val = attribs[i].value;
                    name = name === 'value' ? 'data-value' : name;
                    attribsHtml += delim + name + '="' + $('<div/>').text(val).html() + '"';
                };

                return attribsHtml;
            },

            getOption: function () {
                if (vars.value !== undefined)
                    return $listUl.find('.' + vars.classes.item + '[data-value="' + vars.value + '"]').first();
                else
                    return $listUl.find('.' + vars.classes.item).first();
            },

            guid: function () {
                var g = (fn.s4() + fn.s4() + "-" + fn.s4() + "-4" + fn.s4().substr(0, 3) + "-" + fn.s4() + "-" + fn.s4() + fn.s4() + fn.s4()).toLowerCase();
                return g;
            },

            isIE: function () {
                var nav = navigator.userAgent.toLowerCase();
                return /msie|.net/.test(nav);
            },

            isOpen: function () {
                return $list.css('display') === 'block';
            },

            overrideJQueryFunc: function (method, action) {
                var originalVal = $.fn[method];
                var thisNode = $element;
                var dropNode = $dropdown;
                $.fn[method] = function () {
                    if (this[0] == thisNode[0] || this[0] == dropNode[0]) {
                        return action.apply(this, arguments);
                    }
                    else
                        return originalVal.apply(this, arguments);
                };
            },

            setSelected: function ($opt, triggerChange) {
                triggerChange = triggerChange === undefined ? true : triggerChange;
                var value = $opt.data('value');
                var label = $opt.find('a').html();
                $selected.html(label + ' ' + vars.html.arrowDown);
                $listUl.find('.' + vars.classes.item).removeClass(vars.classes.currentItem);
                $listUl.find('.' + vars.classes.item).removeAttr('selected');
                $opt.closest('li').addClass(vars.classes.currentItem);
                $opt.closest('li').attr('selected', true);

                vars.value = value;

                if (!vars.firstCall && triggerChange) {
                    $dropdown.change();
                    $input.val(value);
                    $element.change();

                    if (typeof settings.change === 'function')
                        settings.change($dropdown, vars.value, label);
                }

                vars.firstCall = false;
            },

            setUniqueId: function ($e) {
                if ($e.attr('id') === undefined)
                    $e.attr('id', fn.guid());
            },

            s4: function () {
                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
            }
        }

        /***********************
        * Public methods
        ***********************/
        plugin.fnGetItems = function () {
            return $listUl.find('.' + vars.classes.item);
        }

        plugin.fnGetSelected = function () {
            return fn.getOption();
        }

        plugin.value = function () {
            return vars.value;
        }

        /***********************
        * Run plugin grand init
        ***********************/
        plugin.init();
    };

    $.fn.wDropdown = function (opts) {
        return this.each(function () {
            if ($(this).data('wDropdown') == undefined) {
                var plugin = new $.wDropdown(this, opts);
                $(this).data('wDropdown', plugin);
            }
        });
    };

})(jQuery);;
