/* Minification failed. Returning unminified contents.
(19755,5838-5843): run-time warning JS1010: Expected identifier: catch
(19755,5838-5843): run-time error JS1137: 'catch' is a new reserved word and should not be used as an identifier: catch
 */
/*
 * ----------------------------- JSTORAGE -------------------------------------
 * Simple local storage wrapper to save data on the browser side, supporting
 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
 *
 * Author: Andris Reinman, andris.reinman@gmail.com
 * Project homepage: www.jstorage.info
 *
 * Licensed under Unlicense:
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * For more information, please refer to <http://unlicense.org/>
 */

/* global ActiveXObject: false */
/* jshint browser: true */

(function() {
    'use strict';

    var
    /* jStorage version */
        JSTORAGE_VERSION = '0.4.12',

        /* detect a dollar object or create one if not found */
        $ = window.jQuery || window.$ || (window.$ = {}),

        /* check for a JSON handling support */
        JSON = {
            parse: window.JSON && (window.JSON.parse || window.JSON.decode) ||
                String.prototype.evalJSON && function(str) {
                    return String(str).evalJSON();
            } ||
                $.parseJSON ||
                $.evalJSON,
            stringify: Object.toJSON ||
                window.JSON && (window.JSON.stringify || window.JSON.encode) ||
                $.toJSON
        };

    // Break if no JSON support was found
    if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') {
        throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page');
    }

    var
    /* This is the object, that holds the cached values */
        _storage = {
            __jstorage_meta: {
                CRC32: {}
            }
        },

        /* Actual browser storage (localStorage or globalStorage['domain']) */
        _storage_service = {
            jStorage: '{}'
        },

        /* DOM element for older IE versions, holds userData behavior */
        _storage_elm = null,

        /* How much space does the storage take */
        _storage_size = 0,

        /* which backend is currently used */
        _backend = false,

        /* onchange observers */
        _observers = {},

        /* timeout to wait after onchange event */
        _observer_timeout = false,

        /* last update time */
        _observer_update = 0,

        /* pubsub observers */
        _pubsub_observers = {},

        /* skip published items older than current timestamp */
        _pubsub_last = +new Date(),

        /* Next check for TTL */
        _ttl_timeout,

        /**
         * XML encoding and decoding as XML nodes can't be JSON'ized
         * XML nodes are encoded and decoded if the node is the value to be saved
         * but not if it's as a property of another object
         * Eg. -
         *   $.jStorage.set('key', xmlNode);        // IS OK
         *   $.jStorage.set('key', {xml: xmlNode}); // NOT OK
         */
        _XMLService = {

            /**
             * Validates a XML node to be XML
             * based on jQuery.isXML function
             */
            isXML: function(elm) {
                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
                return documentElement ? documentElement.nodeName !== 'HTML' : false;
            },

            /**
             * Encodes a XML node to string
             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
             */
            encode: function(xmlNode) {
                if (!this.isXML(xmlNode)) {
                    return false;
                }
                try { // Mozilla, Webkit, Opera
                    return new XMLSerializer().serializeToString(xmlNode);
                } catch (E1) {
                    try { // IE
                        return xmlNode.xml;
                    } catch (E2) {}
                }
                return false;
            },

            /**
             * Decodes a XML node from string
             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
             */
            decode: function(xmlString) {
                var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) ||
                    (window.ActiveXObject && function(_xmlString) {
                        var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
                        xml_doc.async = 'false';
                        xml_doc.loadXML(_xmlString);
                        return xml_doc;
                    }),
                    resultXML;
                if (!dom_parser) {
                    return false;
                }
                resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml');
                return this.isXML(resultXML) ? resultXML : false;
            }
        };


    ////////////////////////// PRIVATE METHODS ////////////////////////

    /**
     * Initialization function. Detects if the browser supports DOM Storage
     * or userData behavior and behaves accordingly.
     */
    function _init() {
        /* Check if browser supports localStorage */
        var localStorageReallyWorks = false;
        if ('localStorage' in window) {
            try {
                window.localStorage.setItem('_tmptest', 'tmpval');
                localStorageReallyWorks = true;
                window.localStorage.removeItem('_tmptest');
            } catch (BogusQuotaExceededErrorOnIos5) {
                // Thanks be to iOS5 Private Browsing mode which throws
                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
            }
        }

        if (localStorageReallyWorks) {
            try {
                if (window.localStorage) {
                    _storage_service = window.localStorage;
                    _backend = 'localStorage';
                    _observer_update = _storage_service.jStorage_update;
                }
            } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ }
        }
        /* Check if browser supports globalStorage */
        else if ('globalStorage' in window) {
            try {
                if (window.globalStorage) {
                    if (window.location.hostname == 'localhost') {
                        _storage_service = window.globalStorage['localhost.localdomain'];
                    } else {
                        _storage_service = window.globalStorage[window.location.hostname];
                    }
                    _backend = 'globalStorage';
                    _observer_update = _storage_service.jStorage_update;
                }
            } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ }
        }
        /* Check if browser supports userData behavior */
        else {
            _storage_elm = document.createElement('link');
            if (_storage_elm.addBehavior) {

                /* Use a DOM element to act as userData storage */
                _storage_elm.style.behavior = 'url(#default#userData)';

                /* userData element needs to be inserted into the DOM! */
                document.getElementsByTagName('head')[0].appendChild(_storage_elm);

                try {
                    _storage_elm.load('jStorage');
                } catch (E) {
                    // try to reset cache
                    _storage_elm.setAttribute('jStorage', '{}');
                    _storage_elm.save('jStorage');
                    _storage_elm.load('jStorage');
                }

                var data = '{}';
                try {
                    data = _storage_elm.getAttribute('jStorage');
                } catch (E5) {}

                try {
                    _observer_update = _storage_elm.getAttribute('jStorage_update');
                } catch (E6) {}

                _storage_service.jStorage = data;
                _backend = 'userDataBehavior';
            } else {
                _storage_elm = null;
                return;
            }
        }

        // Load data from storage
        _load_storage();

        // remove dead keys
        _handleTTL();

        // start listening for changes
        _setupObserver();

        // initialize publish-subscribe service
        _handlePubSub();

        // handle cached navigation
        if ('addEventListener' in window) {
            window.addEventListener('pageshow', function(event) {
                if (event.persisted) {
                    _storageObserver();
                }
            }, false);
        }
    }

    /**
     * Reload data from storage when needed
     */
    function _reloadData() {
        var data = '{}';

        if (_backend == 'userDataBehavior') {
            _storage_elm.load('jStorage');

            try {
                data = _storage_elm.getAttribute('jStorage');
            } catch (E5) {}

            try {
                _observer_update = _storage_elm.getAttribute('jStorage_update');
            } catch (E6) {}

            _storage_service.jStorage = data;
        }

        _load_storage();

        // remove dead keys
        _handleTTL();

        _handlePubSub();
    }

    /**
     * Sets up a storage change observer
     */
    function _setupObserver() {
        if (_backend == 'localStorage' || _backend == 'globalStorage') {
            if ('addEventListener' in window) {
                window.addEventListener('storage', _storageObserver, false);
            } else {
                document.attachEvent('onstorage', _storageObserver);
            }
        } else if (_backend == 'userDataBehavior') {
            setInterval(_storageObserver, 1000);
        }
    }

    /**
     * Fired on any kind of data change, needs to check if anything has
     * really been changed
     */
    function _storageObserver() {
        var updateTime;
        // cumulate change notifications with timeout
        clearTimeout(_observer_timeout);
        _observer_timeout = setTimeout(function() {

            if (_backend == 'localStorage' || _backend == 'globalStorage') {
                updateTime = _storage_service.jStorage_update;
            } else if (_backend == 'userDataBehavior') {
                _storage_elm.load('jStorage');
                try {
                    updateTime = _storage_elm.getAttribute('jStorage_update');
                } catch (E5) {}
            }

            if (updateTime && updateTime != _observer_update) {
                _observer_update = updateTime;
                _checkUpdatedKeys();
            }

        }, 25);
    }

    /**
     * Reloads the data and checks if any keys are changed
     */
    function _checkUpdatedKeys() {
        var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
            newCrc32List;

        _reloadData();
        newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));

        var key,
            updated = [],
            removed = [];

        for (key in oldCrc32List) {
            if (oldCrc32List.hasOwnProperty(key)) {
                if (!newCrc32List[key]) {
                    removed.push(key);
                    continue;
                }
                if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') {
                    updated.push(key);
                }
            }
        }

        for (key in newCrc32List) {
            if (newCrc32List.hasOwnProperty(key)) {
                if (!oldCrc32List[key]) {
                    updated.push(key);
                }
            }
        }

        _fireObservers(updated, 'updated');
        _fireObservers(removed, 'deleted');
    }

    /**
     * Fires observers for updated keys
     *
     * @param {Array|String} keys Array of key names or a key
     * @param {String} action What happened with the value (updated, deleted, flushed)
     */
    function _fireObservers(keys, action) {
        keys = [].concat(keys || []);

        var i, j, len, jlen;

        if (action == 'flushed') {
            keys = [];
            for (var key in _observers) {
                if (_observers.hasOwnProperty(key)) {
                    keys.push(key);
                }
            }
            action = 'deleted';
        }
        for (i = 0, len = keys.length; i < len; i++) {
            if (_observers[keys[i]]) {
                for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
                    _observers[keys[i]][j](keys[i], action);
                }
            }
            if (_observers['*']) {
                for (j = 0, jlen = _observers['*'].length; j < jlen; j++) {
                    _observers['*'][j](keys[i], action);
                }
            }
        }
    }

    /**
     * Publishes key change to listeners
     */
    function _publishChange() {
        var updateTime = (+new Date()).toString();

        if (_backend == 'localStorage' || _backend == 'globalStorage') {
            try {
                _storage_service.jStorage_update = updateTime;
            } catch (E8) {
                // safari private mode has been enabled after the jStorage initialization
                _backend = false;
            }
        } else if (_backend == 'userDataBehavior') {
            _storage_elm.setAttribute('jStorage_update', updateTime);
            _storage_elm.save('jStorage');
        }

        _storageObserver();
    }

    /**
     * Loads the data from the storage based on the supported mechanism
     */
    function _load_storage() {
        /* if jStorage string is retrieved, then decode it */
        if (_storage_service.jStorage) {
            try {
                _storage = JSON.parse(String(_storage_service.jStorage));
            } catch (E6) {
                _storage_service.jStorage = '{}';
            }
        } else {
            _storage_service.jStorage = '{}';
        }
        _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;

        if (!_storage.__jstorage_meta) {
            _storage.__jstorage_meta = {};
        }
        if (!_storage.__jstorage_meta.CRC32) {
            _storage.__jstorage_meta.CRC32 = {};
        }
    }

    /**
     * This functions provides the 'save' mechanism to store the jStorage object
     */
    function _save() {
        _dropOldEvents(); // remove expired events
        try {
            _storage_service.jStorage = JSON.stringify(_storage);
            // If userData is used as the storage engine, additional
            if (_storage_elm) {
                _storage_elm.setAttribute('jStorage', _storage_service.jStorage);
                _storage_elm.save('jStorage');
            }
            _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
        } catch (E7) { /* probably cache is full, nothing is saved this way*/ }
    }

    /**
     * Function checks if a key is set and is string or numberic
     *
     * @param {String} key Key name
     */
    function _checkKey(key) {
        if (typeof key != 'string' && typeof key != 'number') {
            throw new TypeError('Key name must be string or numeric');
        }
        if (key == '__jstorage_meta') {
            throw new TypeError('Reserved key name');
        }
        return true;
    }

    /**
     * Removes expired keys
     */
    function _handleTTL() {
        var curtime, i, TTL, CRC32, nextExpire = Infinity,
            changed = false,
            deleted = [];

        clearTimeout(_ttl_timeout);

        if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') {
            // nothing to do here
            return;
        }

        curtime = +new Date();
        TTL = _storage.__jstorage_meta.TTL;

        CRC32 = _storage.__jstorage_meta.CRC32;
        for (i in TTL) {
            if (TTL.hasOwnProperty(i)) {
                if (TTL[i] <= curtime) {
                    delete TTL[i];
                    delete CRC32[i];
                    delete _storage[i];
                    changed = true;
                    deleted.push(i);
                } else if (TTL[i] < nextExpire) {
                    nextExpire = TTL[i];
                }
            }
        }

        // set next check
        if (nextExpire != Infinity) {
            _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF));
        }

        // save changes
        if (changed) {
            _save();
            _publishChange();
            _fireObservers(deleted, 'deleted');
        }
    }

    /**
     * Checks if there's any events on hold to be fired to listeners
     */
    function _handlePubSub() {
        var i, len;
        if (!_storage.__jstorage_meta.PubSub) {
            return;
        }
        var pubelm,
            _pubsubCurrent = _pubsub_last,
            needFired = [];

        for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
            pubelm = _storage.__jstorage_meta.PubSub[i];
            if (pubelm[0] > _pubsub_last) {
                _pubsubCurrent = pubelm[0];
                needFired.unshift(pubelm);
            }
        }

        for (i = needFired.length - 1; i >= 0; i--) {
            _fireSubscribers(needFired[i][1], needFired[i][2]);
        }

        _pubsub_last = _pubsubCurrent;
    }

    /**
     * Fires all subscriber listeners for a pubsub channel
     *
     * @param {String} channel Channel name
     * @param {Mixed} payload Payload data to deliver
     */
    function _fireSubscribers(channel, payload) {
        if (_pubsub_observers[channel]) {
            for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
                // send immutable data that can't be modified by listeners
                try {
                    _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
                } catch (E) {}
            }
        }
    }

    /**
     * Remove old events from the publish stream (at least 2sec old)
     */
    function _dropOldEvents() {
        if (!_storage.__jstorage_meta.PubSub) {
            return;
        }

        var retire = +new Date() - 2000;

        for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
            if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
                // deleteCount is needed for IE6
                _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
                break;
            }
        }

        if (!_storage.__jstorage_meta.PubSub.length) {
            delete _storage.__jstorage_meta.PubSub;
        }

    }

    /**
     * Publish payload to a channel
     *
     * @param {String} channel Channel name
     * @param {Mixed} payload Payload to send to the subscribers
     */
    function _publish(channel, payload) {
        if (!_storage.__jstorage_meta) {
            _storage.__jstorage_meta = {};
        }
        if (!_storage.__jstorage_meta.PubSub) {
            _storage.__jstorage_meta.PubSub = [];
        }

        _storage.__jstorage_meta.PubSub.unshift([+new Date(), channel, payload]);

        _save();
        _publishChange();
    }


    /**
     * JS Implementation of MurmurHash2
     *
     *  SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
     *
     * @author <a href='mailto:gary.court@gmail.com'>Gary Court</a>
     * @see http://github.com/garycourt/murmurhash-js
     * @author <a href='mailto:aappleby@gmail.com'>Austin Appleby</a>
     * @see http://sites.google.com/site/murmurhash/
     *
     * @param {string} str ASCII only
     * @param {number} seed Positive integer only
     * @return {number} 32-bit positive integer hash
     */

    function murmurhash2_32_gc(str, seed) {
        var
            l = str.length,
            h = seed ^ l,
            i = 0,
            k;

        while (l >= 4) {
            k =
                ((str.charCodeAt(i) & 0xff)) |
                ((str.charCodeAt(++i) & 0xff) << 8) |
                ((str.charCodeAt(++i) & 0xff) << 16) |
                ((str.charCodeAt(++i) & 0xff) << 24);

            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
            k ^= k >>> 24;
            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));

            h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;

            l -= 4;
            ++i;
        }

        switch (l) {
            case 3:
                h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
                /* falls through */
            case 2:
                h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
                /* falls through */
            case 1:
                h ^= (str.charCodeAt(i) & 0xff);
                h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
        }

        h ^= h >>> 13;
        h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
        h ^= h >>> 15;

        return h >>> 0;
    }

    ////////////////////////// PUBLIC INTERFACE /////////////////////////

    $.jStorage = {
        /* Version number */
        version: JSTORAGE_VERSION,

        /**
         * Sets a key's value.
         *
         * @param {String} key Key to set. If this value is not set or not
         *              a string an exception is raised.
         * @param {Mixed} value Value to set. This can be any value that is JSON
         *              compatible (Numbers, Strings, Objects etc.).
         * @param {Object} [options] - possible options to use
         * @param {Number} [options.TTL] - optional TTL value, in milliseconds
         * @return {Mixed} the used value
         */
        set: function(key, value, options) {
            _checkKey(key);

            options = options || {};

            // undefined values are deleted automatically
            if (typeof value == 'undefined') {
                this.deleteKey(key);
                return value;
            }

            if (_XMLService.isXML(value)) {
                value = {
                    _is_xml: true,
                    xml: _XMLService.encode(value)
                };
            } else if (typeof value == 'function') {
                return undefined; // functions can't be saved!
            } else if (value && typeof value == 'object') {
                // clone the object before saving to _storage tree
                value = JSON.parse(JSON.stringify(value));
            }

            _storage[key] = value;

            _storage.__jstorage_meta.CRC32[key] = '2.' + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);

            this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange

            _fireObservers(key, 'updated');
            return value;
        },

        /**
         * Looks up a key in cache
         *
         * @param {String} key - Key to look up.
         * @param {mixed} def - Default value to return, if key didn't exist.
         * @return {Mixed} the key value, default value or null
         */
        get: function(key, def) {
            _checkKey(key);
            if (key in _storage) {
                if (_storage[key] && typeof _storage[key] == 'object' && _storage[key]._is_xml) {
                    return _XMLService.decode(_storage[key].xml);
                } else {
                    return _storage[key];
                }
            }
            return typeof(def) == 'undefined' ? null : def;
        },

        /**
         * Deletes a key from cache.
         *
         * @param {String} key - Key to delete.
         * @return {Boolean} true if key existed or false if it didn't
         */
        deleteKey: function(key) {
            _checkKey(key);
            if (key in _storage) {
                delete _storage[key];
                // remove from TTL list
                if (typeof _storage.__jstorage_meta.TTL == 'object' &&
                    key in _storage.__jstorage_meta.TTL) {
                    delete _storage.__jstorage_meta.TTL[key];
                }

                delete _storage.__jstorage_meta.CRC32[key];

                _save();
                _publishChange();
                _fireObservers(key, 'deleted');
                return true;
            }
            return false;
        },

        /**
         * Sets a TTL for a key, or remove it if ttl value is 0 or below
         *
         * @param {String} key - key to set the TTL for
         * @param {Number} ttl - TTL timeout in milliseconds
         * @return {Boolean} true if key existed or false if it didn't
         */
        setTTL: function(key, ttl) {
            var curtime = +new Date();
            _checkKey(key);
            ttl = Number(ttl) || 0;
            if (key in _storage) {

                if (!_storage.__jstorage_meta.TTL) {
                    _storage.__jstorage_meta.TTL = {};
                }

                // Set TTL value for the key
                if (ttl > 0) {
                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
                } else {
                    delete _storage.__jstorage_meta.TTL[key];
                }

                _save();

                _handleTTL();

                _publishChange();
                return true;
            }
            return false;
        },

        /**
         * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
         *
         * @param {String} key Key to check
         * @return {Number} Remaining TTL in milliseconds
         */
        getTTL: function(key) {
            var curtime = +new Date(),
                ttl;
            _checkKey(key);
            if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
                ttl = _storage.__jstorage_meta.TTL[key] - curtime;
                return ttl || 0;
            }
            return 0;
        },

        /**
         * Deletes everything in cache.
         *
         * @return {Boolean} Always true
         */
        flush: function() {
            _storage = {
                __jstorage_meta: {
                    CRC32: {}
                }
            };
            _save();
            _publishChange();
            _fireObservers(null, 'flushed');
            return true;
        },

        /**
         * Returns a read-only copy of _storage
         *
         * @return {Object} Read-only copy of _storage
         */
        storageObj: function() {
            function F() {}
            F.prototype = _storage;
            return new F();
        },

        /**
         * Returns an index of all used keys as an array
         * ['key1', 'key2',..'keyN']
         *
         * @return {Array} Used keys
         */
        index: function() {
            var index = [],
                i;
            for (i in _storage) {
                if (_storage.hasOwnProperty(i) && i != '__jstorage_meta') {
                    index.push(i);
                }
            }
            return index;
        },

        /**
         * How much space in bytes does the storage take?
         *
         * @return {Number} Storage size in chars (not the same as in bytes,
         *                  since some chars may take several bytes)
         */
        storageSize: function() {
            return _storage_size;
        },

        /**
         * Which backend is currently in use?
         *
         * @return {String} Backend name
         */
        currentBackend: function() {
            return _backend;
        },

        /**
         * Test if storage is available
         *
         * @return {Boolean} True if storage can be used
         */
        storageAvailable: function() {
            return !!_backend;
        },

        /**
         * Register change listeners
         *
         * @param {String} key Key name
         * @param {Function} callback Function to run when the key changes
         */
        listenKeyChange: function(key, callback) {
            _checkKey(key);
            if (!_observers[key]) {
                _observers[key] = [];
            }
            _observers[key].push(callback);
        },

        /**
         * Remove change listeners
         *
         * @param {String} key Key name to unregister listeners against
         * @param {Function} [callback] If set, unregister the callback, if not - unregister all
         */
        stopListening: function(key, callback) {
            _checkKey(key);

            if (!_observers[key]) {
                return;
            }

            if (!callback) {
                delete _observers[key];
                return;
            }

            for (var i = _observers[key].length - 1; i >= 0; i--) {
                if (_observers[key][i] == callback) {
                    _observers[key].splice(i, 1);
                }
            }
        },

        /**
         * Subscribe to a Publish/Subscribe event stream
         *
         * @param {String} channel Channel name
         * @param {Function} callback Function to run when the something is published to the channel
         */
        subscribe: function(channel, callback) {
            channel = (channel || '').toString();
            if (!channel) {
                throw new TypeError('Channel not defined');
            }
            if (!_pubsub_observers[channel]) {
                _pubsub_observers[channel] = [];
            }
            _pubsub_observers[channel].push(callback);
        },

        /**
         * Publish data to an event stream
         *
         * @param {String} channel Channel name
         * @param {Mixed} payload Payload to deliver
         */
        publish: function(channel, payload) {
            channel = (channel || '').toString();
            if (!channel) {
                throw new TypeError('Channel not defined');
            }

            _publish(channel, payload);
        },

        /**
         * Reloads the data from browser storage
         */
        reInit: function() {
            _reloadData();
        },

        /**
         * Removes reference from global objects and saves it as jStorage
         *
         * @param {Boolean} option if needed to save object as simple 'jStorage' in windows context
         */
        noConflict: function(saveInGlobal) {
            delete window.$.jStorage;

            if (saveInGlobal) {
                window.jStorage = this;
            }

            return this;
        }
    };

    // Initialize jStorage
    _init();

})();;/*!
Waypoints - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
!function(){"use strict";function t(o){if(!o)throw new Error("No options passed to Waypoint constructor");if(!o.element)throw new Error("No element option passed to Waypoint constructor");if(!o.handler)throw new Error("No handler option passed to Waypoint constructor");this.key="waypoint-"+e,this.options=t.Adapter.extend({},t.defaults,o),this.element=this.options.element,this.adapter=new t.Adapter(this.element),this.callback=o.handler,this.axis=this.options.horizontal?"horizontal":"vertical",this.enabled=this.options.enabled,this.triggerPoint=null,this.group=t.Group.findOrCreate({name:this.options.group,axis:this.axis}),this.context=t.Context.findOrCreateByElement(this.options.context),t.offsetAliases[this.options.offset]&&(this.options.offset=t.offsetAliases[this.options.offset]),this.group.add(this),this.context.add(this),i[this.key]=this,e+=1}var e=0,i={};t.prototype.queueTrigger=function(t){this.group.queueTrigger(this,t)},t.prototype.trigger=function(t){this.enabled&&this.callback&&this.callback.apply(this,t)},t.prototype.destroy=function(){this.context.remove(this),this.group.remove(this),delete i[this.key]},t.prototype.disable=function(){return this.enabled=!1,this},t.prototype.enable=function(){return this.context.refresh(),this.enabled=!0,this},t.prototype.next=function(){return this.group.next(this)},t.prototype.previous=function(){return this.group.previous(this)},t.invokeAll=function(t){var e=[];for(var o in i)e.push(i[o]);for(var n=0,r=e.length;r>n;n++)e[n][t]()},t.destroyAll=function(){t.invokeAll("destroy")},t.disableAll=function(){t.invokeAll("disable")},t.enableAll=function(){t.Context.refreshAll();for(var e in i)i[e].enabled=!0;return this},t.refreshAll=function(){t.Context.refreshAll()},t.viewportHeight=function(){return window.innerHeight||document.documentElement.clientHeight},t.viewportWidth=function(){return document.documentElement.clientWidth},t.adapters=[],t.defaults={context:window,continuous:!0,enabled:!0,group:"default",horizontal:!1,offset:0},t.offsetAliases={"bottom-in-view":function(){return this.context.innerHeight()-this.adapter.outerHeight()},"right-in-view":function(){return this.context.innerWidth()-this.adapter.outerWidth()}},window.Waypoint=t}(),function(){"use strict";function t(t){window.setTimeout(t,1e3/60)}function e(t){this.element=t,this.Adapter=n.Adapter,this.adapter=new this.Adapter(t),this.key="waypoint-context-"+i,this.didScroll=!1,this.didResize=!1,this.oldScroll={x:this.adapter.scrollLeft(),y:this.adapter.scrollTop()},this.waypoints={vertical:{},horizontal:{}},t.waypointContextKey=this.key,o[t.waypointContextKey]=this,i+=1,n.windowContext||(n.windowContext=!0,n.windowContext=new e(window)),this.createThrottledScrollHandler(),this.createThrottledResizeHandler()}var i=0,o={},n=window.Waypoint,r=window.onload;e.prototype.add=function(t){var e=t.options.horizontal?"horizontal":"vertical";this.waypoints[e][t.key]=t,this.refresh()},e.prototype.checkEmpty=function(){var t=this.Adapter.isEmptyObject(this.waypoints.horizontal),e=this.Adapter.isEmptyObject(this.waypoints.vertical),i=this.element==this.element.window;t&&e&&!i&&(this.adapter.off(".waypoints"),delete o[this.key])},e.prototype.createThrottledResizeHandler=function(){function t(){e.handleResize(),e.didResize=!1}var e=this;this.adapter.on("resize.waypoints",function(){e.didResize||(e.didResize=!0,n.requestAnimationFrame(t))})},e.prototype.createThrottledScrollHandler=function(){function t(){e.handleScroll(),e.didScroll=!1}var e=this;this.adapter.on("scroll.waypoints",function(){(!e.didScroll||n.isTouch)&&(e.didScroll=!0,n.requestAnimationFrame(t))})},e.prototype.handleResize=function(){n.Context.refreshAll()},e.prototype.handleScroll=function(){var t={},e={horizontal:{newScroll:this.adapter.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.adapter.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};for(var i in e){var o=e[i],n=o.newScroll>o.oldScroll,r=n?o.forward:o.backward;for(var s in this.waypoints[i]){var a=this.waypoints[i][s];if(null!==a.triggerPoint){var l=o.oldScroll<a.triggerPoint,h=o.newScroll>=a.triggerPoint,p=l&&h,u=!l&&!h;(p||u)&&(a.queueTrigger(r),t[a.group.id]=a.group)}}}for(var c in t)t[c].flushTriggers();this.oldScroll={x:e.horizontal.newScroll,y:e.vertical.newScroll}},e.prototype.innerHeight=function(){return this.element==this.element.window?n.viewportHeight():this.adapter.innerHeight()},e.prototype.remove=function(t){delete this.waypoints[t.axis][t.key],this.checkEmpty()},e.prototype.innerWidth=function(){return this.element==this.element.window?n.viewportWidth():this.adapter.innerWidth()},e.prototype.destroy=function(){var t=[];for(var e in this.waypoints)for(var i in this.waypoints[e])t.push(this.waypoints[e][i]);for(var o=0,n=t.length;n>o;o++)t[o].destroy()},e.prototype.refresh=function(){var t,e=this.element==this.element.window,i=e?void 0:this.adapter.offset(),o={};this.handleScroll(),t={horizontal:{contextOffset:e?0:i.left,contextScroll:e?0:this.oldScroll.x,contextDimension:this.innerWidth(),oldScroll:this.oldScroll.x,forward:"right",backward:"left",offsetProp:"left"},vertical:{contextOffset:e?0:i.top,contextScroll:e?0:this.oldScroll.y,contextDimension:this.innerHeight(),oldScroll:this.oldScroll.y,forward:"down",backward:"up",offsetProp:"top"}};for(var r in t){var s=t[r];for(var a in this.waypoints[r]){var l,h,p,u,c,d=this.waypoints[r][a],f=d.options.offset,w=d.triggerPoint,y=0,g=null==w;d.element!==d.element.window&&(y=d.adapter.offset()[s.offsetProp]),"function"==typeof f?f=f.apply(d):"string"==typeof f&&(f=parseFloat(f),d.options.offset.indexOf("%")>-1&&(f=Math.ceil(s.contextDimension*f/100))),l=s.contextScroll-s.contextOffset,d.triggerPoint=Math.floor(y+l-f),h=w<s.oldScroll,p=d.triggerPoint>=s.oldScroll,u=h&&p,c=!h&&!p,!g&&u?(d.queueTrigger(s.backward),o[d.group.id]=d.group):!g&&c?(d.queueTrigger(s.forward),o[d.group.id]=d.group):g&&s.oldScroll>=d.triggerPoint&&(d.queueTrigger(s.forward),o[d.group.id]=d.group)}}return n.requestAnimationFrame(function(){for(var t in o)o[t].flushTriggers()}),this},e.findOrCreateByElement=function(t){return e.findByElement(t)||new e(t)},e.refreshAll=function(){for(var t in o)o[t].refresh()},e.findByElement=function(t){return o[t.waypointContextKey]},window.onload=function(){r&&r(),e.refreshAll()},n.requestAnimationFrame=function(e){var i=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||t;i.call(window,e)},n.Context=e}(),function(){"use strict";function t(t,e){return t.triggerPoint-e.triggerPoint}function e(t,e){return e.triggerPoint-t.triggerPoint}function i(t){this.name=t.name,this.axis=t.axis,this.id=this.name+"-"+this.axis,this.waypoints=[],this.clearTriggerQueues(),o[this.axis][this.name]=this}var o={vertical:{},horizontal:{}},n=window.Waypoint;i.prototype.add=function(t){this.waypoints.push(t)},i.prototype.clearTriggerQueues=function(){this.triggerQueues={up:[],down:[],left:[],right:[]}},i.prototype.flushTriggers=function(){for(var i in this.triggerQueues){var o=this.triggerQueues[i],n="up"===i||"left"===i;o.sort(n?e:t);for(var r=0,s=o.length;s>r;r+=1){var a=o[r];(a.options.continuous||r===o.length-1)&&a.trigger([i])}}this.clearTriggerQueues()},i.prototype.next=function(e){this.waypoints.sort(t);var i=n.Adapter.inArray(e,this.waypoints),o=i===this.waypoints.length-1;return o?null:this.waypoints[i+1]},i.prototype.previous=function(e){this.waypoints.sort(t);var i=n.Adapter.inArray(e,this.waypoints);return i?this.waypoints[i-1]:null},i.prototype.queueTrigger=function(t,e){this.triggerQueues[e].push(t)},i.prototype.remove=function(t){var e=n.Adapter.inArray(t,this.waypoints);e>-1&&this.waypoints.splice(e,1)},i.prototype.first=function(){return this.waypoints[0]},i.prototype.last=function(){return this.waypoints[this.waypoints.length-1]},i.findOrCreate=function(t){return o[t.axis][t.name]||new i(t)},n.Group=i}(),function(){"use strict";function t(t){this.$element=e(t)}var e=window.jQuery,i=window.Waypoint;e.each(["innerHeight","innerWidth","off","offset","on","outerHeight","outerWidth","scrollLeft","scrollTop"],function(e,i){t.prototype[i]=function(){var t=Array.prototype.slice.call(arguments);return this.$element[i].apply(this.$element,t)}}),e.each(["extend","inArray","isEmptyObject"],function(i,o){t[o]=e[o]}),i.adapters.push({name:"jquery",Adapter:t}),i.Adapter=t}(),function(){"use strict";function t(t){return function(){var i=[],o=arguments[0];return t.isFunction(arguments[0])&&(o=t.extend({},arguments[1]),o.handler=arguments[0]),this.each(function(){var n=t.extend({},o,{element:this});"string"==typeof n.context&&(n.context=t(this).closest(n.context)[0]),i.push(new e(n))}),i}}var e=window.Waypoint;window.jQuery&&(window.jQuery.fn.waypoint=t(window.jQuery)),window.Zepto&&(window.Zepto.fn.waypoint=t(window.Zepto))}();;/**
 * Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
 * Licensed under MIT
 * @author Ariel Flesler
 * @version 2.1.2
 */
; (function (f) { "use strict"; "function" === typeof define && define.amd ? define(["jquery"], f) : "undefined" !== typeof module && module.exports ? module.exports = f(require("jquery")) : f(jQuery) })(function ($) { "use strict"; function n(a) { return !a.nodeName || -1 !== $.inArray(a.nodeName.toLowerCase(), ["iframe", "#document", "html", "body"]) } function h(a) { return $.isFunction(a) || $.isPlainObject(a) ? a : { top: a, left: a } } var p = $.scrollTo = function (a, d, b) { return $(window).scrollTo(a, d, b) }; p.defaults = { axis: "xy", duration: 0, limit: !0 }; $.fn.scrollTo = function (a, d, b) { "object" === typeof d && (b = d, d = 0); "function" === typeof b && (b = { onAfter: b }); "max" === a && (a = 9E9); b = $.extend({}, p.defaults, b); d = d || b.duration; var u = b.queue && 1 < b.axis.length; u && (d /= 2); b.offset = h(b.offset); b.over = h(b.over); return this.each(function () { function k(a) { var k = $.extend({}, b, { queue: !0, duration: d, complete: a && function () { a.call(q, e, b) } }); r.animate(f, k) } if (null !== a) { var l = n(this), q = l ? this.contentWindow || window : this, r = $(q), e = a, f = {}, t; switch (typeof e) { case "number": case "string": if (/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(e)) { e = h(e); break } e = l ? $(e) : $(e, q); case "object": if (e.length === 0) return; if (e.is || e.style) t = (e = $(e)).offset() } var v = $.isFunction(b.offset) && b.offset(q, e) || b.offset; $.each(b.axis.split(""), function (a, c) { var d = "x" === c ? "Left" : "Top", m = d.toLowerCase(), g = "scroll" + d, h = r[g](), n = p.max(q, c); t ? (f[g] = t[m] + (l ? 0 : h - r.offset()[m]), b.margin && (f[g] -= parseInt(e.css("margin" + d), 10) || 0, f[g] -= parseInt(e.css("border" + d + "Width"), 10) || 0), f[g] += v[m] || 0, b.over[m] && (f[g] += e["x" === c ? "width" : "height"]() * b.over[m])) : (d = e[m], f[g] = d.slice && "%" === d.slice(-1) ? parseFloat(d) / 100 * n : d); b.limit && /^\d+$/.test(f[g]) && (f[g] = 0 >= f[g] ? 0 : Math.min(f[g], n)); !a && 1 < b.axis.length && (h === f[g] ? f = {} : u && (k(b.onAfterFirst), f = {})) }); k(b.onAfter) } }) }; p.max = function (a, d) { var b = "x" === d ? "Width" : "Height", h = "scroll" + b; if (!n(a)) return a[h] - $(a)[b.toLowerCase()](); var b = "client" + b, k = a.ownerDocument || a.document, l = k.documentElement, k = k.body; return Math.max(l[h], k[h]) - Math.min(l[b], k[b]) }; $.Tween.propHooks.scrollLeft = $.Tween.propHooks.scrollTop = { get: function (a) { return $(a.elem)[a.prop]() }, set: function (a) { var d = this.get(a); if (a.options.interrupt && a._last && a._last !== d) return $(a.elem).stop(); var b = Math.round(a.now); d !== b && ($(a.elem)[a.prop](b), a._last = this.get(a)) } }; return p });;;
/* == jquery mousewheel plugin == Version: 3.1.13, License: MIT License (MIT) */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
/* == malihu jquery custom scrollbar plugin == Version: 3.1.5, License: MIT License (MIT) */
!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"undefined"!=typeof module&&module.exports?module.exports=e:e(jQuery,window,document)}(function(e){!function(t){var o="function"==typeof define&&define.amd,a="undefined"!=typeof module&&module.exports,n="https:"==document.location.protocol?"https:":"http:",i="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";o||(a?require("jquery-mousewheel")(e):e.event.special.mousewheel||e("head").append(decodeURI("%3Cscript src="+n+"//"+i+"%3E%3C/script%3E"))),t()}(function(){var t,o="mCustomScrollbar",a="mCS",n=".mCustomScrollbar",i={setTop:0,setLeft:0,axis:"y",scrollbarPosition:"inside",scrollInertia:950,autoDraggerLength:!0,alwaysShowScrollbar:0,snapOffset:0,mouseWheel:{enable:!0,scrollAmount:"auto",axis:"y",deltaFactor:"auto",disableOver:["select","option","keygen","datalist","textarea"]},scrollButtons:{scrollType:"stepless",scrollAmount:"auto"},keyboard:{enable:!0,scrollType:"stepless",scrollAmount:"auto"},contentTouchScroll:25,documentTouchScroll:!0,advanced:{autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",updateOnContentResize:!0,updateOnImageLoad:"auto",autoUpdateTimeout:60},theme:"light",callbacks:{onTotalScrollOffset:0,onTotalScrollBackOffset:0,alwaysTriggerOffsets:!0}},r=0,l={},s=window.attachEvent&&!window.addEventListener?1:0,c=!1,d=["mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar","mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer","mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"],u={init:function(t){var t=e.extend(!0,{},i,t),o=f.call(this);if(t.live){var s=t.liveSelector||this.selector||n,c=e(s);if("off"===t.live)return void m(s);l[s]=setTimeout(function(){c.mCustomScrollbar(t),"once"===t.live&&c.length&&m(s)},500)}else m(s);return t.setWidth=t.set_width?t.set_width:t.setWidth,t.setHeight=t.set_height?t.set_height:t.setHeight,t.axis=t.horizontalScroll?"x":p(t.axis),t.scrollInertia=t.scrollInertia>0&&t.scrollInertia<17?17:t.scrollInertia,"object"!=typeof t.mouseWheel&&1==t.mouseWheel&&(t.mouseWheel={enable:!0,scrollAmount:"auto",axis:"y",preventDefault:!1,deltaFactor:"auto",normalizeDelta:!1,invert:!1}),t.mouseWheel.scrollAmount=t.mouseWheelPixels?t.mouseWheelPixels:t.mouseWheel.scrollAmount,t.mouseWheel.normalizeDelta=t.advanced.normalizeMouseWheelDelta?t.advanced.normalizeMouseWheelDelta:t.mouseWheel.normalizeDelta,t.scrollButtons.scrollType=g(t.scrollButtons.scrollType),h(t),e(o).each(function(){var o=e(this);if(!o.data(a)){o.data(a,{idx:++r,opt:t,scrollRatio:{y:null,x:null},overflowed:null,contentReset:{y:null,x:null},bindEvents:!1,tweenRunning:!1,sequential:{},langDir:o.css("direction"),cbOffsets:null,trigger:null,poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}});var n=o.data(a),i=n.opt,l=o.data("mcs-axis"),s=o.data("mcs-scrollbar-position"),c=o.data("mcs-theme");l&&(i.axis=l),s&&(i.scrollbarPosition=s),c&&(i.theme=c,h(i)),v.call(this),n&&i.callbacks.onCreate&&"function"==typeof i.callbacks.onCreate&&i.callbacks.onCreate.call(this),e("#mCSB_"+n.idx+"_container img:not(."+d[2]+")").addClass(d[2]),u.update.call(null,o)}})},update:function(t,o){var n=t||f.call(this);return e(n).each(function(){var t=e(this);if(t.data(a)){var n=t.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container"),l=e("#mCSB_"+n.idx),s=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];if(!r.length)return;n.tweenRunning&&Q(t),o&&n&&i.callbacks.onBeforeUpdate&&"function"==typeof i.callbacks.onBeforeUpdate&&i.callbacks.onBeforeUpdate.call(this),t.hasClass(d[3])&&t.removeClass(d[3]),t.hasClass(d[4])&&t.removeClass(d[4]),l.css("max-height","none"),l.height()!==t.height()&&l.css("max-height",t.height()),_.call(this),"y"===i.axis||i.advanced.autoExpandHorizontalScroll||r.css("width",x(r)),n.overflowed=y.call(this),M.call(this),i.autoDraggerLength&&S.call(this),b.call(this),T.call(this);var c=[Math.abs(r[0].offsetTop),Math.abs(r[0].offsetLeft)];"x"!==i.axis&&(n.overflowed[0]?s[0].height()>s[0].parent().height()?B.call(this):(G(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}),n.contentReset.y=null):(B.call(this),"y"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[1]&&G(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}))),"y"!==i.axis&&(n.overflowed[1]?s[1].width()>s[1].parent().width()?B.call(this):(G(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}),n.contentReset.x=null):(B.call(this),"x"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[0]&&G(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}))),o&&n&&(2===o&&i.callbacks.onImageLoad&&"function"==typeof i.callbacks.onImageLoad?i.callbacks.onImageLoad.call(this):3===o&&i.callbacks.onSelectorChange&&"function"==typeof i.callbacks.onSelectorChange?i.callbacks.onSelectorChange.call(this):i.callbacks.onUpdate&&"function"==typeof i.callbacks.onUpdate&&i.callbacks.onUpdate.call(this)),N.call(this)}})},scrollTo:function(t,o){if("undefined"!=typeof t&&null!=t){var n=f.call(this);return e(n).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l={trigger:"external",scrollInertia:r.scrollInertia,scrollEasing:"mcsEaseInOut",moveDragger:!1,timeout:60,callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},s=e.extend(!0,{},l,o),c=Y.call(this,t),d=s.scrollInertia>0&&s.scrollInertia<17?17:s.scrollInertia;c[0]=X.call(this,c[0],"y"),c[1]=X.call(this,c[1],"x"),s.moveDragger&&(c[0]*=i.scrollRatio.y,c[1]*=i.scrollRatio.x),s.dur=ne()?0:d,setTimeout(function(){null!==c[0]&&"undefined"!=typeof c[0]&&"x"!==r.axis&&i.overflowed[0]&&(s.dir="y",s.overwrite="all",G(n,c[0].toString(),s)),null!==c[1]&&"undefined"!=typeof c[1]&&"y"!==r.axis&&i.overflowed[1]&&(s.dir="x",s.overwrite="none",G(n,c[1].toString(),s))},s.timeout)}})}},stop:function(){var t=f.call(this);return e(t).each(function(){var t=e(this);t.data(a)&&Q(t)})},disable:function(t){var o=f.call(this);return e(o).each(function(){var o=e(this);if(o.data(a)){o.data(a);N.call(this,"remove"),k.call(this),t&&B.call(this),M.call(this,!0),o.addClass(d[3])}})},destroy:function(){var t=f.call(this);return e(t).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx),s=e("#mCSB_"+i.idx+"_container"),c=e(".mCSB_"+i.idx+"_scrollbar");r.live&&m(r.liveSelector||e(t).selector),N.call(this,"remove"),k.call(this),B.call(this),n.removeData(a),$(this,"mcs"),c.remove(),s.find("img."+d[2]).removeClass(d[2]),l.replaceWith(s.contents()),n.removeClass(o+" _"+a+"_"+i.idx+" "+d[6]+" "+d[7]+" "+d[5]+" "+d[3]).addClass(d[4])}})}},f=function(){return"object"!=typeof e(this)||e(this).length<1?n:this},h=function(t){var o=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],a=["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"],n=["minimal","minimal-dark"],i=["minimal","minimal-dark"],r=["minimal","minimal-dark"];t.autoDraggerLength=e.inArray(t.theme,o)>-1?!1:t.autoDraggerLength,t.autoExpandScrollbar=e.inArray(t.theme,a)>-1?!1:t.autoExpandScrollbar,t.scrollButtons.enable=e.inArray(t.theme,n)>-1?!1:t.scrollButtons.enable,t.autoHideScrollbar=e.inArray(t.theme,i)>-1?!0:t.autoHideScrollbar,t.scrollbarPosition=e.inArray(t.theme,r)>-1?"outside":t.scrollbarPosition},m=function(e){l[e]&&(clearTimeout(l[e]),$(l,e))},p=function(e){return"yx"===e||"xy"===e||"auto"===e?"yx":"x"===e||"horizontal"===e?"x":"y"},g=function(e){return"stepped"===e||"pixels"===e||"step"===e||"click"===e?"stepped":"stepless"},v=function(){var t=e(this),n=t.data(a),i=n.opt,r=i.autoExpandScrollbar?" "+d[1]+"_expand":"",l=["<div id='mCSB_"+n.idx+"_scrollbar_vertical' class='mCSB_scrollTools mCSB_"+n.idx+"_scrollbar mCS-"+i.theme+" mCSB_scrollTools_vertical"+r+"'><div class='"+d[12]+"'><div id='mCSB_"+n.idx+"_dragger_vertical' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>","<div id='mCSB_"+n.idx+"_scrollbar_horizontal' class='mCSB_scrollTools mCSB_"+n.idx+"_scrollbar mCS-"+i.theme+" mCSB_scrollTools_horizontal"+r+"'><div class='"+d[12]+"'><div id='mCSB_"+n.idx+"_dragger_horizontal' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>"],s="yx"===i.axis?"mCSB_vertical_horizontal":"x"===i.axis?"mCSB_horizontal":"mCSB_vertical",c="yx"===i.axis?l[0]+l[1]:"x"===i.axis?l[1]:l[0],u="yx"===i.axis?"<div id='mCSB_"+n.idx+"_container_wrapper' class='mCSB_container_wrapper' />":"",f=i.autoHideScrollbar?" "+d[6]:"",h="x"!==i.axis&&"rtl"===n.langDir?" "+d[7]:"";i.setWidth&&t.css("width",i.setWidth),i.setHeight&&t.css("height",i.setHeight),i.setLeft="y"!==i.axis&&"rtl"===n.langDir?"989999px":i.setLeft,t.addClass(o+" _"+a+"_"+n.idx+f+h).wrapInner("<div id='mCSB_"+n.idx+"' class='mCustomScrollBox mCS-"+i.theme+" "+s+"'><div id='mCSB_"+n.idx+"_container' class='mCSB_container' style='position:relative; top:"+i.setTop+"; left:"+i.setLeft+";' dir='"+n.langDir+"' /></div>");var m=e("#mCSB_"+n.idx),p=e("#mCSB_"+n.idx+"_container");"y"===i.axis||i.advanced.autoExpandHorizontalScroll||p.css("width",x(p)),"outside"===i.scrollbarPosition?("static"===t.css("position")&&t.css("position","relative"),t.css("overflow","visible"),m.addClass("mCSB_outside").after(c)):(m.addClass("mCSB_inside").append(c),p.wrap(u)),w.call(this);var g=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];g[0].css("min-height",g[0].height()),g[1].css("min-width",g[1].width())},x=function(t){var o=[t[0].scrollWidth,Math.max.apply(Math,t.children().map(function(){return e(this).outerWidth(!0)}).get())],a=t.parent().width();return o[0]>a?o[0]:o[1]>a?o[1]:"100%"},_=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx+"_container");if(n.advanced.autoExpandHorizontalScroll&&"y"!==n.axis){i.css({width:"auto","min-width":0,"overflow-x":"scroll"});var r=Math.ceil(i[0].scrollWidth);3===n.advanced.autoExpandHorizontalScroll||2!==n.advanced.autoExpandHorizontalScroll&&r>i.parent().width()?i.css({width:r,"min-width":"100%","overflow-x":"inherit"}):i.css({"overflow-x":"inherit",position:"absolute"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({width:Math.ceil(i[0].getBoundingClientRect().right+.4)-Math.floor(i[0].getBoundingClientRect().left),"min-width":"100%",position:"relative"}).unwrap()}},w=function(){var t=e(this),o=t.data(a),n=o.opt,i=e(".mCSB_"+o.idx+"_scrollbar:first"),r=oe(n.scrollButtons.tabindex)?"tabindex='"+n.scrollButtons.tabindex+"'":"",l=["<a href='#' class='"+d[13]+"' "+r+" />","<a href='#' class='"+d[14]+"' "+r+" />","<a href='#' class='"+d[15]+"' "+r+" />","<a href='#' class='"+d[16]+"' "+r+" />"],s=["x"===n.axis?l[2]:l[0],"x"===n.axis?l[3]:l[1],l[2],l[3]];n.scrollButtons.enable&&i.prepend(s[0]).append(s[1]).next(".mCSB_scrollTools").prepend(s[2]).append(s[3])},S=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[n.height()/i.outerHeight(!1),n.width()/i.outerWidth(!1)],c=[parseInt(r[0].css("min-height")),Math.round(l[0]*r[0].parent().height()),parseInt(r[1].css("min-width")),Math.round(l[1]*r[1].parent().width())],d=s&&c[1]<c[0]?c[0]:c[1],u=s&&c[3]<c[2]?c[2]:c[3];r[0].css({height:d,"max-height":r[0].parent().height()-10}).find(".mCSB_dragger_bar").css({"line-height":c[0]+"px"}),r[1].css({width:u,"max-width":r[1].parent().width()-10})},b=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[i.outerHeight(!1)-n.height(),i.outerWidth(!1)-n.width()],s=[l[0]/(r[0].parent().height()-r[0].height()),l[1]/(r[1].parent().width()-r[1].width())];o.scrollRatio={y:s[0],x:s[1]}},C=function(e,t,o){var a=o?d[0]+"_expanded":"",n=e.closest(".mCSB_scrollTools");"active"===t?(e.toggleClass(d[0]+" "+a),n.toggleClass(d[1]),e[0]._draggable=e[0]._draggable?0:1):e[0]._draggable||("hide"===t?(e.removeClass(d[0]),n.removeClass(d[1])):(e.addClass(d[0]),n.addClass(d[1])))},y=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=null==o.overflowed?i.height():i.outerHeight(!1),l=null==o.overflowed?i.width():i.outerWidth(!1),s=i[0].scrollHeight,c=i[0].scrollWidth;return s>r&&(r=s),c>l&&(l=c),[r>n.height(),l>n.width()]},B=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx),r=e("#mCSB_"+o.idx+"_container"),l=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")];if(Q(t),("x"!==n.axis&&!o.overflowed[0]||"y"===n.axis&&o.overflowed[0])&&(l[0].add(r).css("top",0),G(t,"_resetY")),"y"!==n.axis&&!o.overflowed[1]||"x"===n.axis&&o.overflowed[1]){var s=dx=0;"rtl"===o.langDir&&(s=i.width()-r.outerWidth(!1),dx=Math.abs(s/o.scrollRatio.x)),r.css("left",s),l[1].css("left",dx),G(t,"_resetX")}},T=function(){function t(){r=setTimeout(function(){e.event.special.mousewheel?(clearTimeout(r),W.call(o[0])):t()},100)}var o=e(this),n=o.data(a),i=n.opt;if(!n.bindEvents){if(I.call(this),i.contentTouchScroll&&D.call(this),E.call(this),i.mouseWheel.enable){var r;t()}P.call(this),U.call(this),i.advanced.autoScrollOnFocus&&H.call(this),i.scrollButtons.enable&&F.call(this),i.keyboard.enable&&q.call(this),n.bindEvents=!0}},k=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=".mCSB_"+o.idx+"_scrollbar",l=e("#mCSB_"+o.idx+",#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,"+r+" ."+d[12]+",#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal,"+r+">a"),s=e("#mCSB_"+o.idx+"_container");n.advanced.releaseDraggableSelectors&&l.add(e(n.advanced.releaseDraggableSelectors)),n.advanced.extraDraggableSelectors&&l.add(e(n.advanced.extraDraggableSelectors)),o.bindEvents&&(e(document).add(e(!A()||top.document)).unbind("."+i),l.each(function(){e(this).unbind("."+i)}),clearTimeout(t[0]._focusTimeout),$(t[0],"_focusTimeout"),clearTimeout(o.sequential.step),$(o.sequential,"step"),clearTimeout(s[0].onCompleteTimeout),$(s[0],"onCompleteTimeout"),o.bindEvents=!1)},M=function(t){var o=e(this),n=o.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container_wrapper"),l=r.length?r:e("#mCSB_"+n.idx+"_container"),s=[e("#mCSB_"+n.idx+"_scrollbar_vertical"),e("#mCSB_"+n.idx+"_scrollbar_horizontal")],c=[s[0].find(".mCSB_dragger"),s[1].find(".mCSB_dragger")];"x"!==i.axis&&(n.overflowed[0]&&!t?(s[0].add(c[0]).add(s[0].children("a")).css("display","block"),l.removeClass(d[8]+" "+d[10])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[0].css("display","none"),l.removeClass(d[10])):(s[0].css("display","none"),l.addClass(d[10])),l.addClass(d[8]))),"y"!==i.axis&&(n.overflowed[1]&&!t?(s[1].add(c[1]).add(s[1].children("a")).css("display","block"),l.removeClass(d[9]+" "+d[11])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[1].css("display","none"),l.removeClass(d[11])):(s[1].css("display","none"),l.addClass(d[11])),l.addClass(d[9]))),n.overflowed[0]||n.overflowed[1]?o.removeClass(d[5]):o.addClass(d[5])},O=function(t){var o=t.type,a=t.target.ownerDocument!==document&&null!==frameElement?[e(frameElement).offset().top,e(frameElement).offset().left]:null,n=A()&&t.target.ownerDocument!==top.document&&null!==frameElement?[e(t.view.frameElement).offset().top,e(t.view.frameElement).offset().left]:[0,0];switch(o){case"pointerdown":case"MSPointerDown":case"pointermove":case"MSPointerMove":case"pointerup":case"MSPointerUp":return a?[t.originalEvent.pageY-a[0]+n[0],t.originalEvent.pageX-a[1]+n[1],!1]:[t.originalEvent.pageY,t.originalEvent.pageX,!1];case"touchstart":case"touchmove":case"touchend":var i=t.originalEvent.touches[0]||t.originalEvent.changedTouches[0],r=t.originalEvent.touches.length||t.originalEvent.changedTouches.length;return t.target.ownerDocument!==document?[i.screenY,i.screenX,r>1]:[i.pageY,i.pageX,r>1];default:return a?[t.pageY-a[0]+n[0],t.pageX-a[1]+n[1],!1]:[t.pageY,t.pageX,!1]}},I=function(){function t(e,t,a,n){if(h[0].idleTimer=d.scrollInertia<233?250:0,o.attr("id")===f[1])var i="x",s=(o[0].offsetLeft-t+n)*l.scrollRatio.x;else var i="y",s=(o[0].offsetTop-e+a)*l.scrollRatio.y;G(r,s.toString(),{dir:i,drag:!0})}var o,n,i,r=e(this),l=r.data(a),d=l.opt,u=a+"_"+l.idx,f=["mCSB_"+l.idx+"_dragger_vertical","mCSB_"+l.idx+"_dragger_horizontal"],h=e("#mCSB_"+l.idx+"_container"),m=e("#"+f[0]+",#"+f[1]),p=d.advanced.releaseDraggableSelectors?m.add(e(d.advanced.releaseDraggableSelectors)):m,g=d.advanced.extraDraggableSelectors?e(!A()||top.document).add(e(d.advanced.extraDraggableSelectors)):e(!A()||top.document);m.bind("contextmenu."+u,function(e){e.preventDefault()}).bind("mousedown."+u+" touchstart."+u+" pointerdown."+u+" MSPointerDown."+u,function(t){if(t.stopImmediatePropagation(),t.preventDefault(),ee(t)){c=!0,s&&(document.onselectstart=function(){return!1}),L.call(h,!1),Q(r),o=e(this);var a=o.offset(),l=O(t)[0]-a.top,u=O(t)[1]-a.left,f=o.height()+a.top,m=o.width()+a.left;f>l&&l>0&&m>u&&u>0&&(n=l,i=u),C(o,"active",d.autoExpandScrollbar)}}).bind("touchmove."+u,function(e){e.stopImmediatePropagation(),e.preventDefault();var a=o.offset(),r=O(e)[0]-a.top,l=O(e)[1]-a.left;t(n,i,r,l)}),e(document).add(g).bind("mousemove."+u+" pointermove."+u+" MSPointerMove."+u,function(e){if(o){var a=o.offset(),r=O(e)[0]-a.top,l=O(e)[1]-a.left;if(n===r&&i===l)return;t(n,i,r,l)}}).add(p).bind("mouseup."+u+" touchend."+u+" pointerup."+u+" MSPointerUp."+u,function(){o&&(C(o,"active",d.autoExpandScrollbar),o=null),c=!1,s&&(document.onselectstart=null),L.call(h,!0)})},D=function(){function o(e){if(!te(e)||c||O(e)[2])return void(t=0);t=1,b=0,C=0,d=1,y.removeClass("mCS_touch_action");var o=I.offset();u=O(e)[0]-o.top,f=O(e)[1]-o.left,z=[O(e)[0],O(e)[1]]}function n(e){if(te(e)&&!c&&!O(e)[2]&&(T.documentTouchScroll||e.preventDefault(),e.stopImmediatePropagation(),(!C||b)&&d)){g=K();var t=M.offset(),o=O(e)[0]-t.top,a=O(e)[1]-t.left,n="mcsLinearOut";if(E.push(o),W.push(a),z[2]=Math.abs(O(e)[0]-z[0]),z[3]=Math.abs(O(e)[1]-z[1]),B.overflowed[0])var i=D[0].parent().height()-D[0].height(),r=u-o>0&&o-u>-(i*B.scrollRatio.y)&&(2*z[3]<z[2]||"yx"===T.axis);if(B.overflowed[1])var l=D[1].parent().width()-D[1].width(),h=f-a>0&&a-f>-(l*B.scrollRatio.x)&&(2*z[2]<z[3]||"yx"===T.axis);r||h?(U||e.preventDefault(),b=1):(C=1,y.addClass("mCS_touch_action")),U&&e.preventDefault(),w="yx"===T.axis?[u-o,f-a]:"x"===T.axis?[null,f-a]:[u-o,null],I[0].idleTimer=250,B.overflowed[0]&&s(w[0],R,n,"y","all",!0),B.overflowed[1]&&s(w[1],R,n,"x",L,!0)}}function i(e){if(!te(e)||c||O(e)[2])return void(t=0);t=1,e.stopImmediatePropagation(),Q(y),p=K();var o=M.offset();h=O(e)[0]-o.top,m=O(e)[1]-o.left,E=[],W=[]}function r(e){if(te(e)&&!c&&!O(e)[2]){d=0,e.stopImmediatePropagation(),b=0,C=0,v=K();var t=M.offset(),o=O(e)[0]-t.top,a=O(e)[1]-t.left;if(!(v-g>30)){_=1e3/(v-p);var n="mcsEaseOut",i=2.5>_,r=i?[E[E.length-2],W[W.length-2]]:[0,0];x=i?[o-r[0],a-r[1]]:[o-h,a-m];var u=[Math.abs(x[0]),Math.abs(x[1])];_=i?[Math.abs(x[0]/4),Math.abs(x[1]/4)]:[_,_];var f=[Math.abs(I[0].offsetTop)-x[0]*l(u[0]/_[0],_[0]),Math.abs(I[0].offsetLeft)-x[1]*l(u[1]/_[1],_[1])];w="yx"===T.axis?[f[0],f[1]]:"x"===T.axis?[null,f[1]]:[f[0],null],S=[4*u[0]+T.scrollInertia,4*u[1]+T.scrollInertia];var y=parseInt(T.contentTouchScroll)||0;w[0]=u[0]>y?w[0]:0,w[1]=u[1]>y?w[1]:0,B.overflowed[0]&&s(w[0],S[0],n,"y",L,!1),B.overflowed[1]&&s(w[1],S[1],n,"x",L,!1)}}}function l(e,t){var o=[1.5*t,2*t,t/1.5,t/2];return e>90?t>4?o[0]:o[3]:e>60?t>3?o[3]:o[2]:e>30?t>8?o[1]:t>6?o[0]:t>4?t:o[2]:t>8?t:o[3]}function s(e,t,o,a,n,i){e&&G(y,e.toString(),{dur:t,scrollEasing:o,dir:a,overwrite:n,drag:i})}var d,u,f,h,m,p,g,v,x,_,w,S,b,C,y=e(this),B=y.data(a),T=B.opt,k=a+"_"+B.idx,M=e("#mCSB_"+B.idx),I=e("#mCSB_"+B.idx+"_container"),D=[e("#mCSB_"+B.idx+"_dragger_vertical"),e("#mCSB_"+B.idx+"_dragger_horizontal")],E=[],W=[],R=0,L="yx"===T.axis?"none":"all",z=[],P=I.find("iframe"),H=["touchstart."+k+" pointerdown."+k+" MSPointerDown."+k,"touchmove."+k+" pointermove."+k+" MSPointerMove."+k,"touchend."+k+" pointerup."+k+" MSPointerUp."+k],U=void 0!==document.body.style.touchAction&&""!==document.body.style.touchAction;I.bind(H[0],function(e){o(e)}).bind(H[1],function(e){n(e)}),M.bind(H[0],function(e){i(e)}).bind(H[2],function(e){r(e)}),P.length&&P.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind(H[0],function(e){o(e),i(e)}).bind(H[1],function(e){n(e)}).bind(H[2],function(e){r(e)})})})},E=function(){function o(){return window.getSelection?window.getSelection().toString():document.selection&&"Control"!=document.selection.type?document.selection.createRange().text:0}function n(e,t,o){d.type=o&&i?"stepped":"stepless",d.scrollAmount=10,j(r,e,t,"mcsLinearOut",o?60:null)}var i,r=e(this),l=r.data(a),s=l.opt,d=l.sequential,u=a+"_"+l.idx,f=e("#mCSB_"+l.idx+"_container"),h=f.parent();f.bind("mousedown."+u,function(){t||i||(i=1,c=!0)}).add(document).bind("mousemove."+u,function(e){if(!t&&i&&o()){var a=f.offset(),r=O(e)[0]-a.top+f[0].offsetTop,c=O(e)[1]-a.left+f[0].offsetLeft;r>0&&r<h.height()&&c>0&&c<h.width()?d.step&&n("off",null,"stepped"):("x"!==s.axis&&l.overflowed[0]&&(0>r?n("on",38):r>h.height()&&n("on",40)),"y"!==s.axis&&l.overflowed[1]&&(0>c?n("on",37):c>h.width()&&n("on",39)))}}).bind("mouseup."+u+" dragend."+u,function(){t||(i&&(i=0,n("off",null)),c=!1)})},W=function(){function t(t,a){if(Q(o),!z(o,t.target)){var r="auto"!==i.mouseWheel.deltaFactor?parseInt(i.mouseWheel.deltaFactor):s&&t.deltaFactor<100?100:t.deltaFactor||100,d=i.scrollInertia;if("x"===i.axis||"x"===i.mouseWheel.axis)var u="x",f=[Math.round(r*n.scrollRatio.x),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.width()?.9*l.width():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetLeft),p=c[1][0].offsetLeft,g=c[1].parent().width()-c[1].width(),v="y"===i.mouseWheel.axis?t.deltaY||a:t.deltaX;else var u="y",f=[Math.round(r*n.scrollRatio.y),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.height()?.9*l.height():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetTop),p=c[0][0].offsetTop,g=c[0].parent().height()-c[0].height(),v=t.deltaY||a;"y"===u&&!n.overflowed[0]||"x"===u&&!n.overflowed[1]||((i.mouseWheel.invert||t.webkitDirectionInvertedFromDevice)&&(v=-v),i.mouseWheel.normalizeDelta&&(v=0>v?-1:1),(v>0&&0!==p||0>v&&p!==g||i.mouseWheel.preventDefault)&&(t.stopImmediatePropagation(),t.preventDefault()),t.deltaFactor<5&&!i.mouseWheel.normalizeDelta&&(h=t.deltaFactor,d=17),G(o,(m-v*h).toString(),{dir:u,dur:d}))}}if(e(this).data(a)){var o=e(this),n=o.data(a),i=n.opt,r=a+"_"+n.idx,l=e("#mCSB_"+n.idx),c=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")],d=e("#mCSB_"+n.idx+"_container").find("iframe");d.length&&d.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind("mousewheel."+r,function(e,o){t(e,o)})})}),l.bind("mousewheel."+r,function(e,o){t(e,o)})}},R=new Object,A=function(t){var o=!1,a=!1,n=null;if(void 0===t?a="#empty":void 0!==e(t).attr("id")&&(a=e(t).attr("id")),a!==!1&&void 0!==R[a])return R[a];if(t){try{var i=t.contentDocument||t.contentWindow.document;n=i.body.innerHTML}catch(r){}o=null!==n}else{try{var i=top.document;n=i.body.innerHTML}catch(r){}o=null!==n}return a!==!1&&(R[a]=o),o},L=function(e){var t=this.find("iframe");if(t.length){var o=e?"auto":"none";t.css("pointer-events",o)}},z=function(t,o){var n=o.nodeName.toLowerCase(),i=t.data(a).opt.mouseWheel.disableOver,r=["select","textarea"];return e.inArray(n,i)>-1&&!(e.inArray(n,r)>-1&&!e(o).is(":focus"))},P=function(){var t,o=e(this),n=o.data(a),i=a+"_"+n.idx,r=e("#mCSB_"+n.idx+"_container"),l=r.parent(),s=e(".mCSB_"+n.idx+"_scrollbar ."+d[12]);s.bind("mousedown."+i+" touchstart."+i+" pointerdown."+i+" MSPointerDown."+i,function(o){c=!0,e(o.target).hasClass("mCSB_dragger")||(t=1)}).bind("touchend."+i+" pointerup."+i+" MSPointerUp."+i,function(){c=!1}).bind("click."+i,function(a){if(t&&(t=0,e(a.target).hasClass(d[12])||e(a.target).hasClass("mCSB_draggerRail"))){Q(o);var i=e(this),s=i.find(".mCSB_dragger");if(i.parent(".mCSB_scrollTools_horizontal").length>0){if(!n.overflowed[1])return;var c="x",u=a.pageX>s.offset().left?-1:1,f=Math.abs(r[0].offsetLeft)-u*(.9*l.width())}else{if(!n.overflowed[0])return;var c="y",u=a.pageY>s.offset().top?-1:1,f=Math.abs(r[0].offsetTop)-u*(.9*l.height())}G(o,f.toString(),{dir:c,scrollEasing:"mcsEaseInOut"})}})},H=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=e("#mCSB_"+o.idx+"_container"),l=r.parent();r.bind("focusin."+i,function(){var o=e(document.activeElement),a=r.find(".mCustomScrollBox").length,i=0;o.is(n.advanced.autoScrollOnFocus)&&(Q(t),clearTimeout(t[0]._focusTimeout),t[0]._focusTimer=a?(i+17)*a:0,t[0]._focusTimeout=setTimeout(function(){var e=[ae(o)[0],ae(o)[1]],a=[r[0].offsetTop,r[0].offsetLeft],s=[a[0]+e[0]>=0&&a[0]+e[0]<l.height()-o.outerHeight(!1),a[1]+e[1]>=0&&a[0]+e[1]<l.width()-o.outerWidth(!1)],c="yx"!==n.axis||s[0]||s[1]?"all":"none";"x"===n.axis||s[0]||G(t,e[0].toString(),{dir:"y",scrollEasing:"mcsEaseInOut",overwrite:c,dur:i}),"y"===n.axis||s[1]||G(t,e[1].toString(),{dir:"x",scrollEasing:"mcsEaseInOut",overwrite:c,dur:i})},t[0]._focusTimer))})},U=function(){var t=e(this),o=t.data(a),n=a+"_"+o.idx,i=e("#mCSB_"+o.idx+"_container").parent();i.bind("scroll."+n,function(){0===i.scrollTop()&&0===i.scrollLeft()||e(".mCSB_"+o.idx+"_scrollbar").css("visibility","hidden")})},F=function(){var t=e(this),o=t.data(a),n=o.opt,i=o.sequential,r=a+"_"+o.idx,l=".mCSB_"+o.idx+"_scrollbar",s=e(l+">a");s.bind("contextmenu."+r,function(e){e.preventDefault()}).bind("mousedown."+r+" touchstart."+r+" pointerdown."+r+" MSPointerDown."+r+" mouseup."+r+" touchend."+r+" pointerup."+r+" MSPointerUp."+r+" mouseout."+r+" pointerout."+r+" MSPointerOut."+r+" click."+r,function(a){function r(e,o){i.scrollAmount=n.scrollButtons.scrollAmount,j(t,e,o)}if(a.preventDefault(),ee(a)){var l=e(this).attr("class");switch(i.type=n.scrollButtons.scrollType,a.type){case"mousedown":case"touchstart":case"pointerdown":case"MSPointerDown":if("stepped"===i.type)return;c=!0,o.tweenRunning=!1,r("on",l);break;case"mouseup":case"touchend":case"pointerup":case"MSPointerUp":case"mouseout":case"pointerout":case"MSPointerOut":if("stepped"===i.type)return;c=!1,i.dir&&r("off",l);break;case"click":if("stepped"!==i.type||o.tweenRunning)return;r("on",l)}}})},q=function(){function t(t){function a(e,t){r.type=i.keyboard.scrollType,r.scrollAmount=i.keyboard.scrollAmount,"stepped"===r.type&&n.tweenRunning||j(o,e,t)}switch(t.type){case"blur":n.tweenRunning&&r.dir&&a("off",null);break;case"keydown":case"keyup":var l=t.keyCode?t.keyCode:t.which,s="on";if("x"!==i.axis&&(38===l||40===l)||"y"!==i.axis&&(37===l||39===l)){if((38===l||40===l)&&!n.overflowed[0]||(37===l||39===l)&&!n.overflowed[1])return;"keyup"===t.type&&(s="off"),e(document.activeElement).is(u)||(t.preventDefault(),t.stopImmediatePropagation(),a(s,l))}else if(33===l||34===l){if((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type){Q(o);var f=34===l?-1:1;if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=Math.abs(c[0].offsetLeft)-f*(.9*d.width());else var h="y",m=Math.abs(c[0].offsetTop)-f*(.9*d.height());G(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}else if((35===l||36===l)&&!e(document.activeElement).is(u)&&((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type)){if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=35===l?Math.abs(d.width()-c.outerWidth(!1)):0;else var h="y",m=35===l?Math.abs(d.height()-c.outerHeight(!1)):0;G(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}}var o=e(this),n=o.data(a),i=n.opt,r=n.sequential,l=a+"_"+n.idx,s=e("#mCSB_"+n.idx),c=e("#mCSB_"+n.idx+"_container"),d=c.parent(),u="input,textarea,select,datalist,keygen,[contenteditable='true']",f=c.find("iframe"),h=["blur."+l+" keydown."+l+" keyup."+l];f.length&&f.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind(h[0],function(e){t(e)})})}),s.attr("tabindex","0").bind(h[0],function(e){t(e)})},j=function(t,o,n,i,r){function l(e){u.snapAmount&&(f.scrollAmount=u.snapAmount instanceof Array?"x"===f.dir[0]?u.snapAmount[1]:u.snapAmount[0]:u.snapAmount);var o="stepped"!==f.type,a=r?r:e?o?p/1.5:g:1e3/60,n=e?o?7.5:40:2.5,s=[Math.abs(h[0].offsetTop),Math.abs(h[0].offsetLeft)],d=[c.scrollRatio.y>10?10:c.scrollRatio.y,c.scrollRatio.x>10?10:c.scrollRatio.x],m="x"===f.dir[0]?s[1]+f.dir[1]*(d[1]*n):s[0]+f.dir[1]*(d[0]*n),v="x"===f.dir[0]?s[1]+f.dir[1]*parseInt(f.scrollAmount):s[0]+f.dir[1]*parseInt(f.scrollAmount),x="auto"!==f.scrollAmount?v:m,_=i?i:e?o?"mcsLinearOut":"mcsEaseInOut":"mcsLinear",w=!!e;return e&&17>a&&(x="x"===f.dir[0]?s[1]:s[0]),G(t,x.toString(),{dir:f.dir[0],scrollEasing:_,dur:a,onComplete:w}),e?void(f.dir=!1):(clearTimeout(f.step),void(f.step=setTimeout(function(){l()},a)))}function s(){clearTimeout(f.step),$(f,"step"),Q(t)}var c=t.data(a),u=c.opt,f=c.sequential,h=e("#mCSB_"+c.idx+"_container"),m="stepped"===f.type,p=u.scrollInertia<26?26:u.scrollInertia,g=u.scrollInertia<1?17:u.scrollInertia;switch(o){case"on":if(f.dir=[n===d[16]||n===d[15]||39===n||37===n?"x":"y",n===d[13]||n===d[15]||38===n||37===n?-1:1],Q(t),oe(n)&&"stepped"===f.type)return;l(m);break;case"off":s(),(m||c.tweenRunning&&f.dir)&&l(!0)}},Y=function(t){var o=e(this).data(a).opt,n=[];return"function"==typeof t&&(t=t()),t instanceof Array?n=t.length>1?[t[0],t[1]]:"x"===o.axis?[null,t[0]]:[t[0],null]:(n[0]=t.y?t.y:t.x||"x"===o.axis?null:t,n[1]=t.x?t.x:t.y||"y"===o.axis?null:t),"function"==typeof n[0]&&(n[0]=n[0]()),"function"==typeof n[1]&&(n[1]=n[1]()),n},X=function(t,o){if(null!=t&&"undefined"!=typeof t){var n=e(this),i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx+"_container"),s=l.parent(),c=typeof t;o||(o="x"===r.axis?"x":"y");var d="x"===o?l.outerWidth(!1)-s.width():l.outerHeight(!1)-s.height(),f="x"===o?l[0].offsetLeft:l[0].offsetTop,h="x"===o?"left":"top";switch(c){case"function":return t();case"object":var m=t.jquery?t:e(t);if(!m.length)return;return"x"===o?ae(m)[1]:ae(m)[0];case"string":case"number":if(oe(t))return Math.abs(t);if(-1!==t.indexOf("%"))return Math.abs(d*parseInt(t)/100);if(-1!==t.indexOf("-="))return Math.abs(f-parseInt(t.split("-=")[1]));if(-1!==t.indexOf("+=")){var p=f+parseInt(t.split("+=")[1]);return p>=0?0:Math.abs(p)}if(-1!==t.indexOf("px")&&oe(t.split("px")[0]))return Math.abs(t.split("px")[0]);if("top"===t||"left"===t)return 0;if("bottom"===t)return Math.abs(s.height()-l.outerHeight(!1));if("right"===t)return Math.abs(s.width()-l.outerWidth(!1));if("first"===t||"last"===t){var m=l.find(":"+t);return"x"===o?ae(m)[1]:ae(m)[0]}return e(t).length?"x"===o?ae(e(t))[1]:ae(e(t))[0]:(l.css(h,t),void u.update.call(null,n[0]))}}},N=function(t){function o(){return clearTimeout(f[0].autoUpdate),0===l.parents("html").length?void(l=null):void(f[0].autoUpdate=setTimeout(function(){return c.advanced.updateOnSelectorChange&&(s.poll.change.n=i(),s.poll.change.n!==s.poll.change.o)?(s.poll.change.o=s.poll.change.n,void r(3)):c.advanced.updateOnContentResize&&(s.poll.size.n=l[0].scrollHeight+l[0].scrollWidth+f[0].offsetHeight+l[0].offsetHeight+l[0].offsetWidth,s.poll.size.n!==s.poll.size.o)?(s.poll.size.o=s.poll.size.n,void r(1)):!c.advanced.updateOnImageLoad||"auto"===c.advanced.updateOnImageLoad&&"y"===c.axis||(s.poll.img.n=f.find("img").length,s.poll.img.n===s.poll.img.o)?void((c.advanced.updateOnSelectorChange||c.advanced.updateOnContentResize||c.advanced.updateOnImageLoad)&&o()):(s.poll.img.o=s.poll.img.n,void f.find("img").each(function(){n(this)}))},c.advanced.autoUpdateTimeout))}function n(t){function o(e,t){return function(){
    return t.apply(e, arguments)
}
} function a() { this.onload = null, e(t).addClass(d[2]), r(2) } if (e(t).hasClass(d[2])) return void r(); var n = new Image; n.onload = o(n, a), n.src = t.src
} function i() { c.advanced.updateOnSelectorChange === !0 && (c.advanced.updateOnSelectorChange = "*"); var e = 0, t = f.find(c.advanced.updateOnSelectorChange); return c.advanced.updateOnSelectorChange && t.length > 0 && t.each(function () { e += this.offsetHeight + this.offsetWidth }), e } function r(e) { clearTimeout(f[0].autoUpdate), u.update.call(null, l[0], e) } var l = e(this), s = l.data(a), c = s.opt, f = e("#mCSB_" + s.idx + "_container"); return t ? (clearTimeout(f[0].autoUpdate), void $(f[0], "autoUpdate")) : void o()
}, V = function (e, t, o) { return Math.round(e / t) * t - o }, Q = function (t) { var o = t.data(a), n = e("#mCSB_" + o.idx + "_container,#mCSB_" + o.idx + "_container_wrapper,#mCSB_" + o.idx + "_dragger_vertical,#mCSB_" + o.idx + "_dragger_horizontal"); n.each(function () { Z.call(this) }) }, G = function (t, o, n) { function i(e) { return s && c.callbacks[e] && "function" == typeof c.callbacks[e] } function r() { return [c.callbacks.alwaysTriggerOffsets || w >= S[0] + y, c.callbacks.alwaysTriggerOffsets || -B >= w] } function l() { var e = [h[0].offsetTop, h[0].offsetLeft], o = [x[0].offsetTop, x[0].offsetLeft], a = [h.outerHeight(!1), h.outerWidth(!1)], i = [f.height(), f.width()]; t[0].mcs = { content: h, top: e[0], left: e[1], draggerTop: o[0], draggerLeft: o[1], topPct: Math.round(100 * Math.abs(e[0]) / (Math.abs(a[0]) - i[0])), leftPct: Math.round(100 * Math.abs(e[1]) / (Math.abs(a[1]) - i[1])), direction: n.dir } } var s = t.data(a), c = s.opt, d = { trigger: "internal", dir: "y", scrollEasing: "mcsEaseOut", drag: !1, dur: c.scrollInertia, overwrite: "all", callbacks: !0, onStart: !0, onUpdate: !0, onComplete: !0 }, n = e.extend(d, n), u = [n.dur, n.drag ? 0 : n.dur], f = e("#mCSB_" + s.idx), h = e("#mCSB_" + s.idx + "_container"), m = h.parent(), p = c.callbacks.onTotalScrollOffset ? Y.call(t, c.callbacks.onTotalScrollOffset) : [0, 0], g = c.callbacks.onTotalScrollBackOffset ? Y.call(t, c.callbacks.onTotalScrollBackOffset) : [0, 0]; if (s.trigger = n.trigger, 0 === m.scrollTop() && 0 === m.scrollLeft() || (e(".mCSB_" + s.idx + "_scrollbar").css("visibility", "visible"), m.scrollTop(0).scrollLeft(0)), "_resetY" !== o || s.contentReset.y || (i("onOverflowYNone") && c.callbacks.onOverflowYNone.call(t[0]), s.contentReset.y = 1), "_resetX" !== o || s.contentReset.x || (i("onOverflowXNone") && c.callbacks.onOverflowXNone.call(t[0]), s.contentReset.x = 1), "_resetY" !== o && "_resetX" !== o) { if (!s.contentReset.y && t[0].mcs || !s.overflowed[0] || (i("onOverflowY") && c.callbacks.onOverflowY.call(t[0]), s.contentReset.x = null), !s.contentReset.x && t[0].mcs || !s.overflowed[1] || (i("onOverflowX") && c.callbacks.onOverflowX.call(t[0]), s.contentReset.x = null), c.snapAmount) { var v = c.snapAmount instanceof Array ? "x" === n.dir ? c.snapAmount[1] : c.snapAmount[0] : c.snapAmount; o = V(o, v, c.snapOffset) } switch (n.dir) { case "x": var x = e("#mCSB_" + s.idx + "_dragger_horizontal"), _ = "left", w = h[0].offsetLeft, S = [f.width() - h.outerWidth(!1), x.parent().width() - x.width()], b = [o, 0 === o ? 0 : o / s.scrollRatio.x], y = p[1], B = g[1], T = y > 0 ? y / s.scrollRatio.x : 0, k = B > 0 ? B / s.scrollRatio.x : 0; break; case "y": var x = e("#mCSB_" + s.idx + "_dragger_vertical"), _ = "top", w = h[0].offsetTop, S = [f.height() - h.outerHeight(!1), x.parent().height() - x.height()], b = [o, 0 === o ? 0 : o / s.scrollRatio.y], y = p[0], B = g[0], T = y > 0 ? y / s.scrollRatio.y : 0, k = B > 0 ? B / s.scrollRatio.y : 0 } b[1] < 0 || 0 === b[0] && 0 === b[1] ? b = [0, 0] : b[1] >= S[1] ? b = [S[0], S[1]] : b[0] = -b[0], t[0].mcs || (l(), i("onInit") && c.callbacks.onInit.call(t[0])), clearTimeout(h[0].onCompleteTimeout), J(x[0], _, Math.round(b[1]), u[1], n.scrollEasing), !s.tweenRunning && (0 === w && b[0] >= 0 || w === S[0] && b[0] <= S[0]) || J(h[0], _, Math.round(b[0]), u[0], n.scrollEasing, n.overwrite, { onStart: function () { n.callbacks && n.onStart && !s.tweenRunning && (i("onScrollStart") && (l(), c.callbacks.onScrollStart.call(t[0])), s.tweenRunning = !0, C(x), s.cbOffsets = r()) }, onUpdate: function () { n.callbacks && n.onUpdate && i("whileScrolling") && (l(), c.callbacks.whileScrolling.call(t[0])) }, onComplete: function () { if (n.callbacks && n.onComplete) { "yx" === c.axis && clearTimeout(h[0].onCompleteTimeout); var e = h[0].idleTimer || 0; h[0].onCompleteTimeout = setTimeout(function () { i("onScroll") && (l(), c.callbacks.onScroll.call(t[0])), i("onTotalScroll") && b[1] >= S[1] - T && s.cbOffsets[0] && (l(), c.callbacks.onTotalScroll.call(t[0])), i("onTotalScrollBack") && b[1] <= k && s.cbOffsets[1] && (l(), c.callbacks.onTotalScrollBack.call(t[0])), s.tweenRunning = !1, h[0].idleTimer = 0, C(x, "hide") }, e) } } }) } }, J = function (e, t, o, a, n, i, r) { function l() { S.stop || (x || m.call(), x = K() - v, s(), x >= S.time && (S.time = x > S.time ? x + f - (x - S.time) : x + f - 1, S.time < x + 1 && (S.time = x + 1)), S.time < a ? S.id = h(l) : g.call()) } function s() { a > 0 ? (S.currVal = u(S.time, _, b, a, n), w[t] = Math.round(S.currVal) + "px") : w[t] = o + "px", p.call() } function c() { f = 1e3 / 60, S.time = x + f, h = window.requestAnimationFrame ? window.requestAnimationFrame : function (e) { return s(), setTimeout(e, .01) }, S.id = h(l) } function d() { null != S.id && (window.requestAnimationFrame ? window.cancelAnimationFrame(S.id) : clearTimeout(S.id), S.id = null) } function u(e, t, o, a, n) { switch (n) { case "linear": case "mcsLinear": return o * e / a + t; case "mcsLinearOut": return e /= a, e--, o * Math.sqrt(1 - e * e) + t; case "easeInOutSmooth": return e /= a / 2, 1 > e ? o / 2 * e * e + t : (e--, -o / 2 * (e * (e - 2) - 1) + t); case "easeInOutStrong": return e /= a / 2, 1 > e ? o / 2 * Math.pow(2, 10 * (e - 1)) + t : (e--, o / 2 * (-Math.pow(2, -10 * e) + 2) + t); case "easeInOut": case "mcsEaseInOut": return e /= a / 2, 1 > e ? o / 2 * e * e * e + t : (e -= 2, o / 2 * (e * e * e + 2) + t); case "easeOutSmooth": return e /= a, e--, -o * (e * e * e * e - 1) + t; case "easeOutStrong": return o * (-Math.pow(2, -10 * e / a) + 1) + t; case "easeOut": case "mcsEaseOut": default: var i = (e /= a) * e, r = i * e; return t + o * (.499999999999997 * r * i + -2.5 * i * i + 5.5 * r + -6.5 * i + 4 * e) } } e._mTween || (e._mTween = { top: {}, left: {} }); var f, h, r = r || {}, m = r.onStart || function () { }, p = r.onUpdate || function () { }, g = r.onComplete || function () { }, v = K(), x = 0, _ = e.offsetTop, w = e.style, S = e._mTween[t]; "left" === t && (_ = e.offsetLeft); var b = o - _; S.stop = 0, "none" !== i && d(), c() }, K = function () { return window.performance && window.performance.now ? window.performance.now() : window.performance && window.performance.webkitNow ? window.performance.webkitNow() : Date.now ? Date.now() : (new Date).getTime() }, Z = function () { var e = this; e._mTween || (e._mTween = { top: {}, left: {} }); for (var t = ["top", "left"], o = 0; o < t.length; o++) { var a = t[o]; e._mTween[a].id && (window.requestAnimationFrame ? window.cancelAnimationFrame(e._mTween[a].id) : clearTimeout(e._mTween[a].id), e._mTween[a].id = null, e._mTween[a].stop = 1) } }, $ = function (e, t) { try { delete e[t] } catch (o) { e[t] = null } }, ee = function (e) { return !(e.which && 1 !== e.which) }, te = function (e) { var t = e.originalEvent.pointerType; return !(t && "touch" !== t && 2 !== t) }, oe = function (e) { return !isNaN(parseFloat(e)) && isFinite(e) }, ae = function (e) { var t = e.parents(".mCSB_container"); return [e.offset().top - t.offset().top, e.offset().left - t.offset().left] }, ne = function () { function e() { var e = ["webkit", "moz", "ms", "o"]; if ("hidden" in document) return "hidden"; for (var t = 0; t < e.length; t++) if (e[t] + "Hidden" in document) return e[t] + "Hidden"; return null } var t = e(); return t ? document[t] : !1 }; e.fn[o] = function (t) { return u[t] ? u[t].apply(this, Array.prototype.slice.call(arguments, 1)) : "object" != typeof t && t ? void e.error("Method " + t + " does not exist") : u.init.apply(this, arguments) }, e[o] = function (t) { return u[t] ? u[t].apply(this, Array.prototype.slice.call(arguments, 1)) : "object" != typeof t && t ? void e.error("Method " + t + " does not exist") : u.init.apply(this, arguments) }, e[o].defaults = i, window[o] = !0, e(window).bind("load", function () { e(n)[o](), e.extend(e.expr[":"], { mcsInView: e.expr[":"].mcsInView || function (t) { var o, a, n = e(t), i = n.parents(".mCSB_container"); if (i.length) return o = i.parent(), a = [i[0].offsetTop, i[0].offsetLeft], a[0] + ae(n)[0] >= 0 && a[0] + ae(n)[0] < o.height() - n.outerHeight(!1) && a[1] + ae(n)[1] >= 0 && a[1] + ae(n)[1] < o.width() - n.outerWidth(!1) }, mcsInSight: e.expr[":"].mcsInSight || function (t, o, a) { var n, i, r, l, s = e(t), c = s.parents(".mCSB_container"), d = "exact" === a[3] ? [[1, 0], [1, 0]] : [[.9, .1], [.6, .4]]; if (c.length) return n = [s.outerHeight(!1), s.outerWidth(!1)], r = [c[0].offsetTop + ae(s)[0], c[0].offsetLeft + ae(s)[1]], i = [c.parent()[0].offsetHeight, c.parent()[0].offsetWidth], l = [n[0] < i[0] ? d[0] : d[1], n[1] < i[1] ? d[0] : d[1]], r[0] - i[0] * l[0][0] < 0 && r[0] + n[0] - i[0] * l[0][1] >= 0 && r[1] - i[1] * l[1][0] < 0 && r[1] + n[1] - i[1] * l[1][1] >= 0 }, mcsOverflow: e.expr[":"].mcsOverflow || function (t) { var o = e(t).data(a); if (o) return o.overflowed[0] || o.overflowed[1] } }) })
})
});
;/*!
 * Knockout JavaScript library v3.4.2
 * (c) The Knockout.js team - http://knockoutjs.com/
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

(function() {(function(n){var x=this||(0,eval)("this"),t=x.document,M=x.navigator,u=x.jQuery,H=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in R?a===c:!1}function S(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function T(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function U(a,
c){c&&c!==E?"beforeChange"===c?this.Ob(a):this.Ja(a,c):this.Pb(a)}function V(a,c){null!==c&&c.k&&c.k()}function W(a,c){var d=this.Mc,e=d[s];e.T||(this.ob&&this.Oa[c]?(d.Sb(c,a,this.Oa[c]),this.Oa[c]=null,--this.ob):e.s[c]||d.Sb(c,a,e.t?{$:a}:d.yc(a)),a.Ha&&a.Hc())}function K(b,c,d,e){a.d[b]={init:function(b,g,h,l,m){var k,r;a.m(function(){var q=g(),p=a.a.c(q),p=!d!==!p,A=!r;if(A||c||p!==k)A&&a.xa.Ca()&&(r=a.a.wa(a.f.childNodes(b),!0)),p?(A||a.f.fa(b,a.a.wa(r)),a.hb(e?e(m,q):m,b)):a.f.za(b),k=p},null,
{i:b});return{controlsDescendantBindings:!0}}};a.h.va[b]=!1;a.f.aa[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.H=function(a,c,d){a[c]=d};a.version="3.4.2";a.b("version",a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1};a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=
b;return a}function e(b,c,d,e){var m=b[c].match(r)||[];a.a.r(d.match(r),function(b){a.a.ra(m,b,e)});b[c]=m.join(" ")}var f={__proto__:[]}instanceof Array,g="function"===typeof Symbol,h={},l={};h[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];h.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(h,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)l[b[c]]=a});var m={propertychange:!0},k=
t&&function(){for(var a=3,b=t.createElement("div"),c=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),r=/\S+/g;return{gc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],r:function(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)},o:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Vb:function(a,b,c){for(var d=
0,e=a.length;d<e;d++)if(b.call(c,a[d],d))return a[d];return null},Na:function(b,c){var d=a.a.o(b,c);0<d?b.splice(d,1):0===d&&b.shift()},Wb:function(b){b=b||[];for(var c=[],d=0,e=b.length;d<e;d++)0>a.a.o(c,b[d])&&c.push(b[d]);return c},ib:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)c.push(b(a[d],d));return c},Ma:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)b(a[d],d)&&c.push(a[d]);return c},ta:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<
d;c++)a.push(b[c]);return a},ra:function(b,c,d){var e=a.a.o(a.a.Bb(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},la:f,extend:c,$a:d,ab:f?d:c,D:b,Ea:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},rb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},nc:function(b){b=a.a.W(b);for(var c=(b[0]&&b[0].ownerDocument||t).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.ba(b[d]));return c},wa:function(b,c){for(var d=0,e=b.length,m=[];d<e;d++){var k=
b[d].cloneNode(!0);m.push(c?a.ba(k):k)}return m},fa:function(b,c){a.a.rb(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},uc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],m=e.parentNode,k=0,f=c.length;k<f;k++)m.insertBefore(c[k],e);k=0;for(f=d.length;k<f;k++)a.removeNode(d[k])}},Ba:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=
a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),c=c.nextSibling;a.push(d)}}return a},wc:function(a,b){7>k?a.setAttribute("selected",b):a.selected=b},cb:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},sd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Rc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==
(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},qb:function(b){return a.a.Rc(b,b.ownerDocument.documentElement)},Tb:function(b){return!!a.a.Vb(b,a.a.qb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Zb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Zb(b),c)},dc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},q:function(b,
c,d){var e=a.a.Zb(d);d=k&&m[c];if(a.options.useOnlyNativeEvents||d||!u)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var f=function(a){e.call(b,a)},l="on"+c;b.attachEvent(l,f);a.a.G.qa(b,function(){b.detachEvent(l,f)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else u(b).bind(c,e)},Fa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===
a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!u||d)if("function"==typeof t.createEvent)if("function"==typeof b.dispatchEvent)d=t.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");
else u(b).trigger(c)},c:function(b){return a.I(b)?b():b},Bb:function(b){return a.I(b)?b.p():b},fb:function(b,c,d){var k;c&&("object"===typeof b.classList?(k=b.classList[d?"add":"remove"],a.a.r(c.match(r),function(a){k.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},bb:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.fa(b,[b.ownerDocument.createTextNode(d)]):e.data=
d;a.a.Wc(b)},vc:function(a,b){a.name=b;if(7>=k)try{a.mergeAttributes(t.createElement("<input name='"+a.name+"'/>"),!1)}catch(c){}},Wc:function(a){9<=k&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Sc:function(a){if(k){var b=a.style.width;a.style.width=0;a.style.width=b}},nd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},W:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},bc:function(a){return g?Symbol(a):a},xd:6===k,
yd:7===k,C:k,ic:function(b,c){for(var d=a.a.W(b.getElementsByTagName("input")).concat(a.a.W(b.getElementsByTagName("textarea"))),e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},k=[],m=d.length-1;0<=m;m--)e(d[m])&&k.push(d[m]);return k},kd:function(b){return"string"==typeof b&&(b=a.a.cb(b))?H&&H.parse?H.parse(b):(new Function("return "+b))():null},Gb:function(b,c,d){if(!H||!H.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return H.stringify(a.a.c(b),c,d)},ld:function(c,d,e){e=e||{};var k=e.params||{},m=e.includeFields||this.gc,f=c;if("object"==typeof c&&"form"===a.a.A(c))for(var f=c.action,l=m.length-1;0<=l;l--)for(var g=a.a.ic(c,m[l]),h=g.length-1;0<=h;h--)k[g[h].name]=g[h].value;d=a.a.c(d);var r=t.createElement("form");r.style.display="none";r.action=f;r.method="post";for(var n in d)c=t.createElement("input"),c.type="hidden",c.name=n,c.value=a.a.Gb(a.a.c(d[n])),r.appendChild(c);b(k,function(a,b){var c=t.createElement("input");
c.type="hidden";c.name=a;c.value=b;r.appendChild(c)});t.body.appendChild(r);e.submitter?e.submitter(r):r.submit();setTimeout(function(){r.parentNode.removeChild(r)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.r);a.b("utils.arrayFirst",a.a.Vb);a.b("utils.arrayFilter",a.a.Ma);a.b("utils.arrayGetDistinctValues",a.a.Wb);a.b("utils.arrayIndexOf",a.a.o);a.b("utils.arrayMap",a.a.ib);a.b("utils.arrayPushAll",a.a.ta);a.b("utils.arrayRemoveItem",a.a.Na);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
a.a.gc);a.b("utils.getFormFields",a.a.ic);a.b("utils.peekObservable",a.a.Bb);a.b("utils.postJson",a.a.ld);a.b("utils.parseJson",a.a.kd);a.b("utils.registerEventHandler",a.a.q);a.b("utils.stringifyJson",a.a.Gb);a.b("utils.range",a.a.nd);a.b("utils.toggleDomNodeCssClass",a.a.fb);a.b("utils.triggerEvent",a.a.Fa);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.D);a.b("utils.addOrRemoveItem",a.a.ra);a.b("utils.setTextContent",a.a.bb);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=
function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.e=new function(){function a(b,g){var h=b[d];if(!h||"null"===h||!e[h]){if(!g)return n;h=b[d]="ko"+c++;e[h]={}}return e[h]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===n?n:e[d]},set:function(c,d,e){if(e!==n||a(c,!1)!==n)a(c,!0)[d]=
e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},J:function(){return c++ +d}}};a.b("utils.domData",a.a.e);a.b("utils.domData.clear",a.a.e.clear);a.a.G=new function(){function b(b,c){var e=a.a.e.get(b,d);e===n&&c&&(e=[],a.a.e.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](d);a.a.e.clear(d);a.a.G.cleanExternalData(d);if(f[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.e.J(),e={1:!0,8:!0,9:!0},
f={1:!0,9:!0};return{qa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},tc:function(c,e){var f=b(c,!1);f&&(a.a.Na(f,e),0==f.length&&a.a.e.set(c,d,n))},ba:function(b){if(e[b.nodeType]&&(c(b),f[b.nodeType])){var d=[];a.a.ta(d,b.getElementsByTagName("*"));for(var l=0,m=d.length;l<m;l++)c(d[l])}return b},removeNode:function(b){a.ba(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){u&&"function"==typeof u.cleanData&&u.cleanData([a])}}};
a.ba=a.a.G.ba;a.removeNode=a.a.G.removeNode;a.b("cleanNode",a.ba);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.G);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.G.qa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.G.tc);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},
g=8>=a.a.C;a.a.na=function(c,d){var e;if(u)if(u.parseHTML)e=u.parseHTML(c,d)||[];else{if((e=u.clean([c],d))&&e[0]){for(var k=e[0];k.parentNode&&11!==k.parentNode.nodeType;)k=k.parentNode;k.parentNode&&k.parentNode.removeChild(k)}}else{(e=d)||(e=t);var k=e.parentWindow||e.defaultView||x,r=a.a.cb(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored<div>"+p[1]+c+p[2]+"</div>";"function"==typeof k.innerShiv?q.appendChild(k.innerShiv(p)):(g&&e.appendChild(q),
q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.W(q.lastChild.childNodes)}return e};a.a.Eb=function(b,c){a.a.rb(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),u)u(b).html(c);else for(var d=a.a.na(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.na);a.b("utils.setHtml",a.a.Eb);a.N=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.N.pc(c.nodeValue);null!=f&&e.push({Qc:c,hd:f})}else if(1==c.nodeType)for(var f=
0,g=c.childNodes,h=g.length;f<h;f++)b(g[f],e)}var c={};return{yb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},Bc:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),
!0}finally{delete c[a]}},Cc:function(c,e){var f=[];b(c,f);for(var g=0,h=f.length;g<h;g++){var l=f[g].Qc,m=[l];e&&a.a.ta(m,e);a.N.Bc(f[g].hd,m);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},pc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.N);a.b("memoization.memoize",a.N.yb);a.b("memoization.unmemoize",a.N.Bc);a.b("memoization.parseMemoText",a.N.pc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.N.Cc);a.Z=function(){function b(){if(e)for(var b=
e,c=0,m;g<e;)if(m=d[g++]){if(g>b){if(5E3<=++c){g=e;a.a.dc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(k){a.a.dc(k)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=t.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):t&&"onreadystatechange"in t.createElement("script")?function(a){var b=t.createElement("script");b.onreadystatechange=
function(){b.onreadystatechange=null;t.documentElement.removeChild(b);b=null;a()};t.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Za:function(b){e||a.Z.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&a<e&&(d[a]=null)},resetForTesting:function(){var a=e-g;g=e=d.length=0;return a},rd:b}}();a.b("tasks",a.Z);a.b("tasks.schedule",a.Z.Za);a.b("tasks.runEarly",a.Z.rd);a.Aa={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.B({read:b,write:function(e){clearTimeout(d);
d=a.a.setTimeout(function(){b(e)},c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.gb=!1;f="notifyWhenChangesStop"==e?T:S;a.Wa(function(a){return f(a,d)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.gb||(b.gb=!0,b.Wa(function(c){var e,f=!1;return function(){if(!f){a.Z.cancel(e);e=a.Z.Za(c);try{f=!0,b.notifySubscribers(n,"dirty")}finally{f=
!1}}}}))},notify:function(a,c){a.equalityComparer="always"==c?null:J}};var R={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.Aa);a.zc=function(b,c,d){this.$=b;this.jb=c;this.Pc=d;this.T=!1;a.H(this,"dispose",this.k)};a.zc.prototype.k=function(){this.T=!0;this.Pc()};a.K=function(){a.a.ab(this,D);D.ub(this)};var E="change",D={ub:function(a){a.F={change:[]};a.Qb=1},Y:function(b,c,d){var e=this;d=d||E;var f=new a.zc(e,c?b.bind(c):b,function(){a.a.Na(e.F[d],f);e.Ka&&e.Ka(d)});e.ua&&e.ua(d);
e.F[d]||(e.F[d]=[]);e.F[d].push(f);return f},notifySubscribers:function(b,c){c=c||E;c===E&&this.Kb();if(this.Ra(c)){var d=c===E&&this.Fc||this.F[c].slice(0);try{a.l.Xb();for(var e=0,f;f=d[e];++e)f.T||f.jb(b)}finally{a.l.end()}}},Pa:function(){return this.Qb},Zc:function(a){return this.Pa()!==a},Kb:function(){++this.Qb},Wa:function(b){var c=this,d=a.I(c),e,f,g,h;c.Ja||(c.Ja=c.notifySubscribers,c.notifySubscribers=U);var l=b(function(){c.Ha=!1;d&&h===c&&(h=c.Mb?c.Mb():c());var a=f||c.Ua(g,h);f=e=!1;
a&&c.Ja(g=h)});c.Pb=function(a){c.Fc=c.F[E].slice(0);c.Ha=e=!0;h=a;l()};c.Ob=function(a){e||(g=a,c.Ja(a,"beforeChange"))};c.Hc=function(){c.Ua(g,c.p(!0))&&(f=!0)}},Ra:function(a){return this.F[a]&&this.F[a].length},Xc:function(b){if(b)return this.F[b]&&this.F[b].length||0;var c=0;a.a.D(this.F,function(a,b){"dirty"!==a&&(c+=b.length)});return c},Ua:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},extend:function(b){var c=this;b&&a.a.D(b,function(b,e){var f=a.Aa[b];"function"==
typeof f&&(c=f(c,e)||c)});return c}};a.H(D,"subscribe",D.Y);a.H(D,"extend",D.extend);a.H(D,"getSubscriptionsCount",D.Xc);a.a.la&&a.a.$a(D,Function.prototype);a.K.fn=D;a.lc=function(a){return null!=a&&"function"==typeof a.Y&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.K);a.b("isSubscribable",a.lc);a.xa=a.l=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{Xb:b,end:c,sc:function(b){if(e){if(!a.lc(b))throw Error("Only subscribable things can act as dependencies");
e.jb.call(e.Lc,b,b.Gc||(b.Gc=++f))}},w:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},Ca:function(){if(e)return e.m.Ca()},Va:function(){if(e)return e.Va}}}();a.b("computedContext",a.xa);a.b("computedContext.getDependenciesCount",a.xa.Ca);a.b("computedContext.isInitial",a.xa.Va);a.b("ignoreDependencies",a.wd=a.l.w);var F=a.a.bc("_latestValue");a.O=function(b){function c(){if(0<arguments.length)return c.Ua(c[F],arguments[0])&&(c.ia(),c[F]=arguments[0],c.ha()),this;a.l.sc(c);return c[F]}
c[F]=b;a.a.la||a.a.extend(c,a.K.fn);a.K.fn.ub(c);a.a.ab(c,B);a.options.deferUpdates&&a.Aa.deferred(c,!0);return c};var B={equalityComparer:J,p:function(){return this[F]},ha:function(){this.notifySubscribers(this[F])},ia:function(){this.notifySubscribers(this[F],"beforeChange")}};a.a.la&&a.a.$a(B,a.K.fn);var I=a.O.md="__ko_proto__";B[I]=a.O;a.Qa=function(b,c){return null===b||b===n||b[I]===n?!1:b[I]===c?!0:a.Qa(b[I],c)};a.I=function(b){return a.Qa(b,a.O)};a.Da=function(b){return"function"==typeof b&&
b[I]===a.O||"function"==typeof b&&b[I]===a.B&&b.$c?!0:!1};a.b("observable",a.O);a.b("isObservable",a.I);a.b("isWriteableObservable",a.Da);a.b("isWritableObservable",a.Da);a.b("observable.fn",B);a.H(B,"peek",B.p);a.H(B,"valueHasMutated",B.ha);a.H(B,"valueWillMutate",B.ia);a.ma=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.O(b);a.a.ab(b,a.ma.fn);return b.extend({trackArrayChanges:!0})};
a.ma.fn={remove:function(b){for(var c=this.p(),d=[],e="function"!=typeof b||a.I(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];e(g)&&(0===d.length&&this.ia(),d.push(g),c.splice(f,1),f--)}d.length&&this.ha();return d},removeAll:function(b){if(b===n){var c=this.p(),d=c.slice(0);this.ia();c.splice(0,c.length);this.ha();return d}return b?this.remove(function(c){return 0<=a.a.o(b,c)}):[]},destroy:function(b){var c=this.p(),d="function"!=typeof b||a.I(b)?function(a){return a===b}:b;this.ia();
for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.ha()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.o(b,c)}):[]},indexOf:function(b){var c=this();return a.a.o(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ia(),this.p()[d]=c,this.ha())}};a.a.la&&a.a.$a(a.ma.fn,a.O.fn);a.a.r("pop push reverse shift sort splice unshift".split(" "),function(b){a.ma.fn[b]=function(){var a=this.p();this.ia();this.Yb(a,b,arguments);
var d=a[b].apply(a,arguments);this.ha();return d===a?this:d}});a.a.r(["slice"],function(b){a.ma.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.ma);a.Aa.trackArrayChanges=function(b,c){function d(){if(!e){e=!0;l=b.notifySubscribers;b.notifySubscribers=function(a,b){b&&b!==E||++h;return l.apply(this,arguments)};var c=[].concat(b.p()||[]);f=null;g=b.Y(function(d){d=[].concat(d||[]);if(b.Ra("arrayChange")){var e;if(!f||1<h)f=a.a.lb(c,d,b.kb);e=f}c=d;f=null;h=0;
e&&e.length&&b.notifySubscribers(e,"arrayChange")})}}b.kb={};c&&"object"==typeof c&&a.a.extend(b.kb,c);b.kb.sparse=!0;if(!b.Yb){var e=!1,f=null,g,h=0,l,m=b.ua,k=b.Ka;b.ua=function(a){m&&m.call(b,a);"arrayChange"===a&&d()};b.Ka=function(a){k&&k.call(b,a);"arrayChange"!==a||b.Ra("arrayChange")||(l&&(b.notifySubscribers=l,l=n),g.k(),e=!1)};b.Yb=function(b,c,d){function k(a,b,c){return m[m.length]={status:a,value:b,index:c}}if(e&&!h){var m=[],l=b.length,g=d.length,G=0;switch(c){case "push":G=l;case "unshift":for(c=
0;c<g;c++)k("added",d[c],G+c);break;case "pop":G=l-1;case "shift":l&&k("deleted",b[G],G);break;case "splice":c=Math.min(Math.max(0,0>d[0]?l+d[0]:d[0]),l);for(var l=1===g?l:Math.min(c+(d[1]||0),l),g=c+g-2,G=Math.max(l,g),n=[],s=[],w=2;c<G;++c,++w)c<l&&s.push(k("deleted",b[c],c)),c<g&&n.push(k("added",d[w],c));a.a.hc(s,n);break;default:return}f=m}}}};var s=a.a.bc("_state");a.m=a.B=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.sb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
return this}a.l.sc(e);(g.V||g.t&&e.Sa())&&e.U();return g.M}"object"===typeof b?d=b:(d=d||{},b&&(d.read=b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={M:n,da:!0,V:!0,Ta:!1,Hb:!1,T:!1,Ya:!1,t:!1,od:d.read,sb:c||d.owner,i:d.disposeWhenNodeIsRemoved||d.i||null,ya:d.disposeWhen||d.ya,pb:null,s:{},L:0,fc:null};e[s]=g;e.$c="function"===typeof f;a.a.la||a.a.extend(e,a.K.fn);a.K.fn.ub(e);a.a.ab(e,z);d.pure?(g.Ya=!0,g.t=!0,a.a.extend(e,
Y)):d.deferEvaluation&&a.a.extend(e,Z);a.options.deferUpdates&&a.Aa.deferred(e,!0);g.i&&(g.Hb=!0,g.i.nodeType||(g.i=null));g.t||d.deferEvaluation||e.U();g.i&&e.ca()&&a.a.G.qa(g.i,g.pb=function(){e.k()});return e};var z={equalityComparer:J,Ca:function(){return this[s].L},Sb:function(a,c,d){if(this[s].Ya&&c===this)throw Error("A 'pure' computed must not be called recursively");this[s].s[a]=d;d.Ia=this[s].L++;d.pa=c.Pa()},Sa:function(){var a,c,d=this[s].s;for(a in d)if(d.hasOwnProperty(a)&&(c=d[a],this.oa&&
c.$.Ha||c.$.Zc(c.pa)))return!0},gd:function(){this.oa&&!this[s].Ta&&this.oa(!1)},ca:function(){var a=this[s];return a.V||0<a.L},qd:function(){this.Ha?this[s].V&&(this[s].da=!0):this.ec()},yc:function(a){if(a.gb&&!this[s].i){var c=a.Y(this.gd,this,"dirty"),d=a.Y(this.qd,this);return{$:a,k:function(){c.k();d.k()}}}return a.Y(this.ec,this)},ec:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[s].fc),this[s].fc=a.a.setTimeout(function(){b.U(!0)},c)):b.oa?b.oa(!0):b.U(!0)},U:function(b){var c=
this[s],d=c.ya,e=!1;if(!c.Ta&&!c.T){if(c.i&&!a.a.qb(c.i)||d&&d()){if(!c.Hb){this.k();return}}else c.Hb=!1;c.Ta=!0;try{e=this.Vc(b)}finally{c.Ta=!1}c.L||this.k();return e}},Vc:function(b){var c=this[s],d=!1,e=c.Ya?n:!c.L,f={Mc:this,Oa:c.s,ob:c.L};a.l.Xb({Lc:f,jb:W,m:this,Va:e});c.s={};c.L=0;f=this.Uc(c,f);this.Ua(c.M,f)&&(c.t||this.notifySubscribers(c.M,"beforeChange"),c.M=f,c.t?this.Kb():b&&this.notifySubscribers(c.M),d=!0);e&&this.notifySubscribers(c.M,"awake");return d},Uc:function(b,c){try{var d=
b.od;return b.sb?d.call(b.sb):d()}finally{a.l.end(),c.ob&&!b.t&&a.a.D(c.Oa,V),b.da=b.V=!1}},p:function(a){var c=this[s];(c.V&&(a||!c.L)||c.t&&this.Sa())&&this.U();return c.M},Wa:function(b){a.K.fn.Wa.call(this,b);this.Mb=function(){this[s].da?this.U():this[s].V=!1;return this[s].M};this.oa=function(a){this.Ob(this[s].M);this[s].V=!0;a&&(this[s].da=!0);this.Pb(this)}},k:function(){var b=this[s];!b.t&&b.s&&a.a.D(b.s,function(a,b){b.k&&b.k()});b.i&&b.pb&&a.a.G.tc(b.i,b.pb);b.s=null;b.L=0;b.T=!0;b.da=
!1;b.V=!1;b.t=!1;b.i=null}},Y={ua:function(b){var c=this,d=c[s];if(!d.T&&d.t&&"change"==b){d.t=!1;if(d.da||c.Sa())d.s=null,d.L=0,c.U()&&c.Kb();else{var e=[];a.a.D(d.s,function(a,b){e[b.Ia]=a});a.a.r(e,function(a,b){var e=d.s[a],l=c.yc(e.$);l.Ia=b;l.pa=e.pa;d.s[a]=l})}d.T||c.notifySubscribers(d.M,"awake")}},Ka:function(b){var c=this[s];c.T||"change"!=b||this.Ra("change")||(a.a.D(c.s,function(a,b){b.k&&(c.s[a]={$:b.$,Ia:b.Ia,pa:b.pa},b.k())}),c.t=!0,this.notifySubscribers(n,"asleep"))},Pa:function(){var b=
this[s];b.t&&(b.da||this.Sa())&&this.U();return a.K.fn.Pa.call(this)}},Z={ua:function(a){"change"!=a&&"beforeChange"!=a||this.p()}};a.a.la&&a.a.$a(z,a.K.fn);var P=a.O.md;a.m[P]=a.O;z[P]=a.m;a.bd=function(b){return a.Qa(b,a.m)};a.cd=function(b){return a.Qa(b,a.m)&&b[s]&&b[s].Ya};a.b("computed",a.m);a.b("dependentObservable",a.m);a.b("isComputed",a.bd);a.b("isPureComputed",a.cd);a.b("computed.fn",z);a.H(z,"peek",z.p);a.H(z,"dispose",z.k);a.H(z,"isActive",z.ca);a.H(z,"getDependenciesCount",z.Ca);a.rc=
function(b,c){if("function"===typeof b)return a.m(b,c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.m(b,c)};a.b("pureComputed",a.rc);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var h=a instanceof Array?[]:{};g.save(a,h);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":h[c]=d;break;case "object":case "undefined":var k=
g.get(d);h[c]=k!==n?k:b(d,f,g)}});return h}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.Lb=[]}a.Ac=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.I(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Ac(b);return a.a.Gb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys,
b);0<=d?this.Lb[d]=c:(this.keys.push(b),this.Lb.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Lb[b]:n}}})();a.b("toJS",a.Ac);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.zb):7>=a.a.C?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ja:function(b,
c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.zb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.zb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f<g;++f)if(h=a.j.u(b.options[f]),h==c||""==h&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e;break;default:if(null===
c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.j);a.b("selectExtensions.readValue",a.j.u);a.b("selectExtensions.writeValue",a.j.ja);a.h=function(){function b(b){b=a.a.cb(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),r,h=[],p=0;if(d){d.push(",");for(var A=0,y;y=d[A];++A){var v=y.charCodeAt(0);if(44===v){if(0>=p){c.push(r&&h.length?{key:r,value:h.join("")}:{unknown:r||h.join("")});r=p=0;h=[];continue}}else if(58===v){if(!p&&!r&&1===h.length){r=h.pop();continue}}else 47===
v&&A&&1<y.length?(v=d[A-1].match(f))&&!g[v[0]]&&(b=b.substr(b.indexOf(y)+1),d=b.match(e),d.push(","),A=-1,y="/"):40===v||123===v||91===v?++p:41===v||125===v||93===v?--p:r||h.length||34!==v&&39!==v||(y=y.slice(1,-1));h.push(y)}}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,
g={"in":1,"return":1,"typeof":1},h={};return{va:[],ga:h,Ab:b,Xa:function(e,m){function k(b,e){var m;if(!A){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(e=l.preprocess(e,b,k)))return;if(l=h[b])m=e,0<=a.a.o(c,m)?m=!1:(l=m.match(d),m=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:m),l=m;l&&g.push("'"+b+"':function(_z){"+m+"=_z}")}p&&(e="function(){return "+e+" }");f.push("'"+b+"':"+e)}m=m||{};var f=[],g=[],p=m.valueAccessors,A=m.bindingParams,y="string"===typeof e?b(e):e;a.a.r(y,function(a){k(a.key||
a.unknown,a.value)});g.length&&k("_ko_property_writers","{"+g.join(",")+" }");return f.join(",")},fd:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},Ga:function(b,c,d,e,f){if(b&&a.I(b))!a.Da(b)||f&&b.p()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",a.h.va);a.b("expressionRewriting.parseObjectLiteral",a.h.Ab);a.b("expressionRewriting.preProcessBindings",a.h.Xa);a.b("expressionRewriting._twoWayBindings",
a.h.ga);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Xa);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&h.test(f?a.text:a.nodeValue)}function d(a,d){for(var e=a,f=1,l=[];e=e.nextSibling;){if(c(e)&&(f--,0===f))return l;l.push(e);b(e)&&f++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-
1].nextSibling:a.nextSibling:null}var f=t&&"\x3c!--test--\x3e"===t.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,h=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.f={aa:{},childNodes:function(a){return b(a)?d(a):a.childNodes},za:function(c){if(b(c)){c=a.f.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.rb(c)},fa:function(c,d){if(b(c)){a.f.za(c);for(var e=c.nextSibling,f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],
e)}else a.a.fa(c,d)},qc:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},kc:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.f.qc(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Yc:b,vd:function(a){return(a=
(f?a.text:a.nodeValue).match(g))?a[1]:null},oc:function(d){if(l[a.a.A(d)]){var k=d.firstChild;if(k){do if(1===k.nodeType){var f;f=k.firstChild;var g=null;if(f){do if(g)g.push(f);else if(b(f)){var h=e(f,!0);h?f=h:g=[f]}else c(f)&&(g=[f]);while(f=f.nextSibling)}if(f=g)for(g=k.nextSibling,h=0;h<f.length;h++)g?d.insertBefore(f[h],g):d.appendChild(f[h])}while(k=k.nextSibling)}}}}})();a.b("virtualElements",a.f);a.b("virtualElements.allowedBindings",a.f.aa);a.b("virtualElements.emptyNode",a.f.za);a.b("virtualElements.insertAfter",
a.f.kc);a.b("virtualElements.prepend",a.f.qc);a.b("virtualElements.setDomNodeChildren",a.f.fa);(function(){a.S=function(){this.Kc={}};a.a.extend(a.S.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.f.Yc(b);default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.g.Rb(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,
c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.g.Rb(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.f.vd(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var f=this.Kc,g=b+(e&&e.valueAccessors||""),h;if(!(h=f[g])){var l,m="with($context){with($data||{}){return{"+a.h.Xa(b,e)+"}}}";l=new Function("$context","$element",m);h=f[g]=l}return h(c,d)}catch(k){throw k.message="Unable to parse bindings.\nBindings value: "+
b+"\nMessage: "+k.message,k;}}});a.S.instance=new a.S})();a.b("bindingProvider",a.S);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Ea(a.l.w(b),function(a,c){return function(){return b()[c]}})}function e(c,e,k){return"function"===typeof c?d(c.bind(null,e,k)):a.a.Ea(c,b)}function f(a,b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var e,k=a.f.firstChild(c),f=a.S.instance,m=f.preprocessNode;if(m){for(;e=k;)k=a.f.nextSibling(e),
m.call(f,e);k=a.f.firstChild(c)}for(;e=k;)k=a.f.nextSibling(e),h(b,e,d)}function h(b,c,d){var e=!0,k=1===c.nodeType;k&&a.f.oc(c);if(k&&d||a.S.instance.nodeHasBindings(c))e=m(c,null,b,d).shouldBindDescendants;e&&!r[a.a.A(c)]&&g(b,c,!k)}function l(b){var c=[],d={},e=[];a.a.D(b,function X(k){if(!d[k]){var f=a.getBindingHandler(k);f&&(f.after&&(e.push(k),a.a.r(f.after,function(c){if(b[c]){if(-1!==a.a.o(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));
X(c)}}),e.length--),c.push({key:k,jc:f}));d[k]=!0}});return c}function m(b,d,e,k){var m=a.a.e.get(b,q);if(!d){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.e.set(b,q,!0)}!m&&k&&a.xc(b,e);var g;if(d&&"function"!==typeof d)g=d;else{var h=a.S.instance,r=h.getBindingAccessors||f,p=a.B(function(){(g=d?d(e,b):r.call(h,b,e))&&e.Q&&e.Q();return g},null,{i:b});g&&p.ca()||(p=null)}var s;if(g){var t=p?function(a){return function(){return c(p()[a])}}:function(a){return g[a]},
u=function(){return a.a.Ea(p?p():g,c)};u.get=function(a){return g[a]&&c(t(a))};u.has=function(a){return a in g};k=l(g);a.a.r(k,function(c){var d=c.jc.init,k=c.jc.update,f=c.key;if(8===b.nodeType&&!a.f.aa[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.l.w(function(){var a=d(b,t(f),u,e.$data,e);if(a&&a.controlsDescendantBindings){if(s!==n)throw Error("Multiple bindings ("+s+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
s=f}}),"function"==typeof k&&a.B(function(){k(b,t(f),u,e.$data,e)},null,{i:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:s===n}}function k(b){return b&&b instanceof a.R?b:new a.R(b)}a.d={};var r={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.d[b]};a.R=function(b,c,d,e,k){function f(){var k=g?b():b,m=a.a.c(k);c?(c.Q&&c.Q(),a.a.extend(l,c),l.Q=r):(l.$parents=[],l.$root=m,l.ko=a);l.$rawData=
k;l.$data=m;d&&(l[d]=m);e&&e(l,c,m);return l.$data}function m(){return h&&!a.a.Tb(h)}var l=this,g="function"==typeof b&&!a.I(b),h,r;k&&k.exportDependencies?f():(r=a.B(f,null,{ya:m,i:!0}),r.ca()&&(l.Q=r,r.equalityComparer=null,h=[],r.Dc=function(b){h.push(b);a.a.G.qa(b,function(b){a.a.Na(h,b);h.length||(r.k(),l.Q=r=n)})}))};a.R.prototype.createChildContext=function(b,c,d,e){return new a.R(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);
d&&d(a)},e)};a.R.prototype.extend=function(b){return new a.R(this.Q||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};a.R.prototype.ac=function(a,b){return this.createChildContext(a,b,null,{exportDependencies:!0})};var q=a.a.e.J(),p=a.a.e.J();a.xc=function(b,c){if(2==arguments.length)a.a.e.set(b,p,c),c.Q&&c.Q.Dc(b);else return a.a.e.get(b,p)};a.La=function(b,c,d){1===b.nodeType&&a.f.oc(b);return m(b,c,k(d),!0)};a.Ic=function(b,c,d){d=k(d);return a.La(b,
e(c,d,b),d)};a.hb=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(k(a),b,!0)};a.Ub=function(a,b){!u&&x.jQuery&&(u=x.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||x.document.body;h(k(a),b,!0)};a.nb=function(b){switch(b.nodeType){case 1:case 8:var c=a.xc(b);if(c)return c;if(b.parentNode)return a.nb(b.parentNode)}return n};a.Oc=function(b){return(b=a.nb(b))?b.$data:n};a.b("bindingHandlers",
a.d);a.b("applyBindings",a.Ub);a.b("applyBindingsToDescendants",a.hb);a.b("applyBindingAccessorsToNode",a.La);a.b("applyBindingsToNode",a.Ic);a.b("contextFor",a.nb);a.b("dataFor",a.Oc)})();(function(b){function c(c,e){var m=f.hasOwnProperty(c)?f[c]:b,k;m?m.Y(e):(m=f[c]=new a.K,m.Y(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,dd:e};delete f[c];k||e?m.notifySubscribers(b):a.Z.Za(function(){m.notifySubscribers(b)})}),k=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",
[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,f,k){k||(k=a.g.loaders.slice(0));var g=k.shift();if(g){var q=g[c];if(q){var p=!1;if(q.apply(g,d.concat(function(a){p?f(null):null!==a?f(a):e(c,d,f,k)}))!==b&&(p=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,k)}else f(null)}var f={},g={};a.g={get:function(d,e){var f=g.hasOwnProperty(d)?g[d]:b;f?f.dd?a.l.w(function(){e(f.definition)}):
a.Z.Za(function(){e(f.definition)}):c(d,e)},$b:function(a){delete g[a]},Nb:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.$b)})();(function(){function b(b,c,d,e){function g(){0===--y&&e(h)}var h={},y=2,v=d.template;d=d.viewModel;v?f(c,v,function(c){a.g.Nb("loadTemplate",[b,c],function(a){h.template=a;g()})}):g();d?f(c,d,function(c){a.g.Nb("loadViewModel",[b,c],function(a){h[l]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});
else if("function"===typeof b[l])d(b[l]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.A(b)){case "script":return a.a.na(b.text);case "textarea":return a.a.na(b.value);case "template":if(e(b.content))return a.a.wa(b.content.childNodes)}return a.a.wa(b.childNodes)}function e(a){return x.DocumentFragment?a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?
O||x.require?(O||x.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.wb(b))throw Error("Component "+b+" is already registered");h[b]=c};a.g.wb=function(a){return h.hasOwnProperty(a)};a.g.ud=function(b){delete h[b];a.g.$b(b)};a.g.cc={getConfig:function(a,b){b(h.hasOwnProperty(a)?h[a]:null)},loadComponent:function(a,
c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.na(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.W(c.childNodes));else if(c.element)if(c=c.element,x.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var l=t.getElementById(c);l?f(d(l)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),
b,d)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.wb);a.b("components.unregister",a.g.ud);a.b("components.defaultLoader",a.g.cc);a.g.loaders.push(a.g.cc);a.g.Ec=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ea(f,function(c){return a.m(c,null,{i:b})}),g=a.a.Ea(f,function(c){var e=c.p();return c.ca()?a.m({read:function(){return a.a.c(c())},write:a.Da(e)&&
function(a){c()(a)},i:b}):e});g.hasOwnProperty("$raw")||(g.$raw=f);return g}return{$raw:{}}}a.g.getComponentNameForNode=function(b){var c=a.a.A(b);if(a.g.wb(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.C&&b.tagName===c))return c};a.g.Rb=function(c,e,f,g){if(1===e.nodeType){var h=a.g.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');var l={name:h,params:b(e,f)};c.component=g?function(){return l}:
l}}return c};var c=new a.S;9>a.a.C&&(a.g.register=function(a){return function(b){t.createElement(b);return a.apply(this,arguments)}}(a.g.register),t.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Ec,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(t.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.wa(c);a.f.fa(d,b)}function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a,
d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,h,l,m){function k(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.W(a.f.childNodes(f));a.a.G.qa(f,k);a.m(function(){var l=a.a.c(g()),h,v;"string"===typeof l?h=l:(h=a.a.c(l.name),v=a.a.c(l.params));if(!h)throw Error("No component name specified");var n=q=++e;a.g.get(h,function(e){if(q===n){k();if(!e)throw Error("Unknown component '"+h+"'");c(h,e,f);var l=d(e,f,p,v);e=m.createChildContext(l,b,function(a){a.$component=
l;a.$componentTemplateNodes=p});r=l;a.hb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.aa.component=!0})();var Q={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in Q?(c=Q[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.vc(b,g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,
c,d){function e(){var e=b.checked,f=p?g():e;if(!a.xa.Va()&&(!l||e)){var h=a.l.w(c);if(k){var m=r?h.p():h;q!==f?(e&&(a.a.ra(m,f,!0),a.a.ra(m,q,!1)),q=f):a.a.ra(m,f,e);r&&a.Da(h)&&h(m)}else a.h.Ga(h,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=k?0<=a.a.o(d,g()):h?d:g()===d}var g=a.rc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var m=c(),k=h&&a.a.c(m)instanceof Array,
r=!(k&&m.push&&m.splice),q=k?g():n,p=l||k;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.q(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ga.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.fb(b,c,d)}):(d=a.a.cb(String(d||"")),a.a.fb(b,b.__ko__cssValue,!1),b.__ko__cssValue=d,a.a.fb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());
d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.q(b,g,function(b){var m,k=c()[g];if(k){try{var r=a.a.W(arguments);e=f.$data;r.unshift(e);m=k.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};
a.d.foreach={mc:function(b){return function(){var c=b(),d=a.a.Bb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.X.vb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.X.vb}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.mc(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.mc(c),
d,e,f)}};a.h.va.foreach=!1;a.f.aa.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(k){g=f.body}e=g===b}f=c();a.h.Ga(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.q(b,"focus",f);a.a.q(b,"focusin",f);a.a.q(b,"blur",g);a.a.q(b,"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===
d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Fa,null,[b,d?"focusin":"focusout"]))}};a.h.ga.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ga.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Eb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.ac(c)});var L={};a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0<
b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ma(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&k)a.j.ja(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.wc(e[0],f);A&&!f&&a.l.w(a.a.Fa,null,[b,"change"])}}var h=b.multiple,l=0!=b.length&&h?b.scrollTop:null,m=a.a.c(c()),k=d.get("valueAllowUnset")&&d.has("value"),r=
d.get("optionsIncludeDestroyed");c={};var q,p=[];k||(h?p=a.a.ib(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ma(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c);
a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Db(b,q,function(c,e,g){g.length&&(p=!k&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.bb(e,d.get("optionsCaption")),a.j.ja(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ja(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.bb(e,c));return[e]},c,m);a.l.w(function(){k?a.j.ja(b,a.a.c(d.get("value")),!0):(h?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex])!==p[0]:
p.length||0<=b.selectedIndex)&&a.a.Fa(b,"change")});a.a.Sc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.zb=a.a.e.J();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.q(b,"change",function(){var e=c(),f=[];a.a.r(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.j.u(b))});a.h.Ga(e,d,"selectedOptions",f)})},update:function(b,c){if("select"!=a.a.A(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c()),e=b.scrollTop;
d&&"number"==typeof d.length&&a.a.r(b.getElementsByTagName("option"),function(b){var c=0<=a.a.o(d,a.j.u(b));b.selected!=c&&a.a.wc(b,c)});b.scrollTop=e}};a.h.ga.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.D(d,function(c,d){d=a.a.c(d);if(null===d||d===n||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");a.a.q(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,
b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.bb(b,c())}};a.f.aa.text=!0;(function(){if(x&&x.navigator)var b=function(a){if(a)return parseFloat(a[1])},c=x.opera&&x.opera.version&&parseInt(x.opera.version()),d=x.navigator.userAgent,e=b(d.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(d.match(/Firefox\/([^ ]*)/));if(10>a.a.C)var g=a.a.e.J(),h=a.a.e.J(),l=function(b){var c=
this.activeElement;(c=c&&a.a.e.get(c,h))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.q(d,"selectionchange",l));a.a.e.set(b,h,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.q(b,c,d)}function h(){var c=a.a.c(d());if(null===c||c===n)c="";u!==n&&c===u?a.a.setTimeout(h,4):b.value!==c&&(s=c,b.value=c)}function y(){t||(u=b.value,t=a.a.setTimeout(v,4))}function v(){clearTimeout(t);u=t=n;var c=b.value;s!==c&&(s=c,a.h.Ga(d(),g,"textInput",c))}var s=b.value,
t,u,x=9==a.a.C?y:v;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",v),l("keydown",v)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",v),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",v),l("dragdrop",v),l("drop",v)));l("change",v);a.m(h,null,{i:b})}};a.h.ga.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+
++a.d.uniqueName.Nc;a.a.vc(b,d)}}};a.d.uniqueName.Nc=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ta(e,f),e=a.a.Wb(e));var l=function(){h=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ga(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")||
(a.a.q(b,"propertychange",function(){g=!0}),a.a.q(b,"focus",function(){g=!1}),a.a.q(b,"blur",function(){g&&l()}));a.a.r(e,function(c){var d=l;a.a.sd(c,"after")&&(d=function(){h=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.q(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==h&&e===h)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ja(b,e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Fa,null,[b,"change"])}else a.j.ja(b,
e)};a.m(m,null,{i:b})}else a.La(b,{checkedValue:c})},update:function(){}};a.h.ga.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.P=function(){};a.P.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.P.prototype.createJavaScriptEvaluatorBlock=
function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.P.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||t;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.sa(b);throw Error("Unknown template type: "+b);};a.P.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.P.prototype.isTemplateRewritten=function(a,
c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.P.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.P);a.Ib=function(){function b(b,c,d,h){b=a.h.Ab(b);for(var l=a.h.va,m=0;m<b.length;m++){var k=b[m].key;if(l.hasOwnProperty(k)){var r=l[k];if("function"===typeof r){if(k=r(b[m].value))throw Error(k);}else if(!r)throw Error("This template engine does not support the '"+
k+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Xa(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return h.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Ib.jd(b,
c)},d)},jd:function(a,f){return a.replace(c,function(a,c,d,e,k){return b(k,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Jc:function(b,c){return a.N.yb(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.La(l,b,h)})}}}();a.b("__tr_ambtns",a.Ib.Jc);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.eb="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1===
this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Eb(this.n,c):this.n[b]=c};var b=a.a.e.J()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.J();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).mb||(3===this.eb?b.content:4===this.eb?b:n);a.a.e.set(b,c,{mb:arguments[0]})};a.v.sa=function(a){this.n=
a};a.v.sa.prototype=new a.v.n;a.v.sa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Jb===n&&b.mb&&(b.Jb=b.mb.innerHTML);return b.Jb}a.a.e.set(this.n,c,{Jb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.sa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h=
a.S.instance,n=h.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.Ba(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.N.Cc(b,[d])});a.a.Ba(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,q){q=q||{};var p=(b&&d(b)||f||{}).ownerDocument,n=q.templateEngine||g;
a.Ib.Tc(f,n,p);f=n.renderTemplate(f,h,q,p);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");p=!1;switch(e){case "replaceChildren":a.f.fa(b,f);p=!0;break;case "replaceNode":a.a.uc(b,f);p=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}p&&(c(f,h),q.afterRender&&a.l.w(q.afterRender,null,[f,h.$data]));return f}function f(b,c,d){return a.I(b)?b():"function"===typeof b?b(c,d):b}
var g;a.Fb=function(b){if(b!=n&&!(b instanceof a.P))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Cb=function(b,c,k,h,q){k=k||{};if((k.templateEngine||g)==n)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(h){var p=d(h);return a.B(function(){var g=c&&c instanceof a.R?c:new a.R(c,null,null,null,{exportDependencies:!0}),n=f(b,g.$data,g),g=e(h,q,n,g,k);"replaceNode"==q&&(h=g,p=d(h))},null,{ya:function(){return!p||!a.a.qb(p)},i:p&&
"replaceNode"==q?p.parentNode:p})}return a.N.yb(function(d){a.Cb(b,c,k,d,"replaceNode")})};a.pd=function(b,d,g,h,q){function p(a,b){c(b,t);g.afterRender&&g.afterRender(b,a);t=null}function s(a,c){t=q.createChildContext(a,g.as,function(a){a.$index=c});var d=f(b,a,t);return e(null,"ignoreTargetNode",d,t,g)}var t;return a.B(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Ma(b,function(b){return g.includeDestroyed||b===n||null===b||!a.a.c(b._destroy)});a.l.w(a.a.Db,null,[h,b,
s,g,p])},null,{i:h})};var h=a.a.e.J();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.f.za(b);else{if("nodes"in d){if(d=d.nodes||[],a.I(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.f.childNodes(b);d=a.a.nc(d);(new a.v.sa(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c();c=a.a.c(g);d=!0;e=null;"string"==typeof c?c={}:(g=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in c&&(d=!a.a.c(c.ifnot)));
"foreach"in c?e=a.pd(g||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.ac(c.data,c.as):f,e=a.Cb(g||b,f,c,b)):a.f.za(b);f=e;(c=a.a.e.get(b,h))&&"function"==typeof c.k&&c.k();a.a.e.set(b,h,f&&f.ca()?f:n)}};a.h.va.template=function(b){b=a.h.Ab(b);return 1==b.length&&b[0].unknown||a.h.fd(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.f.aa.template=!0})();a.b("setTemplateEngine",a.Fb);a.b("renderTemplate",a.Cb);a.a.hc=function(a,c,d){if(a.length&&
c.length){var e,f,g,h,l;for(e=f=0;(!d||e<d)&&(h=a[f]);++f){for(g=0;l=c[g];++g)if(h.value===l.value){h.moved=l.index;l.moved=h.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.lb=function(){function b(b,d,e,f,g){var h=Math.min,l=Math.max,m=[],k,n=b.length,q,p=d.length,s=p-n||1,t=n+p+1,v,u,x;for(k=0;k<=n;k++)for(u=v,m.push(v=[]),x=h(p,k+s),q=l(0,k-1);q<=x;q++)v[q]=q?k?b[k-1]===d[q-1]?u[q-1]:h(u[q]||t,v[q-1]||t)+1:q+1:k+1;h=[];l=[];s=[];k=n;for(q=p;k||q;)p=m[k][q]-1,q&&p===m[k][q-1]?l.push(h[h.length]={status:e,
value:d[--q],index:q}):k&&p===m[k-1][q]?s.push(h[h.length]={status:f,value:b[--k],index:k}):(--q,--k,g.sparse||h.push({status:"retained",value:d[q]}));a.a.hc(s,l,!g.dontLimitMoves&&10*n);return h.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.lb);(function(){function b(b,c,d,h,l){var m=[],k=a.B(function(){var k=c(d,l,a.a.Ba(m,b))||[];0<
m.length&&(a.a.uc(m,k),h&&a.l.w(h,null,[d,k,l]));m.length=0;a.a.ta(m,k)},null,{i:b,ya:function(){return!a.a.Tb(m)}});return{ea:m,B:k.ca()?k:n}}var c=a.a.e.J(),d=a.a.e.J();a.a.Db=function(e,f,g,h,l){function m(b,c){w=q[c];u!==c&&(D[b]=w);w.tb(u++);a.a.Ba(w.ea,e);t.push(w);z.push(w)}function k(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.r(c[d].ea,function(a){b(a,d,c[d].ka)})}f=f||[];h=h||{};var r=a.a.e.get(e,c)===n,q=a.a.e.get(e,c)||[],p=a.a.ib(q,function(a){return a.ka}),s=a.a.lb(p,f,h.dontLimitMoves),
t=[],v=0,u=0,x=[],z=[];f=[];for(var D=[],p=[],w,C=0,B,E;B=s[C];C++)switch(E=B.moved,B.status){case "deleted":E===n&&(w=q[v],w.B&&(w.B.k(),w.B=n),a.a.Ba(w.ea,e).length&&(h.beforeRemove&&(t.push(w),z.push(w),w.ka===d?w=null:f[C]=w),w&&x.push.apply(x,w.ea)));v++;break;case "retained":m(C,v++);break;case "added":E!==n?m(C,E):(w={ka:B.value,tb:a.O(u++)},t.push(w),z.push(w),r||(p[C]=w))}a.a.e.set(e,c,t);k(h.beforeMove,D);a.a.r(x,h.beforeRemove?a.ba:a.removeNode);for(var C=0,r=a.f.firstChild(e),F;w=z[C];C++){w.ea||
a.a.extend(w,b(e,g,w.ka,l,w.tb));for(v=0;s=w.ea[v];r=s.nextSibling,F=s,v++)s!==r&&a.f.kc(e,s,F);!w.ad&&l&&(l(w.ka,w.ea,w.tb),w.ad=!0)}k(h.beforeRemove,f);for(C=0;C<f.length;++C)f[C]&&(f[C].ka=d);k(h.afterMove,D);k(h.afterAdd,p)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Db);a.X=function(){this.allowTemplateRewriting=!1};a.X.prototype=new a.P;a.X.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.C?0:b.nodes)?b.nodes():null)return a.a.W(c.cloneNode(!0).childNodes);b=b.text();
return a.a.na(b,e)};a.X.vb=new a.X;a.Fb(a.X.vb);a.b("nativeTemplateEngine",a.X);(function(){a.xb=function(){var a=this.ed=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||t;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+
h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(g.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){t.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(u.tmpl.tag.ko_code={open:"__.push($1 || '');"},u.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.xb.prototype=
new a.P;var b=new a.xb;0<b.ed&&a.Fb(b);a.b("jqueryTmplTemplateEngine",a.xb)})()})})();})();;/*
===============================================================================
    Author:     Eric M. Barnard - @ericmbarnard                                
    License:    MIT (http://opensource.org/licenses/mit-license.php)           
                                                                               
    Description: Validation Library for KnockoutJS                             
===============================================================================
*/
(function(e){typeof require=="function"&&typeof exports=="object"&&typeof module=="object"?e(require("knockout"),exports):typeof define=="function"&&define.amd?define(["knockout","exports"],e):e(ko,ko.validation={})})(function(e,t){function l(e,n,r){return n.validator(e(),r.params===undefined?!0:r.params)?!0:(e.error(t.formatMessage(r.message||n.message,r.params)),e.__valid__(!1),!1)}function c(e,n,r){e.isValidating(!0);var i=function(i){var s=!1,o="";if(!e.__valid__()){e.isValidating(!1);return}i.message?(s=i.isValid,o=i.message):s=i,s||(e.error(t.formatMessage(o||r.message||n.message,r.params)),e.__valid__(s)),e.isValidating(!1)};n.validator(e(),r.params||!0,i)}if(typeof e===undefined)throw"Knockout is required, please ensure it is loaded before loading this validation plug-in";var n=t;e.validation=n;var r={registerExtenders:!0,messagesOnModified:!0,errorsAsTitle:!0,errorsAsTitleOnModified:!1,messageTemplate:null,insertMessages:!0,parseInputAttributes:!1,writeInputAttributes:!1,decorateElement:!1,errorClass:null,errorElementClass:"validationElement",errorMessageClass:"validationMessage",grouping:{deep:!1,observable:!0}},i=e.utils.extend({},r),s=["required","pattern","min","max","step"],o=["email","number","date"],u=function(e){window.setImmediate?window.setImmediate(e):window.setTimeout(e,0)},a=function(){var e=(new Date).getTime(),t={},n="__ko_validation__";return{isArray:function(e){return e.isArray||Object.prototype.toString.call(e)==="[object Array]"},isObject:function(e){return e!==null&&typeof e=="object"},values:function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(e[n]);return t},getValue:function(e){return typeof e=="function"?e():e},hasAttribute:function(e,t){return e.getAttribute(t)!==null},getAttribute:function(e,t){return e.getAttribute(t)},setAttribute:function(e,t,n){return e.setAttribute(t,n)},isValidatable:function(e){return e&&e.rules&&e.isValid&&e.isModified},insertAfter:function(e,t){e.parentNode.insertBefore(t,e.nextSibling)},newId:function(){return e+=1},getConfigOptions:function(e){var t=a.contextFor(e);return t||i},setDomData:function(e,r){var i=e[n];i||(e[n]=i=a.newId()),t[i]=r},getDomData:function(e){var r=e[n];return r?t[r]:undefined},contextFor:function(e){switch(e.nodeType){case 1:case 8:var t=a.getDomData(e);if(t)return t;if(e.parentNode)return a.contextFor(e.parentNode)}return undefined},isEmptyVal:function(e){if(e===undefined)return!0;if(e===null)return!0;if(e==="")return!0},getOriginalElementTitle:function(e){var t=a.getAttribute(e,"data-orig-title"),n=e.title,r=a.hasAttribute(e,"data-orig-title");return r?t:n}}}(),f=function(){var n=0;return{utils:a,init:function(r,s){if(n>0&&!s)return;r=r||{},r.errorElementClass=r.errorElementClass||r.errorClass||i.errorElementClass,r.errorMessageClass=r.errorMessageClass||r.errorClass||i.errorMessageClass,e.utils.extend(i,r),i.registerExtenders&&t.registerExtenders(),n=1},configure:function(e){t.init(e)},reset:function(){i=jQuery.extend(i,r)},group:function(n,r){r=e.utils.extend(e.utils.extend({},i.grouping),r);var s=e.observableArray([]),o=null,u=function f(t,n){var i=[],o=e.utils.unwrapObservable(t);n=n!==undefined?n:r.deep?1:-1,e.isObservable(t)&&(t.isValid||t.extend({validatable:!0}),s.push(t)),o&&(a.isArray(o)?i=o:a.isObject(o)&&(i=a.values(o))),n!==0&&e.utils.arrayForEach(i,function(e){e&&!e.nodeType&&f(e,n+1)})};return r.observable?(u(n),o=e.computed(function(){var t=[];return e.utils.arrayForEach(s(),function(e){e.isValid()||t.push(e.error)}),t})):o=function(){var t=[];return s([]),u(n),e.utils.arrayForEach(s(),function(e){e.isValid()||t.push(e.error)}),t},o.showAllMessages=function(t){t===undefined&&(t=!0),o(),e.utils.arrayForEach(s(),function(e){e.isModified(t)})},n.errors=o,n.isValid=function(){return n.errors().length===0},n.isAnyMessageShown=function(){var t=!1;return o(),e.utils.arrayForEach(s(),function(e){!e.isValid()&&e.isModified()&&(t=!0)}),t},o},formatMessage:function(t,n){return typeof t=="function"?t(n):t.replace(/\{0\}/gi,e.utils.unwrapObservable(n))},addRule:function(e,t){return e.extend({validatable:!0}),e.rules.push(t),e},addAnonymousRule:function(e,n){var r=a.newId();n.message===undefined&&(n.message="Error"),t.rules[r]=n,t.addRule(e,{rule:r,params:n.params})},addExtender:function(n){e.extenders[n]=function(e,r){return r.message||r.onlyIf?t.addRule(e,{rule:n,message:r.message,params:a.isEmptyVal(r.params)?!0:r.params,condition:r.onlyIf}):t.addRule(e,{rule:n,params:r})}},registerExtenders:function(){if(i.registerExtenders)for(var n in t.rules)t.rules.hasOwnProperty(n)&&(e.extenders[n]||t.addExtender(n))},insertValidationMessage:function(e){var t=document.createElement("SPAN");return t.className=a.getConfigOptions(e).errorMessageClass,a.insertAfter(e,t),t},parseInputValidationAttributes:function(n,r){e.utils.arrayForEach(s,function(e){a.hasAttribute(n,e)&&t.addRule(r(),{rule:e,params:n.getAttribute(e)||!0})});var i=n.getAttribute("type");e.utils.arrayForEach(o,function(e){e===i&&t.addRule(r(),{rule:e==="date"?"dateISO":e,params:!0})})},writeInputValidationAttributes:function(t,n){var r=n();if(!r||!r.rules)return;var i=r.rules();e.utils.arrayForEach(s,function(n){var r,s=e.utils.arrayFirst(i,function(e){return e.rule.toLowerCase()===n.toLowerCase()});if(!s)return;r=s.params,s.rule==="pattern"&&s.params instanceof RegExp&&(r=s.params.source),t.setAttribute(n,r)}),i=null},makeBindingHandlerValidatable:function(t){var n=e.bindingHandlers[t].init;e.bindingHandlers[t].init=function(t,r,i,s,o){return n(t,r,i),e.bindingHandlers.validationCore.init(t,r,i,s,o)}}}}();e.utils.extend(n,f),n.rules={},n.rules.required={validator:function(e,t){var n=/^\s+|\s+$/g,r;return e===undefined||e===null?!t:(r=e,typeof e=="string"&&(r=e.replace(n,"")),t?(r+"").length>0:!0)},message:"This field is required."},n.rules.min={validator:function(e,t){return a.isEmptyVal(e)||e>=t},message:"Please enter a value greater than or equal to {0}."},n.rules.max={validator:function(e,t){return a.isEmptyVal(e)||e<=t},message:"Please enter a value less than or equal to {0}."},n.rules.minLength={validator:function(e,t){return a.isEmptyVal(e)||e.length>=t},message:"Please enter at least {0} characters."},n.rules.maxLength={validator:function(e,t){return a.isEmptyVal(e)||e.length<=t},message:"Please enter no more than {0} characters."},n.rules.pattern={validator:function(e,t){return a.isEmptyVal(e)||e.toString().match(t)!==null},message:"Please check this value."},n.rules.step={validator:function(e,t){return a.isEmptyVal(e)||e*100%(t*100)===0},message:"The value must increment by {0}"},n.rules.email={validator:function(e,t){return t?a.isEmptyVal(e)||t&&/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(e):!0},message:"Please enter a proper email address"},n.rules.date={validator:function(e,t){return t?a.isEmptyVal(e)||t&&!/Invalid|NaN/.test(new Date(e)):!0},message:"Please enter a proper date"},n.rules.dateISO={validator:function(e,t){return t?a.isEmptyVal(e)||t&&/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(e):!0},message:"Please enter a proper date"},n.rules.number={validator:function(e,t){return t?a.isEmptyVal(e)||t&&/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(e):!0},message:"Please enter a number"},n.rules.digit={validator:function(e,t){return t?a.isEmptyVal(e)||t&&/^\d+$/.test(e):!0},message:"Please enter a digit"},n.rules.phoneUS={validator:function(e,t){return t?typeof e!="string"?!1:a.isEmptyVal(e)?!0:(e=e.replace(/\s+/g,""),t&&e.length>9&&e.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/)):!0},message:"Please specify a valid phone number"},n.rules.equal={validator:function(e,t){var n=t;return e===a.getValue(n)},message:"Values must equal"},n.rules.notEqual={validator:function(e,t){var n=t;return e!==a.getValue(n)},message:"Please choose another value."},n.rules.unique={validator:function(t,n){var r=a.getValue(n.collection),i=a.getValue(n.externalValue),s=0;return!t||!r?!0:(e.utils.arrayFilter(e.utils.unwrapObservable(r),function(e){t===(n.valueAccessor?n.valueAccessor(e):e)&&s++}),s<(i!==undefined&&t!==i?1:2))},message:"Please make sure the value is unique."},function(){n.registerExtenders()}(),e.bindingHandlers.validationCore=function(){return{init:function(n,r,i,s,o){var f=a.getConfigOptions(n);f.parseInputAttributes&&u(function(){t.parseInputValidationAttributes(n,r)});if(f.insertMessages&&a.isValidatable(r())){var l=t.insertValidationMessage(n);f.messageTemplate?e.renderTemplate(f.messageTemplate,{field:r()},null,l,"replaceNode"):e.applyBindingsToNode(l,{validationMessage:r()})}f.writeInputAttributes&&a.isValidatable(r())&&t.writeInputValidationAttributes(n,r),f.decorateElement&&a.isValidatable(r())&&e.applyBindingsToNode(n,{validationElement:r()})},update:function(e,t,n,r,i){}}}(),f.makeBindingHandlerValidatable("value"),f.makeBindingHandlerValidatable("checked"),e.bindingHandlers.validationMessage={update:function(t,n){var r=n(),i=a.getConfigOptions(t),s=e.utils.unwrapObservable(r),o=null,u=!1,f=!1;r.extend({validatable:!0}),u=r.isModified(),f=r.isValid();var l=function(){return!i.messagesOnModified||u?f?null:r.error:null},c=function(){return!i.messagesOnModified||u?!f:!1};e.bindingHandlers.text.update(t,l),e.bindingHandlers.visible.update(t,c)}},e.bindingHandlers.validationElement={update:function(t,n){var r=n(),i=a.getConfigOptions(t),s=e.utils.unwrapObservable(r),o=null,u=!1,f=!1;r.extend({validatable:!0}),u=r.isModified(),f=r.isValid();var l=function(){var e={},t=u?!f:!1;return i.decorateElement||(t=!1),e[i.errorElementClass]=t,e};e.bindingHandlers.css.update(t,l);if(!i.errorsAsTitle)return;var c=a.getAttribute(t,"data-orig-title"),h=t.title,p=a.getAttribute(t,"data-orig-title")==="true",d=function(){if(!i.errorsAsTitleOnModified||u)return f?{title:a.getOriginalElementTitle(t),"data-orig-title":null}:{title:r.error,"data-orig-title":a.getOriginalElementTitle(t)}};e.bindingHandlers.attr.update(t,d)}},e.bindingHandlers.validationOptions=function(){return{init:function(t,n,r,s,o){var u=e.utils.unwrapObservable(n());if(u){var f=e.utils.extend({},i);e.utils.extend(f,u),a.setDomData(t,f)}}}}(),e.extenders.validation=function(n,r){return e.utils.arrayForEach(a.isArray(r)?r:[r],function(e){t.addAnonymousRule(n,e)}),n},e.extenders.validatable=function(n,r){if(r&&!a.isValidatable(n)){n.error=e.observable(null),n.rules=e.observableArray(),n.isValidating=e.observable(!1),n.__valid__=e.observable(!0),n.isModified=e.observable(!1);var i=e.computed(function(){var e=n(),r=n.rules();return t.validateObservable(n),!0});n.isValid=e.computed(function(){return n.__valid__()});var s=n.subscribe(function(){n.isModified(!0)});n._disposeValidation=function(){n.isValid.dispose(),n.rules.removeAll(),n.isModified._subscriptions.change=[],n.isValidating._subscriptions.change=[],n.__valid__._subscriptions.change=[],s.dispose(),i.dispose(),delete n.rules,delete n.error,delete n.isValid,delete n.isValidating,delete n.__valid__,delete n.isModified}}else r===!1&&a.isValidatable(n)&&n._disposeValidation&&n._disposeValidation();return n},n.validateObservable=function(e){var n=0,r,i,s=e.rules(),o=s.length;for(;n<o;n++){i=s[n];if(i.condition&&!i.condition())continue;r=t.rules[i.rule];if(r.async||i.async)c(e,r,i);else if(!l(e,r,i))return!1}return e.error(null),e.__valid__(!0),!0},e.validatedObservable=function(n){if(!t.utils.isObject(n))return e.observable(n).extend({validatable:!0});var r=e.observable(n);return r.errors=t.group(n),r.isValid=e.computed(function(){return r.errors().length===0}),r},n.localize=function(e){var n,r;for(r in e)t.rules.hasOwnProperty(r)&&(t.rules[r].message=e[r])},e.applyBindingsWithValidation=function(n,r,i){var s=arguments.length,o,u;s>2?(o=r,u=i):s<2?o=document.body:arguments[1].nodeType?o=r:u=arguments[1],t.init(),u&&t.utils.setDomData(o,u),e.applyBindings(n,r)};var h=e.applyBindings;e.applyBindings=function(e,n){t.init(),h(e,n)}});;//     Underscore.js 1.8.3
//     http://underscorejs.org
//     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.

(function () {

    // Baseline setup
    // --------------

    // Establish the root object, `window` in the browser, or `exports` on the server.
    var root = this;

    // Save the previous value of the `_` variable.
    var previousUnderscore = root._;

    // Save bytes in the minified (but not gzipped) version:
    var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

    // Create quick reference variables for speed access to core prototypes.
    var
      push = ArrayProto.push,
      slice = ArrayProto.slice,
      toString = ObjProto.toString,
      hasOwnProperty = ObjProto.hasOwnProperty;

    // All **ECMAScript 5** native function implementations that we hope to use
    // are declared here.
    var
      nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeBind = FuncProto.bind,
      nativeCreate = Object.create;

    // Naked function reference for surrogate-prototype-swapping.
    var Ctor = function () { };

    // Create a safe reference to the Underscore object for use below.
    var _ = function (obj) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
    };

    // Export the Underscore object for **Node.js**, with
    // backwards-compatibility for the old `require()` API. If we're in
    // the browser, add `_` as a global object.
    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = _;
        }
        exports._ = _;
    } else {
        root._ = _;
    }

    // Current version.
    _.VERSION = '1.8.3';

    // Internal function that returns an efficient (for current engines) version
    // of the passed-in callback, to be repeatedly applied in other Underscore
    // functions.
    var optimizeCb = function (func, context, argCount) {
        if (context === void 0) return func;
        switch (argCount == null ? 3 : argCount) {
            case 1: return function (value) {
                return func.call(context, value);
            };
            case 2: return function (value, other) {
                return func.call(context, value, other);
            };
            case 3: return function (value, index, collection) {
                return func.call(context, value, index, collection);
            };
            case 4: return function (accumulator, value, index, collection) {
                return func.call(context, accumulator, value, index, collection);
            };
        }
        return function () {
            return func.apply(context, arguments);
        };
    };

    // A mostly-internal function to generate callbacks that can be applied
    // to each element in a collection, returning the desired result — either
    // identity, an arbitrary callback, a property matcher, or a property accessor.
    var cb = function (value, context, argCount) {
        if (value == null) return _.identity;
        if (_.isFunction(value)) return optimizeCb(value, context, argCount);
        if (_.isObject(value)) return _.matcher(value);
        return _.property(value);
    };
    _.iteratee = function (value, context) {
        return cb(value, context, Infinity);
    };

    // An internal function for creating assigner functions.
    var createAssigner = function (keysFunc, undefinedOnly) {
        return function (obj) {
            var length = arguments.length;
            if (length < 2 || obj == null) return obj;
            for (var index = 1; index < length; index++) {
                var source = arguments[index],
                    keys = keysFunc(source),
                    l = keys.length;
                for (var i = 0; i < l; i++) {
                    var key = keys[i];
                    if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
                }
            }
            return obj;
        };
    };

    // An internal function for creating a new object that inherits from another.
    var baseCreate = function (prototype) {
        if (!_.isObject(prototype)) return {};
        if (nativeCreate) return nativeCreate(prototype);
        Ctor.prototype = prototype;
        var result = new Ctor;
        Ctor.prototype = null;
        return result;
    };

    var property = function (key) {
        return function (obj) {
            return obj == null ? void 0 : obj[key];
        };
    };

    // Helper for collection methods to determine whether a collection
    // should be iterated as an array or as an object
    // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
    // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
    var getLength = property('length');
    var isArrayLike = function (collection) {
        var length = getLength(collection);
        return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
    };

    // Collection Functions
    // --------------------

    // The cornerstone, an `each` implementation, aka `forEach`.
    // Handles raw objects in addition to array-likes. Treats all
    // sparse array-likes as if they were dense.
    _.each = _.forEach = function (obj, iteratee, context) {
        iteratee = optimizeCb(iteratee, context);
        var i, length;
        if (isArrayLike(obj)) {
            for (i = 0, length = obj.length; i < length; i++) {
                iteratee(obj[i], i, obj);
            }
        } else {
            var keys = _.keys(obj);
            for (i = 0, length = keys.length; i < length; i++) {
                iteratee(obj[keys[i]], keys[i], obj);
            }
        }
        return obj;
    };

    // Return the results of applying the iteratee to each element.
    _.map = _.collect = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length,
            results = Array(length);
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    };

    // Create a reducing function iterating left or right.
    function createReduce(dir) {
        // Optimized iterator function as using arguments.length
        // in the main function will deoptimize the, see #1991.
        function iterator(obj, iteratee, memo, keys, index, length) {
            for (; index >= 0 && index < length; index += dir) {
                var currentKey = keys ? keys[index] : index;
                memo = iteratee(memo, obj[currentKey], currentKey, obj);
            }
            return memo;
        }

        return function (obj, iteratee, memo, context) {
            iteratee = optimizeCb(iteratee, context, 4);
            var keys = !isArrayLike(obj) && _.keys(obj),
                length = (keys || obj).length,
                index = dir > 0 ? 0 : length - 1;
            // Determine the initial value if none is provided.
            if (arguments.length < 3) {
                memo = obj[keys ? keys[index] : index];
                index += dir;
            }
            return iterator(obj, iteratee, memo, keys, index, length);
        };
    }

    // **Reduce** builds up a single result from a list of values, aka `inject`,
    // or `foldl`.
    _.reduce = _.foldl = _.inject = createReduce(1);

    // The right-associative version of reduce, also known as `foldr`.
    _.reduceRight = _.foldr = createReduce(-1);

    // Return the first value which passes a truth test. Aliased as `detect`.
    _.find = _.detect = function (obj, predicate, context) {
        var key;
        if (isArrayLike(obj)) {
            key = _.findIndex(obj, predicate, context);
        } else {
            key = _.findKey(obj, predicate, context);
        }
        if (key !== void 0 && key !== -1) return obj[key];
    };

    // Return all the elements that pass a truth test.
    // Aliased as `select`.
    _.filter = _.select = function (obj, predicate, context) {
        var results = [];
        predicate = cb(predicate, context);
        _.each(obj, function (value, index, list) {
            if (predicate(value, index, list)) results.push(value);
        });
        return results;
    };

    // Return all the elements for which a truth test fails.
    _.reject = function (obj, predicate, context) {
        return _.filter(obj, _.negate(cb(predicate)), context);
    };

    // Determine whether all of the elements match a truth test.
    // Aliased as `all`.
    _.every = _.all = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length;
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            if (!predicate(obj[currentKey], currentKey, obj)) return false;
        }
        return true;
    };

    // Determine if at least one element in the object matches a truth test.
    // Aliased as `any`.
    _.some = _.any = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length;
        for (var index = 0; index < length; index++) {
            var currentKey = keys ? keys[index] : index;
            if (predicate(obj[currentKey], currentKey, obj)) return true;
        }
        return false;
    };

    // Determine if the array or object contains a given item (using `===`).
    // Aliased as `includes` and `include`.
    _.contains = _.includes = _.include = function (obj, item, fromIndex, guard) {
        if (!isArrayLike(obj)) obj = _.values(obj);
        if (typeof fromIndex != 'number' || guard) fromIndex = 0;
        return _.indexOf(obj, item, fromIndex) >= 0;
    };

    // Invoke a method (with arguments) on every item in a collection.
    _.invoke = function (obj, method) {
        var args = slice.call(arguments, 2);
        var isFunc = _.isFunction(method);
        return _.map(obj, function (value) {
            var func = isFunc ? method : value[method];
            return func == null ? func : func.apply(value, args);
        });
    };

    // Convenience version of a common use case of `map`: fetching a property.
    _.pluck = function (obj, key) {
        return _.map(obj, _.property(key));
    };

    // Convenience version of a common use case of `filter`: selecting only objects
    // containing specific `key:value` pairs.
    _.where = function (obj, attrs) {
        return _.filter(obj, _.matcher(attrs));
    };

    // Convenience version of a common use case of `find`: getting the first object
    // containing specific `key:value` pairs.
    _.findWhere = function (obj, attrs) {
        return _.find(obj, _.matcher(attrs));
    };

    // Return the maximum element (or element-based computation).
    _.max = function (obj, iteratee, context) {
        var result = -Infinity, lastComputed = -Infinity,
            value, computed;
        if (iteratee == null && obj != null) {
            obj = isArrayLike(obj) ? obj : _.values(obj);
            for (var i = 0, length = obj.length; i < length; i++) {
                value = obj[i];
                if (value > result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            _.each(obj, function (value, index, list) {
                computed = iteratee(value, index, list);
                if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                    result = value;
                    lastComputed = computed;
                }
            });
        }
        return result;
    };

    // Return the minimum element (or element-based computation).
    _.min = function (obj, iteratee, context) {
        var result = Infinity, lastComputed = Infinity,
            value, computed;
        if (iteratee == null && obj != null) {
            obj = isArrayLike(obj) ? obj : _.values(obj);
            for (var i = 0, length = obj.length; i < length; i++) {
                value = obj[i];
                if (value < result) {
                    result = value;
                }
            }
        } else {
            iteratee = cb(iteratee, context);
            _.each(obj, function (value, index, list) {
                computed = iteratee(value, index, list);
                if (computed < lastComputed || computed === Infinity && result === Infinity) {
                    result = value;
                    lastComputed = computed;
                }
            });
        }
        return result;
    };

    // Shuffle a collection, using the modern version of the
    // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
    _.shuffle = function (obj) {
        var set = isArrayLike(obj) ? obj : _.values(obj);
        var length = set.length;
        var shuffled = Array(length);
        for (var index = 0, rand; index < length; index++) {
            rand = _.random(0, index);
            if (rand !== index) shuffled[index] = shuffled[rand];
            shuffled[rand] = set[index];
        }
        return shuffled;
    };

    // Sample **n** random values from a collection.
    // If **n** is not specified, returns a single random element.
    // The internal `guard` argument allows it to work with `map`.
    _.sample = function (obj, n, guard) {
        if (n == null || guard) {
            if (!isArrayLike(obj)) obj = _.values(obj);
            return obj[_.random(obj.length - 1)];
        }
        return _.shuffle(obj).slice(0, Math.max(0, n));
    };

    // Sort the object's values by a criterion produced by an iteratee.
    _.sortBy = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        return _.pluck(_.map(obj, function (value, index, list) {
            return {
                value: value,
                index: index,
                criteria: iteratee(value, index, list)
            };
        }).sort(function (left, right) {
            var a = left.criteria;
            var b = right.criteria;
            if (a !== b) {
                if (a > b || a === void 0) return 1;
                if (a < b || b === void 0) return -1;
            }
            return left.index - right.index;
        }), 'value');
    };

    // An internal function used for aggregate "group by" operations.
    var group = function (behavior) {
        return function (obj, iteratee, context) {
            var result = {};
            iteratee = cb(iteratee, context);
            _.each(obj, function (value, index) {
                var key = iteratee(value, index, obj);
                behavior(result, value, key);
            });
            return result;
        };
    };

    // Groups the object's values by a criterion. Pass either a string attribute
    // to group by, or a function that returns the criterion.
    _.groupBy = group(function (result, value, key) {
        if (_.has(result, key)) result[key].push(value); else result[key] = [value];
    });

    // Indexes the object's values by a criterion, similar to `groupBy`, but for
    // when you know that your index values will be unique.
    _.indexBy = group(function (result, value, key) {
        result[key] = value;
    });

    // Counts instances of an object that group by a certain criterion. Pass
    // either a string attribute to count by, or a function that returns the
    // criterion.
    _.countBy = group(function (result, value, key) {
        if (_.has(result, key)) result[key]++; else result[key] = 1;
    });

    // Safely create a real, live array from anything iterable.
    _.toArray = function (obj) {
        if (!obj) return [];
        if (_.isArray(obj)) return slice.call(obj);
        if (isArrayLike(obj)) return _.map(obj, _.identity);
        return _.values(obj);
    };

    // Return the number of elements in an object.
    _.size = function (obj) {
        if (obj == null) return 0;
        return isArrayLike(obj) ? obj.length : _.keys(obj).length;
    };

    // Split a collection into two arrays: one whose elements all satisfy the given
    // predicate, and one whose elements all do not satisfy the predicate.
    _.partition = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var pass = [], fail = [];
        _.each(obj, function (value, key, obj) {
            (predicate(value, key, obj) ? pass : fail).push(value);
        });
        return [pass, fail];
    };

    // Array Functions
    // ---------------

    // Get the first element of an array. Passing **n** will return the first N
    // values in the array. Aliased as `head` and `take`. The **guard** check
    // allows it to work with `_.map`.
    _.first = _.head = _.take = function (array, n, guard) {
        if (array == null) return void 0;
        if (n == null || guard) return array[0];
        return _.initial(array, array.length - n);
    };

    // Returns everything but the last entry of the array. Especially useful on
    // the arguments object. Passing **n** will return all the values in
    // the array, excluding the last N.
    _.initial = function (array, n, guard) {
        return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
    };

    // Get the last element of an array. Passing **n** will return the last N
    // values in the array.
    _.last = function (array, n, guard) {
        if (array == null) return void 0;
        if (n == null || guard) return array[array.length - 1];
        return _.rest(array, Math.max(0, array.length - n));
    };

    // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
    // Especially useful on the arguments object. Passing an **n** will return
    // the rest N values in the array.
    _.rest = _.tail = _.drop = function (array, n, guard) {
        return slice.call(array, n == null || guard ? 1 : n);
    };

    // Trim out all falsy values from an array.
    _.compact = function (array) {
        return _.filter(array, _.identity);
    };

    // Internal implementation of a recursive `flatten` function.
    var flatten = function (input, shallow, strict, startIndex) {
        var output = [], idx = 0;
        for (var i = startIndex || 0, length = getLength(input) ; i < length; i++) {
            var value = input[i];
            if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
                //flatten current level of array or arguments object
                if (!shallow) value = flatten(value, shallow, strict);
                var j = 0, len = value.length;
                output.length += len;
                while (j < len) {
                    output[idx++] = value[j++];
                }
            } else if (!strict) {
                output[idx++] = value;
            }
        }
        return output;
    };

    // Flatten out an array, either recursively (by default), or just one level.
    _.flatten = function (array, shallow) {
        return flatten(array, shallow, false);
    };

    // Return a version of the array that does not contain the specified value(s).
    _.without = function (array) {
        return _.difference(array, slice.call(arguments, 1));
    };

    // Produce a duplicate-free version of the array. If the array has already
    // been sorted, you have the option of using a faster algorithm.
    // Aliased as `unique`.
    _.uniq = _.unique = function (array, isSorted, iteratee, context) {
        if (!_.isBoolean(isSorted)) {
            context = iteratee;
            iteratee = isSorted;
            isSorted = false;
        }
        if (iteratee != null) iteratee = cb(iteratee, context);
        var result = [];
        var seen = [];
        for (var i = 0, length = getLength(array) ; i < length; i++) {
            var value = array[i],
                computed = iteratee ? iteratee(value, i, array) : value;
            if (isSorted) {
                if (!i || seen !== computed) result.push(value);
                seen = computed;
            } else if (iteratee) {
                if (!_.contains(seen, computed)) {
                    seen.push(computed);
                    result.push(value);
                }
            } else if (!_.contains(result, value)) {
                result.push(value);
            }
        }
        return result;
    };

    // Produce an array that contains the union: each distinct element from all of
    // the passed-in arrays.
    _.union = function () {
        return _.uniq(flatten(arguments, true, true));
    };

    // Produce an array that contains every item shared between all the
    // passed-in arrays.
    _.intersection = function (array) {
        var result = [];
        var argsLength = arguments.length;
        for (var i = 0, length = getLength(array) ; i < length; i++) {
            var item = array[i];
            if (_.contains(result, item)) continue;
            for (var j = 1; j < argsLength; j++) {
                if (!_.contains(arguments[j], item)) break;
            }
            if (j === argsLength) result.push(item);
        }
        return result;
    };

    // Take the difference between one array and a number of other arrays.
    // Only the elements present in just the first array will remain.
    _.difference = function (array) {
        var rest = flatten(arguments, true, true, 1);
        return _.filter(array, function (value) {
            return !_.contains(rest, value);
        });
    };

    // Zip together multiple lists into a single array -- elements that share
    // an index go together.
    _.zip = function () {
        return _.unzip(arguments);
    };

    // Complement of _.zip. Unzip accepts an array of arrays and groups
    // each array's elements on shared indices
    _.unzip = function (array) {
        var length = array && _.max(array, getLength).length || 0;
        var result = Array(length);

        for (var index = 0; index < length; index++) {
            result[index] = _.pluck(array, index);
        }
        return result;
    };

    // Converts lists into objects. Pass either a single array of `[key, value]`
    // pairs, or two parallel arrays of the same length -- one of keys, and one of
    // the corresponding values.
    _.object = function (list, values) {
        var result = {};
        for (var i = 0, length = getLength(list) ; i < length; i++) {
            if (values) {
                result[list[i]] = values[i];
            } else {
                result[list[i][0]] = list[i][1];
            }
        }
        return result;
    };

    // Generator function to create the findIndex and findLastIndex functions
    function createPredicateIndexFinder(dir) {
        return function (array, predicate, context) {
            predicate = cb(predicate, context);
            var length = getLength(array);
            var index = dir > 0 ? 0 : length - 1;
            for (; index >= 0 && index < length; index += dir) {
                if (predicate(array[index], index, array)) return index;
            }
            return -1;
        };
    }

    // Returns the first index on an array-like that passes a predicate test
    _.findIndex = createPredicateIndexFinder(1);
    _.findLastIndex = createPredicateIndexFinder(-1);

    // Use a comparator function to figure out the smallest index at which
    // an object should be inserted so as to maintain order. Uses binary search.
    _.sortedIndex = function (array, obj, iteratee, context) {
        iteratee = cb(iteratee, context, 1);
        var value = iteratee(obj);
        var low = 0, high = getLength(array);
        while (low < high) {
            var mid = Math.floor((low + high) / 2);
            if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
        }
        return low;
    };

    // Generator function to create the indexOf and lastIndexOf functions
    function createIndexFinder(dir, predicateFind, sortedIndex) {
        return function (array, item, idx) {
            var i = 0, length = getLength(array);
            if (typeof idx == 'number') {
                if (dir > 0) {
                    i = idx >= 0 ? idx : Math.max(idx + length, i);
                } else {
                    length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
                }
            } else if (sortedIndex && idx && length) {
                idx = sortedIndex(array, item);
                return array[idx] === item ? idx : -1;
            }
            if (item !== item) {
                idx = predicateFind(slice.call(array, i, length), _.isNaN);
                return idx >= 0 ? idx + i : -1;
            }
            for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
                if (array[idx] === item) return idx;
            }
            return -1;
        };
    }

    // Return the position of the first occurrence of an item in an array,
    // or -1 if the item is not included in the array.
    // If the array is large and already in sort order, pass `true`
    // for **isSorted** to use binary search.
    _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
    _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

    // Generate an integer Array containing an arithmetic progression. A port of
    // the native Python `range()` function. See
    // [the Python documentation](http://docs.python.org/library/functions.html#range).
    _.range = function (start, stop, step) {
        if (stop == null) {
            stop = start || 0;
            start = 0;
        }
        step = step || 1;

        var length = Math.max(Math.ceil((stop - start) / step), 0);
        var range = Array(length);

        for (var idx = 0; idx < length; idx++, start += step) {
            range[idx] = start;
        }

        return range;
    };

    // Function (ahem) Functions
    // ------------------

    // Determines whether to execute a function as a constructor
    // or a normal function with the provided arguments
    var executeBound = function (sourceFunc, boundFunc, context, callingContext, args) {
        if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
        var self = baseCreate(sourceFunc.prototype);
        var result = sourceFunc.apply(self, args);
        if (_.isObject(result)) return result;
        return self;
    };

    // Create a function bound to a given object (assigning `this`, and arguments,
    // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
    // available.
    _.bind = function (func, context) {
        if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
        if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
        var args = slice.call(arguments, 2);
        var bound = function () {
            return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
        };
        return bound;
    };

    // Partially apply a function by creating a version that has had some of its
    // arguments pre-filled, without changing its dynamic `this` context. _ acts
    // as a placeholder, allowing any combination of arguments to be pre-filled.
    _.partial = function (func) {
        var boundArgs = slice.call(arguments, 1);
        var bound = function () {
            var position = 0, length = boundArgs.length;
            var args = Array(length);
            for (var i = 0; i < length; i++) {
                args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
            }
            while (position < arguments.length) args.push(arguments[position++]);
            return executeBound(func, bound, this, this, args);
        };
        return bound;
    };

    // Bind a number of an object's methods to that object. Remaining arguments
    // are the method names to be bound. Useful for ensuring that all callbacks
    // defined on an object belong to it.
    _.bindAll = function (obj) {
        var i, length = arguments.length, key;
        if (length <= 1) throw new Error('bindAll must be passed function names');
        for (i = 1; i < length; i++) {
            key = arguments[i];
            obj[key] = _.bind(obj[key], obj);
        }
        return obj;
    };

    // Memoize an expensive function by storing its results.
    _.memoize = function (func, hasher) {
        var memoize = function (key) {
            var cache = memoize.cache;
            var address = '' + (hasher ? hasher.apply(this, arguments) : key);
            if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
            return cache[address];
        };
        memoize.cache = {};
        return memoize;
    };

    // Delays a function for the given number of milliseconds, and then calls
    // it with the arguments supplied.
    _.delay = function (func, wait) {
        var args = slice.call(arguments, 2);
        return setTimeout(function () {
            return func.apply(null, args);
        }, wait);
    };

    // Defers a function, scheduling it to run after the current call stack has
    // cleared.
    _.defer = _.partial(_.delay, _, 1);

    // Returns a function, that, when invoked, will only be triggered at most once
    // during a given window of time. Normally, the throttled function will run
    // as much as it can, without ever going more than once per `wait` duration;
    // but if you'd like to disable the execution on the leading edge, pass
    // `{leading: false}`. To disable execution on the trailing edge, ditto.
    _.throttle = function (func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options) options = {};
        var later = function () {
            previous = options.leading === false ? 0 : _.now();
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };
        return function () {
            var now = _.now();
            if (!previous && options.leading === false) previous = now;
            var remaining = wait - (now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = now;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
            return result;
        };
    };

    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
    _.debounce = function (func, wait, immediate) {
        var timeout, args, context, timestamp, result;

        var later = function () {
            var last = _.now() - timestamp;

            if (last < wait && last >= 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };

        return function () {
            context = this;
            args = arguments;
            timestamp = _.now();
            var callNow = immediate && !timeout;
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }

            return result;
        };
    };

    // Returns the first function passed as an argument to the second,
    // allowing you to adjust arguments, run code before and after, and
    // conditionally execute the original function.
    _.wrap = function (func, wrapper) {
        return _.partial(wrapper, func);
    };

    // Returns a negated version of the passed-in predicate.
    _.negate = function (predicate) {
        return function () {
            return !predicate.apply(this, arguments);
        };
    };

    // Returns a function that is the composition of a list of functions, each
    // consuming the return value of the function that follows.
    _.compose = function () {
        var args = arguments;
        var start = args.length - 1;
        return function () {
            var i = start;
            var result = args[start].apply(this, arguments);
            while (i--) result = args[i].call(this, result);
            return result;
        };
    };

    // Returns a function that will only be executed on and after the Nth call.
    _.after = function (times, func) {
        return function () {
            if (--times < 1) {
                return func.apply(this, arguments);
            }
        };
    };

    // Returns a function that will only be executed up to (but not including) the Nth call.
    _.before = function (times, func) {
        var memo;
        return function () {
            if (--times > 0) {
                memo = func.apply(this, arguments);
            }
            if (times <= 1) func = null;
            return memo;
        };
    };

    // Returns a function that will be executed at most one time, no matter how
    // often you call it. Useful for lazy initialization.
    _.once = _.partial(_.before, 2);

    // Object Functions
    // ----------------

    // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
    var hasEnumBug = !{ toString: null }.propertyIsEnumerable('toString');
    var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
                        'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];

    function collectNonEnumProps(obj, keys) {
        var nonEnumIdx = nonEnumerableProps.length;
        var constructor = obj.constructor;
        var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;

        // Constructor is a special case.
        var prop = 'constructor';
        if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);

        while (nonEnumIdx--) {
            prop = nonEnumerableProps[nonEnumIdx];
            if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
                keys.push(prop);
            }
        }
    }

    // Retrieve the names of an object's own properties.
    // Delegates to **ECMAScript 5**'s native `Object.keys`
    _.keys = function (obj) {
        if (!_.isObject(obj)) return [];
        if (nativeKeys) return nativeKeys(obj);
        var keys = [];
        for (var key in obj) if (_.has(obj, key)) keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);
        return keys;
    };

    // Retrieve all the property names of an object.
    _.allKeys = function (obj) {
        if (!_.isObject(obj)) return [];
        var keys = [];
        for (var key in obj) keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);
        return keys;
    };

    // Retrieve the values of an object's properties.
    _.values = function (obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var values = Array(length);
        for (var i = 0; i < length; i++) {
            values[i] = obj[keys[i]];
        }
        return values;
    };

    // Returns the results of applying the iteratee to each element of the object
    // In contrast to _.map it returns an object
    _.mapObject = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = _.keys(obj),
              length = keys.length,
              results = {},
              currentKey;
        for (var index = 0; index < length; index++) {
            currentKey = keys[index];
            results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
    };

    // Convert an object into a list of `[key, value]` pairs.
    _.pairs = function (obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var pairs = Array(length);
        for (var i = 0; i < length; i++) {
            pairs[i] = [keys[i], obj[keys[i]]];
        }
        return pairs;
    };

    // Invert the keys and values of an object. The values must be serializable.
    _.invert = function (obj) {
        var result = {};
        var keys = _.keys(obj);
        for (var i = 0, length = keys.length; i < length; i++) {
            result[obj[keys[i]]] = keys[i];
        }
        return result;
    };

    // Return a sorted list of the function names available on the object.
    // Aliased as `methods`
    _.functions = _.methods = function (obj) {
        var names = [];
        for (var key in obj) {
            if (_.isFunction(obj[key])) names.push(key);
        }
        return names.sort();
    };

    // Extend a given object with all the properties in passed-in object(s).
    _.extend = createAssigner(_.allKeys);

    // Assigns a given object with all the own properties in the passed-in object(s)
    // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
    _.extendOwn = _.assign = createAssigner(_.keys);

    // Returns the first key on an object that passes a predicate test
    _.findKey = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = _.keys(obj), key;
        for (var i = 0, length = keys.length; i < length; i++) {
            key = keys[i];
            if (predicate(obj[key], key, obj)) return key;
        }
    };

    // Return a copy of the object only containing the whitelisted properties.
    _.pick = function (object, oiteratee, context) {
        var result = {}, obj = object, iteratee, keys;
        if (obj == null) return result;
        if (_.isFunction(oiteratee)) {
            keys = _.allKeys(obj);
            iteratee = optimizeCb(oiteratee, context);
        } else {
            keys = flatten(arguments, false, false, 1);
            iteratee = function (value, key, obj) { return key in obj; };
            obj = Object(obj);
        }
        for (var i = 0, length = keys.length; i < length; i++) {
            var key = keys[i];
            var value = obj[key];
            if (iteratee(value, key, obj)) result[key] = value;
        }
        return result;
    };

    // Return a copy of the object without the blacklisted properties.
    _.omit = function (obj, iteratee, context) {
        if (_.isFunction(iteratee)) {
            iteratee = _.negate(iteratee);
        } else {
            var keys = _.map(flatten(arguments, false, false, 1), String);
            iteratee = function (value, key) {
                return !_.contains(keys, key);
            };
        }
        return _.pick(obj, iteratee, context);
    };

    // Fill in a given object with default properties.
    _.defaults = createAssigner(_.allKeys, true);

    // Creates an object that inherits from the given prototype object.
    // If additional properties are provided then they will be added to the
    // created object.
    _.create = function (prototype, props) {
        var result = baseCreate(prototype);
        if (props) _.extendOwn(result, props);
        return result;
    };

    // Create a (shallow-cloned) duplicate of an object.
    _.clone = function (obj) {
        if (!_.isObject(obj)) return obj;
        return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    };

    // Invokes interceptor with the obj, and then returns obj.
    // The primary purpose of this method is to "tap into" a method chain, in
    // order to perform operations on intermediate results within the chain.
    _.tap = function (obj, interceptor) {
        interceptor(obj);
        return obj;
    };

    // Returns whether an object has a given set of `key:value` pairs.
    _.isMatch = function (object, attrs) {
        var keys = _.keys(attrs), length = keys.length;
        if (object == null) return !length;
        var obj = Object(object);
        for (var i = 0; i < length; i++) {
            var key = keys[i];
            if (attrs[key] !== obj[key] || !(key in obj)) return false;
        }
        return true;
    };


    // Internal recursive comparison function for `isEqual`.
    var eq = function (a, b, aStack, bStack) {
        // Identical objects are equal. `0 === -0`, but they aren't identical.
        // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
        if (a === b) return a !== 0 || 1 / a === 1 / b;
        // A strict comparison is necessary because `null == undefined`.
        if (a == null || b == null) return a === b;
        // Unwrap any wrapped objects.
        if (a instanceof _) a = a._wrapped;
        if (b instanceof _) b = b._wrapped;
        // Compare `[[Class]]` names.
        var className = toString.call(a);
        if (className !== toString.call(b)) return false;
        switch (className) {
            // Strings, numbers, regular expressions, dates, and booleans are compared by value.
            case '[object RegExp]':
                // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
            case '[object String]':
                // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
                // equivalent to `new String("5")`.
                return '' + a === '' + b;
            case '[object Number]':
                // `NaN`s are equivalent, but non-reflexive.
                // Object(NaN) is equivalent to NaN
                if (+a !== +a) return +b !== +b;
                // An `egal` comparison is performed for other numeric values.
                return +a === 0 ? 1 / +a === 1 / b : +a === +b;
            case '[object Date]':
            case '[object Boolean]':
                // Coerce dates and booleans to numeric primitive values. Dates are compared by their
                // millisecond representations. Note that invalid dates with millisecond representations
                // of `NaN` are not equivalent.
                return +a === +b;
        }

        var areArrays = className === '[object Array]';
        if (!areArrays) {
            if (typeof a != 'object' || typeof b != 'object') return false;

            // Objects with different constructors are not equivalent, but `Object`s or `Array`s
            // from different frames are.
            var aCtor = a.constructor, bCtor = b.constructor;
            if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
                                     _.isFunction(bCtor) && bCtor instanceof bCtor)
                                && ('constructor' in a && 'constructor' in b)) {
                return false;
            }
        }
        // Assume equality for cyclic structures. The algorithm for detecting cyclic
        // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.

        // Initializing stack of traversed objects.
        // It's done here since we only need them for objects and arrays comparison.
        aStack = aStack || [];
        bStack = bStack || [];
        var length = aStack.length;
        while (length--) {
            // Linear search. Performance is inversely proportional to the number of
            // unique nested structures.
            if (aStack[length] === a) return bStack[length] === b;
        }

        // Add the first object to the stack of traversed objects.
        aStack.push(a);
        bStack.push(b);

        // Recursively compare objects and arrays.
        if (areArrays) {
            // Compare array lengths to determine if a deep comparison is necessary.
            length = a.length;
            if (length !== b.length) return false;
            // Deep compare the contents, ignoring non-numeric properties.
            while (length--) {
                if (!eq(a[length], b[length], aStack, bStack)) return false;
            }
        } else {
            // Deep compare objects.
            var keys = _.keys(a), key;
            length = keys.length;
            // Ensure that both objects contain the same number of properties before comparing deep equality.
            if (_.keys(b).length !== length) return false;
            while (length--) {
                // Deep compare each member
                key = keys[length];
                if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
            }
        }
        // Remove the first object from the stack of traversed objects.
        aStack.pop();
        bStack.pop();
        return true;
    };

    // Perform a deep comparison to check if two objects are equal.
    _.isEqual = function (a, b) {
        return eq(a, b);
    };

    // Is a given array, string, or object empty?
    // An "empty" object has no enumerable own-properties.
    _.isEmpty = function (obj) {
        if (obj == null) return true;
        if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
        return _.keys(obj).length === 0;
    };

    // Is a given value a DOM element?
    _.isElement = function (obj) {
        return !!(obj && obj.nodeType === 1);
    };

    // Is a given value an array?
    // Delegates to ECMA5's native Array.isArray
    _.isArray = nativeIsArray || function (obj) {
        return toString.call(obj) === '[object Array]';
    };

    // Is a given variable an object?
    _.isObject = function (obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;
    };

    // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
    _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function (name) {
        _['is' + name] = function (obj) {
            return toString.call(obj) === '[object ' + name + ']';
        };
    });

    // Define a fallback version of the method in browsers (ahem, IE < 9), where
    // there isn't any inspectable "Arguments" type.
    if (!_.isArguments(arguments)) {
        _.isArguments = function (obj) {
            return _.has(obj, 'callee');
        };
    }

    // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
    // IE 11 (#1621), and in Safari 8 (#1929).
    if (typeof /./ != 'function' && typeof Int8Array != 'object') {
        _.isFunction = function (obj) {
            return typeof obj == 'function' || false;
        };
    }

    // Is a given object a finite number?
    _.isFinite = function (obj) {
        return isFinite(obj) && !isNaN(parseFloat(obj));
    };

    // Is the given value `NaN`? (NaN is the only number which does not equal itself).
    _.isNaN = function (obj) {
        return _.isNumber(obj) && obj !== +obj;
    };

    // Is a given value a boolean?
    _.isBoolean = function (obj) {
        return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
    };

    // Is a given value equal to null?
    _.isNull = function (obj) {
        return obj === null;
    };

    // Is a given variable undefined?
    _.isUndefined = function (obj) {
        return obj === void 0;
    };

    // Shortcut function for checking if an object has a given property directly
    // on itself (in other words, not on a prototype).
    _.has = function (obj, key) {
        return obj != null && hasOwnProperty.call(obj, key);
    };

    // Utility Functions
    // -----------------

    // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
    // previous owner. Returns a reference to the Underscore object.
    _.noConflict = function () {
        root._ = previousUnderscore;
        return this;
    };

    // Keep the identity function around for default iteratees.
    _.identity = function (value) {
        return value;
    };

    // Predicate-generating functions. Often useful outside of Underscore.
    _.constant = function (value) {
        return function () {
            return value;
        };
    };

    _.noop = function () { };

    _.property = property;

    // Generates a function for a given object that returns a given property.
    _.propertyOf = function (obj) {
        return obj == null ? function () { } : function (key) {
            return obj[key];
        };
    };

    // Returns a predicate for checking whether an object has a given set of
    // `key:value` pairs.
    _.matcher = _.matches = function (attrs) {
        attrs = _.extendOwn({}, attrs);
        return function (obj) {
            return _.isMatch(obj, attrs);
        };
    };

    // Run a function **n** times.
    _.times = function (n, iteratee, context) {
        var accum = Array(Math.max(0, n));
        iteratee = optimizeCb(iteratee, context, 1);
        for (var i = 0; i < n; i++) accum[i] = iteratee(i);
        return accum;
    };

    // Return a random integer between min and max (inclusive).
    _.random = function (min, max) {
        if (max == null) {
            max = min;
            min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
    };

    // A (possibly faster) way to get the current timestamp as an integer.
    _.now = Date.now || function () {
        return new Date().getTime();
    };

    // List of HTML entities for escaping.
    var escapeMap = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#x27;',
        '`': '&#x60;'
    };
    var unescapeMap = _.invert(escapeMap);

    // Functions for escaping and unescaping strings to/from HTML interpolation.
    var createEscaper = function (map) {
        var escaper = function (match) {
            return map[match];
        };
        // Regexes for identifying a key that needs to be escaped
        var source = '(?:' + _.keys(map).join('|') + ')';
        var testRegexp = RegExp(source);
        var replaceRegexp = RegExp(source, 'g');
        return function (string) {
            string = string == null ? '' : '' + string;
            return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
        };
    };
    _.escape = createEscaper(escapeMap);
    _.unescape = createEscaper(unescapeMap);

    // If the value of the named `property` is a function then invoke it with the
    // `object` as context; otherwise, return it.
    _.result = function (object, property, fallback) {
        var value = object == null ? void 0 : object[property];
        if (value === void 0) {
            value = fallback;
        }
        return _.isFunction(value) ? value.call(object) : value;
    };

    // Generate a unique integer id (unique within the entire client session).
    // Useful for temporary DOM ids.
    var idCounter = 0;
    _.uniqueId = function (prefix) {
        var id = ++idCounter + '';
        return prefix ? prefix + id : id;
    };

    // By default, Underscore uses ERB-style template delimiters, change the
    // following template settings to use alternative delimiters.
    _.templateSettings = {
        evaluate: /<%([\s\S]+?)%>/g,
        interpolate: /<%=([\s\S]+?)%>/g,
        escape: /<%-([\s\S]+?)%>/g
    };

    // When customizing `templateSettings`, if you don't want to define an
    // interpolation, evaluation or escaping regex, we need one that is
    // guaranteed not to match.
    var noMatch = /(.)^/;

    // Certain characters need to be escaped so that they can be put into a
    // string literal.
    var escapes = {
        "'": "'",
        '\\': '\\',
        '\r': 'r',
        '\n': 'n',
        '\u2028': 'u2028',
        '\u2029': 'u2029'
    };

    var escaper = /\\|'|\r|\n|\u2028|\u2029/g;

    var escapeChar = function (match) {
        return '\\' + escapes[match];
    };

    // JavaScript micro-templating, similar to John Resig's implementation.
    // Underscore templating handles arbitrary delimiters, preserves whitespace,
    // and correctly escapes quotes within interpolated code.
    // NB: `oldSettings` only exists for backwards compatibility.
    _.template = function (text, settings, oldSettings) {
        if (!settings && oldSettings) settings = oldSettings;
        settings = _.defaults({}, settings, _.templateSettings);

        // Combine delimiters into one regular expression via alternation.
        var matcher = RegExp([
          (settings.escape || noMatch).source,
          (settings.interpolate || noMatch).source,
          (settings.evaluate || noMatch).source
        ].join('|') + '|$', 'g');

        // Compile the template source, escaping string literals appropriately.
        var index = 0;
        var source = "__p+='";
        text.replace(matcher, function (match, escape, interpolate, evaluate, offset) {
            source += text.slice(index, offset).replace(escaper, escapeChar);
            index = offset + match.length;

            if (escape) {
                source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
            } else if (interpolate) {
                source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
            } else if (evaluate) {
                source += "';\n" + evaluate + "\n__p+='";
            }

            // Adobe VMs need the match returned to produce the correct offest.
            return match;
        });
        source += "';\n";

        // If a variable is not specified, place data values in local scope.
        if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';

        source = "var __t,__p='',__j=Array.prototype.join," +
          "print=function(){__p+=__j.call(arguments,'');};\n" +
          source + 'return __p;\n';

        try {
            var render = new Function(settings.variable || 'obj', '_', source);
        } catch (e) {
            e.source = source;
            throw e;
        }

        var template = function (data) {
            return render.call(this, data, _);
        };

        // Provide the compiled source as a convenience for precompilation.
        var argument = settings.variable || 'obj';
        template.source = 'function(' + argument + '){\n' + source + '}';

        return template;
    };

    // Add a "chain" function. Start chaining a wrapped Underscore object.
    _.chain = function (obj) {
        var instance = _(obj);
        instance._chain = true;
        return instance;
    };

    // OOP
    // ---------------
    // If Underscore is called as a function, it returns a wrapped object that
    // can be used OO-style. This wrapper holds altered versions of all the
    // underscore functions. Wrapped objects may be chained.

    // Helper function to continue chaining intermediate results.
    var result = function (instance, obj) {
        return instance._chain ? _(obj).chain() : obj;
    };

    // Add your own custom functions to the Underscore object.
    _.mixin = function (obj) {
        _.each(_.functions(obj), function (name) {
            var func = _[name] = obj[name];
            _.prototype[name] = function () {
                var args = [this._wrapped];
                push.apply(args, arguments);
                return result(this, func.apply(_, args));
            };
        });
    };

    // Add all of the Underscore functions to the wrapper object.
    _.mixin(_);

    // Add all mutator Array functions to the wrapper.
    _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function (name) {
        var method = ArrayProto[name];
        _.prototype[name] = function () {
            var obj = this._wrapped;
            method.apply(obj, arguments);
            if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
            return result(this, obj);
        };
    });

    // Add all accessor Array functions to the wrapper.
    _.each(['concat', 'join', 'slice'], function (name) {
        var method = ArrayProto[name];
        _.prototype[name] = function () {
            return result(this, method.apply(this._wrapped, arguments));
        };
    });

    // Extracts the result from a wrapped and chained object.
    _.prototype.value = function () {
        return this._wrapped;
    };

    // Provide unwrapping proxy for some methods used in engine operations
    // such as arithmetic and JSON stringification.
    _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

    _.prototype.toString = function () {
        return '' + this._wrapped;
    };

    // AMD registration happens at the end for compatibility with AMD loaders
    // that may not enforce next-turn semantics on modules. Even though general
    // practice for AMD registration is to be anonymous, underscore registers
    // as a named module because, like jQuery, it is a base library that is
    // popular enough to be bundled in a third party lib, but not be part of
    // an AMD load request. Those cases could generate an error when an
    // anonymous define() is called outside of a loader request.
    if (typeof define === 'function' && define.amd) {
        define('underscore', [], function () {
            return _;
        });
    }
}.call(this));;/*!
 * Select2 4.0.3
 * https://select2.github.io
 *
 * Released under the MIT license
 * https://github.com/select2/select2/blob/master/LICENSE.md
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS
        factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function (jQuery) {
    // This is needed so we can catch the AMD loader configuration and use it
    // The inner file should be wrapped (by `banner.start.js`) in a function that
    // returns the AMD loader references.
    var S2 =
  (function () {
      // Restore the Select2 AMD loader so it can be used
      // Needed mostly in the language files, where the loader is not inserted
      if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
          var S2 = jQuery.fn.select2.amd;
      }
      var S2; (function () {
          if (!S2 || !S2.requirejs) {
              if (!S2) { S2 = {}; } else { require = S2; }
              /**
               * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
               * Available via the MIT or new BSD license.
               * see: http://github.com/jrburke/almond for details
               */
              //Going sloppy to avoid 'use strict' string cost, but strict practices should
              //be followed.
              /*jslint sloppy: true */
              /*global setTimeout: false */

              var requirejs, require, define;
              (function (undef) {
                  var main, req, makeMap, handlers,
                      defined = {},
                      waiting = {},
                      config = {},
                      defining = {},
                      hasOwn = Object.prototype.hasOwnProperty,
                      aps = [].slice,
                      jsSuffixRegExp = /\.js$/;

                  function hasProp(obj, prop) {
                      return hasOwn.call(obj, prop);
                  }

                  /**
                   * Given a relative module name, like ./something, normalize it to
                   * a real name that can be mapped to a path.
                   * @param {String} name the relative name
                   * @param {String} baseName a real name that the name arg is relative
                   * to.
                   * @returns {String} normalized name
                   */
                  function normalize(name, baseName) {
                      var nameParts, nameSegment, mapValue, foundMap, lastIndex,
                          foundI, foundStarMap, starI, i, j, part,
                          baseParts = baseName && baseName.split("/"),
                          map = config.map,
                          starMap = (map && map['*']) || {};

                      //Adjust any relative paths.
                      if (name && name.charAt(0) === ".") {
                          //If have a base name, try to normalize against it,
                          //otherwise, assume it is a top-level require that will
                          //be relative to baseUrl in the end.
                          if (baseName) {
                              name = name.split('/');
                              lastIndex = name.length - 1;

                              // Node .js allowance:
                              if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
                                  name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
                              }

                              //Lop off the last part of baseParts, so that . matches the
                              //"directory" and not name of the baseName's module. For instance,
                              //baseName of "one/two/three", maps to "one/two/three.js", but we
                              //want the directory, "one/two" for this normalization.
                              name = baseParts.slice(0, baseParts.length - 1).concat(name);

                              //start trimDots
                              for (i = 0; i < name.length; i += 1) {
                                  part = name[i];
                                  if (part === ".") {
                                      name.splice(i, 1);
                                      i -= 1;
                                  } else if (part === "..") {
                                      if (i === 1 && (name[2] === '..' || name[0] === '..')) {
                                          //End of the line. Keep at least one non-dot
                                          //path segment at the front so it can be mapped
                                          //correctly to disk. Otherwise, there is likely
                                          //no path mapping for a path starting with '..'.
                                          //This can still fail, but catches the most reasonable
                                          //uses of ..
                                          break;
                                      } else if (i > 0) {
                                          name.splice(i - 1, 2);
                                          i -= 2;
                                      }
                                  }
                              }
                              //end trimDots

                              name = name.join("/");
                          } else if (name.indexOf('./') === 0) {
                              // No baseName, so this is ID is resolved relative
                              // to baseUrl, pull off the leading dot.
                              name = name.substring(2);
                          }
                      }

                      //Apply map config if available.
                      if ((baseParts || starMap) && map) {
                          nameParts = name.split('/');

                          for (i = nameParts.length; i > 0; i -= 1) {
                              nameSegment = nameParts.slice(0, i).join("/");

                              if (baseParts) {
                                  //Find the longest baseName segment match in the config.
                                  //So, do joins on the biggest to smallest lengths of baseParts.
                                  for (j = baseParts.length; j > 0; j -= 1) {
                                      mapValue = map[baseParts.slice(0, j).join('/')];

                                      //baseName segment has  config, find if it has one for
                                      //this name.
                                      if (mapValue) {
                                          mapValue = mapValue[nameSegment];
                                          if (mapValue) {
                                              //Match, update name to the new value.
                                              foundMap = mapValue;
                                              foundI = i;
                                              break;
                                          }
                                      }
                                  }
                              }

                              if (foundMap) {
                                  break;
                              }

                              //Check for a star map match, but just hold on to it,
                              //if there is a shorter segment match later in a matching
                              //config, then favor over this star map.
                              if (!foundStarMap && starMap && starMap[nameSegment]) {
                                  foundStarMap = starMap[nameSegment];
                                  starI = i;
                              }
                          }

                          if (!foundMap && foundStarMap) {
                              foundMap = foundStarMap;
                              foundI = starI;
                          }

                          if (foundMap) {
                              nameParts.splice(0, foundI, foundMap);
                              name = nameParts.join('/');
                          }
                      }

                      return name;
                  }

                  function makeRequire(relName, forceSync) {
                      return function () {
                          //A version of a require function that passes a moduleName
                          //value for items that may need to
                          //look up paths relative to the moduleName
                          var args = aps.call(arguments, 0);

                          //If first arg is not require('string'), and there is only
                          //one arg, it is the array form without a callback. Insert
                          //a null so that the following concat is correct.
                          if (typeof args[0] !== 'string' && args.length === 1) {
                              args.push(null);
                          }
                          return req.apply(undef, args.concat([relName, forceSync]));
                      };
                  }

                  function makeNormalize(relName) {
                      return function (name) {
                          return normalize(name, relName);
                      };
                  }

                  function makeLoad(depName) {
                      return function (value) {
                          defined[depName] = value;
                      };
                  }

                  function callDep(name) {
                      if (hasProp(waiting, name)) {
                          var args = waiting[name];
                          delete waiting[name];
                          defining[name] = true;
                          main.apply(undef, args);
                      }

                      if (!hasProp(defined, name) && !hasProp(defining, name)) {
                          throw new Error('No ' + name);
                      }
                      return defined[name];
                  }

                  //Turns a plugin!resource to [plugin, resource]
                  //with the plugin being undefined if the name
                  //did not have a plugin prefix.
                  function splitPrefix(name) {
                      var prefix,
                          index = name ? name.indexOf('!') : -1;
                      if (index > -1) {
                          prefix = name.substring(0, index);
                          name = name.substring(index + 1, name.length);
                      }
                      return [prefix, name];
                  }

                  /**
                   * Makes a name map, normalizing the name, and using a plugin
                   * for normalization if necessary. Grabs a ref to plugin
                   * too, as an optimization.
                   */
                  makeMap = function (name, relName) {
                      var plugin,
                          parts = splitPrefix(name),
                          prefix = parts[0];

                      name = parts[1];

                      if (prefix) {
                          prefix = normalize(prefix, relName);
                          plugin = callDep(prefix);
                      }

                      //Normalize according
                      if (prefix) {
                          if (plugin && plugin.normalize) {
                              name = plugin.normalize(name, makeNormalize(relName));
                          } else {
                              name = normalize(name, relName);
                          }
                      } else {
                          name = normalize(name, relName);
                          parts = splitPrefix(name);
                          prefix = parts[0];
                          name = parts[1];
                          if (prefix) {
                              plugin = callDep(prefix);
                          }
                      }

                      //Using ridiculous property names for space reasons
                      return {
                          f: prefix ? prefix + '!' + name : name, //fullName
                          n: name,
                          pr: prefix,
                          p: plugin
                      };
                  };

                  function makeConfig(name) {
                      return function () {
                          return (config && config.config && config.config[name]) || {};
                      };
                  }

                  handlers = {
                      require: function (name) {
                          return makeRequire(name);
                      },
                      exports: function (name) {
                          var e = defined[name];
                          if (typeof e !== 'undefined') {
                              return e;
                          } else {
                              return (defined[name] = {});
                          }
                      },
                      module: function (name) {
                          return {
                              id: name,
                              uri: '',
                              exports: defined[name],
                              config: makeConfig(name)
                          };
                      }
                  };

                  main = function (name, deps, callback, relName) {
                      var cjsModule, depName, ret, map, i,
                          args = [],
                          callbackType = typeof callback,
                          usingExports;

                      //Use name if no relName
                      relName = relName || name;

                      //Call the callback to define the module, if necessary.
                      if (callbackType === 'undefined' || callbackType === 'function') {
                          //Pull out the defined dependencies and pass the ordered
                          //values to the callback.
                          //Default to [require, exports, module] if no deps
                          deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
                          for (i = 0; i < deps.length; i += 1) {
                              map = makeMap(deps[i], relName);
                              depName = map.f;

                              //Fast path CommonJS standard dependencies.
                              if (depName === "require") {
                                  args[i] = handlers.require(name);
                              } else if (depName === "exports") {
                                  //CommonJS module spec 1.1
                                  args[i] = handlers.exports(name);
                                  usingExports = true;
                              } else if (depName === "module") {
                                  //CommonJS module spec 1.1
                                  cjsModule = args[i] = handlers.module(name);
                              } else if (hasProp(defined, depName) ||
                                         hasProp(waiting, depName) ||
                                         hasProp(defining, depName)) {
                                  args[i] = callDep(depName);
                              } else if (map.p) {
                                  map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
                                  args[i] = defined[depName];
                              } else {
                                  throw new Error(name + ' missing ' + depName);
                              }
                          }

                          ret = callback ? callback.apply(defined[name], args) : undefined;

                          if (name) {
                              //If setting exports via "module" is in play,
                              //favor that over return value and exports. After that,
                              //favor a non-undefined return value over exports use.
                              if (cjsModule && cjsModule.exports !== undef &&
                                      cjsModule.exports !== defined[name]) {
                                  defined[name] = cjsModule.exports;
                              } else if (ret !== undef || !usingExports) {
                                  //Use the return value from the function.
                                  defined[name] = ret;
                              }
                          }
                      } else if (name) {
                          //May just be an object definition for the module. Only
                          //worry about defining if have a module name.
                          defined[name] = callback;
                      }
                  };

                  requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
                      if (typeof deps === "string") {
                          if (handlers[deps]) {
                              //callback in this case is really relName
                              return handlers[deps](callback);
                          }
                          //Just return the module wanted. In this scenario, the
                          //deps arg is the module name, and second arg (if passed)
                          //is just the relName.
                          //Normalize module name, if it contains . or ..
                          return callDep(makeMap(deps, callback).f);
                      } else if (!deps.splice) {
                          //deps is a config object, not an array.
                          config = deps;
                          if (config.deps) {
                              req(config.deps, config.callback);
                          }
                          if (!callback) {
                              return;
                          }

                          if (callback.splice) {
                              //callback is an array, which means it is a dependency list.
                              //Adjust args if there are dependencies
                              deps = callback;
                              callback = relName;
                              relName = null;
                          } else {
                              deps = undef;
                          }
                      }

                      //Support require(['a'])
                      callback = callback || function () { };

                      //If relName is a function, it is an errback handler,
                      //so remove it.
                      if (typeof relName === 'function') {
                          relName = forceSync;
                          forceSync = alt;
                      }

                      //Simulate async callback;
                      if (forceSync) {
                          main(undef, deps, callback, relName);
                      } else {
                          //Using a non-zero value because of concern for what old browsers
                          //do, and latest browsers "upgrade" to 4 if lower value is used:
                          //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
                          //If want a value immediately, use require('id') instead -- something
                          //that works in almond on the global level, but not guaranteed and
                          //unlikely to work in other AMD implementations.
                          setTimeout(function () {
                              main(undef, deps, callback, relName);
                          }, 4);
                      }

                      return req;
                  };

                  /**
                   * Just drops the config on the floor, but returns req in case
                   * the config return value is used.
                   */
                  req.config = function (cfg) {
                      return req(cfg);
                  };

                  /**
                   * Expose module registry for debugging and tooling
                   */
                  requirejs._defined = defined;

                  define = function (name, deps, callback) {
                      if (typeof name !== 'string') {
                          throw new Error('See almond README: incorrect module build, no module name');
                      }

                      //This module may not have dependencies
                      if (!deps.splice) {
                          //deps is not an array, so probably means
                          //an object literal or factory function for
                          //the value. Adjust args.
                          callback = deps;
                          deps = [];
                      }

                      if (!hasProp(defined, name) && !hasProp(waiting, name)) {
                          waiting[name] = [name, deps, callback];
                      }
                  };

                  define.amd = {
                      jQuery: true
                  };
              }());

              S2.requirejs = requirejs; S2.require = require; S2.define = define;
          }
      }());
      S2.define("almond", function () { });

      /* global jQuery:false, $:false */
      S2.define('jquery', [], function () {
          var _$ = jQuery || $;

          if (_$ == null && console && console.error) {
              console.error(
                'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
                'found. Make sure that you are including jQuery before Select2 on your ' +
                'web page.'
              );
          }

          return _$;
      });

      S2.define('select2/utils', [
        'jquery'
      ], function ($) {
          var Utils = {};

          Utils.Extend = function (ChildClass, SuperClass) {
              var __hasProp = {}.hasOwnProperty;

              function BaseConstructor() {
                  this.constructor = ChildClass;
              }

              for (var key in SuperClass) {
                  if (__hasProp.call(SuperClass, key)) {
                      ChildClass[key] = SuperClass[key];
                  }
              }

              BaseConstructor.prototype = SuperClass.prototype;
              ChildClass.prototype = new BaseConstructor();
              ChildClass.__super__ = SuperClass.prototype;

              return ChildClass;
          };

          function getMethods(theClass) {
              var proto = theClass.prototype;

              var methods = [];

              for (var methodName in proto) {
                  var m = proto[methodName];

                  if (typeof m !== 'function') {
                      continue;
                  }

                  if (methodName === 'constructor') {
                      continue;
                  }

                  methods.push(methodName);
              }

              return methods;
          }

          Utils.Decorate = function (SuperClass, DecoratorClass) {
              var decoratedMethods = getMethods(DecoratorClass);
              var superMethods = getMethods(SuperClass);

              function DecoratedClass() {
                  var unshift = Array.prototype.unshift;

                  var argCount = DecoratorClass.prototype.constructor.length;

                  var calledConstructor = SuperClass.prototype.constructor;

                  if (argCount > 0) {
                      unshift.call(arguments, SuperClass.prototype.constructor);

                      calledConstructor = DecoratorClass.prototype.constructor;
                  }

                  calledConstructor.apply(this, arguments);
              }

              DecoratorClass.displayName = SuperClass.displayName;

              function ctr() {
                  this.constructor = DecoratedClass;
              }

              DecoratedClass.prototype = new ctr();

              for (var m = 0; m < superMethods.length; m++) {
                  var superMethod = superMethods[m];

                  DecoratedClass.prototype[superMethod] =
                    SuperClass.prototype[superMethod];
              }

              var calledMethod = function (methodName) {
                  // Stub out the original method if it's not decorating an actual method
                  var originalMethod = function () { };

                  if (methodName in DecoratedClass.prototype) {
                      originalMethod = DecoratedClass.prototype[methodName];
                  }

                  var decoratedMethod = DecoratorClass.prototype[methodName];

                  return function () {
                      var unshift = Array.prototype.unshift;

                      unshift.call(arguments, originalMethod);

                      return decoratedMethod.apply(this, arguments);
                  };
              };

              for (var d = 0; d < decoratedMethods.length; d++) {
                  var decoratedMethod = decoratedMethods[d];

                  DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
              }

              return DecoratedClass;
          };

          var Observable = function () {
              this.listeners = {};
          };

          Observable.prototype.on = function (event, callback) {
              this.listeners = this.listeners || {};

              if (event in this.listeners) {
                  this.listeners[event].push(callback);
              } else {
                  this.listeners[event] = [callback];
              }
          };

          Observable.prototype.trigger = function (event) {
              var slice = Array.prototype.slice;
              var params = slice.call(arguments, 1);

              this.listeners = this.listeners || {};

              // Params should always come in as an array
              if (params == null) {
                  params = [];
              }

              // If there are no arguments to the event, use a temporary object
              if (params.length === 0) {
                  params.push({});
              }

              // Set the `_type` of the first object to the event
              params[0]._type = event;

              if (event in this.listeners) {
                  this.invoke(this.listeners[event], slice.call(arguments, 1));
              }

              if ('*' in this.listeners) {
                  this.invoke(this.listeners['*'], arguments);
              }
          };

          Observable.prototype.invoke = function (listeners, params) {
              for (var i = 0, len = listeners.length; i < len; i++) {
                  listeners[i].apply(this, params);
              }
          };

          Utils.Observable = Observable;

          Utils.generateChars = function (length) {
              var chars = '';

              for (var i = 0; i < length; i++) {
                  var randomChar = Math.floor(Math.random() * 36);
                  chars += randomChar.toString(36);
              }

              return chars;
          };

          Utils.bind = function (func, context) {
              return function () {
                  func.apply(context, arguments);
              };
          };

          Utils._convertData = function (data) {
              for (var originalKey in data) {
                  var keys = originalKey.split('-');

                  var dataLevel = data;

                  if (keys.length === 1) {
                      continue;
                  }

                  for (var k = 0; k < keys.length; k++) {
                      var key = keys[k];

                      // Lowercase the first letter
                      // By default, dash-separated becomes camelCase
                      key = key.substring(0, 1).toLowerCase() + key.substring(1);

                      if (!(key in dataLevel)) {
                          dataLevel[key] = {};
                      }

                      if (k == keys.length - 1) {
                          dataLevel[key] = data[originalKey];
                      }

                      dataLevel = dataLevel[key];
                  }

                  delete data[originalKey];
              }

              return data;
          };

          Utils.hasScroll = function (index, el) {
              // Adapted from the function created by @ShadowScripter
              // and adapted by @BillBarry on the Stack Exchange Code Review website.
              // The original code can be found at
              // http://codereview.stackexchange.com/q/13338
              // and was designed to be used with the Sizzle selector engine.

              var $el = $(el);
              var overflowX = el.style.overflowX;
              var overflowY = el.style.overflowY;

              //Check both x and y declarations
              if (overflowX === overflowY &&
                  (overflowY === 'hidden' || overflowY === 'visible')) {
                  return false;
              }

              if (overflowX === 'scroll' || overflowY === 'scroll') {
                  return true;
              }

              return ($el.innerHeight() < el.scrollHeight ||
                $el.innerWidth() < el.scrollWidth);
          };

          Utils.escapeMarkup = function (markup) {
              var replaceMap = {
                  '\\': '&#92;',
                  '&': '&amp;',
                  '<': '&lt;',
                  '>': '&gt;',
                  '"': '&quot;',
                  '\'': '&#39;',
                  '/': '&#47;'
              };

              // Do not try to escape the markup if it's not a string
              if (typeof markup !== 'string') {
                  return markup;
              }

              return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
                  return replaceMap[match];
              });
          };

          // Append an array of jQuery nodes to a given element.
          Utils.appendMany = function ($element, $nodes) {
              // jQuery 1.7.x does not support $.fn.append() with an array
              // Fall back to a jQuery object collection using $.fn.add()
              if ($.fn.jquery.substr(0, 3) === '1.7') {
                  var $jqNodes = $();

                  $.map($nodes, function (node) {
                      $jqNodes = $jqNodes.add(node);
                  });

                  $nodes = $jqNodes;
              }

              $element.append($nodes);
          };

          return Utils;
      });

      S2.define('select2/results', [
        'jquery',
        './utils'
      ], function ($, Utils) {
          function Results($element, options, dataAdapter) {
              this.$element = $element;
              this.data = dataAdapter;
              this.options = options;
              this.mode = '';

              Results.__super__.constructor.call(this);
          }

          Utils.Extend(Results, Utils.Observable);

          Results.prototype.render = function () {
              var $results = $(
                '<ul class="select2-results__options" role="tree"></ul>'
              );

              if (this.options.get('multiple')) {
                  $results.attr('aria-multiselectable', 'true');
              }

              this.$results = $results;

              return $results;
          };

          Results.prototype.clear = function () {
              this.$results.empty();
          };

          Results.prototype.displayMessage = function (params) {
              var escapeMarkup = this.options.get('escapeMarkup');

              this.clear();
              this.hideLoading();

              var $message = $(
                '<li role="treeitem" aria-live="assertive"' +
                ' class="select2-results__option"></li>'
              );

              var message = this.options.get('translations').get(params.message);

              $message.append(
                escapeMarkup(
                  message(params.args)
                )
              );

              $message[0].className += ' select2-results__message';

              this.$results.append($message);
          };

          Results.prototype.hideMessages = function () {
              this.$results.find('.select2-results__message').remove();
          };

          Results.prototype.append = function (data) {
              this.hideLoading();

              var $options = [];

              if (data.results == null || data.results.length === 0) {
                  if (this.$results.children().length === 0) {
                      this.trigger('results:message', {
                          message: 'noResults'
                      });
                  }

                  return;
              }

              data.results = this.sort(data.results);

              for (var d = 0; d < data.results.length; d++) {
                  var item = data.results[d];

                  var $option = this.option(item);

                  $options.push($option);
              }

              this.$results.append($options);
          };

          Results.prototype.position = function ($results, $dropdown) {
              var $resultsContainer = $dropdown.find('.select2-results');
              $resultsContainer.append($results);
          };

          Results.prototype.sort = function (data) {
              var sorter = this.options.get('sorter');

              return sorter(data);
          };

          Results.prototype.highlightFirstItem = function () {
              var $options = this.$results
                .find('.select2-results__option[aria-selected]');

              var $selected = $options.filter('[aria-selected=true]');

              // Check if there are any selected options
              if ($selected.length > 0) {
                  // If there are selected options, highlight the first
                  $selected.first().trigger('mouseenter');
              } else {
                  // If there are no selected options, highlight the first option
                  // in the dropdown
                  $options.first().trigger('mouseenter');
              }

              this.ensureHighlightVisible();
          };

          Results.prototype.setClasses = function () {
              var self = this;

              this.data.current(function (selected) {
                  var selectedIds = $.map(selected, function (s) {
                      return s.id.toString();
                  });

                  var $options = self.$results
                    .find('.select2-results__option[aria-selected]');

                  $options.each(function () {
                      var $option = $(this);

                      var item = $.data(this, 'data');

                      // id needs to be converted to a string when comparing
                      var id = '' + item.id;

                      if ((item.element != null && item.element.selected) ||
                          (item.element == null && $.inArray(id, selectedIds) > -1)) {
                          $option.attr('aria-selected', 'true');
                      } else {
                          $option.attr('aria-selected', 'false');
                      }
                  });

              });
          };

          Results.prototype.showLoading = function (params) {
              this.hideLoading();

              var loadingMore = this.options.get('translations').get('searching');

              var loading = {
                  disabled: true,
                  loading: true,
                  text: loadingMore(params)
              };
              var $loading = this.option(loading);
              $loading.className += ' loading-results';

              this.$results.prepend($loading);
          };

          Results.prototype.hideLoading = function () {
              this.$results.find('.loading-results').remove();
          };

          Results.prototype.option = function (data) {
              var option = document.createElement('li');
              option.className = 'select2-results__option';

              var attrs = {
                  'role': 'treeitem',
                  'aria-selected': 'false'
              };

              if (data.disabled) {
                  delete attrs['aria-selected'];
                  attrs['aria-disabled'] = 'true';
              }

              if (data.id == null) {
                  delete attrs['aria-selected'];
              }

              if (data._resultId != null) {
                  option.id = data._resultId;
              }

              if (data.title) {
                  option.title = data.title;
              }

              if (data.children) {
                  attrs.role = 'group';
                  attrs['aria-label'] = data.text;
                  delete attrs['aria-selected'];
              }

              for (var attr in attrs) {
                  var val = attrs[attr];

                  option.setAttribute(attr, val);
              }

              if (data.children) {
                  var $option = $(option);

                  var label = document.createElement('strong');
                  label.className = 'select2-results__group';

                  var $label = $(label);
                  this.template(data, label, this.mode);

                  var $children = [];

                  for (var c = 0; c < data.children.length; c++) {
                      var child = data.children[c];

                      var $child = this.option(child);

                      $children.push($child);
                  }

                  var $childrenContainer = $('<ul></ul>', {
                      'class': 'select2-results__options select2-results__options--nested'
                  });

                  $childrenContainer.append($children);

                  $option.append(label);
                  $option.append($childrenContainer);
              } else {
                  this.template(data, option, this.mode);
              }

              $.data(option, 'data', data);

              return option;
          };

          Results.prototype.bind = function (container, $container) {
              var self = this;

              var id = container.id + '-results';

              this.$results.attr('id', id);

              container.on('results:all', function (params) {
                  this.mode = params.query.term ? 'query' : '';
                  self.clear();
                  self.append(params.data);

                  if (container.isOpen()) {
                      self.setClasses();
                      self.highlightFirstItem();
                  }
              });

              container.on('results:append', function (params) {
                  this.mode = params._type;
                  self.append(params.data);

                  if (container.isOpen()) {
                      self.setClasses();
                  }
              });

              container.on('query', function (params) {
                  self.hideMessages();
                  self.showLoading(params);
              });

              container.on('select', function () {
                  if (!container.isOpen()) {
                      return;
                  }

                  self.setClasses();
                  self.highlightFirstItem();
              });

              container.on('unselect', function () {
                  if (!container.isOpen()) {
                      return;
                  }

                  self.setClasses();
                  self.highlightFirstItem();
              });

              container.on('open', function () {
                  // When the dropdown is open, aria-expended="true"
                  self.$results.attr('aria-expanded', 'true');
                  self.$results.attr('aria-hidden', 'false');

                  self.setClasses();
                  self.ensureHighlightVisible();
              });

              container.on('close', function () {
                  // When the dropdown is closed, aria-expended="false"
                  self.$results.attr('aria-expanded', 'false');
                  self.$results.attr('aria-hidden', 'true');
                  self.$results.removeAttr('aria-activedescendant');
              });

              container.on('results:toggle', function () {
                  var $highlighted = self.getHighlightedResults();

                  if ($highlighted.length === 0) {
                      return;
                  }

                  $highlighted.trigger('mouseup');
              });

              container.on('results:select', function () {
                  var $highlighted = self.getHighlightedResults();

                  if ($highlighted.length === 0) {
                      return;
                  }

                  var data = $highlighted.data('data');

                  if ($highlighted.attr('aria-selected') == 'true') {
                      self.trigger('close', {});
                  } else {
                      self.trigger('select', {
                          data: data
                      });
                  }
              });

              container.on('results:previous', function () {
                  var $highlighted = self.getHighlightedResults();

                  var $options = self.$results.find('[aria-selected]');

                  var currentIndex = $options.index($highlighted);

                  // If we are already at te top, don't move further
                  if (currentIndex === 0) {
                      return;
                  }

                  var nextIndex = currentIndex - 1;

                  // If none are highlighted, highlight the first
                  if ($highlighted.length === 0) {
                      nextIndex = 0;
                  }

                  var $next = $options.eq(nextIndex);

                  $next.trigger('mouseenter');

                  var currentOffset = self.$results.offset().top;
                  var nextTop = $next.offset().top;
                  var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);

                  if (nextIndex === 0) {
                      self.$results.scrollTop(0);
                  } else if (nextTop - currentOffset < 0) {
                      self.$results.scrollTop(nextOffset);
                  }
              });

              container.on('results:next', function () {
                  var $highlighted = self.getHighlightedResults();

                  var $options = self.$results.find('[aria-selected]');

                  var currentIndex = $options.index($highlighted);

                  var nextIndex = currentIndex + 1;

                  // If we are at the last option, stay there
                  if (nextIndex >= $options.length) {
                      return;
                  }

                  var $next = $options.eq(nextIndex);

                  $next.trigger('mouseenter');

                  var currentOffset = self.$results.offset().top +
                    self.$results.outerHeight(false);
                  var nextBottom = $next.offset().top + $next.outerHeight(false);
                  var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;

                  if (nextIndex === 0) {
                      self.$results.scrollTop(0);
                  } else if (nextBottom > currentOffset) {
                      self.$results.scrollTop(nextOffset);
                  }
              });

              container.on('results:focus', function (params) {
                  params.element.addClass('select2-results__option--highlighted');
              });

              container.on('results:message', function (params) {
                  self.displayMessage(params);
              });

              if ($.fn.mousewheel) {
                  this.$results.on('mousewheel', function (e) {
                      var top = self.$results.scrollTop();

                      var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;

                      var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
                      var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();

                      if (isAtTop) {
                          self.$results.scrollTop(0);

                          e.preventDefault();
                          e.stopPropagation();
                      } else if (isAtBottom) {
                          self.$results.scrollTop(
                            self.$results.get(0).scrollHeight - self.$results.height()
                          );

                          e.preventDefault();
                          e.stopPropagation();
                      }
                  });
              }

              this.$results.on('mouseup', '.select2-results__option[aria-selected]',
                function (evt) {
                    var $this = $(this);

                    var data = $this.data('data');

                    if ($this.attr('aria-selected') === 'true') {
                        if (self.options.get('multiple')) {
                            self.trigger('unselect', {
                                originalEvent: evt,
                                data: data
                            });
                        } else {
                            self.trigger('close', {});
                        }

                        return;
                    }

                    self.trigger('select', {
                        originalEvent: evt,
                        data: data
                    });
                });

              this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
                function (evt) {
                    var data = $(this).data('data');

                    self.getHighlightedResults()
                        .removeClass('select2-results__option--highlighted');

                    self.trigger('results:focus', {
                        data: data,
                        element: $(this)
                    });
                });
          };

          Results.prototype.getHighlightedResults = function () {
              var $highlighted = this.$results
              .find('.select2-results__option--highlighted');

              return $highlighted;
          };

          Results.prototype.destroy = function () {
              this.$results.remove();
          };

          Results.prototype.ensureHighlightVisible = function () {
              var $highlighted = this.getHighlightedResults();

              if ($highlighted.length === 0) {
                  return;
              }

              var $options = this.$results.find('[aria-selected]');

              var currentIndex = $options.index($highlighted);

              var currentOffset = this.$results.offset().top;
              var nextTop = $highlighted.offset().top;
              var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);

              var offsetDelta = nextTop - currentOffset;
              nextOffset -= $highlighted.outerHeight(false) * 2;

              if (currentIndex <= 2) {
                  this.$results.scrollTop(0);
              } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
                  this.$results.scrollTop(nextOffset);
              }
          };

          Results.prototype.template = function (result, container) {
              var template = this.options.get('templateResult');
              var escapeMarkup = this.options.get('escapeMarkup');

              var content = template(result, container, this.data.container.mode);

              if (content == null) {
                  container.style.display = 'none';
              } else if (typeof content === 'string') {
                  container.innerHTML = escapeMarkup(content);
              } else {
                  $(container).append(content);
              }
          };

          return Results;
      });

      S2.define('select2/keys', [

      ], function () {
          var KEYS = {
              BACKSPACE: 8,
              TAB: 9,
              ENTER: 13,
              SHIFT: 16,
              CTRL: 17,
              ALT: 18,
              ESC: 27,
              SPACE: 32,
              PAGE_UP: 33,
              PAGE_DOWN: 34,
              END: 35,
              HOME: 36,
              LEFT: 37,
              UP: 38,
              RIGHT: 39,
              DOWN: 40,
              DELETE: 46
          };

          return KEYS;
      });

      S2.define('select2/selection/base', [
        'jquery',
        '../utils',
        '../keys'
      ], function ($, Utils, KEYS) {
          function BaseSelection($element, options) {
              this.$element = $element;
              this.options = options;

              BaseSelection.__super__.constructor.call(this);
          }

          Utils.Extend(BaseSelection, Utils.Observable);

          BaseSelection.prototype.render = function () {
              var $selection = $(
                '<span class="select2-selection" role="combobox" ' +
                ' aria-haspopup="true" aria-expanded="false">' +
                '</span>'
              );

              this._tabindex = 0;

              if (this.$element.data('old-tabindex') != null) {
                  this._tabindex = this.$element.data('old-tabindex');
              } else if (this.$element.attr('tabindex') != null) {
                  this._tabindex = this.$element.attr('tabindex');
              }

              $selection.attr('title', this.$element.attr('title'));
              $selection.attr('tabindex', this._tabindex);

              this.$selection = $selection;

              return $selection;
          };

          BaseSelection.prototype.bind = function (container, $container) {
              var self = this;

              var id = container.id + '-container';
              var resultsId = container.id + '-results';

              this.container = container;

              this.$selection.on('focus', function (evt) {
                  self.trigger('focus', evt);
              });

              this.$selection.on('blur', function (evt) {
                  self._handleBlur(evt);
              });

              this.$selection.on('keydown', function (evt) {
                  self.trigger('keypress', evt);

                  if (evt.which === KEYS.SPACE) {
                      evt.preventDefault();
                  }
              });

              container.on('results:focus', function (params) {
                  self.$selection.attr('aria-activedescendant', params.data._resultId);
              });

              container.on('selection:update', function (params) {
                  self.update(params.data);
              });

              container.on('open', function () {
                  // When the dropdown is open, aria-expanded="true"
                  self.$selection.attr('aria-expanded', 'true');
                  self.$selection.attr('aria-owns', resultsId);

                  self._attachCloseHandler(container);
              });

              container.on('close', function () {
                  // When the dropdown is closed, aria-expanded="false"
                  self.$selection.attr('aria-expanded', 'false');
                  self.$selection.removeAttr('aria-activedescendant');
                  self.$selection.removeAttr('aria-owns');

                  self.$selection.focus();

                  self._detachCloseHandler(container);
              });

              container.on('enable', function () {
                  self.$selection.attr('tabindex', self._tabindex);
              });

              container.on('disable', function () {
                  self.$selection.attr('tabindex', '-1');
              });
          };

          BaseSelection.prototype._handleBlur = function (evt) {
              var self = this;

              // This needs to be delayed as the active element is the body when the tab
              // key is pressed, possibly along with others.
              window.setTimeout(function () {
                  // Don't trigger `blur` if the focus is still in the selection
                  if (
                    (document.activeElement == self.$selection[0]) ||
                    ($.contains(self.$selection[0], document.activeElement))
                  ) {
                      return;
                  }

                  self.trigger('blur', evt);
              }, 1);
          };

          BaseSelection.prototype._attachCloseHandler = function (container) {
              var self = this;

              $(document.body).on('mousedown.select2.' + container.id, function (e) {
                  var $target = $(e.target);

                  var $select = $target.closest('.select2');

                  var $all = $('.select2.select2-container--open');

                  $all.each(function () {
                      var $this = $(this);

                      if (this == $select[0]) {
                          return;
                      }

                      var $element = $this.data('element');

                      $element.select2('close');
                  });
              });
          };

          BaseSelection.prototype._detachCloseHandler = function (container) {
              $(document.body).off('mousedown.select2.' + container.id);
          };

          BaseSelection.prototype.position = function ($selection, $container) {
              var $selectionContainer = $container.find('.selection');
              $selectionContainer.append($selection);
          };

          BaseSelection.prototype.destroy = function () {
              this._detachCloseHandler(this.container);
          };

          BaseSelection.prototype.update = function (data) {
              throw new Error('The `update` method must be defined in child classes.');
          };

          return BaseSelection;
      });

      S2.define('select2/selection/single', [
        'jquery',
        './base',
        '../utils',
        '../keys'
      ], function ($, BaseSelection, Utils, KEYS) {
          function SingleSelection() {
              SingleSelection.__super__.constructor.apply(this, arguments);
          }

          Utils.Extend(SingleSelection, BaseSelection);

          SingleSelection.prototype.render = function () {
              var $selection = SingleSelection.__super__.render.call(this);

              $selection.addClass('select2-selection--single');

              $selection.html(
                '<span class="select2-selection__rendered"></span>' +
                '<span class="select2-selection__arrow" role="presentation">' +
                  '<b role="presentation"></b>' +
                '</span>'
              );

              return $selection;
          };

          SingleSelection.prototype.bind = function (container, $container) {
              var self = this;

              SingleSelection.__super__.bind.apply(this, arguments);

              var id = container.id + '-container';

              this.$selection.find('.select2-selection__rendered').attr('id', id);
              this.$selection.attr('aria-labelledby', id);

              this.$selection.on('mousedown', function (evt) {
                  // Only respond to left clicks
                  if (evt.which !== 1) {
                      return;
                  }

                  self.trigger('toggle', {
                      originalEvent: evt
                  });
              });

              this.$selection.on('focus', function (evt) {
                  // User focuses on the container
              });

              this.$selection.on('blur', function (evt) {
                  // User exits the container
              });

              container.on('focus', function (evt) {
                  if (!container.isOpen()) {
                      self.$selection.focus();
                  }
              });

              container.on('selection:update', function (params) {
                  self.update(params.data);
              });
          };

          SingleSelection.prototype.clear = function () {
              this.$selection.find('.select2-selection__rendered').empty();
          };

          SingleSelection.prototype.display = function (data, container) {
              var template = this.options.get('templateSelection');
              var escapeMarkup = this.options.get('escapeMarkup');

              return escapeMarkup(template(data, container));
          };

          SingleSelection.prototype.selectionContainer = function () {
              return $('<span></span>');
          };

          SingleSelection.prototype.update = function (data) {
              if (data.length === 0) {
                  this.clear();
                  return;
              }

              var selection = data[0];

              var $rendered = this.$selection.find('.select2-selection__rendered');
              var formatted = this.display(selection, $rendered);

              $rendered.empty().append(formatted);
              $rendered.prop('title', selection.title || selection.text);
          };

          return SingleSelection;
      });

      S2.define('select2/selection/multiple', [
        'jquery',
        './base',
        '../utils'
      ], function ($, BaseSelection, Utils) {
          function MultipleSelection($element, options) {
              MultipleSelection.__super__.constructor.apply(this, arguments);
          }

          Utils.Extend(MultipleSelection, BaseSelection);

          MultipleSelection.prototype.render = function () {
              var $selection = MultipleSelection.__super__.render.call(this);

              $selection.addClass('select2-selection--multiple');

              $selection.html(
                '<ul class="select2-selection__rendered"></ul>'
              );

              return $selection;
          };

          MultipleSelection.prototype.bind = function (container, $container) {
              var self = this;

              MultipleSelection.__super__.bind.apply(this, arguments);

              this.$selection.on('click', function (evt) {
                  self.trigger('toggle', {
                      originalEvent: evt
                  });
              });

              this.$selection.on(
                'click',
                '.select2-selection__choice__remove',
                function (evt) {
                    // Ignore the event if it is disabled
                    if (self.options.get('disabled')) {
                        return;
                    }

                    var $remove = $(this);
                    var $selection = $remove.parent();

                    var data = $selection.data('data');

                    self.trigger('unselect', {
                        originalEvent: evt,
                        data: data
                    });
                }
              );
          };

          MultipleSelection.prototype.clear = function () {
              this.$selection.find('.select2-selection__rendered').empty();
          };

          MultipleSelection.prototype.display = function (data, container) {
              var template = this.options.get('templateSelection');
              var escapeMarkup = this.options.get('escapeMarkup');

              return escapeMarkup(template(data, container));
          };

          MultipleSelection.prototype.selectionContainer = function () {
              var $container = $(
                '<li class="select2-selection__choice">' +
                  '<span class="select2-selection__choice__remove" role="presentation">' +
                    '&times;' +
                  '</span>' +
                '</li>'
              );

              return $container;
          };

          MultipleSelection.prototype.update = function (data) {
              this.clear();

              if (data.length === 0) {
                  return;
              }

              var $selections = [];

              for (var d = 0; d < data.length; d++) {
                  var selection = data[d];

                  var $selection = this.selectionContainer();
                  var formatted = this.display(selection, $selection);

                  $selection.append(formatted);
                  $selection.prop('title', selection.title || selection.text);

                  $selection.data('data', selection);

                  $selections.push($selection);
              }

              var $rendered = this.$selection.find('.select2-selection__rendered');

              Utils.appendMany($rendered, $selections);
          };

          return MultipleSelection;
      });

      S2.define('select2/selection/placeholder', [
        '../utils'
      ], function (Utils) {
          function Placeholder(decorated, $element, options) {
              this.placeholder = this.normalizePlaceholder(options.get('placeholder'));

              decorated.call(this, $element, options);
          }

          Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
              if (typeof placeholder === 'string') {
                  placeholder = {
                      id: '',
                      text: placeholder
                  };
              }

              return placeholder;
          };

          Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
              var $placeholder = this.selectionContainer();

              $placeholder.html(this.display(placeholder));
              $placeholder.addClass('select2-selection__placeholder')
                          .removeClass('select2-selection__choice');

              return $placeholder;
          };

          Placeholder.prototype.update = function (decorated, data) {
              var singlePlaceholder = (
                data.length == 1 && data[0].id != this.placeholder.id
              );
              var multipleSelections = data.length > 1;

              if (multipleSelections || singlePlaceholder) {
                  return decorated.call(this, data);
              }

              this.clear();

              var $placeholder = this.createPlaceholder(this.placeholder);

              this.$selection.find('.select2-selection__rendered').append($placeholder);
          };

          return Placeholder;
      });

      S2.define('select2/selection/allowClear', [
        'jquery',
        '../keys'
      ], function ($, KEYS) {
          function AllowClear() { }

          AllowClear.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              if (this.placeholder == null) {
                  if (this.options.get('debug') && window.console && console.error) {
                      console.error(
                        'Select2: The `allowClear` option should be used in combination ' +
                        'with the `placeholder` option.'
                      );
                  }
              }

              this.$selection.on('mousedown', '.select2-selection__clear',
                function (evt) {
                    self._handleClear(evt);
                });

              container.on('keypress', function (evt) {
                  self._handleKeyboardClear(evt, container);
              });
          };

          AllowClear.prototype._handleClear = function (_, evt) {
              // Ignore the event if it is disabled
              if (this.options.get('disabled')) {
                  return;
              }

              var $clear = this.$selection.find('.select2-selection__clear');

              // Ignore the event if nothing has been selected
              if ($clear.length === 0) {
                  return;
              }

              evt.stopPropagation();

              var data = $clear.data('data');

              for (var d = 0; d < data.length; d++) {
                  var unselectData = {
                      data: data[d]
                  };

                  // Trigger the `unselect` event, so people can prevent it from being
                  // cleared.
                  this.trigger('unselect', unselectData);

                  // If the event was prevented, don't clear it out.
                  if (unselectData.prevented) {
                      return;
                  }
              }

              this.$element.val(this.placeholder.id).trigger('change');

              this.trigger('toggle', {});
          };

          AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
              if (container.isOpen()) {
                  return;
              }

              if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
                  this._handleClear(evt);
              }
          };

          AllowClear.prototype.update = function (decorated, data) {
              decorated.call(this, data);

              if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
                  data.length === 0) {
                  return;
              }

              var $remove = $(
                '<span class="select2-selection__clear">' +
                  '&times;' +
                '</span>'
              );
              $remove.data('data', data);

              this.$selection.find('.select2-selection__rendered').prepend($remove);
          };

          return AllowClear;
      });

      S2.define('select2/selection/search', [
        'jquery',
        '../utils',
        '../keys'
      ], function ($, Utils, KEYS) {
          function Search(decorated, $element, options) {
              decorated.call(this, $element, options);
          }

          Search.prototype.render = function (decorated) {
              var $search = $(
                '<li class="select2-search select2-search--inline">' +
                  '<input class="select2-search__field" type="search" tabindex="-1"' +
                  ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
                  ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
                '</li>'
              );

              this.$searchContainer = $search;
              this.$search = $search.find('input');

              var $rendered = decorated.call(this);

              this._transferTabIndex();

              return $rendered;
          };

          Search.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              container.on('open', function () {
                  self.$search.trigger('focus');
              });

              container.on('close', function () {
                  self.$search.val('');
                  self.$search.removeAttr('aria-activedescendant');
                  self.$search.trigger('focus');
              });

              container.on('enable', function () {
                  self.$search.prop('disabled', false);

                  self._transferTabIndex();
              });

              container.on('disable', function () {
                  self.$search.prop('disabled', true);
              });

              container.on('focus', function (evt) {
                  self.$search.trigger('focus');
              });

              container.on('results:focus', function (params) {
                  self.$search.attr('aria-activedescendant', params.id);
              });

              this.$selection.on('focusin', '.select2-search--inline', function (evt) {
                  self.trigger('focus', evt);
              });

              this.$selection.on('focusout', '.select2-search--inline', function (evt) {
                  self._handleBlur(evt);
              });

              this.$selection.on('keydown', '.select2-search--inline', function (evt) {
                  evt.stopPropagation();

                  self.trigger('keypress', evt);

                  self._keyUpPrevented = evt.isDefaultPrevented();

                  var key = evt.which;

                  if (key === KEYS.BACKSPACE && self.$search.val() === '') {
                      var $previousChoice = self.$searchContainer
                        .prev('.select2-selection__choice');

                      if ($previousChoice.length > 0) {
                          var item = $previousChoice.data('data');

                          self.searchRemoveChoice(item);

                          evt.preventDefault();
                      }
                  }
              });

              // Try to detect the IE version should the `documentMode` property that
              // is stored on the document. This is only implemented in IE and is
              // slightly cleaner than doing a user agent check.
              // This property is not available in Edge, but Edge also doesn't have
              // this bug.
              var msie = document.documentMode;
              var disableInputEvents = msie && msie <= 11;

              // Workaround for browsers which do not support the `input` event
              // This will prevent double-triggering of events for browsers which support
              // both the `keyup` and `input` events.
              this.$selection.on(
                'input.searchcheck',
                '.select2-search--inline',
                function (evt) {
                    // IE will trigger the `input` event when a placeholder is used on a
                    // search box. To get around this issue, we are forced to ignore all
                    // `input` events in IE and keep using `keyup`.
                    if (disableInputEvents) {
                        self.$selection.off('input.search input.searchcheck');
                        return;
                    }

                    // Unbind the duplicated `keyup` event
                    self.$selection.off('keyup.search');
                }
              );

              this.$selection.on(
                'keyup.search input.search',
                '.select2-search--inline',
                function (evt) {
                    // IE will trigger the `input` event when a placeholder is used on a
                    // search box. To get around this issue, we are forced to ignore all
                    // `input` events in IE and keep using `keyup`.
                    if (disableInputEvents && evt.type === 'input') {
                        self.$selection.off('input.search input.searchcheck');
                        return;
                    }

                    var key = evt.which;

                    // We can freely ignore events from modifier keys
                    if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
                        return;
                    }

                    // Tabbing will be handled during the `keydown` phase
                    if (key == KEYS.TAB) {
                        return;
                    }

                    self.handleSearch(evt);
                }
              );
          };

          /**
           * This method will transfer the tabindex attribute from the rendered
           * selection to the search box. This allows for the search box to be used as
           * the primary focus instead of the selection container.
           *
           * @private
           */
          Search.prototype._transferTabIndex = function (decorated) {
              this.$search.attr('tabindex', this.$selection.attr('tabindex'));
              this.$selection.attr('tabindex', '-1');
          };

          Search.prototype.createPlaceholder = function (decorated, placeholder) {
              this.$search.attr('placeholder', placeholder.text);
          };

          Search.prototype.update = function (decorated, data) {
              var searchHadFocus = this.$search[0] == document.activeElement;

              this.$search.attr('placeholder', '');

              decorated.call(this, data);

              this.$selection.find('.select2-selection__rendered')
                             .append(this.$searchContainer);

              this.resizeSearch();
              if (searchHadFocus) {
                  this.$search.focus();
              }
          };

          Search.prototype.handleSearch = function () {
              this.resizeSearch();

              if (!this._keyUpPrevented) {
                  var input = this.$search.val();

                  this.trigger('query', {
                      term: input
                  });
              }

              this._keyUpPrevented = false;
          };

          Search.prototype.searchRemoveChoice = function (decorated, item) {
              this.trigger('unselect', {
                  data: item
              });

              this.$search.val(item.text);
              this.handleSearch();
          };

          Search.prototype.resizeSearch = function () {
              this.$search.css('width', '25px');

              var width = '';

              if (this.$search.attr('placeholder') !== '') {
                  width = this.$selection.find('.select2-selection__rendered').innerWidth();
              } else {
                  var minimumWidth = this.$search.val().length + 1;

                  width = (minimumWidth * 0.75) + 'em';
              }

              this.$search.css('width', width);
          };

          return Search;
      });

      S2.define('select2/selection/eventRelay', [
        'jquery'
      ], function ($) {
          function EventRelay() { }

          EventRelay.prototype.bind = function (decorated, container, $container) {
              var self = this;
              var relayEvents = [
                'open', 'opening',
                'close', 'closing',
                'select', 'selecting',
                'unselect', 'unselecting'
              ];

              var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting'];

              decorated.call(this, container, $container);

              container.on('*', function (name, params) {
                  // Ignore events that should not be relayed
                  if ($.inArray(name, relayEvents) === -1) {
                      return;
                  }

                  // The parameters should always be an object
                  params = params || {};

                  // Generate the jQuery event for the Select2 event
                  var evt = $.Event('select2:' + name, {
                      params: params
                  });

                  self.$element.trigger(evt);

                  // Only handle preventable events if it was one
                  if ($.inArray(name, preventableEvents) === -1) {
                      return;
                  }

                  params.prevented = evt.isDefaultPrevented();
              });
          };

          return EventRelay;
      });

      S2.define('select2/translation', [
        'jquery',
        'require'
      ], function ($, require) {
          function Translation(dict) {
              this.dict = dict || {};
          }

          Translation.prototype.all = function () {
              return this.dict;
          };

          Translation.prototype.get = function (key) {
              return this.dict[key];
          };

          Translation.prototype.extend = function (translation) {
              this.dict = $.extend({}, translation.all(), this.dict);
          };

          // Static functions

          Translation._cache = {};

          Translation.loadPath = function (path) {
              if (!(path in Translation._cache)) {
                  var translations = require(path);

                  Translation._cache[path] = translations;
              }

              return new Translation(Translation._cache[path]);
          };

          return Translation;
      });

      S2.define('select2/diacritics', [

      ], function () {
          var diacritics = {
              '\u24B6': 'A',
              '\uFF21': 'A',
              '\u00C0': 'A',
              '\u00C1': 'A',
              '\u00C2': 'A',
              '\u1EA6': 'A',
              '\u1EA4': 'A',
              '\u1EAA': 'A',
              '\u1EA8': 'A',
              '\u00C3': 'A',
              '\u0100': 'A',
              '\u0102': 'A',
              '\u1EB0': 'A',
              '\u1EAE': 'A',
              '\u1EB4': 'A',
              '\u1EB2': 'A',
              '\u0226': 'A',
              '\u01E0': 'A',
              '\u00C4': 'A',
              '\u01DE': 'A',
              '\u1EA2': 'A',
              '\u00C5': 'A',
              '\u01FA': 'A',
              '\u01CD': 'A',
              '\u0200': 'A',
              '\u0202': 'A',
              '\u1EA0': 'A',
              '\u1EAC': 'A',
              '\u1EB6': 'A',
              '\u1E00': 'A',
              '\u0104': 'A',
              '\u023A': 'A',
              '\u2C6F': 'A',
              '\uA732': 'AA',
              '\u00C6': 'AE',
              '\u01FC': 'AE',
              '\u01E2': 'AE',
              '\uA734': 'AO',
              '\uA736': 'AU',
              '\uA738': 'AV',
              '\uA73A': 'AV',
              '\uA73C': 'AY',
              '\u24B7': 'B',
              '\uFF22': 'B',
              '\u1E02': 'B',
              '\u1E04': 'B',
              '\u1E06': 'B',
              '\u0243': 'B',
              '\u0182': 'B',
              '\u0181': 'B',
              '\u24B8': 'C',
              '\uFF23': 'C',
              '\u0106': 'C',
              '\u0108': 'C',
              '\u010A': 'C',
              '\u010C': 'C',
              '\u00C7': 'C',
              '\u1E08': 'C',
              '\u0187': 'C',
              '\u023B': 'C',
              '\uA73E': 'C',
              '\u24B9': 'D',
              '\uFF24': 'D',
              '\u1E0A': 'D',
              '\u010E': 'D',
              '\u1E0C': 'D',
              '\u1E10': 'D',
              '\u1E12': 'D',
              '\u1E0E': 'D',
              '\u0110': 'D',
              '\u018B': 'D',
              '\u018A': 'D',
              '\u0189': 'D',
              '\uA779': 'D',
              '\u01F1': 'DZ',
              '\u01C4': 'DZ',
              '\u01F2': 'Dz',
              '\u01C5': 'Dz',
              '\u24BA': 'E',
              '\uFF25': 'E',
              '\u00C8': 'E',
              '\u00C9': 'E',
              '\u00CA': 'E',
              '\u1EC0': 'E',
              '\u1EBE': 'E',
              '\u1EC4': 'E',
              '\u1EC2': 'E',
              '\u1EBC': 'E',
              '\u0112': 'E',
              '\u1E14': 'E',
              '\u1E16': 'E',
              '\u0114': 'E',
              '\u0116': 'E',
              '\u00CB': 'E',
              '\u1EBA': 'E',
              '\u011A': 'E',
              '\u0204': 'E',
              '\u0206': 'E',
              '\u1EB8': 'E',
              '\u1EC6': 'E',
              '\u0228': 'E',
              '\u1E1C': 'E',
              '\u0118': 'E',
              '\u1E18': 'E',
              '\u1E1A': 'E',
              '\u0190': 'E',
              '\u018E': 'E',
              '\u24BB': 'F',
              '\uFF26': 'F',
              '\u1E1E': 'F',
              '\u0191': 'F',
              '\uA77B': 'F',
              '\u24BC': 'G',
              '\uFF27': 'G',
              '\u01F4': 'G',
              '\u011C': 'G',
              '\u1E20': 'G',
              '\u011E': 'G',
              '\u0120': 'G',
              '\u01E6': 'G',
              '\u0122': 'G',
              '\u01E4': 'G',
              '\u0193': 'G',
              '\uA7A0': 'G',
              '\uA77D': 'G',
              '\uA77E': 'G',
              '\u24BD': 'H',
              '\uFF28': 'H',
              '\u0124': 'H',
              '\u1E22': 'H',
              '\u1E26': 'H',
              '\u021E': 'H',
              '\u1E24': 'H',
              '\u1E28': 'H',
              '\u1E2A': 'H',
              '\u0126': 'H',
              '\u2C67': 'H',
              '\u2C75': 'H',
              '\uA78D': 'H',
              '\u24BE': 'I',
              '\uFF29': 'I',
              '\u00CC': 'I',
              '\u00CD': 'I',
              '\u00CE': 'I',
              '\u0128': 'I',
              '\u012A': 'I',
              '\u012C': 'I',
              '\u0130': 'I',
              '\u00CF': 'I',
              '\u1E2E': 'I',
              '\u1EC8': 'I',
              '\u01CF': 'I',
              '\u0208': 'I',
              '\u020A': 'I',
              '\u1ECA': 'I',
              '\u012E': 'I',
              '\u1E2C': 'I',
              '\u0197': 'I',
              '\u24BF': 'J',
              '\uFF2A': 'J',
              '\u0134': 'J',
              '\u0248': 'J',
              '\u24C0': 'K',
              '\uFF2B': 'K',
              '\u1E30': 'K',
              '\u01E8': 'K',
              '\u1E32': 'K',
              '\u0136': 'K',
              '\u1E34': 'K',
              '\u0198': 'K',
              '\u2C69': 'K',
              '\uA740': 'K',
              '\uA742': 'K',
              '\uA744': 'K',
              '\uA7A2': 'K',
              '\u24C1': 'L',
              '\uFF2C': 'L',
              '\u013F': 'L',
              '\u0139': 'L',
              '\u013D': 'L',
              '\u1E36': 'L',
              '\u1E38': 'L',
              '\u013B': 'L',
              '\u1E3C': 'L',
              '\u1E3A': 'L',
              '\u0141': 'L',
              '\u023D': 'L',
              '\u2C62': 'L',
              '\u2C60': 'L',
              '\uA748': 'L',
              '\uA746': 'L',
              '\uA780': 'L',
              '\u01C7': 'LJ',
              '\u01C8': 'Lj',
              '\u24C2': 'M',
              '\uFF2D': 'M',
              '\u1E3E': 'M',
              '\u1E40': 'M',
              '\u1E42': 'M',
              '\u2C6E': 'M',
              '\u019C': 'M',
              '\u24C3': 'N',
              '\uFF2E': 'N',
              '\u01F8': 'N',
              '\u0143': 'N',
              '\u00D1': 'N',
              '\u1E44': 'N',
              '\u0147': 'N',
              '\u1E46': 'N',
              '\u0145': 'N',
              '\u1E4A': 'N',
              '\u1E48': 'N',
              '\u0220': 'N',
              '\u019D': 'N',
              '\uA790': 'N',
              '\uA7A4': 'N',
              '\u01CA': 'NJ',
              '\u01CB': 'Nj',
              '\u24C4': 'O',
              '\uFF2F': 'O',
              '\u00D2': 'O',
              '\u00D3': 'O',
              '\u00D4': 'O',
              '\u1ED2': 'O',
              '\u1ED0': 'O',
              '\u1ED6': 'O',
              '\u1ED4': 'O',
              '\u00D5': 'O',
              '\u1E4C': 'O',
              '\u022C': 'O',
              '\u1E4E': 'O',
              '\u014C': 'O',
              '\u1E50': 'O',
              '\u1E52': 'O',
              '\u014E': 'O',
              '\u022E': 'O',
              '\u0230': 'O',
              '\u00D6': 'O',
              '\u022A': 'O',
              '\u1ECE': 'O',
              '\u0150': 'O',
              '\u01D1': 'O',
              '\u020C': 'O',
              '\u020E': 'O',
              '\u01A0': 'O',
              '\u1EDC': 'O',
              '\u1EDA': 'O',
              '\u1EE0': 'O',
              '\u1EDE': 'O',
              '\u1EE2': 'O',
              '\u1ECC': 'O',
              '\u1ED8': 'O',
              '\u01EA': 'O',
              '\u01EC': 'O',
              '\u00D8': 'O',
              '\u01FE': 'O',
              '\u0186': 'O',
              '\u019F': 'O',
              '\uA74A': 'O',
              '\uA74C': 'O',
              '\u01A2': 'OI',
              '\uA74E': 'OO',
              '\u0222': 'OU',
              '\u24C5': 'P',
              '\uFF30': 'P',
              '\u1E54': 'P',
              '\u1E56': 'P',
              '\u01A4': 'P',
              '\u2C63': 'P',
              '\uA750': 'P',
              '\uA752': 'P',
              '\uA754': 'P',
              '\u24C6': 'Q',
              '\uFF31': 'Q',
              '\uA756': 'Q',
              '\uA758': 'Q',
              '\u024A': 'Q',
              '\u24C7': 'R',
              '\uFF32': 'R',
              '\u0154': 'R',
              '\u1E58': 'R',
              '\u0158': 'R',
              '\u0210': 'R',
              '\u0212': 'R',
              '\u1E5A': 'R',
              '\u1E5C': 'R',
              '\u0156': 'R',
              '\u1E5E': 'R',
              '\u024C': 'R',
              '\u2C64': 'R',
              '\uA75A': 'R',
              '\uA7A6': 'R',
              '\uA782': 'R',
              '\u24C8': 'S',
              '\uFF33': 'S',
              '\u1E9E': 'S',
              '\u015A': 'S',
              '\u1E64': 'S',
              '\u015C': 'S',
              '\u1E60': 'S',
              '\u0160': 'S',
              '\u1E66': 'S',
              '\u1E62': 'S',
              '\u1E68': 'S',
              '\u0218': 'S',
              '\u015E': 'S',
              '\u2C7E': 'S',
              '\uA7A8': 'S',
              '\uA784': 'S',
              '\u24C9': 'T',
              '\uFF34': 'T',
              '\u1E6A': 'T',
              '\u0164': 'T',
              '\u1E6C': 'T',
              '\u021A': 'T',
              '\u0162': 'T',
              '\u1E70': 'T',
              '\u1E6E': 'T',
              '\u0166': 'T',
              '\u01AC': 'T',
              '\u01AE': 'T',
              '\u023E': 'T',
              '\uA786': 'T',
              '\uA728': 'TZ',
              '\u24CA': 'U',
              '\uFF35': 'U',
              '\u00D9': 'U',
              '\u00DA': 'U',
              '\u00DB': 'U',
              '\u0168': 'U',
              '\u1E78': 'U',
              '\u016A': 'U',
              '\u1E7A': 'U',
              '\u016C': 'U',
              '\u00DC': 'U',
              '\u01DB': 'U',
              '\u01D7': 'U',
              '\u01D5': 'U',
              '\u01D9': 'U',
              '\u1EE6': 'U',
              '\u016E': 'U',
              '\u0170': 'U',
              '\u01D3': 'U',
              '\u0214': 'U',
              '\u0216': 'U',
              '\u01AF': 'U',
              '\u1EEA': 'U',
              '\u1EE8': 'U',
              '\u1EEE': 'U',
              '\u1EEC': 'U',
              '\u1EF0': 'U',
              '\u1EE4': 'U',
              '\u1E72': 'U',
              '\u0172': 'U',
              '\u1E76': 'U',
              '\u1E74': 'U',
              '\u0244': 'U',
              '\u24CB': 'V',
              '\uFF36': 'V',
              '\u1E7C': 'V',
              '\u1E7E': 'V',
              '\u01B2': 'V',
              '\uA75E': 'V',
              '\u0245': 'V',
              '\uA760': 'VY',
              '\u24CC': 'W',
              '\uFF37': 'W',
              '\u1E80': 'W',
              '\u1E82': 'W',
              '\u0174': 'W',
              '\u1E86': 'W',
              '\u1E84': 'W',
              '\u1E88': 'W',
              '\u2C72': 'W',
              '\u24CD': 'X',
              '\uFF38': 'X',
              '\u1E8A': 'X',
              '\u1E8C': 'X',
              '\u24CE': 'Y',
              '\uFF39': 'Y',
              '\u1EF2': 'Y',
              '\u00DD': 'Y',
              '\u0176': 'Y',
              '\u1EF8': 'Y',
              '\u0232': 'Y',
              '\u1E8E': 'Y',
              '\u0178': 'Y',
              '\u1EF6': 'Y',
              '\u1EF4': 'Y',
              '\u01B3': 'Y',
              '\u024E': 'Y',
              '\u1EFE': 'Y',
              '\u24CF': 'Z',
              '\uFF3A': 'Z',
              '\u0179': 'Z',
              '\u1E90': 'Z',
              '\u017B': 'Z',
              '\u017D': 'Z',
              '\u1E92': 'Z',
              '\u1E94': 'Z',
              '\u01B5': 'Z',
              '\u0224': 'Z',
              '\u2C7F': 'Z',
              '\u2C6B': 'Z',
              '\uA762': 'Z',
              '\u24D0': 'a',
              '\uFF41': 'a',
              '\u1E9A': 'a',
              '\u00E0': 'a',
              '\u00E1': 'a',
              '\u00E2': 'a',
              '\u1EA7': 'a',
              '\u1EA5': 'a',
              '\u1EAB': 'a',
              '\u1EA9': 'a',
              '\u00E3': 'a',
              '\u0101': 'a',
              '\u0103': 'a',
              '\u1EB1': 'a',
              '\u1EAF': 'a',
              '\u1EB5': 'a',
              '\u1EB3': 'a',
              '\u0227': 'a',
              '\u01E1': 'a',
              '\u00E4': 'a',
              '\u01DF': 'a',
              '\u1EA3': 'a',
              '\u00E5': 'a',
              '\u01FB': 'a',
              '\u01CE': 'a',
              '\u0201': 'a',
              '\u0203': 'a',
              '\u1EA1': 'a',
              '\u1EAD': 'a',
              '\u1EB7': 'a',
              '\u1E01': 'a',
              '\u0105': 'a',
              '\u2C65': 'a',
              '\u0250': 'a',
              '\uA733': 'aa',
              '\u00E6': 'ae',
              '\u01FD': 'ae',
              '\u01E3': 'ae',
              '\uA735': 'ao',
              '\uA737': 'au',
              '\uA739': 'av',
              '\uA73B': 'av',
              '\uA73D': 'ay',
              '\u24D1': 'b',
              '\uFF42': 'b',
              '\u1E03': 'b',
              '\u1E05': 'b',
              '\u1E07': 'b',
              '\u0180': 'b',
              '\u0183': 'b',
              '\u0253': 'b',
              '\u24D2': 'c',
              '\uFF43': 'c',
              '\u0107': 'c',
              '\u0109': 'c',
              '\u010B': 'c',
              '\u010D': 'c',
              '\u00E7': 'c',
              '\u1E09': 'c',
              '\u0188': 'c',
              '\u023C': 'c',
              '\uA73F': 'c',
              '\u2184': 'c',
              '\u24D3': 'd',
              '\uFF44': 'd',
              '\u1E0B': 'd',
              '\u010F': 'd',
              '\u1E0D': 'd',
              '\u1E11': 'd',
              '\u1E13': 'd',
              '\u1E0F': 'd',
              '\u0111': 'd',
              '\u018C': 'd',
              '\u0256': 'd',
              '\u0257': 'd',
              '\uA77A': 'd',
              '\u01F3': 'dz',
              '\u01C6': 'dz',
              '\u24D4': 'e',
              '\uFF45': 'e',
              '\u00E8': 'e',
              '\u00E9': 'e',
              '\u00EA': 'e',
              '\u1EC1': 'e',
              '\u1EBF': 'e',
              '\u1EC5': 'e',
              '\u1EC3': 'e',
              '\u1EBD': 'e',
              '\u0113': 'e',
              '\u1E15': 'e',
              '\u1E17': 'e',
              '\u0115': 'e',
              '\u0117': 'e',
              '\u00EB': 'e',
              '\u1EBB': 'e',
              '\u011B': 'e',
              '\u0205': 'e',
              '\u0207': 'e',
              '\u1EB9': 'e',
              '\u1EC7': 'e',
              '\u0229': 'e',
              '\u1E1D': 'e',
              '\u0119': 'e',
              '\u1E19': 'e',
              '\u1E1B': 'e',
              '\u0247': 'e',
              '\u025B': 'e',
              '\u01DD': 'e',
              '\u24D5': 'f',
              '\uFF46': 'f',
              '\u1E1F': 'f',
              '\u0192': 'f',
              '\uA77C': 'f',
              '\u24D6': 'g',
              '\uFF47': 'g',
              '\u01F5': 'g',
              '\u011D': 'g',
              '\u1E21': 'g',
              '\u011F': 'g',
              '\u0121': 'g',
              '\u01E7': 'g',
              '\u0123': 'g',
              '\u01E5': 'g',
              '\u0260': 'g',
              '\uA7A1': 'g',
              '\u1D79': 'g',
              '\uA77F': 'g',
              '\u24D7': 'h',
              '\uFF48': 'h',
              '\u0125': 'h',
              '\u1E23': 'h',
              '\u1E27': 'h',
              '\u021F': 'h',
              '\u1E25': 'h',
              '\u1E29': 'h',
              '\u1E2B': 'h',
              '\u1E96': 'h',
              '\u0127': 'h',
              '\u2C68': 'h',
              '\u2C76': 'h',
              '\u0265': 'h',
              '\u0195': 'hv',
              '\u24D8': 'i',
              '\uFF49': 'i',
              '\u00EC': 'i',
              '\u00ED': 'i',
              '\u00EE': 'i',
              '\u0129': 'i',
              '\u012B': 'i',
              '\u012D': 'i',
              '\u00EF': 'i',
              '\u1E2F': 'i',
              '\u1EC9': 'i',
              '\u01D0': 'i',
              '\u0209': 'i',
              '\u020B': 'i',
              '\u1ECB': 'i',
              '\u012F': 'i',
              '\u1E2D': 'i',
              '\u0268': 'i',
              '\u0131': 'i',
              '\u24D9': 'j',
              '\uFF4A': 'j',
              '\u0135': 'j',
              '\u01F0': 'j',
              '\u0249': 'j',
              '\u24DA': 'k',
              '\uFF4B': 'k',
              '\u1E31': 'k',
              '\u01E9': 'k',
              '\u1E33': 'k',
              '\u0137': 'k',
              '\u1E35': 'k',
              '\u0199': 'k',
              '\u2C6A': 'k',
              '\uA741': 'k',
              '\uA743': 'k',
              '\uA745': 'k',
              '\uA7A3': 'k',
              '\u24DB': 'l',
              '\uFF4C': 'l',
              '\u0140': 'l',
              '\u013A': 'l',
              '\u013E': 'l',
              '\u1E37': 'l',
              '\u1E39': 'l',
              '\u013C': 'l',
              '\u1E3D': 'l',
              '\u1E3B': 'l',
              '\u017F': 'l',
              '\u0142': 'l',
              '\u019A': 'l',
              '\u026B': 'l',
              '\u2C61': 'l',
              '\uA749': 'l',
              '\uA781': 'l',
              '\uA747': 'l',
              '\u01C9': 'lj',
              '\u24DC': 'm',
              '\uFF4D': 'm',
              '\u1E3F': 'm',
              '\u1E41': 'm',
              '\u1E43': 'm',
              '\u0271': 'm',
              '\u026F': 'm',
              '\u24DD': 'n',
              '\uFF4E': 'n',
              '\u01F9': 'n',
              '\u0144': 'n',
              '\u00F1': 'n',
              '\u1E45': 'n',
              '\u0148': 'n',
              '\u1E47': 'n',
              '\u0146': 'n',
              '\u1E4B': 'n',
              '\u1E49': 'n',
              '\u019E': 'n',
              '\u0272': 'n',
              '\u0149': 'n',
              '\uA791': 'n',
              '\uA7A5': 'n',
              '\u01CC': 'nj',
              '\u24DE': 'o',
              '\uFF4F': 'o',
              '\u00F2': 'o',
              '\u00F3': 'o',
              '\u00F4': 'o',
              '\u1ED3': 'o',
              '\u1ED1': 'o',
              '\u1ED7': 'o',
              '\u1ED5': 'o',
              '\u00F5': 'o',
              '\u1E4D': 'o',
              '\u022D': 'o',
              '\u1E4F': 'o',
              '\u014D': 'o',
              '\u1E51': 'o',
              '\u1E53': 'o',
              '\u014F': 'o',
              '\u022F': 'o',
              '\u0231': 'o',
              '\u00F6': 'o',
              '\u022B': 'o',
              '\u1ECF': 'o',
              '\u0151': 'o',
              '\u01D2': 'o',
              '\u020D': 'o',
              '\u020F': 'o',
              '\u01A1': 'o',
              '\u1EDD': 'o',
              '\u1EDB': 'o',
              '\u1EE1': 'o',
              '\u1EDF': 'o',
              '\u1EE3': 'o',
              '\u1ECD': 'o',
              '\u1ED9': 'o',
              '\u01EB': 'o',
              '\u01ED': 'o',
              '\u00F8': 'o',
              '\u01FF': 'o',
              '\u0254': 'o',
              '\uA74B': 'o',
              '\uA74D': 'o',
              '\u0275': 'o',
              '\u01A3': 'oi',
              '\u0223': 'ou',
              '\uA74F': 'oo',
              '\u24DF': 'p',
              '\uFF50': 'p',
              '\u1E55': 'p',
              '\u1E57': 'p',
              '\u01A5': 'p',
              '\u1D7D': 'p',
              '\uA751': 'p',
              '\uA753': 'p',
              '\uA755': 'p',
              '\u24E0': 'q',
              '\uFF51': 'q',
              '\u024B': 'q',
              '\uA757': 'q',
              '\uA759': 'q',
              '\u24E1': 'r',
              '\uFF52': 'r',
              '\u0155': 'r',
              '\u1E59': 'r',
              '\u0159': 'r',
              '\u0211': 'r',
              '\u0213': 'r',
              '\u1E5B': 'r',
              '\u1E5D': 'r',
              '\u0157': 'r',
              '\u1E5F': 'r',
              '\u024D': 'r',
              '\u027D': 'r',
              '\uA75B': 'r',
              '\uA7A7': 'r',
              '\uA783': 'r',
              '\u24E2': 's',
              '\uFF53': 's',
              '\u00DF': 's',
              '\u015B': 's',
              '\u1E65': 's',
              '\u015D': 's',
              '\u1E61': 's',
              '\u0161': 's',
              '\u1E67': 's',
              '\u1E63': 's',
              '\u1E69': 's',
              '\u0219': 's',
              '\u015F': 's',
              '\u023F': 's',
              '\uA7A9': 's',
              '\uA785': 's',
              '\u1E9B': 's',
              '\u24E3': 't',
              '\uFF54': 't',
              '\u1E6B': 't',
              '\u1E97': 't',
              '\u0165': 't',
              '\u1E6D': 't',
              '\u021B': 't',
              '\u0163': 't',
              '\u1E71': 't',
              '\u1E6F': 't',
              '\u0167': 't',
              '\u01AD': 't',
              '\u0288': 't',
              '\u2C66': 't',
              '\uA787': 't',
              '\uA729': 'tz',
              '\u24E4': 'u',
              '\uFF55': 'u',
              '\u00F9': 'u',
              '\u00FA': 'u',
              '\u00FB': 'u',
              '\u0169': 'u',
              '\u1E79': 'u',
              '\u016B': 'u',
              '\u1E7B': 'u',
              '\u016D': 'u',
              '\u00FC': 'u',
              '\u01DC': 'u',
              '\u01D8': 'u',
              '\u01D6': 'u',
              '\u01DA': 'u',
              '\u1EE7': 'u',
              '\u016F': 'u',
              '\u0171': 'u',
              '\u01D4': 'u',
              '\u0215': 'u',
              '\u0217': 'u',
              '\u01B0': 'u',
              '\u1EEB': 'u',
              '\u1EE9': 'u',
              '\u1EEF': 'u',
              '\u1EED': 'u',
              '\u1EF1': 'u',
              '\u1EE5': 'u',
              '\u1E73': 'u',
              '\u0173': 'u',
              '\u1E77': 'u',
              '\u1E75': 'u',
              '\u0289': 'u',
              '\u24E5': 'v',
              '\uFF56': 'v',
              '\u1E7D': 'v',
              '\u1E7F': 'v',
              '\u028B': 'v',
              '\uA75F': 'v',
              '\u028C': 'v',
              '\uA761': 'vy',
              '\u24E6': 'w',
              '\uFF57': 'w',
              '\u1E81': 'w',
              '\u1E83': 'w',
              '\u0175': 'w',
              '\u1E87': 'w',
              '\u1E85': 'w',
              '\u1E98': 'w',
              '\u1E89': 'w',
              '\u2C73': 'w',
              '\u24E7': 'x',
              '\uFF58': 'x',
              '\u1E8B': 'x',
              '\u1E8D': 'x',
              '\u24E8': 'y',
              '\uFF59': 'y',
              '\u1EF3': 'y',
              '\u00FD': 'y',
              '\u0177': 'y',
              '\u1EF9': 'y',
              '\u0233': 'y',
              '\u1E8F': 'y',
              '\u00FF': 'y',
              '\u1EF7': 'y',
              '\u1E99': 'y',
              '\u1EF5': 'y',
              '\u01B4': 'y',
              '\u024F': 'y',
              '\u1EFF': 'y',
              '\u24E9': 'z',
              '\uFF5A': 'z',
              '\u017A': 'z',
              '\u1E91': 'z',
              '\u017C': 'z',
              '\u017E': 'z',
              '\u1E93': 'z',
              '\u1E95': 'z',
              '\u01B6': 'z',
              '\u0225': 'z',
              '\u0240': 'z',
              '\u2C6C': 'z',
              '\uA763': 'z',
              '\u0386': '\u0391',
              '\u0388': '\u0395',
              '\u0389': '\u0397',
              '\u038A': '\u0399',
              '\u03AA': '\u0399',
              '\u038C': '\u039F',
              '\u038E': '\u03A5',
              '\u03AB': '\u03A5',
              '\u038F': '\u03A9',
              '\u03AC': '\u03B1',
              '\u03AD': '\u03B5',
              '\u03AE': '\u03B7',
              '\u03AF': '\u03B9',
              '\u03CA': '\u03B9',
              '\u0390': '\u03B9',
              '\u03CC': '\u03BF',
              '\u03CD': '\u03C5',
              '\u03CB': '\u03C5',
              '\u03B0': '\u03C5',
              '\u03C9': '\u03C9',
              '\u03C2': '\u03C3'
          };

          return diacritics;
      });

      S2.define('select2/data/base', [
        '../utils'
      ], function (Utils) {
          function BaseAdapter($element, options) {
              BaseAdapter.__super__.constructor.call(this);
          }

          Utils.Extend(BaseAdapter, Utils.Observable);

          BaseAdapter.prototype.current = function (callback) {
              throw new Error('The `current` method must be defined in child classes.');
          };

          BaseAdapter.prototype.query = function (params, callback) {
              throw new Error('The `query` method must be defined in child classes.');
          };

          BaseAdapter.prototype.bind = function (container, $container) {
              // Can be implemented in subclasses
          };

          BaseAdapter.prototype.destroy = function () {
              // Can be implemented in subclasses
          };

          BaseAdapter.prototype.generateResultId = function (container, data) {
              var id = container.id + '-result-';

              id += Utils.generateChars(4);

              if (data.id != null) {
                  id += '-' + data.id.toString();
              } else {
                  id += '-' + Utils.generateChars(4);
              }
              return id;
          };

          return BaseAdapter;
      });

      S2.define('select2/data/select', [
        './base',
        '../utils',
        'jquery'
      ], function (BaseAdapter, Utils, $) {
          function SelectAdapter($element, options) {
              this.$element = $element;
              this.options = options;

              SelectAdapter.__super__.constructor.call(this);
          }

          Utils.Extend(SelectAdapter, BaseAdapter);

          SelectAdapter.prototype.current = function (callback) {
              var data = [];
              var self = this;

              this.$element.find(':selected').each(function () {
                  var $option = $(this);

                  var option = self.item($option);

                  data.push(option);
              });

              callback(data);
          };

          SelectAdapter.prototype.select = function (data) {
              var self = this;

              data.selected = true;

              // If data.element is a DOM node, use it instead
              if ($(data.element).is('option')) {
                  data.element.selected = true;

                  this.$element.trigger('change');

                  return;
              }

              if (this.$element.prop('multiple')) {
                  this.current(function (currentData) {
                      var val = [];

                      data = [data];
                      data.push.apply(data, currentData);

                      for (var d = 0; d < data.length; d++) {
                          var id = data[d].id;

                          if ($.inArray(id, val) === -1) {
                              val.push(id);
                          }
                      }

                      self.$element.val(val);
                      self.$element.trigger('change');
                  });
              } else {
                  var val = data.id;

                  this.$element.val(val);
                  this.$element.trigger('change');
              }
          };

          SelectAdapter.prototype.unselect = function (data) {
              var self = this;

              if (!this.$element.prop('multiple')) {
                  return;
              }

              data.selected = false;

              if ($(data.element).is('option')) {
                  data.element.selected = false;

                  this.$element.trigger('change');

                  return;
              }

              this.current(function (currentData) {
                  var val = [];

                  for (var d = 0; d < currentData.length; d++) {
                      var id = currentData[d].id;

                      if (id !== data.id && $.inArray(id, val) === -1) {
                          val.push(id);
                      }
                  }

                  self.$element.val(val);

                  self.$element.trigger('change');
              });
          };

          SelectAdapter.prototype.bind = function (container, $container) {
              var self = this;

              this.container = container;

              container.on('select', function (params) {
                  self.select(params.data);
              });

              container.on('unselect', function (params) {
                  self.unselect(params.data);
              });
          };

          SelectAdapter.prototype.destroy = function () {
              // Remove anything added to child elements
              this.$element.find('*').each(function () {
                  // Remove any custom data set by Select2
                  $.removeData(this, 'data');
              });
          };

          SelectAdapter.prototype.query = function (params, callback) {
              var data = [];
              var self = this;

              var $options = this.$element.children();

              $options.each(function () {
                  var $option = $(this);

                  if (!$option.is('option') && !$option.is('optgroup')) {
                      return;
                  }

                  var option = self.item($option);

                  var matches = self.matches(params, option);

                  if (matches !== null) {
                      data.push(matches);
                  }
              });

              callback({
                  results: data
              });
          };

          SelectAdapter.prototype.addOptions = function ($options) {
              Utils.appendMany(this.$element, $options);
          };

          SelectAdapter.prototype.option = function (data) {
              var option;

              if (data.children) {
                  option = document.createElement('optgroup');
                  option.label = data.text;
              } else {
                  option = document.createElement('option');

                  if (option.textContent !== undefined) {
                      option.textContent = data.text;
                  } else {
                      option.innerText = data.text;
                  }
              }

              if (data.id) {
                  option.value = data.id;
              }

              if (data.disabled) {
                  option.disabled = true;
              }

              if (data.selected) {
                  option.selected = true;
              }

              if (data.title) {
                  option.title = data.title;
              }

              var $option = $(option);

              var normalizedData = this._normalizeItem(data);
              normalizedData.element = option;

              // Override the option's data with the combined data
              $.data(option, 'data', normalizedData);

              return $option;
          };

          SelectAdapter.prototype.item = function ($option) {
              var data = {};

              data = $.data($option[0], 'data');

              if (data != null) {
                  return data;
              }

              if ($option.is('option')) {
                  data = {
                      id: $option.val(),
                      text: $option.text(),
                      disabled: $option.prop('disabled'),
                      selected: $option.prop('selected'),
                      title: $option.prop('title')
                  };
              } else if ($option.is('optgroup')) {
                  data = {
                      text: $option.prop('label'),
                      children: [],
                      title: $option.prop('title')
                  };

                  var $children = $option.children('option');
                  var children = [];

                  for (var c = 0; c < $children.length; c++) {
                      var $child = $($children[c]);

                      var child = this.item($child);

                      children.push(child);
                  }

                  data.children = children;
              }

              data = this._normalizeItem(data);
              data.element = $option[0];

              $.data($option[0], 'data', data);

              return data;
          };

          SelectAdapter.prototype._normalizeItem = function (item) {
              if (!$.isPlainObject(item)) {
                  item = {
                      id: item,
                      text: item
                  };
              }

              item = $.extend({}, {
                  text: ''
              }, item);

              var defaults = {
                  selected: false,
                  disabled: false
              };

              if (item.id != null) {
                  item.id = item.id.toString();
              }

              if (item.text != null) {
                  item.text = item.text.toString();
              }

              if (item._resultId == null && item.id && this.container != null) {
                  item._resultId = this.generateResultId(this.container, item);
              }

              return $.extend({}, defaults, item);
          };

          SelectAdapter.prototype.matches = function (params, data) {
              var matcher = this.options.get('matcher');

              return matcher(params, data);
          };

          return SelectAdapter;
      });

      S2.define('select2/data/array', [
        './select',
        '../utils',
        'jquery'
      ], function (SelectAdapter, Utils, $) {
          function ArrayAdapter($element, options) {
              var data = options.get('data') || [];

              ArrayAdapter.__super__.constructor.call(this, $element, options);

              this.addOptions(this.convertToOptions(data));
          }

          Utils.Extend(ArrayAdapter, SelectAdapter);

          ArrayAdapter.prototype.select = function (data) {
              var $option = this.$element.find('option').filter(function (i, elm) {
                  return elm.value == data.id.toString();
              });

              if ($option.length === 0) {
                  $option = this.option(data);

                  this.addOptions($option);
              }

              ArrayAdapter.__super__.select.call(this, data);
          };

          ArrayAdapter.prototype.convertToOptions = function (data) {
              var self = this;

              var $existing = this.$element.find('option');
              var existingIds = $existing.map(function () {
                  return self.item($(this)).id;
              }).get();

              var $options = [];

              // Filter out all items except for the one passed in the argument
              function onlyItem(item) {
                  return function () {
                      return $(this).val() == item.id;
                  };
              }

              for (var d = 0; d < data.length; d++) {
                  var item = this._normalizeItem(data[d]);

                  // Skip items which were pre-loaded, only merge the data
                  if ($.inArray(item.id, existingIds) >= 0) {
                      var $existingOption = $existing.filter(onlyItem(item));

                      var existingData = this.item($existingOption);
                      var newData = $.extend(true, {}, item, existingData);

                      var $newOption = this.option(newData);

                      $existingOption.replaceWith($newOption);

                      continue;
                  }

                  var $option = this.option(item);

                  if (item.children) {
                      var $children = this.convertToOptions(item.children);

                      Utils.appendMany($option, $children);
                  }

                  $options.push($option);
              }

              return $options;
          };

          return ArrayAdapter;
      });



      S2.define('select2/data/ajax', [
        './array',
        '../utils',
        'jquery'
      ], function (ArrayAdapter, Utils, $) {
          function AjaxAdapter($element, options) {
              this.ajaxOptions = this._applyDefaults(options.get('ajax'));

              if (this.ajaxOptions.processResults != null) {
                  this.processResults = this.ajaxOptions.processResults;
              }

              AjaxAdapter.__super__.constructor.call(this, $element, options);
          }

          Utils.Extend(AjaxAdapter, ArrayAdapter);

          AjaxAdapter.prototype._applyDefaults = function (options) {
              var defaults = {
                  data: function (params) {
                      return $.extend({}, params, {
                          q: params.term
                      });
                  },
                  transport: function (params, success, failure) {
                      var $request = $.ajax(params);

                      $request.then(success);
                      $request.fail(failure);

                      return $request;
                  }
              };

              return $.extend({}, defaults, options, true);
          };

          AjaxAdapter.prototype.processResults = function (results) {
              return results;
          };

          AjaxAdapter.prototype.query = function (params, callback) {
              var matches = [];
              var self = this;

              if (this._request != null) {
                  // JSONP requests cannot always be aborted
                  if ($.isFunction(this._request.abort)) {
                      this._request.abort();
                  }

                  this._request = null;
              }

              var options = $.extend({
                  type: 'GET'
              }, this.ajaxOptions);

              if (typeof options.url === 'function') {
                  options.url = options.url.call(this.$element, params);
              }

              if (typeof options.data === 'function') {
                  options.data = options.data.call(this.$element, params);
              }

              function request() {
                  var $request = options.transport(options, function (data) {
                      var results = self.processResults(data, params);

                      if (self.options.get('debug') && window.console && console.error) {
                          // Check to make sure that the response included a `results` key.
                          if (!results || !results.results || !$.isArray(results.results)) {
                              console.error(
                                'Select2: The AJAX results did not return an array in the ' +
                                '`results` key of the response.'
                              );
                          }
                      }

                      callback(results);
                  }, function () {
                      // Attempt to detect if a request was aborted
                      // Only works if the transport exposes a status property
                      if ($request.status && $request.status === '0') {
                          return;
                      }

                      self.trigger('results:message', {
                          message: 'errorLoading'
                      });
                  });

                  self._request = $request;
              }

              if (this.ajaxOptions.delay && params.term != null) {
                  if (this._queryTimeout) {
                      window.clearTimeout(this._queryTimeout);
                  }

                  this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
              } else {
                  request();
              }
          };

          return AjaxAdapter;
      });


      S2.define('select2/data/excercise', [
              'select2/data/array',
              'select2/utils'
      ], function (ArrayData, Utils) {

          function TrainingExcerciseAdapter($element, options) {
              TrainingExcerciseAdapter.__super__.constructor.call(this, $element, options);
              this.trainingExcercise = options.get('trainingExcercise');
          }

          Utils.Extend(TrainingExcerciseAdapter, ArrayData);

          TrainingExcerciseAdapter.prototype.current = function (callback) {
              var self = this;
              var data = [];
              var dets = self.trainingExcercise.ExcerciseDetails();
              if (dets && dets.Name) {
                  dets.id = dets.Id;
                  dets.text = dets.Name;
                  data.push(dets);
                  //self.trainingExcercise.ExerciseTypeId(dets.Id);
              } else {
                  data.push({ id: 0, text: translations.ChooseExercise });
              }
              callback(data);
          };

          TrainingExcerciseAdapter.prototype.query = function (params, callback) {
              var self = this;
              self.trainingExcercise.SearchForTypes(function () {
                  var data = {
                      results: self.trainingExcercise.AvailableExercises(),
                      text: function (item) { return item.Name + item.Tags.join() + item.Description; }
                  };

                  callback(data);
              }, params.term);

          };

          TrainingExcerciseAdapter.prototype.select = function (data) {
              var self = this;

              data.selected = true;

              // If data.element is a DOM node, use it instead
              if ($(data.element).is('option')) {
                  data.element.selected = true;

                  this.$element.trigger('change');

                  return;
              }

              var val = data.id;

              self.trainingExcercise.ExerciseTypeId(data.id);

              this.$element.val(val);
              this.$element.trigger('change');

          };

          return TrainingExcerciseAdapter;
      });


      S2.define('select2/data/hashtags', [
              'select2/data/ajax',
              'select2/utils'
      ], function (AjaxData, Utils) {

          function HashTagsAdapter($element, options) {
              HashTagsAdapter.__super__.constructor.call(this, $element, options);
              this.hashTags = options.get('hashTags');
          }

          Utils.Extend(HashTagsAdapter, AjaxData);

          HashTagsAdapter.prototype.current = function (callback) {
              var self = this;
              var data = [];
              var dets = self.hashTags();
              var item;
              for (var i = 0, len = dets.length; i < len; i++) {
                  item = dets[i];
                  data.push({ id: item, text: item });
              }

              callback(data);
          };


          HashTagsAdapter.prototype.select = function (data) {
              var self = this;

              data.selected = true;

              var txt = data.text.replace(/#/g, '');

              self.hashTags.push(txt);
          };

          HashTagsAdapter.prototype.unselect = function (data) {
              var self = this;

              data.selected = false;

              if ($(data.element).is('option')) {
                  data.element.selected = false;

                  this.$element.trigger('change');

                  return;
              }

              var idx = self.hashTags().indexOf(data.id);
              if (idx > -1) {
                  self.hashTags().splice(idx, 1);
              }

              this.current(function (currentData) {
                  var val = [];

                  for (var d = 0; d < currentData.length; d++) {
                      val.push(currentData[d].id);
                  }

                  self.$element.val(val);
                  self.$element.trigger('change');
              });
          };


          return HashTagsAdapter;
      });


      S2.define('select2/data/tags', [
        'jquery'
      ], function ($) {
          function Tags(decorated, $element, options) {
              var tags = options.get('tags');

              var createTag = options.get('createTag');

              if (createTag !== undefined) {
                  this.createTag = createTag;
              }

              var insertTag = options.get('insertTag');

              if (insertTag !== undefined) {
                  this.insertTag = insertTag;
              }

              decorated.call(this, $element, options);

              if ($.isArray(tags)) {
                  for (var t = 0; t < tags.length; t++) {
                      var tag = tags[t];
                      var item = this._normalizeItem(tag);

                      var $option = this.option(item);

                      this.$element.append($option);
                  }
              }
          }

          Tags.prototype.query = function (decorated, params, callback) {
              var self = this;

              this._removeOldTags();

              if (params.term == null || params.page != null) {
                  decorated.call(this, params, callback);
                  return;
              }

              function wrapper(obj, child) {
                  var data = obj.results;

                  for (var i = 0; i < data.length; i++) {
                      var option = data[i];

                      var checkChildren = (
                        option.children != null &&
                        !wrapper({
                            results: option.children
                        }, true)
                      );

                      var checkText = option.text === params.term;

                      if (checkText || checkChildren) {
                          if (child) {
                              return false;
                          }

                          obj.data = data;
                          callback(obj);

                          return;
                      }
                  }

                  if (child) {
                      return true;
                  }

                  var tag = self.createTag(params);

                  if (tag != null) {
                      var $option = self.option(tag);
                      $option.attr('data-select2-tag', true);

                      self.addOptions([$option]);

                      self.insertTag(data, tag);
                  }

                  obj.results = data;

                  callback(obj);
              }

              decorated.call(this, params, wrapper);
          };

          Tags.prototype.createTag = function (decorated, params) {
              var term = $.trim(params.term);

              if (term === '') {
                  return null;
              }

              return {
                  id: term,
                  text: term
              };
          };

          Tags.prototype.insertTag = function (_, data, tag) {
              data.unshift(tag);
          };

          Tags.prototype._removeOldTags = function (_) {
              var tag = this._lastTag;

              var $options = this.$element.find('option[data-select2-tag]');

              $options.each(function () {
                  if (this.selected) {
                      return;
                  }

                  $(this).remove();
              });
          };

          return Tags;
      });

      S2.define('select2/data/tokenizer', [
        'jquery'
      ], function ($) {
          function Tokenizer(decorated, $element, options) {
              var tokenizer = options.get('tokenizer');

              if (tokenizer !== undefined) {
                  this.tokenizer = tokenizer;
              }

              decorated.call(this, $element, options);
          }

          Tokenizer.prototype.bind = function (decorated, container, $container) {
              decorated.call(this, container, $container);

              this.$search = container.dropdown.$search || container.selection.$search ||
                $container.find('.select2-search__field');
          };

          Tokenizer.prototype.query = function (decorated, params, callback) {
              var self = this;

              function createAndSelect(data) {
                  // Normalize the data object so we can use it for checks
                  var item = self._normalizeItem(data);

                  // Check if the data object already exists as a tag
                  // Select it if it doesn't
                  var $existingOptions = self.$element.find('option').filter(function () {
                      return $(this).val() === item.id;
                  });

                  // If an existing option wasn't found for it, create the option
                  if (!$existingOptions.length) {
                      var $option = self.option(item);
                      $option.attr('data-select2-tag', true);

                      self._removeOldTags();
                      self.addOptions([$option]);
                  }

                  // Select the item, now that we know there is an option for it
                  select(item);
              }

              function select(data) {
                  self.trigger('select', {
                      data: data
                  });
              }

              params.term = params.term || '';

              var tokenData = this.tokenizer(params, this.options, createAndSelect);

              if (tokenData.term !== params.term) {
                  // Replace the search term if we have the search box
                  if (this.$search.length) {
                      this.$search.val(tokenData.term);
                      this.$search.focus();
                  }

                  params.term = tokenData.term;
              }

              decorated.call(this, params, callback);
          };

          Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
              var separators = options.get('tokenSeparators') || [];
              var term = params.term;
              var i = 0;

              var createTag = this.createTag || function (params) {
                  return {
                      id: params.term,
                      text: params.term
                  };
              };

              while (i < term.length) {
                  var termChar = term[i];

                  if ($.inArray(termChar, separators) === -1) {
                      i++;

                      continue;
                  }

                  var part = term.substr(0, i);
                  var partParams = $.extend({}, params, {
                      term: part
                  });

                  var data = createTag(partParams);

                  if (data == null) {
                      i++;
                      continue;
                  }

                  callback(data);

                  // Reset the term to not include the tokenized portion
                  term = term.substr(i + 1) || '';
                  i = 0;
              }

              return {
                  term: term
              };
          };

          return Tokenizer;
      });

      S2.define('select2/data/minimumInputLength', [

      ], function () {
          function MinimumInputLength(decorated, $e, options) {
              this.minimumInputLength = options.get('minimumInputLength');

              decorated.call(this, $e, options);
          }

          MinimumInputLength.prototype.query = function (decorated, params, callback) {
              params.term = params.term || '';

              if (params.term.length < this.minimumInputLength) {
                  this.trigger('results:message', {
                      message: 'inputTooShort',
                      args: {
                          minimum: this.minimumInputLength,
                          input: params.term,
                          params: params
                      }
                  });

                  return;
              }

              decorated.call(this, params, callback);
          };

          return MinimumInputLength;
      });

      S2.define('select2/data/maximumInputLength', [

      ], function () {
          function MaximumInputLength(decorated, $e, options) {
              this.maximumInputLength = options.get('maximumInputLength');

              decorated.call(this, $e, options);
          }

          MaximumInputLength.prototype.query = function (decorated, params, callback) {
              params.term = params.term || '';

              if (this.maximumInputLength > 0 &&
                  params.term.length > this.maximumInputLength) {
                  this.trigger('results:message', {
                      message: 'inputTooLong',
                      args: {
                          maximum: this.maximumInputLength,
                          input: params.term,
                          params: params
                      }
                  });

                  return;
              }

              decorated.call(this, params, callback);
          };

          return MaximumInputLength;
      });

      S2.define('select2/data/maximumSelectionLength', [

      ], function () {
          function MaximumSelectionLength(decorated, $e, options) {
              this.maximumSelectionLength = options.get('maximumSelectionLength');

              decorated.call(this, $e, options);
          }

          MaximumSelectionLength.prototype.query =
            function (decorated, params, callback) {
                var self = this;

                this.current(function (currentData) {
                    var count = currentData != null ? currentData.length : 0;
                    if (self.maximumSelectionLength > 0 &&
                      count >= self.maximumSelectionLength) {
                        self.trigger('results:message', {
                            message: 'maximumSelected',
                            args: {
                                maximum: self.maximumSelectionLength
                            }
                        });
                        return;
                    }
                    decorated.call(self, params, callback);
                });
            };

          return MaximumSelectionLength;
      });

      S2.define('select2/dropdown', [
        'jquery',
        './utils'
      ], function ($, Utils) {
          function Dropdown($element, options) {
              this.$element = $element;
              this.options = options;

              Dropdown.__super__.constructor.call(this);
          }

          Utils.Extend(Dropdown, Utils.Observable);

          Dropdown.prototype.render = function () {
              var $dropdown = $(
                '<span class="select2-dropdown">' +
                  '<span class="select2-results"></span>' +
                '</span>'
              );

              var additionalDropdownClass = this.options.get('additionalDropdownClass');
              if (additionalDropdownClass) {
                  $dropdown.addClass(additionalDropdownClass);
              }

              $dropdown.attr('dir', this.options.get('dir'));

              this.$dropdown = $dropdown;

              return $dropdown;
          };

          Dropdown.prototype.bind = function () {
              // Should be implemented in subclasses
          };

          Dropdown.prototype.position = function ($dropdown, $container) {
              // Should be implmented in subclasses
          };

          Dropdown.prototype.destroy = function () {
              // Remove the dropdown from the DOM
              this.$dropdown.remove();
          };

          return Dropdown;
      });

      S2.define('select2/dropdown/search', [
        'jquery',
        '../utils'
      ], function ($, Utils) {
          function Search() { }

          Search.prototype.render = function (decorated) {
              var $rendered = decorated.call(this);

              var $search = $(
                '<span class="select2-search select2-search--dropdown">' +
                  '<input class="select2-search__field" type="search" tabindex="-1"' +
                  ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
                  ' spellcheck="false" role="textbox" />' +
                '</span>'
              );

              this.$searchContainer = $search;
              this.$search = $search.find('input');

              $rendered.prepend($search);

              return $rendered;
          };

          Search.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              this.$search.on('keydown', function (evt) {
                  self.trigger('keypress', evt);

                  self._keyUpPrevented = evt.isDefaultPrevented();
              });

              // Workaround for browsers which do not support the `input` event
              // This will prevent double-triggering of events for browsers which support
              // both the `keyup` and `input` events.
              this.$search.on('input', function (evt) {
                  // Unbind the duplicated `keyup` event
                  $(this).off('keyup');
              });

              this.$search.on('keyup input', function (evt) {
                  self.handleSearch(evt);
              });

              container.on('open', function () {
                  self.$search.attr('tabindex', 0);

                  self.$search.focus();

                  window.setTimeout(function () {
                      self.$search.focus();
                  }, 0);
              });

              container.on('close', function () {
                  self.$search.attr('tabindex', -1);

                  self.$search.val('');
              });

              container.on('focus', function () {
                  if (container.isOpen()) {
                      self.$search.focus();
                  }
              });

              container.on('results:all', function (params) {
                  if (params.query.term == null || params.query.term === '') {
                      var showSearch = self.showSearch(params);

                      if (showSearch) {
                          self.$searchContainer.removeClass('select2-search--hide');
                      } else {
                          self.$searchContainer.addClass('select2-search--hide');
                      }
                  }
              });
          };

          Search.prototype.handleSearch = function (evt) {
              if (!this._keyUpPrevented) {
                  var input = this.$search.val();

                  this.trigger('query', {
                      term: input
                  });
              }

              this._keyUpPrevented = false;
          };

          Search.prototype.showSearch = function (_, params) {
              return true;
          };

          return Search;
      });

      S2.define('select2/dropdown/hidePlaceholder', [

      ], function () {
          function HidePlaceholder(decorated, $element, options, dataAdapter) {
              this.placeholder = this.normalizePlaceholder(options.get('placeholder'));

              decorated.call(this, $element, options, dataAdapter);
          }

          HidePlaceholder.prototype.append = function (decorated, data) {
              data.results = this.removePlaceholder(data.results);

              decorated.call(this, data);
          };

          HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
              if (typeof placeholder === 'string') {
                  placeholder = {
                      id: '',
                      text: placeholder
                  };
              }

              return placeholder;
          };

          HidePlaceholder.prototype.removePlaceholder = function (_, data) {
              var modifiedData = data.slice(0);

              for (var d = data.length - 1; d >= 0; d--) {
                  var item = data[d];

                  if (this.placeholder.id === item.id) {
                      modifiedData.splice(d, 1);
                  }
              }

              return modifiedData;
          };

          return HidePlaceholder;
      });

      S2.define('select2/dropdown/infiniteScroll', [
        'jquery'
      ], function ($) {
          function InfiniteScroll(decorated, $element, options, dataAdapter) {
              this.lastParams = {};

              decorated.call(this, $element, options, dataAdapter);

              this.$loadingMore = this.createLoadingMore();
              this.loading = false;
          }

          InfiniteScroll.prototype.append = function (decorated, data) {
              this.$loadingMore.remove();
              this.loading = false;

              decorated.call(this, data);

              if (this.showLoadingMore(data)) {
                  this.$results.append(this.$loadingMore);
              }
          };

          InfiniteScroll.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              container.on('query', function (params) {
                  self.lastParams = params;
                  self.loading = true;
              });

              container.on('query:append', function (params) {
                  self.lastParams = params;
                  self.loading = true;
              });

              this.$results.on('scroll', function () {
                  var isLoadMoreVisible = $.contains(
                    document.documentElement,
                    self.$loadingMore[0]
                  );

                  if (self.loading || !isLoadMoreVisible) {
                      return;
                  }

                  var currentOffset = self.$results.offset().top +
                    self.$results.outerHeight(false);
                  var loadingMoreOffset = self.$loadingMore.offset().top +
                    self.$loadingMore.outerHeight(false);

                  if (currentOffset + 50 >= loadingMoreOffset) {
                      self.loadMore();
                  }
              });
          };

          InfiniteScroll.prototype.loadMore = function () {
              this.loading = true;

              var params = $.extend({}, { page: 1 }, this.lastParams);

              params.page++;

              this.trigger('query:append', params);
          };

          InfiniteScroll.prototype.showLoadingMore = function (_, data) {
              return data.pagination && data.pagination.more;
          };

          InfiniteScroll.prototype.createLoadingMore = function () {
              var $option = $(
                '<li ' +
                'class="select2-results__option select2-results__option--load-more"' +
                'role="treeitem" aria-disabled="true"></li>'
              );

              var message = this.options.get('translations').get('loadingMore');

              $option.html(message(this.lastParams));

              return $option;
          };

          return InfiniteScroll;
      });

      S2.define('select2/dropdown/attachBody', [
        'jquery',
        '../utils'
      ], function ($, Utils) {
          function AttachBody(decorated, $element, options) {
              this.$dropdownParent = options.get('dropdownParent') || $(document.body);

              decorated.call(this, $element, options);
          }

          AttachBody.prototype.bind = function (decorated, container, $container) {
              var self = this;

              var setupResultsEvents = false;

              decorated.call(this, container, $container);

              container.on('open', function () {
                  self._showDropdown();
                  self._attachPositioningHandler(container);

                  if (!setupResultsEvents) {
                      setupResultsEvents = true;

                      container.on('results:all', function () {
                          self._positionDropdown();
                          self._resizeDropdown();
                      });

                      container.on('results:append', function () {
                          self._positionDropdown();
                          self._resizeDropdown();
                      });
                  }
              });

              container.on('close', function () {
                  self._hideDropdown();
                  self._detachPositioningHandler(container);
              });

              this.$dropdownContainer.on('mousedown', function (evt) {
                  evt.stopPropagation();
              });
          };

          AttachBody.prototype.destroy = function (decorated) {
              decorated.call(this);

              this.$dropdownContainer.remove();
          };

          AttachBody.prototype.position = function (decorated, $dropdown, $container) {
              // Clone all of the container classes
              $dropdown.attr('class', $container.attr('class'));

              $dropdown.removeClass('select2');
              $dropdown.addClass('select2-container--open');

              $dropdown.css({
                  position: 'absolute',
                  top: -999999
              });

              this.$container = $container;
          };

          AttachBody.prototype.render = function (decorated) {
              var $container = $('<span></span>');

              var $dropdown = decorated.call(this);
              $container.append($dropdown);

              this.$dropdownContainer = $container;

              return $container;
          };

          AttachBody.prototype._hideDropdown = function (decorated) {
              this.$dropdownContainer.detach();
          };

          AttachBody.prototype._attachPositioningHandler =
              function (decorated, container) {
                  var self = this;

                  var scrollEvent = 'scroll.select2.' + container.id;
                  var resizeEvent = 'resize.select2.' + container.id;
                  var orientationEvent = 'orientationchange.select2.' + container.id;

                  var $watchers = this.$container.parents().filter(Utils.hasScroll);
                  $watchers.each(function () {
                      $(this).data('select2-scroll-position', {
                          x: $(this).scrollLeft(),
                          y: $(this).scrollTop()
                      });
                  });

                  $watchers.on(scrollEvent, function (ev) {
                      var position = $(this).data('select2-scroll-position');
                      $(this).scrollTop(position.y);
                  });

                  $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
                    function (e) {
                        self._positionDropdown();
                        self._resizeDropdown();
                    });
              };

          AttachBody.prototype._detachPositioningHandler =
              function (decorated, container) {
                  var scrollEvent = 'scroll.select2.' + container.id;
                  var resizeEvent = 'resize.select2.' + container.id;
                  var orientationEvent = 'orientationchange.select2.' + container.id;

                  var $watchers = this.$container.parents().filter(Utils.hasScroll);
                  $watchers.off(scrollEvent);

                  $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
              };

          AttachBody.prototype._positionDropdown = function () {
              var $window = $(window);

              var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
              var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');

              var newDirection = null;

              var offset = this.$container.offset();

              offset.bottom = offset.top + this.$container.outerHeight(false);

              var container = {
                  height: this.$container.outerHeight(false)
              };

              container.top = offset.top;
              container.bottom = offset.top + container.height;

              var dropdown = {
                  height: this.$dropdown.outerHeight(false)
              };

              var viewport = {
                  top: $window.scrollTop(),
                  bottom: $window.scrollTop() + $window.height()
              };

              var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
              var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);

              var css = {
                  left: offset.left,
                  top: container.bottom
              };

              // Determine what the parent element is to use for calciulating the offset
              var $offsetParent = this.$dropdownParent;

              // For statically positoned elements, we need to get the element
              // that is determining the offset
              if ($offsetParent.css('position') === 'static') {
                  $offsetParent = $offsetParent.offsetParent();
              }

              var parentOffset = $offsetParent.offset();

              css.top -= parentOffset.top;
              css.left -= parentOffset.left;

              if (!isCurrentlyAbove && !isCurrentlyBelow) {
                  newDirection = 'below';
              }

              if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
                  newDirection = 'above';
              } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
                  newDirection = 'below';
              }

              if (newDirection == 'above' ||
                (isCurrentlyAbove && newDirection !== 'below')) {
                  css.top = container.top - parentOffset.top - dropdown.height;
              }

              if (newDirection != null) {
                  this.$dropdown
                    .removeClass('select2-dropdown--below select2-dropdown--above')
                    .addClass('select2-dropdown--' + newDirection);
                  this.$container
                    .removeClass('select2-container--below select2-container--above')
                    .addClass('select2-container--' + newDirection);
              }

              this.$dropdownContainer.css(css);
          };

          AttachBody.prototype._resizeDropdown = function () {
              var css = {
                  width: this.$container.outerWidth(false) + 'px'
              };

              if (this.options.get('dropdownAutoWidth')) {
                  css.minWidth = css.width;
                  css.position = 'relative';
                  css.width = 'auto';
              }

              this.$dropdown.css(css);
          };

          AttachBody.prototype._showDropdown = function (decorated) {
              this.$dropdownContainer.appendTo(this.$dropdownParent);

              this._positionDropdown();
              this._resizeDropdown();
          };

          return AttachBody;
      });

      S2.define('select2/dropdown/minimumResultsForSearch', [

      ], function () {
          function countResults(data) {
              var count = 0;

              for (var d = 0; d < data.length; d++) {
                  var item = data[d];

                  if (item.children) {
                      count += countResults(item.children);
                  } else {
                      count++;
                  }
              }

              return count;
          }

          function MinimumResultsForSearch(decorated, $element, options, dataAdapter) {
              this.minimumResultsForSearch = options.get('minimumResultsForSearch');

              if (this.minimumResultsForSearch < 0) {
                  this.minimumResultsForSearch = Infinity;
              }

              decorated.call(this, $element, options, dataAdapter);
          }

          MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
              if (countResults(params.data.results) < this.minimumResultsForSearch) {
                  return false;
              }

              return decorated.call(this, params);
          };

          return MinimumResultsForSearch;
      });

      S2.define('select2/dropdown/selectOnClose', [

      ], function () {
          function SelectOnClose() { }

          SelectOnClose.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              container.on('close', function (params) {
                  self._handleSelectOnClose(params);
              });
          };

          SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
              if (params && params.originalSelect2Event != null) {
                  var event = params.originalSelect2Event;

                  // Don't select an item if the close event was triggered from a select or
                  // unselect event
                  if (event._type === 'select' || event._type === 'unselect') {
                      return;
                  }
              }

              var $highlightedResults = this.getHighlightedResults();

              // Only select highlighted results
              if ($highlightedResults.length < 1) {
                  return;
              }

              var data = $highlightedResults.data('data');

              // Don't re-select already selected resulte
              if (
                (data.element != null && data.element.selected) ||
                (data.element == null && data.selected)
              ) {
                  return;
              }

              this.trigger('select', {
                  data: data
              });
          };

          return SelectOnClose;
      });

      S2.define('select2/dropdown/closeOnSelect', [

      ], function () {
          function CloseOnSelect() { }

          CloseOnSelect.prototype.bind = function (decorated, container, $container) {
              var self = this;

              decorated.call(this, container, $container);

              container.on('select', function (evt) {
                  self._selectTriggered(evt);
              });

              container.on('unselect', function (evt) {
                  self._selectTriggered(evt);
              });
          };

          CloseOnSelect.prototype._selectTriggered = function (_, evt) {
              var originalEvent = evt.originalEvent;

              // Don't close if the control key is being held
              if (originalEvent && originalEvent.ctrlKey) {
                  return;
              }

              this.trigger('close', {
                  originalEvent: originalEvent,
                  originalSelect2Event: evt
              });
          };

          return CloseOnSelect;
      });

      S2.define('select2/i18n/en', [], function () {
          // English
          return {
              errorLoading: function () {
                  return 'The results could not be loaded.';
              },
              inputTooLong: function (args) {
                  var overChars = args.input.length - args.maximum;

                  var message = 'Please delete ' + overChars + ' character';

                  if (overChars != 1) {
                      message += 's';
                  }

                  return message;
              },
              inputTooShort: function (args) {
                  var remainingChars = args.minimum - args.input.length;

                  var message = 'Please enter ' + remainingChars + ' or more characters';

                  return message;
              },
              loadingMore: function () {
                  return 'Loading more results…';
              },
              maximumSelected: function (args) {
                  var message = 'You can only select ' + args.maximum + ' item';

                  if (args.maximum != 1) {
                      message += 's';
                  }

                  return message;
              },
              noResults: function () {
                  return 'No results found';
              },
              searching: function () {
                  return 'Searching…';
              }
          };
      });

      S2.define('select2/defaults', [
        'jquery',
        'require',

        './results',

        './selection/single',
        './selection/multiple',
        './selection/placeholder',
        './selection/allowClear',
        './selection/search',
        './selection/eventRelay',

        './utils',
        './translation',
        './diacritics',

        './data/select',
        './data/array',
        './data/ajax',
        './data/excercise',
        './data/hashtags',
        './data/tags',
        './data/tokenizer',
        './data/minimumInputLength',
        './data/maximumInputLength',
        './data/maximumSelectionLength',

        './dropdown',
        './dropdown/search',
        './dropdown/hidePlaceholder',
        './dropdown/infiniteScroll',
        './dropdown/attachBody',
        './dropdown/minimumResultsForSearch',
        './dropdown/selectOnClose',
        './dropdown/closeOnSelect',

        './i18n/en'
      ], function ($, require,

                   ResultsList,

                   SingleSelection, MultipleSelection, Placeholder, AllowClear,
                   SelectionSearch, EventRelay,

                   Utils, Translation, DIACRITICS,

                   SelectData, ArrayData, AjaxData, TrainingExcerciseAdapter, HashTagsData, Tags, Tokenizer,
                   MinimumInputLength, MaximumInputLength, MaximumSelectionLength,

                   Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
                   AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,

                   EnglishTranslation) {
          function Defaults() {
              this.reset();
          }

          Defaults.prototype.apply = function (options) {
              options = $.extend(true, {}, this.defaults, options);

              if (options.dataAdapter == null) {
                  if (options.hashTags != null) {
                      options.dataAdapter = HashTagsData;
                  } else if (options.ajax != null) {
                      options.dataAdapter = AjaxData;
                  } else if (options.data != null) {
                      options.dataAdapter = ArrayData;
                  } else if (options.trainingExcercise != null) {
                      options.dataAdapter = TrainingExcerciseAdapter;
                  } else  {
                      options.dataAdapter = SelectData;
                  }

                  if (options.minimumInputLength > 0) {
                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        MinimumInputLength
                      );
                  }

                  if (options.maximumInputLength > 0) {
                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        MaximumInputLength
                      );
                  }

                  if (options.maximumSelectionLength > 0) {
                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        MaximumSelectionLength
                      );
                  }

                  if (options.tags) {
                      options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
                  }

                  if (options.tokenSeparators != null || options.tokenizer != null) {
                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        Tokenizer
                      );
                  }

                  if (options.query != null) {
                      var Query = require(options.amdBase + 'compat/query');

                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        Query
                      );
                  }

                  if (options.initSelection != null) {
                      var InitSelection = require(options.amdBase + 'compat/initSelection');

                      options.dataAdapter = Utils.Decorate(
                        options.dataAdapter,
                        InitSelection
                      );
                  }
              }

              if (options.resultsAdapter == null) {
                  options.resultsAdapter = ResultsList;

                  if (options.ajax != null) {
                      options.resultsAdapter = Utils.Decorate(
                        options.resultsAdapter,
                        InfiniteScroll
                      );
                  }

                  if (options.placeholder != null) {
                      options.resultsAdapter = Utils.Decorate(
                        options.resultsAdapter,
                        HidePlaceholder
                      );
                  }

                  if (options.selectOnClose) {
                      options.resultsAdapter = Utils.Decorate(
                        options.resultsAdapter,
                        SelectOnClose
                      );
                  }
              }

              if (options.dropdownAdapter == null) {
                  if (options.multiple) {
                      options.dropdownAdapter = Dropdown;
                  } else {
                      var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);

                      options.dropdownAdapter = SearchableDropdown;
                  }

                  if (options.minimumResultsForSearch !== 0) {
                      options.dropdownAdapter = Utils.Decorate(
                        options.dropdownAdapter,
                        MinimumResultsForSearch
                      );
                  }

                  if (options.closeOnSelect) {
                      options.dropdownAdapter = Utils.Decorate(
                        options.dropdownAdapter,
                        CloseOnSelect
                      );
                  }

                  if (
                    options.dropdownCssClass != null ||
                    options.dropdownCss != null ||
                    options.adaptDropdownCssClass != null
                  ) {
                      var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');

                      options.dropdownAdapter = Utils.Decorate(
                        options.dropdownAdapter,
                        DropdownCSS
                      );
                  }

                  options.dropdownAdapter = Utils.Decorate(
                    options.dropdownAdapter,
                    AttachBody
                  );
              }

              if (options.selectionAdapter == null) {
                  if (options.multiple) {
                      options.selectionAdapter = MultipleSelection;
                  } else {
                      options.selectionAdapter = SingleSelection;
                  }

                  // Add the placeholder mixin if a placeholder was specified
                  if (options.placeholder != null) {
                      options.selectionAdapter = Utils.Decorate(
                        options.selectionAdapter,
                        Placeholder
                      );
                  }

                  if (options.allowClear) {
                      options.selectionAdapter = Utils.Decorate(
                        options.selectionAdapter,
                        AllowClear
                      );
                  }

                  if (options.multiple) {
                      options.selectionAdapter = Utils.Decorate(
                        options.selectionAdapter,
                        SelectionSearch
                      );
                  }

                  if (
                    options.containerCssClass != null ||
                    options.containerCss != null ||
                    options.adaptContainerCssClass != null
                  ) {
                      var ContainerCSS = require(options.amdBase + 'compat/containerCss');

                      options.selectionAdapter = Utils.Decorate(
                        options.selectionAdapter,
                        ContainerCSS
                      );
                  }

                  options.selectionAdapter = Utils.Decorate(
                    options.selectionAdapter,
                    EventRelay
                  );
              }

              if (typeof options.language === 'string') {
                  // Check if the language is specified with a region
                  if (options.language.indexOf('-') > 0) {
                      // Extract the region information if it is included
                      var languageParts = options.language.split('-');
                      var baseLanguage = languageParts[0];

                      options.language = [options.language, baseLanguage];
                  } else {
                      options.language = [options.language];
                  }
              }

              if ($.isArray(options.language)) {
                  var languages = new Translation();
                  options.language.push('en');

                  var languageNames = options.language;

                  for (var l = 0; l < languageNames.length; l++) {
                      var name = languageNames[l];
                      var language = {};

                      try {
                          // Try to load it with the original name
                          language = Translation.loadPath(name);
                      } catch (e) {
                          try {
                              // If we couldn't load it, check if it wasn't the full path
                              name = this.defaults.amdLanguageBase + name;
                              language = Translation.loadPath(name);
                          } catch (ex) {
                              // The translation could not be loaded at all. Sometimes this is
                              // because of a configuration problem, other times this can be
                              // because of how Select2 helps load all possible translation files.
                              if (options.debug && window.console && console.warn) {
                                  console.warn(
                                    'Select2: The language file for "' + name + '" could not be ' +
                                    'automatically loaded. A fallback will be used instead.'
                                  );
                              }

                              continue;
                          }
                      }

                      languages.extend(language);
                  }

                  options.translations = languages;
              } else {
                  var baseTranslation = Translation.loadPath(
                    this.defaults.amdLanguageBase + 'en'
                  );
                  var customTranslation = new Translation(options.language);

                  customTranslation.extend(baseTranslation);

                  options.translations = customTranslation;
              }

              return options;
          };

          Defaults.prototype.reset = function () {
              function stripDiacritics(text) {
                  // Used 'uni range + named function' from http://jsperf.com/diacritics/18
                  function match(a) {
                      return DIACRITICS[a] || a;
                  }

                  return text.replace(/[^\u0000-\u007E]/g, match);
              }

              function matcher(params, data) {
                  // Always return the object if there is nothing to compare
                  if ($.trim(params.term) === '') {
                      return data;
                  }

                  // Do a recursive check for options with children
                  if (data.children && data.children.length > 0) {
                      // Clone the data object if there are children
                      // This is required as we modify the object to remove any non-matches
                      var match = $.extend(true, {}, data);

                      // Check each child of the option
                      for (var c = data.children.length - 1; c >= 0; c--) {
                          var child = data.children[c];

                          var matches = matcher(params, child);

                          // If there wasn't a match, remove the object in the array
                          if (matches == null) {
                              match.children.splice(c, 1);
                          }
                      }

                      // If any children matched, return the new object
                      if (match.children.length > 0) {
                          return match;
                      }

                      // If there were no matching children, check just the plain object
                      return matcher(params, match);
                  }

                  var txt = data.parent ? data.text + ' ' + data.parent : data.text;
                  var original = stripDiacritics(txt).toUpperCase();
                  var term = stripDiacritics(params.term).toUpperCase();

                  // Check if the text contains the term
                  if (original.indexOf(term) > -1) {
                      return data;
                  }

                  // If it doesn't contain the term, don't return anything
                  return null;
              }

              this.defaults = {
                  amdBase: './',
                  amdLanguageBase: './i18n/',
                  closeOnSelect: true,
                  debug: false,
                  dropdownAutoWidth: false,
                  escapeMarkup: Utils.escapeMarkup,
                  language: EnglishTranslation,
                  matcher: matcher,
                  minimumInputLength: 0,
                  maximumInputLength: 0,
                  maximumSelectionLength: 0,
                  minimumResultsForSearch: 0,
                  selectOnClose: false,
                  sorter: function (data) {
                      return data;
                  },
                  templateResult: function (result) {
                      return result.text;
                  },
                  templateSelection: function (selection) {
                      return selection.text;
                  },
                  theme: 'default',
                  width: 'resolve',
                  additionalDropdownClass:''
              };
          };

          Defaults.prototype.set = function (key, value) {
              var camelKey = $.camelCase(key);

              var data = {};
              data[camelKey] = value;

              var convertedData = Utils._convertData(data);

              $.extend(this.defaults, convertedData);
          };

          var defaults = new Defaults();

          return defaults;
      });

      S2.define('select2/options', [
        'require',
        'jquery',
        './defaults',
        './utils'
      ], function (require, $, Defaults, Utils) {
          function Options(options, $element) {
              this.options = options;

              if ($element != null) {
                  this.fromElement($element);
              }

              this.options = Defaults.apply(this.options);

              if ($element && $element.is('input')) {
                  var InputCompat = require(this.get('amdBase') + 'compat/inputData');

                  this.options.dataAdapter = Utils.Decorate(
                    this.options.dataAdapter,
                    InputCompat
                  );
              }
          }

          Options.prototype.fromElement = function ($e) {
              var excludedData = ['select2'];

              if (this.options.multiple == null) {
                  this.options.multiple = $e.prop('multiple');
              }

              if (this.options.disabled == null) {
                  this.options.disabled = $e.prop('disabled');
              }

              if (this.options.language == null) {
                  if ($e.prop('lang')) {
                      this.options.language = $e.prop('lang').toLowerCase();
                  } else if ($e.closest('[lang]').prop('lang')) {
                      this.options.language = $e.closest('[lang]').prop('lang');
                  }
              }

              if (this.options.dir == null) {
                  if ($e.prop('dir')) {
                      this.options.dir = $e.prop('dir');
                  } else if ($e.closest('[dir]').prop('dir')) {
                      this.options.dir = $e.closest('[dir]').prop('dir');
                  } else {
                      this.options.dir = 'ltr';
                  }
              }

              $e.prop('disabled', this.options.disabled);
              $e.prop('multiple', this.options.multiple);

              if ($e.data('select2Tags')) {
                  if (this.options.debug && window.console && console.warn) {
                      console.warn(
                        'Select2: The `data-select2-tags` attribute has been changed to ' +
                        'use the `data-data` and `data-tags="true"` attributes and will be ' +
                        'removed in future versions of Select2.'
                      );
                  }

                  $e.data('data', $e.data('select2Tags'));
                  $e.data('tags', true);
              }

              if ($e.data('ajaxUrl')) {
                  if (this.options.debug && window.console && console.warn) {
                      console.warn(
                        'Select2: The `data-ajax-url` attribute has been changed to ' +
                        '`data-ajax--url` and support for the old attribute will be removed' +
                        ' in future versions of Select2.'
                      );
                  }

                  $e.attr('ajax--url', $e.data('ajaxUrl'));
                  $e.data('ajax--url', $e.data('ajaxUrl'));
              }

              var dataset = {};

              // Prefer the element's `dataset` attribute if it exists
              // jQuery 1.x does not correctly handle data attributes with multiple dashes
              if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
                  dataset = $.extend(true, {}, $e[0].dataset, $e.data());
              } else {
                  dataset = $e.data();
              }

              var data = $.extend(true, {}, dataset);

              data = Utils._convertData(data);

              for (var key in data) {
                  if ($.inArray(key, excludedData) > -1) {
                      continue;
                  }

                  if ($.isPlainObject(this.options[key])) {
                      $.extend(this.options[key], data[key]);
                  } else {
                      this.options[key] = data[key];
                  }
              }

              return this;
          };

          Options.prototype.get = function (key) {
              return this.options[key];
          };

          Options.prototype.set = function (key, val) {
              this.options[key] = val;
          };

          return Options;
      });

      S2.define('select2/core', [
        'jquery',
        './options',
        './utils',
        './keys'
      ], function ($, Options, Utils, KEYS) {
          var Select2 = function ($element, options) {
              if ($element.data('select2') != null) {
                  $element.data('select2').destroy();
              }

              this.$element = $element;

              this.id = this._generateId($element);

              options = options || {};

              this.options = new Options(options, $element);

              Select2.__super__.constructor.call(this);

              // Set up the tabindex

              var tabindex = $element.attr('tabindex') || 0;
              $element.data('old-tabindex', tabindex);
              $element.attr('tabindex', '-1');

              // Set up containers and adapters

              var DataAdapter = this.options.get('dataAdapter');
              this.dataAdapter = new DataAdapter($element, this.options);

              var $container = this.render();

              this._placeContainer($container);

              var SelectionAdapter = this.options.get('selectionAdapter');
              this.selection = new SelectionAdapter($element, this.options);
              this.$selection = this.selection.render();

              this.selection.position(this.$selection, $container);

              var DropdownAdapter = this.options.get('dropdownAdapter');
              this.dropdown = new DropdownAdapter($element, this.options);
              this.$dropdown = this.dropdown.render();

              this.dropdown.position(this.$dropdown, $container);

              var ResultsAdapter = this.options.get('resultsAdapter');
              this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
              this.$results = this.results.render();

              this.results.position(this.$results, this.$dropdown);

              // Bind events

              var self = this;

              // Bind the container to all of the adapters
              this._bindAdapters();

              // Register any DOM event handlers
              this._registerDomEvents();

              // Register any internal event handlers
              this._registerDataEvents();
              this._registerSelectionEvents();
              this._registerDropdownEvents();
              this._registerResultsEvents();
              this._registerEvents();

              // Set the initial state
              this.dataAdapter.current(function (initialData) {
                  self.trigger('selection:update', {
                      data: initialData
                  });
              });

              // Hide the original select
              $element.addClass('select2-hidden-accessible');
              $element.attr('aria-hidden', 'true');

              // Synchronize any monitored attributes
              this._syncAttributes();

              $element.data('select2', this);
          };

          Utils.Extend(Select2, Utils.Observable);

          Select2.prototype._generateId = function ($element) {
              var id = '';

              if ($element.attr('id') != null) {
                  id = $element.attr('id');
              } else if ($element.attr('name') != null) {
                  id = $element.attr('name') + '-' + Utils.generateChars(2);
              } else {
                  id = Utils.generateChars(4);
              }

              id = id.replace(/(:|\.|\[|\]|,)/g, '');
              id = 'select2-' + id;

              return id;
          };

          Select2.prototype._placeContainer = function ($container) {
              $container.insertAfter(this.$element);

              var width = this._resolveWidth(this.$element, this.options.get('width'));

              if (width != null) {
                  $container.css('width', width);
              }
          };

          Select2.prototype._resolveWidth = function ($element, method) {
              var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;

              if (method == 'resolve') {
                  var styleWidth = this._resolveWidth($element, 'style');

                  if (styleWidth != null) {
                      return styleWidth;
                  }

                  return this._resolveWidth($element, 'element');
              }

              if (method == 'element') {
                  var elementWidth = $element.outerWidth(false);

                  if (elementWidth <= 0) {
                      return 'auto';
                  }

                  return elementWidth + 'px';
              }

              if (method == 'style') {
                  var style = $element.attr('style');

                  if (typeof (style) !== 'string') {
                      return null;
                  }

                  var attrs = style.split(';');

                  for (var i = 0, l = attrs.length; i < l; i = i + 1) {
                      var attr = attrs[i].replace(/\s/g, '');
                      var matches = attr.match(WIDTH);

                      if (matches !== null && matches.length >= 1) {
                          return matches[1];
                      }
                  }

                  return null;
              }

              return method;
          };

          Select2.prototype._bindAdapters = function () {
              this.dataAdapter.bind(this, this.$container);
              this.selection.bind(this, this.$container);

              this.dropdown.bind(this, this.$container);
              this.results.bind(this, this.$container);
          };

          Select2.prototype._registerDomEvents = function () {
              var self = this;

              this.$element.on('change.select2', function () {
                  self.dataAdapter.current(function (data) {
                      self.trigger('selection:update', {
                          data: data
                      });
                  });
              });

              this.$element.on('focus.select2', function (evt) {
                  self.trigger('focus', evt);
              });

              this._syncA = Utils.bind(this._syncAttributes, this);
              this._syncS = Utils.bind(this._syncSubtree, this);

              if (this.$element[0].attachEvent) {
                  this.$element[0].attachEvent('onpropertychange', this._syncA);
              }

              var observer = window.MutationObserver ||
                window.WebKitMutationObserver ||
                window.MozMutationObserver
              ;

              if (observer != null) {
                  this._observer = new observer(function (mutations) {
                      $.each(mutations, self._syncA);
                      $.each(mutations, self._syncS);
                  });
                  this._observer.observe(this.$element[0], {
                      attributes: true,
                      childList: true,
                      subtree: false
                  });
              } else if (this.$element[0].addEventListener) {
                  this.$element[0].addEventListener(
                    'DOMAttrModified',
                    self._syncA,
                    false
                  );
                  this.$element[0].addEventListener(
                    'DOMNodeInserted',
                    self._syncS,
                    false
                  );
                  this.$element[0].addEventListener(
                    'DOMNodeRemoved',
                    self._syncS,
                    false
                  );
              }
          };

          Select2.prototype._registerDataEvents = function () {
              var self = this;

              this.dataAdapter.on('*', function (name, params) {
                  self.trigger(name, params);
              });
          };

          Select2.prototype._registerSelectionEvents = function () {
              var self = this;
              var nonRelayEvents = ['toggle', 'focus'];

              this.selection.on('toggle', function () {
                  self.toggleDropdown();
              });

              this.selection.on('focus', function (params) {
                  self.focus(params);
              });

              this.selection.on('*', function (name, params) {
                  if ($.inArray(name, nonRelayEvents) !== -1) {
                      return;
                  }

                  self.trigger(name, params);
              });
          };

          Select2.prototype._registerDropdownEvents = function () {
              var self = this;

              this.dropdown.on('*', function (name, params) {
                  self.trigger(name, params);
              });
          };

          Select2.prototype._registerResultsEvents = function () {
              var self = this;

              this.results.on('*', function (name, params) {
                  self.trigger(name, params);
              });
          };

          Select2.prototype._registerEvents = function () {
              var self = this;

              this.on('open', function () {
                  self.$container.addClass('select2-container--open');
              });

              this.on('close', function () {
                  self.$container.removeClass('select2-container--open');
              });

              this.on('enable', function () {
                  self.$container.removeClass('select2-container--disabled');
              });

              this.on('disable', function () {
                  self.$container.addClass('select2-container--disabled');
              });

              this.on('blur', function () {
                  self.$container.removeClass('select2-container--focus');
              });

              this.on('query', function (params) {
                  if (!self.isOpen()) {
                      self.trigger('open', {});
                  }

                  this.dataAdapter.query(params, function (data) {
                      self.trigger('results:all', {
                          data: data,
                          query: params
                      });
                  });
              });

              this.on('query:append', function (params) {
                  this.dataAdapter.query(params, function (data) {
                      self.trigger('results:append', {
                          data: data,
                          query: params
                      });
                  });
              });

              this.on('keypress', function (evt) {
                  var key = evt.which;

                  if (self.isOpen()) {
                      if (key === KEYS.ESC || key === KEYS.TAB ||
                          (key === KEYS.UP && evt.altKey)) {
                          self.close();

                          evt.preventDefault();
                      } else if (key === KEYS.ENTER) {
                          self.trigger('results:select', {});

                          evt.preventDefault();
                      } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
                          self.trigger('results:toggle', {});

                          evt.preventDefault();
                      } else if (key === KEYS.UP) {
                          self.trigger('results:previous', {});

                          evt.preventDefault();
                      } else if (key === KEYS.DOWN) {
                          self.trigger('results:next', {});

                          evt.preventDefault();
                      }
                  } else {
                      if (key === KEYS.ENTER || key === KEYS.SPACE ||
                          (key === KEYS.DOWN && evt.altKey)) {
                          self.open();

                          evt.preventDefault();
                      }
                  }
              });
          };

          Select2.prototype._syncAttributes = function () {
              this.options.set('disabled', this.$element.prop('disabled'));

              if (this.options.get('disabled')) {
                  if (this.isOpen()) {
                      this.close();
                  }

                  this.trigger('disable', {});
              } else {
                  this.trigger('enable', {});
              }
          };

          Select2.prototype._syncSubtree = function (evt, mutations) {
              var changed = false;
              var self = this;

              // Ignore any mutation events raised for elements that aren't options or
              // optgroups. This handles the case when the select element is destroyed
              if (
                evt && evt.target && (
                  evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
                )
              ) {
                  return;
              }

              if (!mutations) {
                  // If mutation events aren't supported, then we can only assume that the
                  // change affected the selections
                  changed = true;
              } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
                  for (var n = 0; n < mutations.addedNodes.length; n++) {
                      var node = mutations.addedNodes[n];

                      if (node.selected) {
                          changed = true;
                      }
                  }
              } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
                  changed = true;
              }

              // Only re-pull the data if we think there is a change
              if (changed) {
                  this.dataAdapter.current(function (currentData) {
                      self.trigger('selection:update', {
                          data: currentData
                      });
                  });
              }
          };

          /**
           * Override the trigger method to automatically trigger pre-events when
           * there are events that can be prevented.
           */
          Select2.prototype.trigger = function (name, args) {
              var actualTrigger = Select2.__super__.trigger;
              var preTriggerMap = {
                  'open': 'opening',
                  'close': 'closing',
                  'select': 'selecting',
                  'unselect': 'unselecting'
              };

              if (args === undefined) {
                  args = {};
              }

              if (name in preTriggerMap) {
                  var preTriggerName = preTriggerMap[name];
                  var preTriggerArgs = {
                      prevented: false,
                      name: name,
                      args: args
                  };

                  actualTrigger.call(this, preTriggerName, preTriggerArgs);

                  if (preTriggerArgs.prevented) {
                      args.prevented = true;

                      return;
                  }
              }

              actualTrigger.call(this, name, args);
          };

          Select2.prototype.toggleDropdown = function () {
              if (this.options.get('disabled')) {
                  return;
              }

              if (this.isOpen()) {
                  this.close();
              } else {
                  this.open();
              }
          };

          Select2.prototype.open = function () {
              if (this.isOpen()) {
                  return;
              }

              this.trigger('query', {});
          };

          Select2.prototype.close = function () {
              if (!this.isOpen()) {
                  return;
              }

              this.trigger('close', {});
          };

          Select2.prototype.isOpen = function () {
              return this.$container.hasClass('select2-container--open');
          };

          Select2.prototype.hasFocus = function () {
              return this.$container.hasClass('select2-container--focus');
          };

          Select2.prototype.focus = function (data) {
              // No need to re-trigger focus events if we are already focused
              if (this.hasFocus()) {
                  return;
              }

              this.$container.addClass('select2-container--focus');
              this.trigger('focus', {});
          };

          Select2.prototype.enable = function (args) {
              if (this.options.get('debug') && window.console && console.warn) {
                  console.warn(
                    'Select2: The `select2("enable")` method has been deprecated and will' +
                    ' be removed in later Select2 versions. Use $element.prop("disabled")' +
                    ' instead.'
                  );
              }

              if (args == null || args.length === 0) {
                  args = [true];
              }

              var disabled = !args[0];

              this.$element.prop('disabled', disabled);
          };

          Select2.prototype.data = function () {
              if (this.options.get('debug') &&
                  arguments.length > 0 && window.console && console.warn) {
                  console.warn(
                    'Select2: Data can no longer be set using `select2("data")`. You ' +
                    'should consider setting the value instead using `$element.val()`.'
                  );
              }

              var data = [];

              this.dataAdapter.current(function (currentData) {
                  data = currentData;
              });

              return data;
          };

          Select2.prototype.val = function (args) {
              if (this.options.get('debug') && window.console && console.warn) {
                  console.warn(
                    'Select2: The `select2("val")` method has been deprecated and will be' +
                    ' removed in later Select2 versions. Use $element.val() instead.'
                  );
              }

              if (args == null || args.length === 0) {
                  return this.$element.val();
              }

              var newVal = args[0];

              if ($.isArray(newVal)) {
                  newVal = $.map(newVal, function (obj) {
                      return obj.toString();
                  });
              }

              this.$element.val(newVal).trigger('change');
          };

          Select2.prototype.destroy = function () {
              this.$container.remove();

              if (this.$element[0].detachEvent) {
                  this.$element[0].detachEvent('onpropertychange', this._syncA);
              }

              if (this._observer != null) {
                  this._observer.disconnect();
                  this._observer = null;
              } else if (this.$element[0].removeEventListener) {
                  this.$element[0]
                    .removeEventListener('DOMAttrModified', this._syncA, false);
                  this.$element[0]
                    .removeEventListener('DOMNodeInserted', this._syncS, false);
                  this.$element[0]
                    .removeEventListener('DOMNodeRemoved', this._syncS, false);
              }

              this._syncA = null;
              this._syncS = null;

              this.$element.off('.select2');
              this.$element.attr('tabindex', this.$element.data('old-tabindex'));

              this.$element.removeClass('select2-hidden-accessible');
              this.$element.attr('aria-hidden', 'false');
              this.$element.removeData('select2');

              this.dataAdapter.destroy();
              this.selection.destroy();
              this.dropdown.destroy();
              this.results.destroy();

              this.dataAdapter = null;
              this.selection = null;
              this.dropdown = null;
              this.results = null;
          };

          Select2.prototype.render = function () {
              var $container = $(
                '<span class="select2 select2-container">' +
                  '<span class="selection"></span>' +
                  '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
                '</span>'
              );

              $container.attr('dir', this.options.get('dir'));

              this.$container = $container;

              this.$container.addClass('select2-container--' + this.options.get('theme'));

              $container.data('element', this.$element);

              return $container;
          };

          return Select2;
      });

      S2.define('jquery-mousewheel', [
        'jquery'
      ], function ($) {
          // Used to shim jQuery.mousewheel for non-full builds.
          return $;
      });

      S2.define('jquery.select2', [
        'jquery',
        'jquery-mousewheel',

        './select2/core',
        './select2/defaults'
      ], function ($, _, Select2, Defaults) {
          if ($.fn.select2 == null) {
              // All methods that should return the element
              var thisMethods = ['open', 'close', 'destroy'];

              $.fn.select2 = function (options) {
                  options = options || {};

                  if (typeof options === 'object') {
                      this.each(function () {
                          var instanceOptions = $.extend(true, {}, options);

                          var instance = new Select2($(this), instanceOptions);
                      });

                      return this;
                  } else if (typeof options === 'string') {
                      var ret;
                      var args = Array.prototype.slice.call(arguments, 1);

                      this.each(function () {
                          var instance = $(this).data('select2');

                          if (instance == null && window.console && console.error) {
                              console.error(
                                'The select2(\'' + options + '\') method was called on an ' +
                                'element that is not using Select2.'
                              );
                          }

                          ret = instance[options].apply(instance, args);
                      });

                      // Check if we should be returning `this`
                      if ($.inArray(options, thisMethods) > -1) {
                          return this;
                      }

                      return ret;
                  } else {
                      throw new Error('Invalid arguments for Select2: ' + options);
                  }
              };
          }

          if ($.fn.select2.defaults == null) {
              $.fn.select2.defaults = Defaults;
          }

          return Select2;
      });

      // Return the AMD loader configuration so it can be used outside of this file
      return {
          define: S2.define,
          require: S2.require
      };
  }());

    // Autoload the jQuery bindings
    // We know that all of the modules exist above this, so we're safe
    var select2 = S2.require('jquery.select2');

    // Hold the AMD module references on the jQuery function that was just loaded
    // This allows Select2 to use the internal loader outside of this file, such
    // as in the language files.
    jQuery.fn.select2.amd = S2;

    // Return the Select2 instance for anyone who is importing it.
    return select2;
}));

;ko.bindingHandlers.select2 = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var obj = valueAccessor(),
            allBindings = allBindingsAccessor(),
            lookupKey = allBindings.lookupKey;
        obj.language = translations.lang;
        $(element).select2(obj);
        if (lookupKey) {
            var value = ko.utils.unwrapObservable(allBindings.value);
            $(element).select2('data', ko.utils.arrayFirst(obj.data.results, function (item) {
                return item[lookupKey] === value;
            }));
        }

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).select2('destroy');
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        $(element).trigger('change');
    }
};

ko.bindingHandlers.select22 = {
    init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
        ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
            $(el).select2('destroy');
        });

        var allBindings = allBindingsAccessor(),
            select22 = ko.utils.unwrapObservable(allBindings.select22);
        select22.language = translations.lang;

        $(el).select2(select22);
    },
    update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
        var allBindings = allBindingsAccessor();

        if ("value" in allBindings) {
            if (allBindings.select22.multiple && allBindings.value().constructor != Array) {
                $(el).select2("val", allBindings.value().split(","));
            }
            else {
                $(el).select2("val", allBindings.value());
            }
        } else if ("selectedOptions" in allBindings) {
            var converted = [];
            var textAccessor = function (value) { return value; };
            if ("optionsText" in allBindings) {
                textAccessor = function (value) {
                    var valueAccessor = function (item) { return item; }
                    if ("optionsValue" in allBindings) {
                        valueAccessor = function (item) { return item[allBindings.optionsValue]; }
                    }
                    var items = $.grep(allBindings.options(), function (e) { return valueAccessor(e) == value });
                    if (items.length == 0 || items.length > 1) {
                        return "UNKNOWN";
                    }
                    return items[0][allBindings.optionsText];
                }
            }
            $.each(allBindings.selectedOptions(), function (key, value) {
                converted.push({ id: value, text: textAccessor(value) });
            });
            $(el).select2("data", converted);
        }
    }
};
/*
ko.bindingHandlers.selectHash = {
    init: function (el, valueAccessor, allBindingsAccessor, viewModel) {
        ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
            $(el).select2('destroy');
        });

        var allBindings = allBindingsAccessor(),
            selectHash = ko.utils.unwrapObservable(allBindings.selectHash);
        $el = $(el);

        $el.select2(selectHash);

        var selecting = ko.utils.unwrapObservable(allBindings.onSelecting);
        if (selecting) {
            $el.on('select2:selecting', selecting);
        }
        var select = ko.utils.unwrapObservable(allBindings.onSelect);


        var parentId = allBindings.parentId;
        if (parentId) {
            parentId.subscribe(function (newVal) {
                $el.select2("destroy");
                $el.select2(selectHash);
                if (selecting) {
                    $el.on('select2:selecting', selecting);
                }
                if (select) {
                    $el.on('select2:select', select);
                }
                $el.trigger('change');
            });
        }

    },
    update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
        var allBindings = allBindingsAccessor();

        var converted = [];
        var selects = [];
        $.each(allBindings.selectedOptions(), function (key, value) {
            converted.push({ id: value, text: value });
            selects.push(value);
        });
        $(el).select2("data", converted);
        $(el).val(selects).trigger('change');
    }
};
*/
;eval(function (p, a, c, k, e, d) { e = function (c) { return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) }; while (c--) { if (k[c]) { p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]) } } return p }('o.5=B(9,b,2){6(h b!=\'E\'){2=2||{};6(b===n){b=\'\';2.3=-1}4 3=\'\';6(2.3&&(h 2.3==\'j\'||2.3.k)){4 7;6(h 2.3==\'j\'){7=w u();7.t(7.q()+(2.3*r*l*l*x))}m{7=2.3}3=\'; 3=\'+7.k()}4 8=2.8?\'; 8=\'+(2.8):\'\';4 a=2.a?\'; a=\'+(2.a):\'\';4 c=2.c?\'; c\':\'\';d.5=[9,\'=\',C(b),3,8,a,c].y(\'\')}m{4 e=n;6(d.5&&d.5!=\'\'){4 g=d.5.A(\';\');s(4 i=0;i<g.f;i++){4 5=o.z(g[i]);6(5.p(0,9.f+1)==(9+\'=\')){e=D(5.p(9.f+1));v}}}F e}};', 42, 42, '||options|expires|var|cookie|if|date|path|name|domain|value|secure|document|cookieValue|length|cookies|typeof||number|toUTCString|60|else|null|jQuery|substring|getTime|24|for|setTime|Date|break|new|1000|join|trim|split|function|encodeURIComponent|decodeURIComponent|undefined|return'.split('|')));/*!
* Bootstrap.js by @fat & @mdo
* Copyright 2013 Twitter, Inc.
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('<div class="dropdown-backdrop"/>').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);;/* =========================================================
* bootstrap-datepicker.js 
* http://www.eyecon.ro/bootstrap-datepicker
* =========================================================
* Copyright 2012 Stefan Petre
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */

!function ($) {

    // Picker object

    var Datepicker = function (element, options) {
        this.element = $(element);
        this.format = DPGlobal.parseFormat(options.format || this.element.data('date-format') || 'yyyy-mm-dd');
        this.leftOffset = 0;
        if (options.leftOffset) {
            this.leftOffset = options.leftOffset;
        }
        if (options.left) {
            this.picker = $(DPGlobal.templateLeft)
                                .appendTo('body')
                                .on({
                                    click: $.proxy(this.click, this),
                                    mousedown: $.proxy(this.mousedown, this)
                                });
        } else {
            this.picker = $(DPGlobal.template)
                    .appendTo('body')
                    .on({
                        click: $.proxy(this.click, this),
                        mousedown: $.proxy(this.mousedown, this)
                    });
        }
        this.isInput = this.element.is('input');
        this.component = this.element.is('.date') ? this.element.find('.add-on') : false;

        this.OutsideInputClick = false;

        if (this.isInput) {
            this.element.on({
                focus: $.proxy(this.show, this),
                blur: $.proxy(this.hide, this),
                keyup: $.proxy(this.update, this)
            });
        } else {
            if (this.component) {
                this.component.on('click', $.proxy(this.show, this));
            } else {
                this.element.on('click', $.proxy(this.show, this));
            }
        }
        this.minViewMode = options.minViewMode || this.element.data('date-minviewmode') || 0;
        if (typeof this.minViewMode === 'string') {
            switch (this.minViewMode) {
                case 'months':
                    this.minViewMode = 1;
                    break;
                case 'years':
                    this.minViewMode = 2;
                    break;
                default:
                    this.minViewMode = 0;
                    break;
            }
        }
        this.viewMode = options.viewMode || this.element.data('date-viewmode') || 0;
        if (typeof this.viewMode === 'string') {
            switch (this.viewMode) {
                case 'months':
                    this.viewMode = 1;
                    break;
                case 'years':
                    this.viewMode = 2;
                    break;
                default:
                    this.viewMode = 0;
                    break;
            }
        }
        this.startViewMode = this.viewMode;
        this.weekStart = options.weekStart || this.element.data('date-weekstart') || 1;
        this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
        this.onRender = options.onRender;
        this.fillDow();
        this.fillMonths();
        this.update();
        this.showMode();
    };

    Datepicker.prototype = {
        constructor: Datepicker,

        show: function (e) {
            var outsideInputClick = e && $.type(e) === "boolean";
            this.picker.show();
            this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
            this.place();
            $(window).on('resize', $.proxy(this.place, this));
            if (e && !outsideInputClick) {
                e.stopPropagation();
                e.preventDefault();
            }
            if (!this.isInput || outsideInputClick) {
                this.OutsideInputClick = outsideInputClick;
                $(document).on('mousedown', $.proxy(this.hide, this));
            }
            this.element.trigger({
                type: 'show',
                date: this.date
            });
        },

        hide: function () {
            this.picker.hide();
            $(window).off('resize', this.place);
            this.viewMode = this.startViewMode;
            this.showMode();
            if (!this.isInput || this.OutsideInputClick) {
                this.OutsideInputClick = false;
                $(document).off('mousedown', this.hide);
            }
            this.set();
            this.element.trigger({
                type: 'hide',
                date: this.date
            });
        },

        set: function () {
            var formated = DPGlobal.formatDate(this.date, this.format);
            if (!this.isInput) {
                if (this.component) {
                    this.element.find('input').prop('value', formated);
                }
                this.element.data('date', formated);
            } else {
                this.element.prop('value', formated);
            }
        },

        setValue: function (newDate) {
            if (typeof newDate === 'string') {
                this.date = DPGlobal.parseDate(newDate, this.format);
            } else {
                this.date = new Date(newDate);
            }
            this.set();
            this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
            this.fill();
        },

        place: function () {
            var offset = this.component ? this.component.offset() : this.element.offset();
            this.picker.css({
                top: offset.top + this.height,
                left: offset.left + this.leftOffset
            });
        },

        update: function (newDate) {
            this.date = DPGlobal.parseDate(
				typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')),
				this.format
			);
            this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
            this.fill();
        },

        fillDow: function () {
            var dowCnt = this.weekStart;
            var html = '<tr>';
            while (dowCnt < this.weekStart + 7) {
                html += '<th class="dow">' + DPGlobal.dates.daysMin[(dowCnt++) % 7] + '</th>';
            }
            html += '</tr>';
            this.picker.find('.datepicker-days thead').append(html);
        },

        fillMonths: function () {
            var html = '';
            var i = 0
            while (i < 12) {
                html += '<span class="month">' + DPGlobal.dates.monthsShort[i++] + '</span>';
            }
            this.picker.find('.datepicker-months td').append(html);
        },

        fill: function () {
            var d = new Date(this.viewDate),
				year = d.getFullYear(),
				month = d.getMonth(),
				currentDate = this.date.valueOf();
            this.picker.find('.datepicker-days th:eq(1)')
						.text(DPGlobal.dates.months[month] + ' ' + year);
            var prevMonth = new Date(year, month - 1, 28, 0, 0, 0, 0),
				day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
            prevMonth.setDate(day);
            prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7) % 7);
            var nextMonth = new Date(prevMonth);
            nextMonth.setDate(nextMonth.getDate() + 42);
            nextMonth = nextMonth.valueOf();
            html = [];
            var clsName;
            while (prevMonth.valueOf() < nextMonth) {
                if (prevMonth.getDay() === this.weekStart) {
                    html.push('<tr>');
                }
                clsName = ' '+ this.onRender(prevMonth);
                if (prevMonth.getMonth() < month) {
                    clsName += ' old';
                } else if (prevMonth.getMonth() > month) {
                    clsName += ' new';
                }
                if (prevMonth.valueOf() === currentDate) {
                    clsName += ' active';
                }
                html.push('<td class="day' + clsName + '">' + prevMonth.getDate() + '</td>');
                if (prevMonth.getDay() === this.weekEnd) {
                    html.push('</tr>');
                }
                prevMonth.setDate(prevMonth.getDate() + 1);
            }
            this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
            var currentYear = this.date.getFullYear();

            var months = this.picker.find('.datepicker-months')
						.find('th:eq(1)')
							.text(year)
							.end()
						.find('span').removeClass('active');
            if (currentYear === year) {
                months.eq(this.date.getMonth()).addClass('active');
            }

            html = '';
            year = parseInt(year / 10, 10) * 10;
            var yearCont = this.picker.find('.datepicker-years')
								.find('th:eq(1)')
									.text(year + '-' + (year + 9))
									.end()
								.find('td');
            year -= 1;
            for (var i = -1; i < 11; i++) {
                html += '<span class="year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + '">' + year + '</span>';
                year += 1;
            }
            yearCont.html(html);
        },

        click: function (e) {
            e.stopPropagation();
            e.preventDefault();
            var target = $(e.target).closest('span, td, th');
            if (target.length === 1) {
                switch (target[0].nodeName.toLowerCase()) {
                    case 'th':
                        switch (target[0].className) {
                            case 'switch':
                                this.showMode(1);
                                break;
                            case 'prev':
                            case 'next':
                                this.viewDate['set' + DPGlobal.modes[this.viewMode].navFnc].call(
									this.viewDate,
									this.viewDate['get' + DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
									DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1)
								);
                                this.fill();
                                this.set();
                                break;
                        }
                        break;
                    case 'span':
                        if (target.is('.month')) {
                            var month = target.parent().find('span').index(target);
                            this.viewDate.setMonth(month);
                        } else {
                            var year = parseInt(target.text(), 10) || 0;
                            this.viewDate.setFullYear(year);
                        }
                        if (this.viewMode !== 0) {
                            this.date = new Date(this.viewDate);
                            this.element.trigger({
                                type: 'changeDate',
                                date: this.date,
                                viewMode: DPGlobal.modes[this.viewMode].clsName
                            });
                        }
                        this.showMode(-1);
                        this.fill();
                        this.set();
                        break;
                    case 'td':
                        if (target.is('.day')) {
                            var day = parseInt(target.text(), 10) || 1;
                            var month = this.viewDate.getMonth();
                            if (target.is('.old')) {
                                month -= 1;
                            } else if (target.is('.new')) {
                                month += 1;
                            }
                            var year = this.viewDate.getFullYear();
                            this.date = new Date(year, month, day, 0, 0, 0, 0);
                            this.viewDate = new Date(year, month, Math.min(28, day), 0, 0, 0, 0);
                            this.fill();
                            this.set();
                            this.element.trigger({
                                type: 'changeDate',
                                date: this.date,
                                viewMode: DPGlobal.modes[this.viewMode].clsName
                            });
                        }
                        break;
                }
            }
        },

        mousedown: function (e) {
            e.stopPropagation();
            e.preventDefault();
        },

        showMode: function (dir) {
            if (dir) {
                this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
            }
            this.picker.find('.cloudContent>div').hide().filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName).show();
        }
    };

    $.fn.datepicker = function (option, val) {
        return this.each(function () {
            var $this = $(this),
				data = $this.data('datepicker'),
				options = typeof option === 'object' && option;
            if (!data) {
                $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults, options))));
            }
            if (typeof option === 'string') data[option](val);
        });
    };

    $.fn.datepicker.defaults = {
    };

    $.fn.datepicker.defaults = {
        onRender: function (date) {
            return '';
        }
    };

    $.fn.datepicker.Constructor = Datepicker;

    var DPGlobal = {
        modes: [
                {
                    clsName: 'days',
                    navFnc: 'Month',
                    navStep: 1
                },
                {
                    clsName: 'months',
                    navFnc: 'FullYear',
                    navStep: 1
                },
                {
                    clsName: 'years',
                    navFnc: 'FullYear',
                    navStep: 10
                }],
        dates: translations.lang=='pl'?{
            days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"],
            daysShort: ["Nie", "Pon", "Wto", "Śro", "Czw", "Pią", "Sob", "Nie"],
            daysMin: ["Ni", "Po", "Wt", "Śr", "Cz", "Pi", "So", "Ni"],
            months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
            monthsShort: ["Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Paź", "Lis", "Gru"],
            today: "Dzisiaj"
        } : {
            days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
            daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
            daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
            today: "Today"
        },
        isLeapYear: function (year) {
            return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
        },
        getDaysInMonth: function (year, month) {
            return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
        },
        parseFormat: function (format) {
            var separator = format.match(/[.\/\-\s].*?/),
                    parts = format.split(/\W+/);
            if (!separator || !parts || parts.length === 0) {
                throw new Error("Invalid date format.");
            }
            return { separator: separator, parts: parts };
        },
        parseDate: function (date, format) {
            var parts = date.split(format.separator),
                    date = new Date(),
                    val;
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(0);
            date.setMilliseconds(0);
            if (parts.length === format.parts.length) {
                for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
                    val = parseInt(parts[i], 10) || 1;
                    switch (format.parts[i]) {
                        case 'dd':
                        case 'd':
                            date.setDate(val);
                            break;
                        case 'mm':
                        case 'm':
                            date.setMonth(val - 1);
                            break;
                        case 'yy':
                            date.setFullYear(2000 + val);
                            break;
                        case 'yyyy':
                            date.setFullYear(val);
                            break;
                    }
                }
            }
            return date;
        },
        formatDate: function (date, format) {
            var val = {
                d: date.getDate(),
                m: date.getMonth() + 1,
                yy: date.getFullYear().toString().substring(2),
                yyyy: date.getFullYear()
            };
            val.dd = (val.d < 10 ? '0' : '') + val.d;
            val.mm = (val.m < 10 ? '0' : '') + val.m;
            var date = [];
            for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
                date.push(val[format.parts[i]]);
            }
            return date.join(format.separator);
        },
        headTemplate: '<thead>' +
                                '<tr>' +
                                    '<th class="prev">&lsaquo;</th>' +
                                    '<th colspan="5" class="switch"></th>' +
                                    '<th class="next">&rsaquo;</th>' +
                                '</tr>' +
                            '</thead>',
        contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
    };    

    DPGlobal.template = '<div class="datepicker dropdown-menu"><div class="hrCloud right down"><div class="cloudBottom"><div class="cloudBottomFill"></div></div><div class="cloudContent">' +
                                '<div class="datepicker-days">' +
                                    '<table class=" table-condensed">' +
                                        DPGlobal.headTemplate +
                                        '<tbody></tbody>' +
                                    '</table>' +
                                '</div>' +
                                '<div class="datepicker-months">' +
                                    '<table class="table-condensed">' +
                                        DPGlobal.headTemplate +
                                        DPGlobal.contTemplate +
                                    '</table>' +
                                '</div>' +
                                '<div class="datepicker-years">' +
                                    '<table class="table-condensed">' +
                                        DPGlobal.headTemplate +
                                        DPGlobal.contTemplate +
                                    '</table>' +
                                '</div>' +
                            '</div></div></div>';
    DPGlobal.templateLeft = '<div class="datepicker dropdown-menu"><div class="hrCloud left down"><div class="cloudBottom"><div class="cloudBottomFill"></div></div><div class="cloudContent">' +
                                '<div class="datepicker-days">' +
                                    '<table class=" table-condensed">' +
                                        DPGlobal.headTemplate +
                                        '<tbody></tbody>' +
                                    '</table>' +
                                '</div>' +
                                '<div class="datepicker-months">' +
                                    '<table class="table-condensed">' +
                                        DPGlobal.headTemplate +
                                        DPGlobal.contTemplate +
                                    '</table>' +
                                '</div>' +
                                '<div class="datepicker-years">' +
                                    '<table class="table-condensed">' +
                                        DPGlobal.headTemplate +
                                        DPGlobal.contTemplate +
                                    '</table>' +
                                '</div>' +
                            '</div></div></div>';

}(window.jQuery)

ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options        
        var options = allBindingsAccessor().datepickerOptions || {};
        var elem = $(element);

        var past = elem.data('onlypast');
        var feature = elem.data('onlyfuture');
        
        if (past || feature) {
            var nowTemp = new Date();
            var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0);

            if (past) {
                options.onRender = function (date) {
                    return date.valueOf() > now.valueOf() ? 'disabled' : '';
                };
            } else if (feature) {
                options.onRender = function (date) {
                    return date.valueOf() < now.valueOf() ? 'disabled' : '';
                };
            }
        }

        elem.datepicker(options).on('changeDate', function (ev) {
            var observable = valueAccessor();

            var d = ev.date;
            var curr_date = d.getDate();
            var curr_month = d.getMonth() + 1; //Months are zero based
            var curr_year = d.getFullYear();

            if (curr_month < 10) curr_month = '0' + curr_month;
            if (curr_date < 10) curr_date = '0' + curr_date;

            observable(curr_year + "-" + curr_month + "-" + curr_date);

            elem.blur();
        });
        $(element).change(function () {
            var observable = valueAccessor();
            observable($(element).val());
        });
    },
    update: function (element, valueAccessor) {

        var value = ko.utils.unwrapObservable(valueAccessor());
        current = $(element).val();
        if (value != current) {
            $(element).datepicker("setValue", value);
        }
    }
};

function OpenDataCtrlFromKo(data, event) {
    var elem = $(event.target);
    var goToCallendar = elem.closest('.goToCallendar');
    if (goToCallendar.length) elem = goToCallendar;
    var txb = elem.parent().find('input');
    txb.datepicker('show', true);
};/*	Add scroll support to divs
	Based off of Chris Barr's solution
	http://chris-barr.com/files/touchScroll.htm	*/
(function($){
	$.fn.extend({ 
	touchScroll: function() {
			// Detect if is touchscreen device, if it is not simply return
			if ((navigator.userAgent.match(/android 3/i)) || (navigator.userAgent.match(/honeycomb/i))) {
				return this;
			}
			try {
				document.createEvent("TouchEvent");
			} catch (e) {
				return this;
			}
			return this.each(function() {
				var el = this, scrollStartPosY = 0, scrollStartPosX = 0;
				el.addEventListener("touchstart", function (event) {
					scrollStartPosY = this.scrollTop + event.touches[0].pageY;
					scrollStartPosX = this.scrollLeft + event.touches[0].pageX;
					
				}, false);
				el.addEventListener("touchmove", function (event) {
					
					if ((this.scrollTop < this.scrollHeight - this.offsetHeight && this.scrollTop + event.touches[0].pageY < scrollStartPosY - 5) || (this.scrollTop !== 0 && this.scrollTop + event.touches[0].pageY > scrollStartPosY + 5)) {
						event.preventDefault();
					}
					if ((this.scrollLeft < this.scrollWidth - this.offsetWidth && this.scrollLeft + event.touches[0].pageX < scrollStartPosX - 5) || (this.scrollLeft !== 0 && this.scrollLeft + event.touches[0].pageX > scrollStartPosX + 5)) {
						event.preventDefault();
					}
					this.scrollTop = scrollStartPosY - event.touches[0].pageY;
					this.scrollLeft = scrollStartPosX - event.touches[0].pageX;
				}, false);
			});
		}
	});
})(jQuery);;/*! DataTables 1.10.0
 * ©2008-2014 SpryMedia Ltd - datatables.net/license
 */
(function(za,N,l){var M=function(g){function S(a){var b,c,d={};g.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),d[c]=e,"o"===b[1]&&S(a[e])});a._hungarianMap=d}function G(a,b,c){a._hungarianMap||S(a);var d;g.each(b,function(e){d=a._hungarianMap[e];if(d!==l&&(c||b[d]===l))"o"===d.charAt(0)?(b[d]||(b[d]={}),g.extend(!0,b[d],b[e]),G(a[d],b[d],c)):b[d]=b[e]})}function M(a){var b=p.defaults.oLanguage,c=a.sZeroRecords;
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&D(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&D(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&bb(a)}function cb(a){w(a,"ordering","bSort");w(a,"orderMulti","bSortMulti");w(a,"orderClasses","bSortClasses");w(a,"orderCellsTop","bSortCellsTop");w(a,"order","aaSorting");w(a,"orderFixed","aaSortingFixed");w(a,"paging","bPaginate");
w(a,"pagingType","sPaginationType");w(a,"pageLength","iDisplayLength");w(a,"searching","bFilter")}function db(a){w(a,"orderable","bSortable");w(a,"orderData","aDataSort");w(a,"orderSequence","asSorting");w(a,"orderDataType","sortDataType")}function eb(a){var a=a.oBrowser,b=g("<div/>").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(g("<div/>").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(g('<div class="test"/>').css({width:"100%",height:10}))).appendTo("body"),
c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==c.offset().left;b.remove()}function fb(a,b,c,d,e,f){var h,i=!1;c!==l&&(h=c,i=!0);for(;d!==e;)a.hasOwnProperty(d)&&(h=i?b(h,a[d],d,a):a[d],i=!0,d+=f);return h}function Aa(a,b){var c=p.defaults.column,d=a.aoColumns.length,c=g.extend({},p.models.oColumn,c,{nTh:b?b:N.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;
c[d]=g.extend({},p.models.oSearch,c[d]);fa(a,d,null)}function fa(a,b,c){var d=a.aoColumns[b],b=a.oClasses,e=g(d.nTh);if(!d.sWidthOrig){d.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%])/);f&&(d.sWidthOrig=f[1])}c!==l&&null!==c&&(db(c),G(p.defaults.column,c),c.mDataProp!==l&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(d._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),g.extend(d,c),D(d,c,"sWidth","sWidthOrig"),"number"===typeof c.iDataSort&&
(d.aDataSort=[c.iDataSort]),D(d,c,"aDataSort"));var c=d.mData,h=T(c),i=d.mRender?T(d.mRender):null,f=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};d._bAttrSrc=g.isPlainObject(c)&&(f(c.sort)||f(c.type)||f(c.filter));d.fnGetData=function(a,b){var c=h(a,b);return d.mRender&&b&&""!==b?i(c,b,a):c};d.fnSetData=Ba(c);a.oFeatures.bSort||(d.bSortable=!1,e.addClass(b.sSortableNone));a=-1!==g.inArray("asc",d.asSorting);e=-1!==g.inArray("desc",d.asSorting);!d.bSortable||!a&&!e?(d.sSortingClass=
b.sSortableNone,d.sSortingClassJUI=""):a&&!e?(d.sSortingClass=b.sSortableAsc,d.sSortingClassJUI=b.sSortJUIAscAllowed):!a&&e?(d.sSortingClass=b.sSortableDesc,d.sSortingClassJUI=b.sSortJUIDescAllowed):(d.sSortingClass=b.sSortable,d.sSortingClassJUI=b.sSortJUI)}function U(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ca(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&V(a);t(a,null,"column-sizing",[a])}function ga(a,b){var c=W(a,"bVisible");
return"number"===typeof c[b]?c[b]:null}function X(a,b){var c=W(a,"bVisible"),c=g.inArray(b,c);return-1!==c?c:null}function Y(a){return W(a,"bVisible").length}function W(a,b){var c=[];g.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Da(a){var b=a.aoColumns,c=a.aoData,d=p.ext.type.detect,e,f,h,i,j,g,m,o,k;e=0;for(f=b.length;e<f;e++)if(m=b[e],k=[],!m.sType&&m._sManualType)m.sType=m._sManualType;else if(!m.sType){h=0;for(i=d.length;h<i;h++){j=0;for(g=c.length;j<g&&!(k[j]===l&&(k[j]=
A(a,j,e,"type")),o=d[h](k[j],a),!o||"html"===o);j++);if(o){m.sType=o;break}}m.sType||(m.sType="string")}}function gb(a,b,c,d){var e,f,h,i,j,n,m=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){n=b[e];var o=n.targets!==l?n.targets:n.aTargets;g.isArray(o)||(o=[o]);f=0;for(h=o.length;f<h;f++)if("number"===typeof o[f]&&0<=o[f]){for(;m.length<=o[f];)Aa(a);d(o[f],n)}else if("number"===typeof o[f]&&0>o[f])d(m.length+o[f],n);else if("string"===typeof o[f]){i=0;for(j=m.length;i<j;i++)("_all"==o[f]||g(m[i].nTh).hasClass(o[f]))&&
d(i,n)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function H(a,b,c,d){var e=a.aoData.length,f=g.extend(!0,{},p.models.oRow,{src:c?"dom":"data"});f._aData=b;a.aoData.push(f);for(var b=a.aoColumns,f=0,h=b.length;f<h;f++)c&&Ea(a,e,f,A(a,e,f)),b[f].sType=null;a.aiDisplayMaster.push(e);a.oFeatures.bDeferRender||Fa(a,e,c,d);return e}function ha(a,b){var c;b instanceof g||(b=g(b));return b.map(function(b,e){c=ia(a,e);return H(a,c.data,e,c.cells)})}function A(a,b,c,d){var c=a.aoColumns[c],e=a.aoData[b]._aData,
f=c.fnGetData(e,d);if(f===l)return a.iDrawError!=a.iDraw&&null===c.sDefaultContent&&(O(a,0,"Requested unknown parameter "+("function"==typeof c.mData?"{function}":"'"+c.mData+"'")+" for row "+b,4),a.iDrawError=a.iDraw),c.sDefaultContent;if((f===e||null===f)&&null!==c.sDefaultContent)f=c.sDefaultContent;else if("function"===typeof f)return f();return null===f&&"display"==d?"":f}function Ea(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d)}function Ga(a){return g.map(a.match(/(\\.|[^\.])+/g),
function(a){return a.replace("\\.",".")})}function T(a){if(g.isPlainObject(a)){var b={};g.each(a,function(a,c){c&&(b[a]=T(c))});return function(a,c,f){var h=b[c]||b._;return h!==l?h(a,c,f):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f){return a(b,c,f)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var h,i;if(""!==f){i=Ga(f);for(var j=0,g=i.length;j<g;j++){f=i[j].match(Z);h=i[j].match(P);if(f){i[j]=
i[j].replace(Z,"");""!==i[j]&&(a=a[i[j]]);h=[];i.splice(0,j+1);i=i.join(".");j=0;for(g=a.length;j<g;j++)h.push(c(a[j],b,i));a=f[0].substring(1,f[0].length-1);a=""===a?h:h.join(a);break}else if(h){i[j]=i[j].replace(P,"");a=a[i[j]]();continue}if(null===a||a[i[j]]===l)return l;a=a[i[j]]}}return a};return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function Ba(a){if(g.isPlainObject(a))return Ba(a._);if(null===a)return function(){};if("function"===typeof a)return function(b,d){a(b,"set",
d)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=Ga(e),f;f=e[e.length-1];for(var h,i,j=0,g=e.length-1;j<g;j++){h=e[j].match(Z);i=e[j].match(P);if(h){e[j]=e[j].replace(Z,"");a[e[j]]=[];f=e.slice();f.splice(0,j+1);h=f.join(".");i=0;for(g=d.length;i<g;i++)f={},b(f,d[i],h),a[e[j]].push(f);return}i&&(e[j]=e[j].replace(P,""),a=a[e[j]](d));if(null===a[e[j]]||a[e[j]]===l)a[e[j]]={};a=a[e[j]]}if(f.match(P))a[f.replace(P,"")](d);else a[f.replace(Z,
"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ha(a){return B(a.aoData,"_aData")}function ja(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0}function ka(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===l&&a.splice(d,1)}function la(a,b,c,d){var e=a.aoData[b],f;if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=ia(a,e).data;else{var h=e.anCells;if(h){c=0;for(f=h.length;c<f;c++)h[c].innerHTML=A(a,b,c,"display")}}e._aSortData=
null;e._aFilterData=null;a=a.aoColumns;if(d!==l)a[d].sType=null;else{c=0;for(f=a.length;c<f;c++)a[c].sType=null}Ia(e)}function ia(a,b){var c=[],d=[],e=b.firstChild,f,h,i,j=0,n,m=a.aoColumns,o=function(a,b,c){"string"===typeof a&&(b=a.indexOf("@"),-1!==b&&(a=a.substring(b+1),i["@"+a]=c.getAttribute(a)))},k=function(a){h=m[j];n=g.trim(a.innerHTML);h&&h._bAttrSrc?(i={display:n},o(h.mData.sort,i,a),o(h.mData.type,i,a),o(h.mData.filter,i,a),c.push(i)):c.push(n);d.push(a);j++};if(e)for(;e;)f=e.nodeName.toUpperCase(),
("TD"==f||"TH"==f)&&k(e),e=e.nextSibling;else{d=b.anCells;e=0;for(f=d.length;e<f;e++)k(d[e])}return{data:c,cells:d}}function Fa(a,b,c,d){var e=a.aoData[b],f=e._aData,h=[],i,j,g,m,o;if(null===e.nTr){i=c||N.createElement("tr");e.nTr=i;e.anCells=h;i._DT_RowIndex=b;Ia(e);m=0;for(o=a.aoColumns.length;m<o;m++){g=a.aoColumns[m];j=c?d[m]:N.createElement(g.sCellType);h.push(j);if(!c||g.mRender||g.mData!==m)j.innerHTML=A(a,b,m,"display");g.sClass&&(j.className+=" "+g.sClass);g.bVisible&&!c?i.appendChild(j):
!g.bVisible&&c&&j.parentNode.removeChild(j);g.fnCreatedCell&&g.fnCreatedCell.call(a.oInstance,j,A(a,b,m,"display"),f,b,m)}t(a,"aoRowCreatedCallback",null,[i,f,b])}e.nTr.setAttribute("role","row")}function Ia(a){var b=a.nTr,c=a._aData;if(b){c.DT_RowId&&(b.id=c.DT_RowId);if(c.DT_RowClass){var d=c.DT_RowClass.split(" ");a.__rowc=a.__rowc?Ja(a.__rowc.concat(d)):d;g(b).removeClass(a.__rowc.join(" ")).addClass(c.DT_RowClass)}c.DT_RowData&&g(b).data(c.DT_RowData)}}function hb(a){var b,c,d,e,f,h=a.nTHead,
i=a.nTFoot,j=0===g("th, td",h).length,n=a.oClasses,m=a.aoColumns;j&&(e=g("<tr/>").appendTo(h));b=0;for(c=m.length;b<c;b++)f=m[b],d=g(f.nTh).addClass(f.sClass),j&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Ka(a,f.nTh,b))),f.sTitle!=d.html()&&d.html(f.sTitle),La(a,"header")(a,d,f,n);j&&$(a.aoHeader,h);g(h).find(">tr").attr("role","row");g(h).find(">tr>th, >tr>td").addClass(n.sHeaderTH);g(i).find(">tr>th, >tr>td").addClass(n.sFooterTH);
if(null!==i){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=m[b],f.nTf=a[b].cell,f.sClass&&g(f.nTf).addClass(f.sClass)}}function I(a,b,c){var d,e,f,h=[],i=[],j=a.aoColumns.length,n;if(b){c===l&&(c=!1);d=0;for(e=b.length;d<e;d++){h[d]=b[d].slice();h[d].nTr=b[d].nTr;for(f=j-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&h[d].splice(f,1);i.push([])}d=0;for(e=h.length;d<e;d++){if(a=h[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=h[d].length;f<b;f++)if(n=j=1,i[d][f]===l){a.appendChild(h[d][f].cell);for(i[d][f]=
1;h[d+j]!==l&&h[d][f].cell==h[d+j][f].cell;)i[d+j][f]=1,j++;for(;h[d][f+n]!==l&&h[d][f].cell==h[d][f+n].cell;){for(c=0;c<j;c++)i[d+c][f+n]=1;n++}g(h[d][f].cell).attr("rowspan",j).attr("colspan",n)}}}}function J(a){var b=t(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==g.inArray(!1,b))C(a,!1);else{var b=[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,h=a.iInitDisplayStart,i="ssp"==z(a),j=a.aiDisplay;a.bDrawing=!0;h!==l&&-1!==h&&(a._iDisplayStart=i?h:h>=a.fnRecordsDisplay()?0:h,a.iInitDisplayStart=
-1);var h=a._iDisplayStart,n=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(i){if(!a.bDestroying&&!ib(a))return}else a.iDraw++;if(0!==j.length){f=i?a.aoData.length:n;for(i=i?0:h;i<f;i++){var m=j[i],o=a.aoData[m];null===o.nTr&&Fa(a,m);m=o.nTr;if(0!==e){var k=d[c%e];o._sRowStripe!=k&&(g(m).removeClass(o._sRowStripe).addClass(k),o._sRowStripe=k)}t(a,"aoRowCallback",null,[m,o._aData,c,i]);b.push(m);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==z(a)?c=f.sLoadingRecords:
f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=g("<tr/>",{"class":e?d[0]:""}).append(g("<td />",{valign:"top",colSpan:Y(a),"class":a.oClasses.sRowEmpty}).html(c))[0];t(a,"aoHeaderCallback","header",[g(a.nTHead).children("tr")[0],Ha(a),h,n,j]);t(a,"aoFooterCallback","footer",[g(a.nTFoot).children("tr")[0],Ha(a),h,n,j]);d=g(a.nTBody);d.children().detach();d.append(g(b));t(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function K(a,b){var c=a.oFeatures,d=c.bFilter;
c.bSort&&jb(a);d?aa(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);J(a)}function kb(a){var b=a.oClasses,c=g(a.nTable),c=g("<div/>").insertBefore(c),d=a.oFeatures,e=g("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),h,i,j,n,m,o,k=0;k<f.length;k++){h=null;i=f[k];if("<"==i){j=g("<div/>")[0];n=f[k+1];if("'"==n||'"'==n){m=
"";for(o=2;f[k+o]!=n;)m+=f[k+o],o++;"H"==m?m=b.sJUIHeader:"F"==m&&(m=b.sJUIFooter);-1!=m.indexOf(".")?(n=m.split("."),j.id=n[0].substr(1,n[0].length-1),j.className=n[1]):"#"==m.charAt(0)?j.id=m.substr(1,m.length-1):j.className=m;k+=o}e.append(j);e=g(j)}else if(">"==i)e=e.parent();else if("l"==i&&d.bPaginate&&d.bLengthChange)h=lb(a);else if("f"==i&&d.bFilter)h=mb(a);else if("r"==i&&d.bProcessing)h=nb(a);else if("t"==i)h=ob(a);else if("i"==i&&d.bInfo)h=pb(a);else if("p"==i&&d.bPaginate)h=qb(a);else if(0!==
p.ext.feature.length){j=p.ext.feature;o=0;for(n=j.length;o<n;o++)if(i==j[o].cFeature){h=j[o].fnInit(a);break}}h&&(j=a.aanFeatures,j[i]||(j[i]=[]),j[i].push(h),e.append(h))}c.replaceWith(e)}function $(a,b){var c=g(b).children("tr"),d,e,f,h,i,j,n,m,o,k;a.splice(0,a.length);f=0;for(j=c.length;f<j;f++)a.push([]);f=0;for(j=c.length;f<j;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){m=1*e.getAttribute("colspan");o=1*e.getAttribute("rowspan");m=!m||
0===m||1===m?1:m;o=!o||0===o||1===o?1:o;h=0;for(i=a[f];i[h];)h++;n=h;k=1===m?!0:!1;for(i=0;i<m;i++)for(h=0;h<o;h++)a[f+h][n+i]={cell:e,unique:k},a[f+h].nTr=d}e=e.nextSibling}}}function ma(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],$(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,h=c[b].length;f<h;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function na(a,b,c){t(a,"aoServerParams","serverParams",[b]);if(b&&g.isArray(b)){var d={},e=/(.*?)\[\]$/;g.each(b,function(a,b){var c=
b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,h=a.ajax,i=a.oInstance;if(g.isPlainObject(h)&&h.data){f=h.data;var j=g.isFunction(f)?f(b):f,b=g.isFunction(f)&&j?j:g.extend(!0,b,j);delete h.data}j={data:b,success:function(b){var d=b.error||b.sError;d&&a.oApi._fnLog(a,0,d);a.json=b;t(a,null,"xhr",[a,b]);c(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=a.oApi._fnLog;"parsererror"==c?d(a,0,"Invalid JSON response",1):4===b.readyState&&
d(a,0,"Ajax error",7);C(a,!1)}};a.oAjaxData=b;t(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(i,a.sAjaxSource,g.map(b,function(a,b){return{name:b,value:a}}),c,a):a.sAjaxSource||"string"===typeof h?a.jqXHR=g.ajax(g.extend(j,{url:h||a.sAjaxSource})):g.isFunction(h)?a.jqXHR=h.call(i,b,c,a):(a.jqXHR=g.ajax(g.extend(j,h)),h.data=f)}function ib(a){if(a.bAjaxDataGet){a.iDraw++;C(a,!0);var b=rb(a);na(a,b,function(b){sb(a,b)},a);return!1}return!0}function rb(a){var b=a.aoColumns,c=b.length,d=a.oFeatures,
e=a.oPreviousSearch,f=a.aoPreSearchCols,h,i=[],j,n,m,o=Q(a);h=a._iDisplayStart;j=!1!==d.bPaginate?a._iDisplayLength:-1;var k=function(a,b){i.push({name:a,value:b})};k("sEcho",a.iDraw);k("iColumns",c);k("sColumns",B(b,"sName").join(","));k("iDisplayStart",h);k("iDisplayLength",j);var l={draw:a.iDraw,columns:[],order:[],start:h,length:j,search:{value:e.sSearch,regex:e.bRegex}};for(h=0;h<c;h++)n=b[h],m=f[h],j="function"==typeof n.mData?"function":n.mData,l.columns.push({data:j,name:n.sName,searchable:n.bSearchable,
orderable:n.bSortable,search:{value:m.sSearch,regex:m.bRegex}}),k("mDataProp_"+h,j),d.bFilter&&(k("sSearch_"+h,m.sSearch),k("bRegex_"+h,m.bRegex),k("bSearchable_"+h,n.bSearchable)),d.bSort&&k("bSortable_"+h,n.bSortable);d.bFilter&&(k("sSearch",e.sSearch),k("bRegex",e.bRegex));d.bSort&&(g.each(o,function(a,b){l.order.push({column:b.col,dir:b.dir});k("iSortCol_"+a,b.col);k("sSortDir_"+a,b.dir)}),k("iSortingCols",o.length));b=p.ext.legacy.ajax;return null===b?a.sAjaxSource?i:l:b?i:l}function sb(a,b){var c=
b.sEcho!==l?b.sEcho:b.draw,d=b.iTotalRecords!==l?b.iTotalRecords:b.recordsTotal,e=b.iTotalDisplayRecords!==l?b.iTotalDisplayRecords:b.recordsFiltered;if(c){if(1*c<a.iDraw)return;a.iDraw=1*c}ja(a);a._iRecordsTotal=parseInt(d,10);a._iRecordsDisplay=parseInt(e,10);c=oa(a,b);d=0;for(e=c.length;d<e;d++)H(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;J(a);a._bInitComplete||pa(a,b);a.bAjaxDataGet=!0;C(a,!1)}function oa(a,b){var c=g.isPlainObject(a.ajax)&&a.ajax.dataSrc!==l?a.ajax.dataSrc:
a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?T(c)(b):b}function mb(a){var b=a.oClasses,c=a.sTableId,d=a.oPreviousSearch,e=a.aanFeatures,f='<input type="search" class="'+b.sFilterInput+'"/>',h=a.oLanguage.sSearch,h=h.match(/_INPUT_/)?h.replace("_INPUT_",f):h+f,b=g("<div/>",{id:!e.f?c+"_filter":null,"class":b.sFilter}).append(g("<label/>").append(h)),e=function(){var b=!this.value?"":this.value;b!=d.sSearch&&(aa(a,{sSearch:b,bRegex:d.bRegex,bSmart:d.bSmart,bCaseInsensitive:d.bCaseInsensitive}),
a._iDisplayStart=0,J(a))},i=g("input",b).val(d.sSearch.replace('"',"&quot;")).bind("keyup.DT search.DT input.DT paste.DT cut.DT","ssp"===z(a)?Ma(e,400):e).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);g(a.nTable).on("filter.DT",function(){try{i[0]!==N.activeElement&&i.val(d.sSearch)}catch(a){}});return b[0]}function aa(a,b,c){var d=a.oPreviousSearch,e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};
Da(a);if("ssp"!=z(a)){tb(a,b.sSearch,c,b.bEscapeRegex!==l?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)ub(a,e[b].sSearch,b,e[b].bEscapeRegex!==l?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);vb(a)}else f(b);a.bFiltered=!0;t(a,null,"search",[a])}function vb(a){for(var b=p.ext.search,c=a.aiDisplay,d,e,f=0,h=b.length;f<h;f++)for(var i=c.length-1;0<=i;i--)e=c[i],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData)||c.splice(i,1)}function ub(a,b,c,d,
e,f){if(""!==b)for(var h=a.aiDisplay,d=Na(b,d,e,f),e=h.length-1;0<=e;e--)b=a.aoData[h[e]]._aFilterData[c],d.test(b)||h.splice(e,1)}function tb(a,b,c,d,e,f){var d=Na(b,d,e,f),e=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,h;0!==p.ext.search.length&&(c=!0);h=wb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(h||c||e.length>b.length||0!==b.indexOf(e)||a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)d.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Na(a,b,c,d){a=b?a:
Oa(a);c&&(a="^(?=.*?"+g.map(a.match(/"[^"]+"|[^ ]+/g)||"",function(a){return'"'===a.charAt(0)?a.match(/^"(.*)"$/)[1]:a}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function Oa(a){return a.replace(Sb,"\\$1")}function wb(a){var b=a.aoColumns,c,d,e,f,h,i,j,g,m=p.ext.type.search;c=!1;d=0;for(f=a.aoData.length;d<f;d++)if(g=a.aoData[d],!g._aFilterData){i=[];e=0;for(h=b.length;e<h;e++)c=b[e],c.bSearchable?(j=A(a,d,e,"filter"),j=m[c.sType]?m[c.sType](j):null!==j?j:""):j="",j.indexOf&&-1!==j.indexOf("&")&&
(qa.innerHTML=j,j=Tb?qa.textContent:qa.innerText),j.replace&&(j=j.replace(/[\r\n]/g,"")),i.push(j);g._aFilterData=i;g._sFilterRow=i.join("  ");c=!0}return c}function pb(a){var b=a.sTableId,c=a.aanFeatures.i,d=g("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:xb,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),g(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function xb(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,
d=a._iDisplayStart+1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),h=a.fnRecordsDisplay(),i=h?c.sInfo:c.sInfoEmpty;h!==f&&(i+=" "+c.sInfoFiltered);i+=c.sInfoPostFix;i=yb(a,i);c=c.fnInfoCallback;null!==c&&(i=c.call(a.oInstance,a,d,e,f,h,i));g(b).html(i)}}function yb(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),h=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,
c.call(a,f)).replace(/_PAGE_/g,c.call(a,h?1:Math.ceil(d/e))).replace(/_PAGES_/g,c.call(a,h?1:Math.ceil(f/e)))}function ra(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;if(a.bInitialised){kb(a);hb(a);I(a,a.aoHeader);I(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ca(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=s(f.sWidth));K(a);e=z(a);"ssp"!=e&&("ajax"==e?na(a,[],function(c){var f=oa(a,c);for(b=0;b<f.length;b++)H(a,f[b]);a.iInitDisplayStart=d;K(a);C(a,!1);pa(a,c)},a):(C(a,
!1),pa(a)))}else setTimeout(function(){ra(a)},200)}function pa(a,b){a._bInitComplete=!0;b&&U(a);t(a,"aoInitComplete","init",[a,b])}function Pa(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Qa(a);t(a,null,"length",[a,c])}function lb(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=g.isArray(d[0]),f=e?d[0]:d,e=e?d[1]:d,d=g("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),h=0,i=f.length;h<i;h++)d[0][h]=new Option(e[h],f[h]);var j=g("<div><label/></div>").addClass(b.sLength);
a.aanFeatures.l||(j[0].id=c+"_length");b=a.oLanguage.sLengthMenu.split(/(_MENU_)/);j.children().append(1<b.length?[b[0],d,b[2]]:b[0]);g("select",j).val(a._iDisplayLength).bind("change.DT",function(){Pa(a,g(this).val());J(a)});g(a.nTable).bind("length.dt.DT",function(a,b,c){g("select",j).val(c)});return j[0]}function qb(a){var b=a.sPaginationType,c=p.ext.pager[b],d="function"===typeof c,e=function(a){J(a)},b=g("<div/>").addClass(a.oClasses.sPaging+b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=
a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,j=a._iDisplayLength,g=a.fnRecordsDisplay(),m=-1===j,b=m?0:Math.ceil(b/j),j=m?1:Math.ceil(g/j),g=c(b,j),o,m=0;for(o=f.p.length;m<o;m++)La(a,"pageButton")(a,f.p[m],m,g,b,j)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Ra(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>
d&&(d=0)):"next"==b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:O(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(t(a,null,"page",[a]),c&&J(a));return b}function nb(a){return g("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&g(a.aanFeatures.r).css("display",b?"block":"none");t(a,null,"processing",[a,b])}function ob(a){var b=
g(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,h=b.children("caption"),i=h.length?h[0]._captionSide:null,j=g(b[0].cloneNode(!1)),n=g(b[0].cloneNode(!1)),m=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");m.length||(m=null);c=g("<div/>",{"class":f.sScrollWrapper}).append(g("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:s(d):"100%"}).append(g("<div/>",
{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(j.removeAttr("id").css("margin-left",0).append(b.children("thead")))).append("top"===i?h:null)).append(g("<div/>",{"class":f.sScrollBody}).css({overflow:"auto",height:!e?null:s(e),width:!d?null:s(d)}).append(b));m&&c.append(g("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:s(d):"100%"}).append(g("<div/>",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",
0).append(b.children("tfoot")))).append("bottom"===i?h:null));var b=c.children(),o=b[0],f=b[1],k=m?b[2]:null;d&&g(f).scroll(function(){var a=this.scrollLeft;o.scrollLeft=a;m&&(k.scrollLeft=a)});a.nScrollHead=o;a.nScrollBody=f;a.nScrollFoot=k;a.aoDrawCallback.push({fn:V,sName:"scrolling"});return c[0]}function V(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,f=b.iBarWidth,h=g(a.nScrollHead),i=h[0].style,j=h.children("div"),n=j[0].style,m=j.children("table"),j=a.nScrollBody,o=g(j),k=j.style,l=g(a.nScrollFoot).children("div"),
p=l.children("table"),r=g(a.nTHead),q=g(a.nTable),ba=q[0],L=ba.style,t=a.nTFoot?g(a.nTFoot):null,ca=a.oBrowser,v=ca.bScrollOversize,x,u,y,w,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};q.children("thead, tfoot").remove();z=r.clone().prependTo(q);x=r.find("tr");y=z.find("tr");z.find("th, td").removeAttr("tabindex");t&&(w=t.clone().prependTo(q),u=t.find("tr"),w=w.find("tr"));c||(k.width="100%",h[0].style.width=
"100%");g.each(ma(a,z),function(b,c){D=ga(a,b);c.style.width=a.aoColumns[D].sWidth});t&&F(function(a){a.style.width=""},w);b.bCollapse&&""!==e&&(k.height=o[0].offsetHeight+r[0].offsetHeight+"px");h=q.outerWidth();if(""===c){if(L.width="100%",v&&(q.find("tbody").height()>j.offsetHeight||"scroll"==o.css("overflow-y")))L.width=s(q.outerWidth()-f)}else""!==d?L.width=s(d):h==o.width()&&o.height()<q.height()?(L.width=s(h-f),q.outerWidth()>h-f&&(L.width=s(h))):L.width=s(h);h=q.outerWidth();F(E,y);F(function(a){C.push(a.innerHTML);
A.push(s(g(a).css("width")))},y);F(function(a,b){a.style.width=A[b]},x);g(y).height(0);t&&(F(E,w),F(function(a){B.push(s(g(a).css("width")))},w),F(function(a,b){a.style.width=B[b]},u),g(w).height(0));F(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+C[b]+"</div>";a.style.width=A[b]},y);t&&F(function(a,b){a.innerHTML="";a.style.width=B[b]},w);if(q.outerWidth()<h){u=j.scrollHeight>j.offsetHeight||"scroll"==o.css("overflow-y")?h+f:h;if(v&&(j.scrollHeight>
j.offsetHeight||"scroll"==o.css("overflow-y")))L.width=s(u-f);(""===c||""!==d)&&O(a,1,"Possible column misalignment",6)}else u="100%";k.width=s(u);i.width=s(u);t&&(a.nScrollFoot.style.width=s(u));!e&&v&&(k.height=s(ba.offsetHeight+f));e&&b.bCollapse&&(k.height=s(e),b=c&&ba.offsetWidth>j.offsetWidth?f:0,ba.offsetHeight<j.offsetHeight&&(k.height=s(ba.offsetHeight+b)));b=q.outerWidth();m[0].style.width=s(b);n.width=s(b);m=q.height()>j.clientHeight||"scroll"==o.css("overflow-y");ca="padding"+(ca.bScrollbarLeft?
"Left":"Right");n[ca]=m?f+"px":"0px";t&&(p[0].style.width=s(b),l[0].style.width=s(b),l[0].style[ca]=m?f+"px":"0px");o.scroll();if(a.bSorted||a.bFiltered)j.scrollTop=0}function F(a,b,c){for(var d=0,e=0,f=b.length,h,i;e<f;){h=b[e].firstChild;for(i=c?c[e].firstChild:null;h;)1===h.nodeType&&(c?a(h,i,d):a(h,d),d++),h=h.nextSibling,i=c?i.nextSibling:null;e++}}function Ca(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,e=d.sY,f=d.sX,h=d.sXInner,i=c.length,d=W(a,"bVisible"),j=g("th",a.nTHead),n=b.getAttribute("width"),
m=b.parentNode,o=!1,k,l;for(k=0;k<d.length;k++)l=c[d[k]],null!==l.sWidth&&(l.sWidth=zb(l.sWidthOrig,m),o=!0);if(!o&&!f&&!e&&i==Y(a)&&i==j.length)for(k=0;k<i;k++)c[k].sWidth=s(j.eq(k).width());else{i=g(b.cloneNode(!1)).css("visibility","hidden").removeAttr("id").append(g(a.nTHead).clone(!1)).append(g(a.nTFoot).clone(!1)).append(g("<tbody><tr/></tbody>"));i.find("tfoot th, tfoot td").css("width","");var p=i.find("tbody tr"),j=ma(a,i.find("thead")[0]);for(k=0;k<d.length;k++)l=c[d[k]],j[k].style.width=
null!==l.sWidthOrig&&""!==l.sWidthOrig?s(l.sWidthOrig):"";if(a.aoData.length)for(k=0;k<d.length;k++)o=d[k],l=c[o],g(Ab(a,o)).clone(!1).append(l.sContentPadding).appendTo(p);i.appendTo(m);f&&h?i.width(h):f?(i.css("width","auto"),i.width()<m.offsetWidth&&i.width(m.offsetWidth)):e?i.width(m.offsetWidth):n&&i.width(n);Bb(a,i[0]);if(f){for(k=h=0;k<d.length;k++)l=c[d[k]],e=g(j[k]).outerWidth(),h+=null===l.sWidthOrig?e:parseInt(l.sWidth,10)+e-g(j[k]).width();i.width(s(h));b.style.width=s(h)}for(k=0;k<d.length;k++)if(l=
c[d[k]],e=g(j[k]).width())l.sWidth=s(e);b.style.width=s(i.css("width"));i.remove()}n&&(b.style.width=s(n));if((n||f)&&!a._reszEvt)g(za).bind("resize.DT-"+a.sInstance,Ma(function(){U(a)})),a._reszEvt=!0}function Ma(a,b){var c=b||200,d,e;return function(){var b=this,h=+new Date,i=arguments;d&&h<d+c?(clearTimeout(e),e=setTimeout(function(){d=l;a.apply(b,i)},c)):d?(d=h,a.apply(b,i)):d=h}}function zb(a,b){if(!a)return 0;var c=g("<div/>").css("width",s(a)).appendTo(b||N.body),d=c[0].offsetWidth;c.remove();
return d}function Bb(a,b){var c=a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(g(b).outerWidth()-c)}function Ab(a,b){var c=Cb(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?g("<td/>").html(A(a,c,b,"display"))[0]:d.anCells[b]}function Cb(a,b){for(var c,d=-1,e=-1,f=0,h=a.aoData.length;f<h;f++)c=A(a,f,b,"display")+"",c=c.replace(Ub,""),c.length>d&&(d=c.length,e=f);return e}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Db(){if(!p.__scrollbarWidth){var a=
g("<p/>").css({width:"100%",height:200,padding:0})[0],b=g("<div/>").css({position:"absolute",top:0,left:0,width:200,height:150,padding:0,overflow:"hidden",visibility:"hidden"}).append(a).appendTo("body"),c=a.offsetWidth;b.css("overflow","scroll");a=a.offsetWidth;c===a&&(a=b[0].clientWidth);b.remove();p.__scrollbarWidth=c-a}return p.__scrollbarWidth}function Q(a){var b,c,d=[],e=a.aoColumns,f,h,i,j;b=a.aaSortingFixed;c=g.isPlainObject(b);var n=[];f=function(a){a.length&&!g.isArray(a[0])?n.push(a):n.push.apply(n,
a)};g.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<n.length;a++){j=n[a][0];f=e[j].aDataSort;b=0;for(c=f.length;b<c;b++)h=f[b],i=e[h].sType||"string",d.push({src:j,col:h,dir:n[a][1],index:n[a][2],type:i,formatter:p.ext.type.order[i+"-pre"]})}return d}function jb(a){var b,c,d=[],e=p.ext.type.order,f=a.aoData,h=0,i,g=a.aiDisplayMaster,n;Da(a);n=Q(a);b=0;for(c=n.length;b<c;b++)i=n[b],i.formatter&&h++,Eb(a,i.col);if("ssp"!=z(a)&&0!==n.length){b=0;for(c=g.length;b<c;b++)d[g[b]]=
b;h===n.length?g.sort(function(a,b){var c,e,h,g,i=n.length,j=f[a]._aSortData,l=f[b]._aSortData;for(h=0;h<i;h++)if(g=n[h],c=j[g.col],e=l[g.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===g.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):g.sort(function(a,b){var c,h,g,i,j=n.length,l=f[a]._aSortData,p=f[b]._aSortData;for(g=0;g<j;g++)if(i=n[g],c=l[i.col],h=p[i.col],i=e[i.type+"-"+i.dir]||e["string-"+i.dir],c=i(c,h),0!==c)return c;c=d[a];h=d[b];return c<h?-1:c>h?1:0})}a.bSorted=!0}function Fb(a){for(var b,c,
d=a.aoColumns,e=Q(a),a=a.oLanguage.oAria,f=0,h=d.length;f<h;f++){c=d[f];var i=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var g=c.nTh;g.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(g.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=i[e[0].index+1]||i[0]):c=i[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);g.setAttribute("aria-label",b)}}function Sa(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,h=function(a){var b=a._idx;b===l&&(b=g.inArray(a[1],
f));return b+1>=f.length?0:b+1};c&&a.oFeatures.bSortMulti?(c=g.inArray(b,B(e,"0")),-1!==c?(b=h(e[c]),e[c][1]=f[b],e[c]._idx=b):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=h(e[0]),e.length=1,e[0][1]=f[b],e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);K(a);"function"==typeof d&&d(a)}function Ka(a,b,c,d){var e=a.aoColumns[c];Ta(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Sa(a,c,b.shiftKey,d);"ssp"!==z(a)&&C(a,!1)},0)):Sa(a,
c,b.shiftKey,d))})}function sa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=Q(a),e=a.oFeatures,f,h;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)h=b[e].src,g(B(a.aoData,"anCells",h)).removeClass(c+(2>e?e+1:3));e=0;for(f=d.length;e<f;e++)h=d[e].src,g(B(a.aoData,"anCells",h)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Eb(a,b){var c=a.aoColumns[b],d=p.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,X(a,b)));for(var f,h=p.ext.type.order[c.sType+"-pre"],g=0,j=a.aoData.length;g<
j;g++)if(c=a.aoData[g],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[g]:A(a,g,b,"sort"),c._aSortData[b]=h?h(f):f}function ta(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={iCreate:+new Date,iStart:a._iDisplayStart,iLength:a._iDisplayLength,aaSorting:g.extend(!0,[],a.aaSorting),oSearch:g.extend(!0,{},a.oPreviousSearch),aoSearchCols:g.extend(!0,[],a.aoPreSearchCols),abVisCols:B(a.aoColumns,"bVisible")};t(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.fnStateSaveCallback.call(a.oInstance,
a,b)}}function Gb(a){var b,c,d=a.aoColumns;if(a.oFeatures.bStateSave){var e=a.fnStateLoadCallback.call(a.oInstance,a);if(e&&(b=t(a,"aoStateLoadParams","stateLoadParams",[a,e]),-1===g.inArray(!1,b)&&(b=a.iStateDuration,!(0<b&&e.iCreate<+new Date-1E3*b)&&d.length===e.aoSearchCols.length))){a.oLoadedState=g.extend(!0,{},e);a._iDisplayStart=e.iStart;a.iInitDisplayStart=e.iStart;a._iDisplayLength=e.iLength;a.aaSorting=g.map(e.aaSorting,function(a){return a[0]>=d.length?[0,a[1]]:a});g.extend(a.oPreviousSearch,
e.oSearch);g.extend(!0,a.aoPreSearchCols,e.aoSearchCols);var f=e.abVisCols;b=0;for(c=f.length;b<c;b++)d[b].bVisible=f[b];t(a,"aoStateLoaded","stateLoaded",[a,e])}}}function ua(a){var b=p.settings,a=g.inArray(a,B(b,"nTable"));return-1!==a?b[a]:null}function O(a,b,c,d){c="DataTables warning: "+(null!==a?"table id="+a.sTableId+" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)za.console&&console.log&&console.log(c);else if(a=p.ext,"alert"==(a.sErrMode||
a.errMode))alert(c);else throw Error(c);}function D(a,b,c,d){g.isArray(c)?g.each(c,function(c,d){g.isArray(d)?D(a,b,d[0],d[1]):D(a,b,d)}):(d===l&&(d=c),b[c]!==l&&(a[d]=b[c]))}function Hb(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],g.isPlainObject(d)?(g.isPlainObject(a[e])||(a[e]={}),g.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&g.isArray(d)?d.slice():d);return a}function Ta(a,b,c){g(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&
(a.preventDefault(),c(a))}).bind("selectstart.DT",function(){return!1})}function y(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function t(a,b,c,d){var e=[];b&&(e=g.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&g(a.nTable).trigger(c+".dt",d);return e}function Qa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;c===a.fnRecordsDisplay()&&(b=c-d);if(-1===d||0>b)b=0;a._iDisplayStart=b}function La(a,b){var c=a.renderer,d=p.ext.renderer[b];return g.isPlainObject(c)&&
c[b]?d[c[b]]||d._:"string"===typeof c?d[c]||d._:d._}function z(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Ua(a,b){var c=[],c=Ib.numbers_length,d=Math.floor(c/2);b<=c?c=R(0,b):a<=d?(c=R(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=R(b-(c-2),b):(c=R(a-1,a+2),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function bb(a){g.each({num:function(b){return va(b,a)},"num-fmt":function(b){return va(b,a,Va)},"html-num":function(b){return va(b,
a,wa)},"html-num-fmt":function(b){return va(b,a,wa,Va)}},function(b,c){u.type.order[b+a+"-pre"]=c})}function Jb(a){return function(){var b=[ua(this[p.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return p.ext.internal[a].apply(this,b)}}var p,u,q,r,x,Wa={},Kb=/[\r\n]/g,wa=/<.*?>/g,Vb=/^[\d\+\-a-zA-Z]/,Sb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Va=/[',$\u00a3\u20ac\u00a5%\u2009\u202F]/g,da=function(a){return!a||"-"===a?!0:!1},Lb=function(a){var b=
parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Mb=function(a,b){Wa[b]||(Wa[b]=RegExp(Oa(b),"g"));return"string"===typeof a?a.replace(/\./g,"").replace(Wa[b],"."):a},Xa=function(a,b,c){var d="string"===typeof a;b&&d&&(a=Mb(a,b));c&&d&&(a=a.replace(Va,""));return!a||"-"===a||!isNaN(parseFloat(a))&&isFinite(a)},Nb=function(a,b,c){return da(a)?!0:a&&"string"!==typeof a?null:Xa(a.replace(wa,""),b,c)?!0:null},B=function(a,b,c){var d=[],e=0,f=a.length;if(c!==l)for(;e<f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);
else for(;e<f;e++)a[e]&&d.push(a[e][b]);return d},xa=function(a,b,c,d){var e=[],f=0,h=b.length;if(d!==l)for(;f<h;f++)e.push(a[b[f]][c][d]);else for(;f<h;f++)e.push(a[b[f]][c]);return e},R=function(a,b){var c=[],d;b===l?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Ja=function(a){var b=[],c,d,e=a.length,f,h=0;d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<h;f++)if(b[f]===c)continue a;b.push(c);h++}return b},w=function(a,b,c){a[b]!==l&&(a[c]=a[b])},Z=/\[.*?\]$/,P=/\(\)$/,qa=g("<div>")[0],Tb=qa.textContent!==
l,Ub=/<.*?>/g;p=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new q(ua(this[u.iApiIndex])):new q(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=g.isArray(a)&&(g.isArray(a[0])||g.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===l||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===l||
a?b.draw(!1):(""!==d.sX||""!==d.sY)&&V(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===l||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],g=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,g);(c===l||c)&&d.draw();return g};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(!a)};this.fnFilter=function(a,b,c,d,e,g){e=this.api(!0);null===
b||b===l?e.search(a,c,d,g):e.column(b).search(a,c,d,g);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==l){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==l||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==l?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==
c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===l||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===l||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return ua(this[u.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};
this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var g=this.api(!0);c===l||null===c?g.row(b).data(a):g.cell(b,c).data(a);(e===l||e)&&g.columns.adjust();(d===l||d)&&g.draw();return 0};this.fnVersionCheck=u.fnVersionCheck;var b=this,c=a===l,d=this.length;c&&(a={});this.oApi=this.internal=u.internal;for(var e in p.ext.internal)e&&(this[e]=Jb(e));this.each(function(){var e={},h=1<d?Hb(e,a,!0):a,i=0,j,n=this.getAttribute("id"),e=!1,m=p.defaults;
if("table"!=this.nodeName.toLowerCase())O(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{cb(m);db(m.column);G(m,m,!0);G(m.column,m.column,!0);G(m,h);var o=p.settings,i=0;for(j=o.length;i<j;i++){if(o[i].nTable==this){j=h.bRetrieve!==l?h.bRetrieve:m.bRetrieve;if(c||j)return o[i].oInstance;if(h.bDestroy!==l?h.bDestroy:m.bDestroy){o[i].oInstance.fnDestroy();break}else{O(o[i],0,"Cannot reinitialise DataTable",3);return}}if(o[i].sTableId==this.id){o.splice(i,1);break}}if(null===n||""===
n)this.id=n="DataTables_Table_"+p.ext._unique++;var k=g.extend(!0,{},p.models.oSettings,{nTable:this,oApi:b.internal,oInit:h,sDestroyWidth:g(this)[0].style.width,sInstance:n,sTableId:n});o.push(k);k.oInstance=1===b.length?b:g(this).dataTable();cb(h);h.oLanguage&&M(h.oLanguage);h.aLengthMenu&&!h.iDisplayLength&&(h.iDisplayLength=g.isArray(h.aLengthMenu[0])?h.aLengthMenu[0][0]:h.aLengthMenu[0]);h=Hb(g.extend(!0,{},m),h);D(k.oFeatures,h,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));
D(k,h,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);D(k.oScroll,h,[["sScrollX","sX"],["sScrollXInner","sXInner"],
["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);D(k.oLanguage,h,"fnInfoCallback");y(k,"aoDrawCallback",h.fnDrawCallback,"user");y(k,"aoServerParams",h.fnServerParams,"user");y(k,"aoStateSaveParams",h.fnStateSaveParams,"user");y(k,"aoStateLoadParams",h.fnStateLoadParams,"user");y(k,"aoStateLoaded",h.fnStateLoaded,"user");y(k,"aoRowCallback",h.fnRowCallback,"user");y(k,"aoRowCreatedCallback",h.fnCreatedRow,"user");y(k,"aoHeaderCallback",h.fnHeaderCallback,"user");y(k,"aoFooterCallback",h.fnFooterCallback,
"user");y(k,"aoInitComplete",h.fnInitComplete,"user");y(k,"aoPreDrawCallback",h.fnPreDrawCallback,"user");n=k.oClasses;h.bJQueryUI?(g.extend(n,p.ext.oJUIClasses,h.oClasses),h.sDom===m.sDom&&"lfrtip"===m.sDom&&(k.sDom='<"H"lfr>t<"F"ip>'),k.renderer)?g.isPlainObject(k.renderer)&&!k.renderer.header&&(k.renderer.header="jqueryui"):k.renderer="jqueryui":g.extend(n,p.ext.classes,h.oClasses);g(this).addClass(n.sTable);if(""!==k.oScroll.sX||""!==k.oScroll.sY)k.oScroll.iBarWidth=Db();!0===k.oScroll.sX&&(k.oScroll.sX=
"100%");k.iInitDisplayStart===l&&(k.iInitDisplayStart=h.iDisplayStart,k._iDisplayStart=h.iDisplayStart);null!==h.iDeferLoading&&(k.bDeferLoading=!0,i=g.isArray(h.iDeferLoading),k._iRecordsDisplay=i?h.iDeferLoading[0]:h.iDeferLoading,k._iRecordsTotal=i?h.iDeferLoading[1]:h.iDeferLoading);""!==h.oLanguage.sUrl?(k.oLanguage.sUrl=h.oLanguage.sUrl,g.getJSON(k.oLanguage.sUrl,null,function(a){M(a);G(m.oLanguage,a);g.extend(true,k.oLanguage,h.oLanguage,a);ra(k)}),e=!0):g.extend(!0,k.oLanguage,h.oLanguage);
null===h.asStripeClasses&&(k.asStripeClasses=[n.sStripeOdd,n.sStripeEven]);var i=k.asStripeClasses,r=g("tbody tr:eq(0)",this);-1!==g.inArray(!0,g.map(i,function(a){return r.hasClass(a)}))&&(g("tbody tr",this).removeClass(i.join(" ")),k.asDestroyStripes=i.slice());var o=[],q,i=this.getElementsByTagName("thead");0!==i.length&&($(k.aoHeader,i[0]),o=ma(k));if(null===h.aoColumns){q=[];i=0;for(j=o.length;i<j;i++)q.push(null)}else q=h.aoColumns;i=0;for(j=q.length;i<j;i++)Aa(k,o?o[i]:null);gb(k,h.aoColumnDefs,
q,function(a,b){fa(k,a,b)});if(r.length){var s=function(a,b){return a.getAttribute("data-"+b)?b:null};g.each(ia(k,r[0]).cells,function(a,b){var c=k.aoColumns[a];if(c.mData===a){var d=s(b,"sort")||s(b,"order"),e=s(b,"filter")||s(b,"search");if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:l,type:d!==null?a+".@data-"+d:l,filter:e!==null?a+".@data-"+e:l};fa(k,a)}}})}var u=k.oFeatures;h.bStateSave&&(u.bStateSave=!0,Gb(k,h),y(k,"aoDrawCallback",ta,"state_save"));if(h.aaSorting===
l){o=k.aaSorting;i=0;for(j=o.length;i<j;i++)o[i][1]=k.aoColumns[i].asSorting[0]}sa(k);u.bSort&&y(k,"aoDrawCallback",function(){if(k.bSorted){var a=Q(k),b={};g.each(a,function(a,c){b[c.src]=c.dir});t(k,null,"order",[k,a,b]);Fb(k)}});y(k,"aoDrawCallback",function(){(k.bSorted||z(k)==="ssp"||u.bDeferRender)&&sa(k)},"sc");eb(k);i=g(this).children("caption").each(function(){this._captionSide=g(this).css("caption-side")});j=g(this).children("thead");0===j.length&&(j=g("<thead/>").appendTo(this));k.nTHead=
j[0];j=g(this).children("tbody");0===j.length&&(j=g("<tbody/>").appendTo(this));k.nTBody=j[0];j=g(this).children("tfoot");if(0===j.length&&0<i.length&&(""!==k.oScroll.sX||""!==k.oScroll.sY))j=g("<tfoot/>").appendTo(this);0===j.length||0===j.children().length?g(this).addClass(n.sNoFooter):0<j.length&&(k.nTFoot=j[0],$(k.aoFooter,k.nTFoot));if(h.aaData)for(i=0;i<h.aaData.length;i++)H(k,h.aaData[i]);else(k.bDeferLoading||"dom"==z(k))&&ha(k,g(k.nTBody).children("tr"));k.aiDisplay=k.aiDisplayMaster.slice();
k.bInitialised=!0;!1===e&&ra(k)}});b=null;return this};var Ob=[],v=Array.prototype,Wb=function(a){var b,c,d=p.settings,e=g.map(d,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=g.inArray(a,e),-1!==b?[d[b]]:null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=g(a):a instanceof g&&(c=a)}else return[];if(c)return c.map(function(){b=g.inArray(this,e);return-1!==b?d[b]:null}).toArray()};
p.Api=q=function(a,b){if(!this instanceof q)throw"DT API must be constructed as a new object";var c=[],d=function(a){(a=Wb(a))&&c.push.apply(c,a)};if(g.isArray(a))for(var e=0,f=a.length;e<f;e++)d(a[e]);else d(a);this.context=Ja(c);b&&this.push.apply(this,b.toArray?b.toArray():b);this.selector={rows:null,cols:null,opts:null};q.extend(this,this,Ob)};q.prototype={concat:v.concat,context:[],each:function(a){if(v.forEach)v.forEach.call(this,a,this);else for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],
b,this);return this},eq:function(a){var b=this.context;return b.length>a?new q(b[a],this[a]):null},filter:function(a){var b=[];if(v.filter)b=v.filter.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new q(this.context,b)},flatten:function(){var a=[];return new q(this.context,a.concat.apply(a,this.toArray()))},join:v.join,indexOf:v.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,
b,c){var d=[],e,f,h,g,j,n=this.context,m,o,k=this.selector;"string"===typeof a&&(c=b,b=a,a=!1);f=0;for(h=n.length;f<h;f++)if("table"===b)e=c(n[f],f),e!==l&&d.push(e);else if("columns"===b||"rows"===b)e=c(n[f],this[f],f),e!==l&&d.push(e);else if("column"===b||"column-rows"===b||"row"===b||"cell"===b){o=this[f];"column-rows"===b&&(m=Ya(n[f],k.opts));g=0;for(j=o.length;g<j;g++)e=o[g],e="cell"===b?c(n[f],e.row,e.column,f,g):c(n[f],e,f,g,m),e!==l&&d.push(e)}return d.length?(a=new q(n,a?d.concat.apply([],
d):d),b=a.selector,b.rows=k.rows,b.cols=k.cols,b.opts=k.opts,a):this},lastIndexOf:v.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(v.map)b=v.map.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new q(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:v.pop,push:v.push,reduce:v.reduce||function(a,b){return fb(this,a,b,0,this.length,1)},reduceRight:v.reduceRight||
function(a,b){return fb(this,a,b,this.length-1,-1,-1)},reverse:v.reverse,selector:null,shift:v.shift,sort:v.sort,splice:v.splice,toArray:function(){return v.slice.call(this)},to$:function(){return g(this)},toJQuery:function(){return g(this)},unique:function(){return new q(this.context,Ja(this))},unshift:v.unshift};q.extend=function(a,b,c){if(b&&(b instanceof q||b.__dt_wrapper)){var d,e,f,h=function(b,c){return function(){var d=b.apply(a,arguments);q.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<
e;d++)f=c[d],b[f.name]="function"===typeof f.val?h(f.val,f):g.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,q.extend(a,b[f.name],f.propExt)}};q.register=r=function(a,b){if(g.isArray(a))for(var c=0,d=a.length;c<d;c++)q.register(a[c],b);else{for(var e=a.split("."),f=Ob,h,i,c=0,d=e.length;c<d;c++){h=(i=-1!==e[c].indexOf("()"))?e[c].replace("()",""):e[c];var j;a:{j=0;for(var n=f.length;j<n;j++)if(f[j].name===h){j=f[j];break a}j=null}j||(j={name:h,val:{},methodExt:[],propExt:[]},f.push(j));c===
d-1?j.val=b:f=i?j.methodExt:j.propExt}q.ready&&p.api.build()}};q.registerPlural=x=function(a,b,c){q.register(a,c);q.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof q?a.length?g.isArray(a[0])?new q(a.context,a[0]):a[0]:l:a})};r("tables()",function(a){var b;if(a){b=q;var c=this.context;if("number"===typeof a)a=[c[a]];else var d=g.map(c,function(a){return a.nTable}),a=g(d).filter(a).map(function(){var a=g.inArray(this,d);return c[a]}).toArray();b=new b(a)}else b=
this;return b});r("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new q(b[0]):a});x("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable})});x("tables().body()","table().body()",function(){return this.iterator("table",function(a){return a.nTBody})});x("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead})});x("tables().footer()","table().footer()",function(){return this.iterator("table",
function(a){return a.nTFoot})});r("draw()",function(a){return this.iterator("table",function(b){K(b,!1===a)})});r("page()",function(a){return a===l?this.page.info().page:this.iterator("table",function(b){Ra(b,a)})});r("page.info()",function(){if(0===this.context.length)return l;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,d=a.fnRecordsDisplay(),e=-1===c;return{page:e?0:Math.floor(b/c),pages:e?1:Math.ceil(d/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d}});
r("page.len()",function(a){return a===l?0!==this.context.length?this.context[0]._iDisplayLength:l:this.iterator("table",function(b){Pa(b,a)})});var Pb=function(a,b,c){"ssp"==z(a)?K(a,b):(C(a,!0),na(a,[],function(c){ja(a);for(var c=oa(a,c),d=0,h=c.length;d<h;d++)H(a,c[d]);K(a,b);C(a,!1)}));if(c){var d=new q(a);d.one("draw",function(){c(d.ajax.json())})}};r("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});r("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});
r("ajax.reload()",function(a,b){return this.iterator("table",function(c){Pb(c,!1===b,a)})});r("ajax.url()",function(a){var b=this.context;if(a===l){if(0===b.length)return l;b=b[0];return b.ajax?g.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){g.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});r("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Pb(c,!1===b,a)})});var Za=function(a,b){var c=[],d,e,f,h,i,j;if(!a||"string"===typeof a||
a.length===l)a=[a];f=0;for(h=a.length;f<h;f++){e=a[f]&&a[f].split?a[f].split(","):[a[f]];i=0;for(j=e.length;i<j;i++)(d=b("string"===typeof e[i]?g.trim(e[i]):e[i]))&&d.length&&c.push.apply(c,d)}return c},$a=function(a){a||(a={});a.filter&&!a.search&&(a.search=a.filter);return{search:a.search||"none",order:a.order||"current",page:a.page||"all"}},ab=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a.length=1,a.context=[a.context[b]],a;a.length=0;return a},Ya=function(a,b){var c,
d,e,f=[],h=a.aiDisplay;c=a.aiDisplayMaster;var i=b.search;d=b.order;e=b.page;if("ssp"==z(a))return"removed"===i?[]:R(0,c.length);if("current"==e){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(h[c])}else if("current"==d||"applied"==d)f="none"==i?c.slice():"applied"==i?h.slice():g.map(c,function(a){return-1===g.inArray(a,h)?a:null});else if("index"==d||"original"==d){c=0;for(d=a.aoData.length;c<d;c++)"none"==i?f.push(c):(e=g.inArray(c,h),(-1===e&&"removed"==i||1===e&&"applied"==i)&&f.push(c))}return f};
r("rows()",function(a,b){a===l?a="":g.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=b;return Za(a,function(a){var b=Lb(a);if(b!==null&&!e)return[b];var i=Ya(c,e);if(b!==null&&g.inArray(b,i)!==-1)return[b];if(!a)return i;for(var b=[],j=0,n=i.length;j<n;j++)b.push(c.aoData[i[j]].nTr);return a.nodeName&&g.inArray(a,b)!==-1?[a._DT_RowIndex]:g(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()})});c.selector.rows=a;c.selector.opts=b;return c});r("rows().nodes()",
function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||l})});r("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return xa(a.aoData,b,"_aData")})});x("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return"search"===a?d._aFilterData:d._aSortData})});x("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",function(b,c){la(b,c,a)})});x("rows().indexes()","row().index()",function(){return this.iterator("row",
function(a,b){return b})});x("rows().remove()","row().remove()",function(){var a=this;return this.iterator("row",function(b,c,d){var e=b.aoData;e.splice(c,1);for(var f=0,h=e.length;f<h;f++)null!==e[f].nTr&&(e[f].nTr._DT_RowIndex=f);g.inArray(c,b.aiDisplay);ka(b.aiDisplayMaster,c);ka(b.aiDisplay,c);ka(a[d],c,!1);Qa(b)})});r("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,h,g=[];f=0;for(h=a.length;f<h;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?g.push(ha(b,c)[0]):
g.push(H(b,c));return g}),c=this.rows(-1);c.pop();c.push.apply(c,b.toArray());return c});r("row()",function(a,b){return ab(this.rows(a,b))});r("row().data()",function(a){var b=this.context;if(a===l)return b.length&&this.length?b[0].aoData[this[0]]._aData:l;b[0].aoData[this[0]]._aData=a;la(b[0],this[0],"data");return this});r("row().node()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||null:null});r("row.add()",function(a){a instanceof g&&a.length&&(a=a[0]);var b=
this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ha(b,a)[0]:H(b,a)});return this.row(b[0])});var Qb=function(a){var b=this.context;if(b.length&&this.length){var c=b[0].aoData[this[0]];if(c._details){(c._detailsShow=a)?c._details.insertAfter(c.nTr):c._details.remove();var d=b[0],e=new q(d);e.off("draw.dt.DT_details column-visibility.dt.DT_details");0<B(d.aoData,"_details").length&&(e.on("draw.dt.DT_details",function(){e.rows({page:"current"}).eq(0).each(function(a){a=
d.aoData[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),e.on("column-visibility.dt.DT_details",function(a,b){for(var c,d=Y(b),e=0,g=b.aoData.length;e<g;e++)c=b.aoData[e],c._details&&c._details.children("td[colspan]").attr("colspan",d)}))}}return this};r("row().child()",function(a,b){var c=this.context;if(a===l)return c.length&&this.length?c[0].aoData[this[0]]._details:l;if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(a.nodeName&&"tr"===a.nodeName.toLowerCase())e.push(a);
else{var c=g("<tr><td/></tr>");g("td",c).addClass(b).html(a)[0].colSpan=Y(d);e.push(c[0])}};if(g.isArray(a)||a instanceof g)for(var h=0,i=a.length;h<i;h++)f(a[h],b);else f(a,b);c._details&&c._details.remove();c._details=g(e);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});r(["row().child.show()","row().child().show()"],function(){Qb.call(this,!0);return this});r(["row().child.hide()","row().child().hide()"],function(){Qb.call(this,!1);return this});r("row().child.isShown()",function(){var a=
this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var Xb=/^(.*):(name|visIdx|visible)$/;r("columns()",function(a,b){a===l?a="":g.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(b){var c=a,f=b.aoColumns,h=B(f,"sName"),i=B(f,"nTh");return Za(c,function(a){var c=Lb(a);if(a==="")return R(f.length);if(c!==null)return[c>=0?c:f.length+c];var e=typeof a==="string"?a.match(Xb):"";if(e)switch(e[2]){case "visIdx":case "visible":a=parseInt(e[1],10);
if(a<0){c=g.map(f,function(a,b){return a.bVisible?b:null});return[c[c.length+a]]}return[ga(b,a)];case "name":return g.map(h,function(a,b){return a===e[1]?b:null})}else return g(i).filter(a).map(function(){return g.inArray(this,i)}).toArray()})});c.selector.cols=a;c.selector.opts=b;return c});x("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh})});x("columns().footer()","column().footer()",function(){return this.iterator("column",
function(a,b){return a.aoColumns[b].nTf})});x("columns().data()","column().data()",function(){return this.iterator("column-rows",function(a,b,c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(A(a,e[d],b,""));return c})});x("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return xa(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)})});x("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,
e){return xa(a.aoData,e,"anCells",b)})});x("columns().visible()","column().visible()",function(a){return this.iterator("column",function(b,c){var d;if(a===l)d=b.aoColumns[c].bVisible;else{var e=b.aoColumns;d=e[c];var f=b.aoData,h,i,j;if(a===l)d=d.bVisible;else{if(d.bVisible!==a){if(a){var n=g.inArray(!0,B(e,"bVisible"),c+1);h=0;for(i=f.length;h<i;h++)j=f[h].nTr,e=f[h].anCells,j&&j.insertBefore(e[c],e[n]||null)}else g(B(b.aoData,"anCells",c)).detach(),d.bVisible=!1,I(b,b.aoHeader),I(b,b.aoFooter),
ta(b);d.bVisible=a;I(b,b.aoHeader);I(b,b.aoFooter);U(b);(b.oScroll.sX||b.oScroll.sY)&&V(b);t(b,null,"column-visibility",[b,c,a]);ta(b)}d=void 0}}return d})});x("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===a?X(b,c):c})});r("columns.adjust()",function(){return this.iterator("table",function(a){U(a)})});r("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return ga(c,
b);if("fromData"===a||"toVisible"===a)return X(c,b)}});r("column()",function(a,b){return ab(this.columns(a,b))});r("cells()",function(a,b,c){g.isPlainObject(a)&&(a.row?(c=b,b=null):(c=a,a=null));g.isPlainObject(b)&&(c=b,b=null);if(null===b||b===l)return this.iterator("table",function(b){var d=a,e=$a(c),f=b.aoData,h=Ya(b,e),e=xa(f,h,"anCells"),i=g([].concat.apply([],e)),j,m=b.aoColumns.length,n,l,p,r;return Za(d,function(a){if(a){if(g.isPlainObject(a))return[a]}else{n=[];l=0;for(p=h.length;l<p;l++){j=
h[l];for(r=0;r<m;r++)n.push({row:j,column:r})}return n}return i.filter(a).map(function(a,b){j=b.parentNode._DT_RowIndex;return{row:j,column:g.inArray(b,f[j].anCells)}}).toArray()})});var d=this.columns(b,c),e=this.rows(a,c),f,h,i,j,n,m=this.iterator("table",function(a,b){f=[];h=0;for(i=e[b].length;h<i;h++){j=0;for(n=d[b].length;j<n;j++)f.push({row:e[b][h],column:d[b][j]})}return f});g.extend(m.selector,{cols:b,rows:a,opts:c});return m});x("cells().nodes()","cell().node()",function(){return this.iterator("cell",
function(a,b,c){return a.aoData[b].anCells[c]})});r("cells().data()",function(){return this.iterator("cell",function(a,b,c){return A(a,b,c)})});x("cells().cache()","cell().cache()",function(a){a="search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]})});x("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:X(a,c)}})});r(["cells().invalidate()","cell().invalidate()"],function(a){var b=
this.selector;this.rows(b.rows,b.opts).invalidate(a);return this});r("cell()",function(a,b,c){return ab(this.cells(a,b,c))});r("cell().data()",function(a){var b=this.context,c=this[0];if(a===l)return b.length&&c.length?A(b[0],c[0].row,c[0].column):l;Ea(b[0],c[0].row,c[0].column,a);la(b[0],c[0].row,"data",c[0].column);return this});r("order()",function(a,b){var c=this.context;if(a===l)return 0!==c.length?c[0].aaSorting:l;"number"===typeof a?a=[[a,b]]:g.isArray(a[0])||(a=Array.prototype.slice.call(arguments));
return this.iterator("table",function(b){b.aaSorting=a.slice()})});r("order.listener()",function(a,b,c){return this.iterator("table",function(d){Ka(d,a,b,c)})});r(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,d){var e=[];g.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});r("search()",function(a,b,c,d){var e=this.context;return a===l?0!==e.length?e[0].oPreviousSearch.sSearch:l:this.iterator("table",function(e){e.oFeatures.bFilter&&
aa(e,g.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),1)})});r(["columns().search()","column().search()"],function(a,b,c,d){return this.iterator("column",function(e,f){var h=e.aoPreSearchCols;if(a===l)return h[f].sSearch;e.oFeatures.bFilter&&(g.extend(h[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),aa(e,e.oPreviousSearch,1))})});p.versionCheck=p.fnVersionCheck=function(a){for(var b=
p.version.split("."),a=a.split("."),c,d,e=0,f=a.length;e<f;e++)if(c=parseInt(b[e],10)||0,d=parseInt(a[e],10)||0,c!==d)return c>d;return!0};p.isDataTable=p.fnIsDataTable=function(a){var b=g(a).get(0),c=!1;g.each(p.settings,function(a,e){if(e.nTable===b||e.nScrollHead===b||e.nScrollFoot===b)c=!0});return c};p.tables=p.fnTables=function(a){return jQuery.map(p.settings,function(b){if(!a||a&&g(b.nTable).is(":visible"))return b.nTable})};p.camelToHungarian=G;r("$()",function(a,b){var c=this.rows(b).nodes(),
c=g(c);return g([].concat(c.filter(a).toArray(),c.find(a).toArray()))});g.each(["on","one","off"],function(a,b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);-1===a[0].indexOf(".dt")&&(a[0]+=".dt");var d=g(this.tables().nodes());d[b].apply(d,a);return this})});r("clear()",function(){return this.iterator("table",function(a){ja(a)})});r("settings()",function(){return new q(this.context,this.context)});r("data()",function(){return this.iterator("table",function(a){return B(a.aoData,
"_aData")}).flatten()});r("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,h=b.nTHead,i=b.nTFoot,j=g(e),f=g(f),n=g(b.nTableWrapper),m=g.map(b.aoData,function(a){return a.nTr}),l;b.bDestroying=!0;t(b,"aoDestroyCallback","destroy",[b]);a||(new q(b)).columns().visible(!0);n.unbind(".DT").find(":not(tbody *)").unbind(".DT");g(za).unbind(".DT-"+b.sInstance);e!=h.parentNode&&(j.children("thead").detach(),j.append(h));
i&&e!=i.parentNode&&(j.children("tfoot").detach(),j.append(i));j.detach();n.detach();b.aaSorting=[];b.aaSortingFixed=[];sa(b);g(m).removeClass(b.asStripeClasses.join(" "));g("th, td",h).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&&(g("th span."+d.sSortIcon+", td span."+d.sSortIcon,h).detach(),g("th, td",h).each(function(){var a=g("div."+d.sSortJUIWrapper,this);g(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(e,b.nTableReinsertBefore);
f.children().detach();f.append(m);j.css("width",b.sDestroyWidth).removeClass(d.sTable);(l=b.asDestroyStripes.length)&&f.children().each(function(a){g(this).addClass(b.asDestroyStripes[a%l])});c=g.inArray(b,p.settings);-1!==c&&p.settings.splice(c,1)})});p.version="1.10.0";p.settings=[];p.models={};p.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};p.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};p.models.oColumn=
{idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};p.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,
aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,
fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,
iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",
sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sUrl:"",sZeroRecords:"No matching records found"},oSearch:g.extend({},p.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};S(p.defaults);p.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,
mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};S(p.defaults.column);p.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,
bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,
nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:l,oAjaxData:l,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,
oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==z(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==z(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,
nScrollFoot:null,aLastSort:[],oPlugins:{}};p.ext=u={classes:{},errMode:"alert",feature:[],search:[],internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:p.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:p.version};g.extend(u,{afnFiltering:u.search,aTypes:u.type.detect,ofnSearch:u.type.search,oSort:u.type.order,afnSortData:u.order,aoFeatures:u.feature,oApi:u.internal,oStdClasses:u.classes,oPagination:u.pager});
g.extend(p.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",
sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",
sJUIHeader:"",sJUIFooter:""});var ya="",ya="",E=ya+"ui-state-default",ea=ya+"css_right ui-icon ui-icon-",Rb=ya+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";g.extend(p.ext.oJUIClasses,p.ext.classes,{sPageButton:"fg-button ui-button "+E,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:E+" sorting_asc",sSortDesc:E+" sorting_desc",sSortable:E+" sorting",
sSortableAsc:E+" sorting_asc_disabled",sSortableDesc:E+" sorting_desc_disabled",sSortableNone:E+" sorting_disabled",sSortJUIAsc:ea+"triangle-1-n",sSortJUIDesc:ea+"triangle-1-s",sSortJUI:ea+"carat-2-n-s",sSortJUIAscAllowed:ea+"carat-1-n",sSortJUIDescAllowed:ea+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+E,sScrollFoot:"dataTables_scrollFoot "+E,sHeaderTH:E,sFooterTH:E,sJUIHeader:Rb+" ui-corner-tl ui-corner-tr",sJUIFooter:Rb+
" ui-corner-bl ui-corner-br"});var Ib=p.ext.pager;g.extend(Ib,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",Ua(a,b),"next"]},full_numbers:function(a,b){return["first","previous",Ua(a,b),"next","last"]},_numbers:Ua,numbers_length:7});g.extend(!0,p.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var h=a.oClasses,i=a.oLanguage.oPaginate,j,l,m=0,o=function(b,d){var k,p,r,q,s=function(b){Ra(a,b.data.action,
true)};k=0;for(p=d.length;k<p;k++){q=d[k];if(g.isArray(q)){r=g("<"+(q.DT_el||"div")+"/>").appendTo(b);o(r,q)}else{l=j="";switch(q){case "ellipsis":b.append("<span>&hellip;</span>");break;case "first":j=i.sFirst;l=q+(e>0?"":" "+h.sPageButtonDisabled);break;case "previous":j=i.sPrevious;l=q+(e>0?"":" "+h.sPageButtonDisabled);break;case "next":j=i.sNext;l=q+(e<f-1?"":" "+h.sPageButtonDisabled);break;case "last":j=i.sLast;l=q+(e<f-1?"":" "+h.sPageButtonDisabled);break;default:j=q+1;l=e===q?h.sPageButtonActive:
""}if(j){r=g("<a>",{"class":h.sPageButton+" "+l,"aria-controls":a.sTableId,"data-dt-idx":m,tabindex:a.iTabIndex,id:c===0&&typeof q==="string"?a.sTableId+"_"+q:null}).html(j).appendTo(b);Ta(r,{action:q},s);m++}}}},k=g(N.activeElement).data("dt-idx");o(g(b).empty(),d);k!==null&&g(b).find("[data-dt-idx="+k+"]").focus()}}});var va=function(a,b,c,d){if(!a||"-"===a)return-Infinity;b&&(a=Mb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};g.extend(u.type.order,{"date-pre":function(a){return Date.parse(a)||
0},"html-pre":function(a){return!a?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return"string"===typeof a?a.toLowerCase():!a||!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});bb("");g.extend(p.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c)?"num"+c:null},function(a){if(a&&!Vb.test(a))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||da(a)?"date":
null},function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Nb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Nb(a,c,!0)?"html-num-fmt"+c:null},function(a){return da(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);g.extend(p.ext.type.search,{html:function(a){return da(a)?"":"string"===typeof a?a.replace(Kb," ").replace(wa,""):""},string:function(a){return da(a)?"":"string"===typeof a?a.replace(Kb,
" "):a}});g.extend(!0,p.ext.renderer,{header:{_:function(a,b,c,d){g(a.nTable).on("order.dt.DT",function(a,f,g,i){a=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(i[a]=="asc"?d.sSortAsc:i[a]=="desc"?d.sSortDesc:c.sSortingClass)})},jqueryui:function(a,b,c,d){var e=c.idx;g("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(g("<span/>").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);g(a.nTable).on("order.dt.DT",function(a,g,i,j){b.removeClass(d.sSortAsc+
" "+d.sSortDesc).addClass(j[e]=="asc"?d.sSortAsc:j[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(j[e]=="asc"?d.sSortJUIAsc:j[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)})}}});p.render={number:function(a,b,c,d){return{display:function(e){var e=parseFloat(e),f=parseInt(e,10),e=c?(b+(e-f).toFixed(c)).substring(2):"";return(d||"")+f.toString().replace(/\B(?=(\d{3})+(?!\d))/g,
a)+e}}}};g.extend(p.ext.internal,{_fnExternApiFunc:Jb,_fnBuildAjax:na,_fnAjaxUpdate:ib,_fnAjaxParameters:rb,_fnAjaxUpdateDraw:sb,_fnAjaxDataSrc:oa,_fnAddColumn:Aa,_fnColumnOptions:fa,_fnAdjustColumnSizing:U,_fnVisibleToColumnIndex:ga,_fnColumnIndexToVisible:X,_fnVisbleColumns:Y,_fnGetColumns:W,_fnColumnTypes:Da,_fnApplyColumnDefs:gb,_fnHungarianMap:S,_fnCamelToHungarian:G,_fnLanguageCompat:M,_fnBrowserDetect:eb,_fnAddData:H,_fnAddTr:ha,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==l?b._DT_RowIndex:
null},_fnNodeToColumnIndex:function(a,b,c){return g.inArray(c,a.aoData[b].anCells)},_fnGetCellData:A,_fnSetCellData:Ea,_fnSplitObjNotation:Ga,_fnGetObjectDataFn:T,_fnSetObjectDataFn:Ba,_fnGetDataMaster:Ha,_fnClearTable:ja,_fnDeleteIndex:ka,_fnInvalidateRow:la,_fnGetRowElements:ia,_fnCreateTr:Fa,_fnBuildHead:hb,_fnDrawHead:I,_fnDraw:J,_fnReDraw:K,_fnAddOptionsHtml:kb,_fnDetectHeader:$,_fnGetUniqueThs:ma,_fnFeatureHtmlFilter:mb,_fnFilterComplete:aa,_fnFilterCustom:vb,_fnFilterColumn:ub,_fnFilter:tb,
_fnFilterCreateSearch:Na,_fnEscapeRegex:Oa,_fnFilterData:wb,_fnFeatureHtmlInfo:pb,_fnUpdateInfo:xb,_fnInfoMacros:yb,_fnInitialise:ra,_fnInitComplete:pa,_fnLengthChange:Pa,_fnFeatureHtmlLength:lb,_fnFeatureHtmlPaginate:qb,_fnPageChange:Ra,_fnFeatureHtmlProcessing:nb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:ob,_fnScrollDraw:V,_fnApplyToChildren:F,_fnCalculateColumnWidths:Ca,_fnThrottle:Ma,_fnConvertToWidth:zb,_fnScrollingWidthAdjust:Bb,_fnGetWidestNode:Ab,_fnGetMaxLenString:Cb,_fnStringToCss:s,_fnScrollBarWidth:Db,
_fnSortFlatten:Q,_fnSort:jb,_fnSortAria:Fb,_fnSortListener:Sa,_fnSortAttachListener:Ka,_fnSortingClasses:sa,_fnSortData:Eb,_fnSaveState:ta,_fnLoadState:Gb,_fnSettingsFromNode:ua,_fnLog:O,_fnMap:D,_fnBindAction:Ta,_fnCallbackReg:y,_fnCallbackFire:t,_fnLengthOverflow:Qa,_fnRenderer:La,_fnDataSource:z,_fnRowAttributes:Ia,_fnCalculateEnd:function(){}});g.fn.dataTable=p;g.fn.dataTableSettings=p.settings;g.fn.dataTableExt=p.ext;g.fn.DataTable=function(a){return g(this).dataTable(a).api()};g.each(p,function(a,
b){g.fn.DataTable[a]=b});return g.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],M):"object"===typeof exports?M(require("jquery")):jQuery&&!jQuery.fn.dataTable&&M(jQuery)})(window,document);
;// Helper function so we know what has changed
// http://stackoverflow.com/questions/12166982
ko.observableArray.fn.subscribeArrayChanged = function (addCallback, deleteCallback, clearCallback) {
    var previousValue = undefined;
    this.subscribe(function (_previousValue) {
        previousValue = _previousValue.slice(0);
    }, undefined, 'beforeChange');
    this.subscribe(function (latestValue) {

        if (!latestValue || !latestValue.length) {
            if (clearCallback)
                clearCallback();
        } else {
            var editScript = ko.utils.compareArrays(previousValue, latestValue);
            for (var i = 0, j = editScript.length; i < j; i++) {
                switch (editScript[i].status) {
                    case "retained":
                        break;
                    case "deleted":
                        if (deleteCallback)
                            deleteCallback(editScript[i].value);
                        break;
                    case "added":
                        if (addCallback)
                            addCallback(editScript[i].value, editScript[i].index);
                        break;
                }
            }
        }
        previousValue = undefined;
    });
};

;/*!
 * Timepicker Component for Twitter Bootstrap
 *
 * Copyright 2013 Joris de Wit
 *
 * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
(function($, window, document, undefined) {
  'use strict';

  // TIMEPICKER PUBLIC CLASS DEFINITION
  var Timepicker = function(element, options) {
    this.widget = '';
    this.$element = $(element);
    this.defaultTime = options.defaultTime;
    this.disableFocus = options.disableFocus;
    this.isOpen = options.isOpen;
    this.minuteStep = options.minuteStep;
    this.modalBackdrop = options.modalBackdrop;
    this.secondStep = options.secondStep;
    this.showInputs = options.showInputs;
    this.showMeridian = options.showMeridian;
    this.showSeconds = options.showSeconds;
    this.template = options.template;
    this.appendWidgetTo = options.appendWidgetTo;
    this.duration = options.duration;
    this._init();
  };

  Timepicker.prototype = {

    constructor: Timepicker,

    _init: function() {
      var self = this;

      if (this.$element.parent().hasClass('input-append') || this.$element.parent().hasClass('input-prepend')) {
        this.$element.parent('.input-append, .input-prepend').find('.add-on').on({
          'click.timepicker': $.proxy(this.showWidget, this)
        });
        this.$element.on({
          'focus.timepicker': $.proxy(this.highlightUnit, this),
          'click.timepicker': $.proxy(this.highlightUnit, this),
          'keydown.timepicker': $.proxy(this.elementKeydown, this),
          'blur.timepicker': $.proxy(this.blurElement, this)
        });
      } else {
        if (this.template) {
          this.$element.on({
            'focus.timepicker': $.proxy(this.showWidget, this),
            'click.timepicker': $.proxy(this.showWidget, this),
            'blur.timepicker': $.proxy(this.blurElement, this)
          });
        } else {
          this.$element.on({
            'focus.timepicker': $.proxy(this.highlightUnit, this),
            'click.timepicker': $.proxy(this.highlightUnit, this),
            'keydown.timepicker': $.proxy(this.elementKeydown, this),
            'blur.timepicker': $.proxy(this.blurElement, this)
          });
        }
      }

      if (this.template !== false) {
        this.$widget = $(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on('click', $.proxy(this.widgetClick, this));
      } else {
        this.$widget = false;
      }

      if (this.showInputs && this.$widget !== false) {
        this.$widget.find('input').each(function() {
          $(this).on({
            'click.timepicker': function() { $(this).select(); },
            'keydown.timepicker': $.proxy(self.widgetKeydown, self)
          });
        });
      }

      this.setDefaultTime(this.defaultTime);
    },

    blurElement: function() {
      this.highlightedUnit = undefined;
      this.updateFromElementVal();
    },

    decrementHour: function() {
      if (this.showMeridian) {
        if (this.hour === 1) {
          this.hour = 12;
        } else if (this.hour === 12) {
          this.hour--;

          return this.toggleMeridian();
        } else if (this.hour === 0) {
          this.hour = 11;

          return this.toggleMeridian();
        } else {
          this.hour--;
        }
      } else {
          if (this.hour === 0) {
              if (!this.duration) {
                  this.hour = 23;
              }
        } else {
          this.hour--;
        }
      }
      this.update();
    },

    decrementMinute: function(step) {
      var newVal;

      if (step) {
        newVal = this.minute - step;
      } else {
        newVal = this.minute - this.minuteStep;
      }

      if (newVal < 0) {
        this.decrementHour();
        this.minute = newVal + 60;
      } else {
        this.minute = newVal;
      }
      this.update();
    },

    decrementSecond: function() {
      var newVal = this.second - this.secondStep;

      if (newVal < 0) {
        this.decrementMinute(true);
        this.second = newVal + 60;
      } else {
        this.second = newVal;
      }
      this.update();
    },

    elementKeydown: function(e) {
      switch (e.keyCode) {
      case 9: //tab
        this.updateFromElementVal();

        switch (this.highlightedUnit) {
        case 'hour':
          e.preventDefault();
          this.highlightNextUnit();
          break;
        case 'minute':
          if (this.showMeridian || this.showSeconds) {
            e.preventDefault();
            this.highlightNextUnit();
          }
          break;
        case 'second':
          if (this.showMeridian) {
            e.preventDefault();
            this.highlightNextUnit();
          }
          break;
        }
        break;
      case 27: // escape
        this.updateFromElementVal();
        break;
      case 37: // left arrow
        e.preventDefault();
        this.highlightPrevUnit();
        this.updateFromElementVal();
        break;
      case 38: // up arrow
        e.preventDefault();
        switch (this.highlightedUnit) {
        case 'hour':
          this.incrementHour();
          this.highlightHour();
          break;
        case 'minute':
          this.incrementMinute();
          this.highlightMinute();
          break;
        case 'second':
          this.incrementSecond();
          this.highlightSecond();
          break;
        case 'meridian':
          this.toggleMeridian();
          this.highlightMeridian();
          break;
        }
        break;
      case 39: // right arrow
        e.preventDefault();
        this.updateFromElementVal();
        this.highlightNextUnit();
        break;
      case 40: // down arrow
        e.preventDefault();
        switch (this.highlightedUnit) {
        case 'hour':
          this.decrementHour();
          this.highlightHour();
          break;
        case 'minute':
          this.decrementMinute();
          this.highlightMinute();
          break;
        case 'second':
          this.decrementSecond();
          this.highlightSecond();
          break;
        case 'meridian':
          this.toggleMeridian();
          this.highlightMeridian();
          break;
        }
        break;
      }
    },

    formatTime: function(hour, minute, second, meridian) {
      hour = hour < 10 ? '0' + hour : hour;
      minute = minute < 10 ? '0' + minute : minute;
      second = second < 10 ? '0' + second : second;

      return hour + ':' + minute + (this.showSeconds ? ':' + second : '') + (this.showMeridian ? ' ' + meridian : '');
    },

    getCursorPosition: function() {
      var input = this.$element.get(0);

      if ('selectionStart' in input) {// Standard-compliant browsers

        return input.selectionStart;
      } else if (document.selection) {// IE fix
        input.focus();
        var sel = document.selection.createRange(),
          selLen = document.selection.createRange().text.length;

        sel.moveStart('character', - input.value.length);

        return sel.text.length - selLen;
      }
    },

    getTemplate: function() {
      var template,
        hourTemplate,
        minuteTemplate,
        secondTemplate,
        meridianTemplate,
        templateContent;

      if (this.showInputs) {
        hourTemplate = '<input type="text" name="hour" class="bootstrap-timepicker-hour" maxlength="2"/>';
        minuteTemplate = '<input type="text" name="minute" class="bootstrap-timepicker-minute" maxlength="2"/>';
        secondTemplate = '<input type="text" name="second" class="bootstrap-timepicker-second" maxlength="2"/>';
        meridianTemplate = '<input type="text" name="meridian" class="bootstrap-timepicker-meridian" maxlength="2"/>';
      } else {
        hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
        minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
        secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
        meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
      }

      templateContent = '<div class="hrCloud right"><div class="cloudContent"><table>' +
         '<tr>'+
           '<td><a href="#" data-action="incrementHour" class="up"></a></td>' +
           '<td class="separator">&nbsp;</td>'+
           '<td><a href="#" data-action="incrementMinute" class="up"></a></td>' +
           (this.showSeconds ?
             '<td class="separator">&nbsp;</td>'+
             '<td><a href="#" data-action="incrementSecond" class="up"></a></td>'
           : '') +
           (this.showMeridian ?
             '<td class="separator">&nbsp;</td>'+
             '<td class="meridian-column"><a href="#" data-action="toggleMeridian" class="up"></a></td>'
           : '') +
         '</tr>'+
         '<tr>'+
           '<td>'+ hourTemplate +'</td> '+
           '<td class="separator">:</td>'+
           '<td>'+ minuteTemplate +'</td> '+
           (this.showSeconds ?
            '<td class="separator">:</td>'+
            '<td>'+ secondTemplate +'</td>'
           : '') +
           (this.showMeridian ?
            '<td class="separator">&nbsp;</td>'+
            '<td>'+ meridianTemplate +'</td>'
           : '') +
         '</tr>'+
         '<tr>'+
           '<td><a href="#" data-action="decrementHour" class="down"></a></td>'+
           '<td class="separator"></td>'+
           '<td><a href="#" data-action="decrementMinute" class="down"></a></td>' +
           (this.showSeconds ?
            '<td class="separator">&nbsp;</td>'+
            '<td><a href="#" data-action="decrementSecond" class="down"></a></td>'
           : '') +
           (this.showMeridian ?
            '<td class="separator">&nbsp;</td>'+
            '<td><a href="#" data-action="toggleMeridian" class="down"></i></a></td>'
           : '') +
         '</tr>'+
       '</table></div><div class="cloudBottom"><div class="cloudBottomFill"></div></div></div>';

      switch(this.template) {
      case 'modal':
        template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
          '<div class="modal-header">'+
            '<a href="#" class="close" data-dismiss="modal">×</a>'+
            '<h3>Pick a Time</h3>'+
          '</div>'+
          '<div class="modal-content">'+
            templateContent +
          '</div>'+
          '<div class="modal-footer">'+
            '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
          '</div>'+
        '</div>';
        break;
      case 'dropdown':
        template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
        break;
      }

      return template;
    },

    getTime: function() {
      return this.formatTime(this.hour, this.minute, this.second, this.meridian);
    },

    hideWidget: function() {
      if (this.isOpen === false) {
        return;
      }

			if (this.showInputs) {
				this.updateFromWidgetInputs();
			}

      this.$element.trigger({
        'type': 'hide.timepicker',
        'time': {
          'value': this.getTime(),
          'hours': this.hour,
          'minutes': this.minute,
          'seconds': this.second,
          'meridian': this.meridian
        }
      });

      if (this.template === 'modal' && this.$widget.modal) {
        this.$widget.modal('hide');
      } else {
        this.$widget.removeClass('open');
      }

      $(document).off('mousedown.timepicker');

      this.isOpen = false;
    },

    highlightUnit: function() {
      this.position = this.getCursorPosition();
      if (this.position >= 0 && this.position <= 2) {
        this.highlightHour();
      } else if (this.position >= 3 && this.position <= 5) {
        this.highlightMinute();
      } else if (this.position >= 6 && this.position <= 8) {
        if (this.showSeconds) {
          this.highlightSecond();
        } else {
          this.highlightMeridian();
        }
      } else if (this.position >= 9 && this.position <= 11) {
        this.highlightMeridian();
      }
    },

    highlightNextUnit: function() {
      switch (this.highlightedUnit) {
      case 'hour':
        this.highlightMinute();
        break;
      case 'minute':
        if (this.showSeconds) {
          this.highlightSecond();
        } else if (this.showMeridian){
          this.highlightMeridian();
        } else {
          this.highlightHour();
        }
        break;
      case 'second':
        if (this.showMeridian) {
          this.highlightMeridian();
        } else {
          this.highlightHour();
        }
        break;
      case 'meridian':
        this.highlightHour();
        break;
      }
    },

    highlightPrevUnit: function() {
      switch (this.highlightedUnit) {
      case 'hour':
        this.highlightMeridian();
        break;
      case 'minute':
        this.highlightHour();
        break;
      case 'second':
        this.highlightMinute();
        break;
      case 'meridian':
        if (this.showSeconds) {
          this.highlightSecond();
        } else {
          this.highlightMinute();
        }
        break;
      }
    },

    highlightHour: function() {
      var $element = this.$element.get(0);

      this.highlightedUnit = 'hour';

			if ($element.setSelectionRange) {
				setTimeout(function() {
					$element.setSelectionRange(0,2);
				}, 0);
			}
    },

    highlightMinute: function() {
      var $element = this.$element.get(0);

      this.highlightedUnit = 'minute';

			if ($element.setSelectionRange) {
				setTimeout(function() {
					$element.setSelectionRange(3,5);
				}, 0);
			}
    },

    highlightSecond: function() {
      var $element = this.$element.get(0);

      this.highlightedUnit = 'second';

			if ($element.setSelectionRange) {
				setTimeout(function() {
					$element.setSelectionRange(6,8);
				}, 0);
			}
    },

    highlightMeridian: function() {
      var $element = this.$element.get(0);

      this.highlightedUnit = 'meridian';

			if ($element.setSelectionRange) {
				if (this.showSeconds) {
					setTimeout(function() {
						$element.setSelectionRange(9,11);
					}, 0);
				} else {
					setTimeout(function() {
						$element.setSelectionRange(6,8);
					}, 0);
				}
			}
    },

    incrementHour: function() {
      if (this.showMeridian) {
        if (this.hour === 11) {
          this.hour++;
          return this.toggleMeridian();
        } else if (this.hour === 12) {
          this.hour = 0;
        }
      }
      if (this.hour === 23 && !this.duration) {
        this.hour = 0;

        return;
      }
      this.hour++;
      this.update();
    },

    incrementMinute: function(step) {
      var newVal;

      if (step) {
        newVal = this.minute + step;
      } else {
        newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
      }

      if (newVal > 59) {
        this.incrementHour();
        this.minute = newVal - 60;
      } else {
        this.minute = newVal;
      }
      this.update();
    },

    incrementSecond: function() {
      var newVal = this.second + this.secondStep - (this.second % this.secondStep);

      if (newVal > 59) {
        this.incrementMinute(true);
        this.second = newVal - 60;
      } else {
        this.second = newVal;
      }
      this.update();
    },

    remove: function() {
      $('document').off('.timepicker');
      if (this.$widget) {
        this.$widget.remove();
      }
      delete this.$element.data().timepicker;
    },

    setDefaultTime: function(defaultTime){
      if (!this.$element.val()) {
        if (defaultTime === 'current') {
          var dTime = new Date(),
            hours = dTime.getHours(),
            minutes = Math.floor(dTime.getMinutes() / this.minuteStep) * this.minuteStep,
            seconds = Math.floor(dTime.getSeconds() / this.secondStep) * this.secondStep,
            meridian = 'AM';

          if (this.showMeridian) {
            if (hours === 0) {
              hours = 12;
            } else if (hours >= 12) {
              if (hours > 12) {
                hours = hours - 12;
              }
              meridian = 'PM';
            } else {
              meridian = 'AM';
            }
          }

          this.hour = hours;
          this.minute = minutes;
          this.second = seconds;
          this.meridian = meridian;

          this.update();

        } else if (defaultTime === false) {
          this.hour = 0;
          this.minute = 0;
          this.second = 0;
          this.meridian = 'AM';
        } else {
          this.setTime(defaultTime);
        }
      } else {
        this.updateFromElementVal();
      }
    },

    setTime: function(time) {
      var arr,
        timeArray;

      if (this.showMeridian) {
        arr = time.split(' ');
        timeArray = arr[0].split(':');
        this.meridian = arr[1];
      } else {
        timeArray = time.split(':');
      }

      this.hour = parseInt(timeArray[0], 10);
      this.minute = parseInt(timeArray[1], 10);
      this.second = parseInt(timeArray[2], 10);

      if (isNaN(this.hour)) {
        this.hour = 0;
      }
      if (isNaN(this.minute)) {
        this.minute = 0;
      }

      if (this.showMeridian) {
        if (this.hour > 12) {
          this.hour = 12;
        } else if (this.hour < 1) {
          this.hour = 12;
        }

        if (this.meridian === 'am' || this.meridian === 'a') {
          this.meridian = 'AM';
        } else if (this.meridian === 'pm' || this.meridian === 'p') {
          this.meridian = 'PM';
        }

        if (this.meridian !== 'AM' && this.meridian !== 'PM') {
          this.meridian = 'AM';
        }
      } else {
        if (this.hour >= 24 && !this.duration) {
          this.hour = 23;
        } else if (this.hour < 0) {
          this.hour = 0;
        }
      }

      if (this.minute < 0) {
        this.minute = 0;
      } else if (this.minute >= 60) {
        this.minute = 59;
      }

      if (this.showSeconds) {
        if (isNaN(this.second)) {
          this.second = 0;
        } else if (this.second < 0) {
          this.second = 0;
        } else if (this.second >= 60) {
          this.second = 59;
        }
      }

      this.update();
    },

    showWidget: function() {
      if (this.isOpen) {
        return;
      }

      if (this.$element.is(':disabled')) {
        return;
      }

      var self = this;
      $(document).on('mousedown.timepicker', function (e) {
        // Clicked outside the timepicker, hide it
        if ($(e.target).closest('.bootstrap-timepicker-widget').length === 0) {
          self.hideWidget();
        }
      });

      var val = this.$element.val();
      if (val == '' && !this.defaultTime) {
          this.setTime('0:00');
      }

      this.$element.trigger({
        'type': 'show.timepicker',
        'time': {
          'value': this.getTime(),
          'hours': this.hour,
          'minutes': this.minute,
          'seconds': this.second,
          'meridian': this.meridian
        }
      });

      if (this.disableFocus) {
        this.$element.blur();
      }      

      this.updateFromElementVal();

      if (this.template === 'modal' && this.$widget.modal) {
        this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
      } else {
        if (this.isOpen === false) {
          this.$widget.addClass('open');
        }
      }

      this.isOpen = true;
    },

    toggleMeridian: function() {
      this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
      this.update();
    },

    update: function() {
      this.$element.trigger({
        'type': 'changeTime.timepicker',
        'time': {
          'value': this.getTime(),
          'hours': this.hour,
          'minutes': this.minute,
          'seconds': this.second,
          'meridian': this.meridian
        }
      });

      this.updateElement();
      this.updateWidget();
    },

    updateElement: function() {
      this.$element.val(this.getTime()).change();
    },

    updateFromElementVal: function() {
			var val = this.$element.val();

			if (val) {
				this.setTime(val);
			}
    },

    updateWidget: function() {
      if (this.$widget === false) {
        return;
      }

      var hour = this.hour < 10 ? '0' + this.hour : this.hour,
          minute = this.minute < 10 ? '0' + this.minute : this.minute,
          second = this.second < 10 ? '0' + this.second : this.second;

      if (this.showInputs) {
        this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
        this.$widget.find('input.bootstrap-timepicker-minute').val(minute);

        if (this.showSeconds) {
          this.$widget.find('input.bootstrap-timepicker-second').val(second);
        }
        if (this.showMeridian) {
          this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
        }
      } else {
        this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
        this.$widget.find('span.bootstrap-timepicker-minute').text(minute);

        if (this.showSeconds) {
          this.$widget.find('span.bootstrap-timepicker-second').text(second);
        }
        if (this.showMeridian) {
          this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
        }
      }
    },

    updateFromWidgetInputs: function() {
      if (this.$widget === false) {
        return;
      }
      var time = $('input.bootstrap-timepicker-hour', this.$widget).val() + ':' +
        $('input.bootstrap-timepicker-minute', this.$widget).val() +
        (this.showSeconds ? ':' + $('input.bootstrap-timepicker-second', this.$widget).val() : '') +
        (this.showMeridian ? ' ' + $('input.bootstrap-timepicker-meridian', this.$widget).val() : '');

      this.setTime(time);
    },

    widgetClick: function(e) {
      e.stopPropagation();
      e.preventDefault();

      var action = $(e.target).closest('a').data('action');
      if (action) {
        this[action]();
      }
    },

    widgetKeydown: function(e) {
      var $input = $(e.target).closest('input'),
          name = $input.attr('name');

      switch (e.keyCode) {
      case 9: //tab
        if (this.showMeridian) {
          if (name === 'meridian') {
            return this.hideWidget();
          }
        } else {
          if (this.showSeconds) {
            if (name === 'second') {
              return this.hideWidget();
            }
          } else {
            if (name === 'minute') {
              return this.hideWidget();
            }
          }
        }

        this.updateFromWidgetInputs();
        break;
      case 27: // escape
        this.hideWidget();
        break;
      case 38: // up arrow
        e.preventDefault();
        switch (name) {
        case 'hour':
          this.incrementHour();
          break;
        case 'minute':
          this.incrementMinute();
          break;
        case 'second':
          this.incrementSecond();
          break;
        case 'meridian':
          this.toggleMeridian();
          break;
        }
        break;
      case 40: // down arrow
        e.preventDefault();
        switch (name) {
        case 'hour':
          this.decrementHour();
          break;
        case 'minute':
          this.decrementMinute();
          break;
        case 'second':
          this.decrementSecond();
          break;
        case 'meridian':
          this.toggleMeridian();
          break;
        }
        break;
      }
    }
  };


  //TIMEPICKER PLUGIN DEFINITION
  $.fn.timepicker = function(option) {
    var args = Array.apply(null, arguments);
    args.shift();
    return this.each(function() {
      var $this = $(this),
        data = $this.data('timepicker'),
        options = typeof option === 'object' && option;

      if (!data) {
        $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
      }

      if (typeof option === 'string') {
        data[option].apply(data, args);
      }
    });
  };

  $.fn.timepicker.defaults = {
    defaultTime: 'current',
    disableFocus: false,
    isOpen: false,
    minuteStep: 15,
    modalBackdrop: false,
    secondStep: 15,
    showSeconds: false,
    showInputs: true,
    showMeridian: true,
    template: 'dropdown',
    appendWidgetTo: '.bootstrap-timepicker',
    duration: false
  };

  $.fn.timepicker.Constructor = Timepicker;

})(jQuery, window, document);
;/* ===========================================================
 * bootstrap-modalmanager.js v2.2.0
 * ===========================================================
 * Copyright 2012 Jordan Schroter.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function ($) {

	"use strict"; // jshint ;_;

	/* MODAL MANAGER CLASS DEFINITION
	* ====================== */

	var ModalManager = function (element, options) {
		this.init(element, options);
	};

	ModalManager.prototype = {

		constructor: ModalManager,

		init: function (element, options) {
			this.$element = $(element);
			this.options = $.extend({}, $.fn.modalmanager.defaults, this.$element.data(), typeof options == 'object' && options);
			this.stack = [];
			this.backdropCount = 0;

			if (this.options.resize) {
				var resizeTimeout,
					that = this;

				$(window).on('resize.modal', function(){
					resizeTimeout && clearTimeout(resizeTimeout);
					resizeTimeout = setTimeout(function(){
						for (var i = 0; i < that.stack.length; i++){
							that.stack[i].isShown && that.stack[i].layout();
						}
					}, 10);
				});
			}
		},

		createModal: function (element, options) {
			$(element).modal($.extend({ manager: this }, options));
		},

		appendModal: function (modal) {
			this.stack.push(modal);

			var that = this;

			modal.$element.on('show.modalmanager', targetIsSelf(function (e) {

				var showModal = function(){
					modal.isShown = true;

					var transition = $.support.transition && modal.$element.hasClass('fade');

					that.$element
						.toggleClass('modal-open', that.hasOpenModal())
						.toggleClass('page-overflow', $(window).height() < that.$element.height());

					modal.$parent = modal.$element.parent();

					modal.$container = that.createContainer(modal);

					modal.$element.appendTo(modal.$container);

					that.backdrop(modal, function () {
						modal.$element.show();

						if (transition) {       
							//modal.$element[0].style.display = 'run-in';       
							modal.$element[0].offsetWidth;
							//modal.$element.one($.support.transition.end, function () { modal.$element[0].style.display = 'block' });  
						}
						
						modal.layout();

						modal.$element
							.addClass('in')
							.attr('aria-hidden', false);

						var complete = function () {
							that.setFocus();
							modal.$element.trigger('shown');
						};

						transition ?
							modal.$element.one($.support.transition.end, complete) :
							complete();
					});
				};

				modal.options.replace ?
					that.replace(showModal) :
					showModal();
			}));

			modal.$element.on('hidden.modalmanager', targetIsSelf(function (e) {

				that.backdrop(modal);
				if (modal.$backdrop){
					var transition = $.support.transition && modal.$element.hasClass('fade');

					// trigger a relayout due to firebox's buggy transition end event 
					if (transition) { modal.$element[0].offsetWidth; }

					$.support.transition && modal.$element.hasClass('fade') ?
						modal.$backdrop.one($.support.transition.end, function () { that.destroyModal(modal) }) :
						that.destroyModal(modal);
				} else {
					that.destroyModal(modal);
				}

			}));

			modal.$element.on('destroy.modalmanager', targetIsSelf(function (e) {
				that.removeModal(modal);
			}));

		},

		destroyModal: function (modal) {
			modal.destroy();

			var hasOpenModal = this.hasOpenModal();

			this.$element.toggleClass('modal-open', hasOpenModal);

			if (!hasOpenModal){
				this.$element.removeClass('page-overflow');
			}

			this.removeContainer(modal);

			this.setFocus();
		},

		getOpenModals: function () {
			var openModals = [];
			for (var i = 0; i < this.stack.length; i++){
				if (this.stack[i].isShown) openModals.push(this.stack[i]);
			}

			return openModals;
		},

		hasOpenModal: function () {
			return this.getOpenModals().length > 0;
		},

		setFocus: function () {
			var topModal;

			for (var i = 0; i < this.stack.length; i++){
				if (this.stack[i].isShown) topModal = this.stack[i];
			}

			if (!topModal) return;

			topModal.focus();

		},

		removeModal: function (modal) {
			modal.$element.off('.modalmanager');
			if (modal.$backdrop) this.removeBackdrop(modal);
			this.stack.splice(this.getIndexOfModal(modal), 1);
		},

		getModalAt: function (index) {
			return this.stack[index];
		},

		getIndexOfModal: function (modal) {
			for (var i = 0; i < this.stack.length; i++){
				if (modal === this.stack[i]) return i;
			}
		},

		replace: function (callback) {
			var topModal;

			for (var i = 0; i < this.stack.length; i++){
				if (this.stack[i].isShown) topModal = this.stack[i];
			}

			if (topModal) {
				this.$backdropHandle = topModal.$backdrop;
				topModal.$backdrop = null;

				callback && topModal.$element.one('hidden',
					targetIsSelf( $.proxy(callback, this) ));

				topModal.hide();
			} else if (callback) {
				callback();
			}
		},

		removeBackdrop: function (modal) {
			modal.$backdrop.remove();
			modal.$backdrop = null;
		},

		createBackdrop: function (animate, tmpl) {
			var $backdrop;

			if (!this.$backdropHandle) {
				$backdrop = $(tmpl)
					.addClass(animate)
					.appendTo(this.$element);
			} else {
				$backdrop = this.$backdropHandle;
				$backdrop.off('.modalmanager');
				this.$backdropHandle = null;
				this.isLoading && this.removeSpinner();
			}

			return $backdrop;
		},

		removeContainer: function (modal) {
			modal.$container.remove();
			modal.$container = null;
		},

		createContainer: function (modal) {
			var $container;

			$container = $('<div class="modal-scrollable">')
				.css('z-index', getzIndex('modal', this.getOpenModals().length))
				.appendTo(this.$element);

			if (modal && modal.options.backdrop != 'static') {
				$container.on('click.modal', targetIsSelf(function (e) {
					modal.hide();
				}));
			} else if (modal) {
				$container.on('click.modal', targetIsSelf(function (e) {
					modal.attention();
				}));
			}

			return $container;

		},

		backdrop: function (modal, callback) {
			var animate = modal.$element.hasClass('fade') ? 'fade' : '',
				showBackdrop = modal.options.backdrop &&
					this.backdropCount < this.options.backdropLimit;

			if (modal.isShown && showBackdrop) {
				var doAnimate = $.support.transition && animate && !this.$backdropHandle;

				modal.$backdrop = this.createBackdrop(animate, modal.options.backdropTemplate);

				modal.$backdrop.css('z-index', getzIndex( 'backdrop', this.getOpenModals().length ));

				if (doAnimate) modal.$backdrop[0].offsetWidth; // force reflow

				modal.$backdrop.addClass('in');

				this.backdropCount += 1;

				doAnimate ?
					modal.$backdrop.one($.support.transition.end, callback) :
					callback();

			} else if (!modal.isShown && modal.$backdrop) {
				modal.$backdrop.removeClass('in');

				this.backdropCount -= 1;

				var that = this;

				$.support.transition && modal.$element.hasClass('fade')?
					modal.$backdrop.one($.support.transition.end, function () { that.removeBackdrop(modal) }) :
					that.removeBackdrop(modal);

			} else if (callback) {
				callback();
			}
		},

		removeSpinner: function(){
			this.$spinner && this.$spinner.remove();
			this.$spinner = null;
			this.isLoading = false;
		},

		removeLoading: function () {
			this.$backdropHandle && this.$backdropHandle.remove();
			this.$backdropHandle = null;
			this.removeSpinner();
		},

		loading: function (callback) {
			callback = callback || function () { };

			this.$element
				.toggleClass('modal-open', !this.isLoading || this.hasOpenModal())
				.toggleClass('page-overflow', $(window).height() < this.$element.height());

			if (!this.isLoading) {

				this.$backdropHandle = this.createBackdrop('fade', this.options.backdropTemplate);

				this.$backdropHandle[0].offsetWidth; // force reflow

				var openModals = this.getOpenModals();

				this.$backdropHandle
					.css('z-index', getzIndex('backdrop', openModals.length + 1))
					.addClass('in');

				var $spinner = $(this.options.spinner)
					.css('z-index', getzIndex('modal', openModals.length + 1))
					.appendTo(this.$element)
					.addClass('in');

				this.$spinner = $(this.createContainer())
					.append($spinner)
					.on('click.modalmanager', $.proxy(this.loading, this));

				this.isLoading = true;

				$.support.transition ?
					this.$backdropHandle.one($.support.transition.end, callback) :
					callback();

			} else if (this.isLoading && this.$backdropHandle) {
				this.$backdropHandle.removeClass('in');

				var that = this;
				$.support.transition ?
					this.$backdropHandle.one($.support.transition.end, function () { that.removeLoading() }) :
					that.removeLoading();

			} else if (callback) {
				callback(this.isLoading);
			}
		}
	};

	/* PRIVATE METHODS
	* ======================= */

	// computes and caches the zindexes
	var getzIndex = (function () {
		var zIndexFactor,
			baseIndex = {};

		return function (type, pos) {

			if (typeof zIndexFactor === 'undefined'){
				var $baseModal = $('<div class="modal hide" />').appendTo('body'),
					$baseBackdrop = $('<div class="modal-backdrop hide" />').appendTo('body');

				baseIndex['modal'] = +$baseModal.css('z-index');
				baseIndex['backdrop'] = +$baseBackdrop.css('z-index');
				zIndexFactor = baseIndex['modal'] - baseIndex['backdrop'];

				$baseModal.remove();
				$baseBackdrop.remove();
				$baseBackdrop = $baseModal = null;
			}

			return baseIndex[type] + (zIndexFactor * pos);

		}
	}());

	// make sure the event target is the modal itself in order to prevent
	// other components such as tabsfrom triggering the modal manager.
	// if Boostsrap namespaced events, this would not be needed.
	function targetIsSelf(callback){
		return function (e) {
			if (this === e.target){
				return callback.apply(this, arguments);
			}
		}
	}


	/* MODAL MANAGER PLUGIN DEFINITION
	* ======================= */

	$.fn.modalmanager = function (option, args) {
		return this.each(function () {
			var $this = $(this),
				data = $this.data('modalmanager');

			if (!data) $this.data('modalmanager', (data = new ModalManager(this, option)));
			if (typeof option === 'string') data[option].apply(data, [].concat(args))
		})
	};

	$.fn.modalmanager.defaults = {
		backdropLimit: 999,
		resize: true,
		spinner: '<div class="loading-spinner fade" style="width: 200px; margin-left: -100px;"><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div></div>',
		backdropTemplate: '<div class="modal-backdrop" />'
	};

	$.fn.modalmanager.Constructor = ModalManager

}(jQuery);
;/* ===========================================================
 * bootstrap-modal.js v2.2.0
 * ===========================================================
 * Copyright 2012 Jordan Schroter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */


!function ($) {

	"use strict"; // jshint ;_;

	/* MODAL CLASS DEFINITION
	* ====================== */

	var Modal = function (element, options) {
		this.init(element, options);
	};

	Modal.prototype = {

		constructor: Modal,

		init: function (element, options) {
			var that = this;

			this.options = options;

			this.$element = $(element)
				.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this));

			this.options.remote && this.$element.find('.modal-body').load(this.options.remote, function () {
				var e = $.Event('loaded');
				that.$element.trigger(e);
			});

			var manager = typeof this.options.manager === 'function' ?
				this.options.manager.call(this) : this.options.manager;

			manager = manager.appendModal ?
				manager : $(manager).modalmanager().data('modalmanager');

			manager.appendModal(this);
		},

		toggle: function () {
			return this[!this.isShown ? 'show' : 'hide']();
		},

		show: function () {
			var e = $.Event('show');

			if (this.isShown) return;

			this.$element.trigger(e);

			if (e.isDefaultPrevented()) return;

			this.escape();

			this.tab();

			this.options.loading && this.loading();
		},

		hide: function (e) {
			e && e.preventDefault();

			e = $.Event('hide');

			this.$element.trigger(e);

			if (!this.isShown || e.isDefaultPrevented()) return (this.isShown = false);

			this.isShown = false;

			this.escape();

			this.tab();

			this.isLoading && this.loading();

			$(document).off('focusin.modal');

			this.$element
				.removeClass('in')
				.removeClass('animated')
				.removeClass(this.options.attentionAnimation)
				.removeClass('modal-overflow')
				.attr('aria-hidden', true);

			$.support.transition && this.$element.hasClass('fade') ?
				this.hideWithTransition() :
				this.hideModal();
		},

		layout: function () {
			var prop = this.options.height ? 'height' : 'max-height',
				value = this.options.height || this.options.maxHeight;

			if (this.options.width){
				this.$element.css('width', this.options.width);

				var that = this;
				this.$element.css('margin-left', function () {
					if (/%/ig.test(that.options.width)){
						return -(parseInt(that.options.width) / 2) + '%';
					} else {
						return -($(this).width() / 2) + 'px';
					}
				});
			} else {
				this.$element.css('width', '');
				this.$element.css('margin-left', '');
			}

			this.$element.find('.modal-body')
				.css('overflow', '')
				.css(prop, '');

			if (value){
				this.$element.find('.modal-body')
					.css('overflow', 'auto')
					.css(prop, value);
			}

			var modalOverflow = $(window).height() - 10 < this.$element.height();
            
			if (modalOverflow || this.options.modalOverflow) {
				this.$element
					.css('margin-top', 0)
					.addClass('modal-overflow');
			} else {
				this.$element
					.css('margin-top', 0 - this.$element.height() / 2)
					.removeClass('modal-overflow');
			}
		},

		tab: function () {
			var that = this;

			if (this.isShown && this.options.consumeTab) {
				this.$element.on('keydown.tabindex.modal', '[data-tabindex]', function (e) {
			    	if (e.keyCode && e.keyCode == 9){
						var $next = $(this),
							$rollover = $(this);

						that.$element.find('[data-tabindex]:enabled:not([readonly])').each(function (e) {
							if (!e.shiftKey){
						 		$next = $next.data('tabindex') < $(this).data('tabindex') ?
									$next = $(this) :
									$rollover = $(this);
							} else {
								$next = $next.data('tabindex') > $(this).data('tabindex') ?
									$next = $(this) :
									$rollover = $(this);
							}
						});

						$next[0] !== $(this)[0] ?
							$next.focus() : $rollover.focus();

						e.preventDefault();
					}
				});
			} else if (!this.isShown) {
				this.$element.off('keydown.tabindex.modal');
			}
		},

		escape: function () {
			var that = this;
			if (this.isShown && this.options.keyboard) {
				if (!this.$element.attr('tabindex')) this.$element.attr('tabindex', -1);

				this.$element.on('keyup.dismiss.modal', function (e) {
					e.which == 27 && that.hide();
				});
			} else if (!this.isShown) {
				this.$element.off('keyup.dismiss.modal')
			}
		},

		hideWithTransition: function () {
			var that = this
				, timeout = setTimeout(function () {
					that.$element.off($.support.transition.end);
					that.hideModal();
				}, 500);

			this.$element.one($.support.transition.end, function () {
				clearTimeout(timeout);
				that.hideModal();
			});
		},

		hideModal: function () {
			var prop = this.options.height ? 'height' : 'max-height';
			var value = this.options.height || this.options.maxHeight;

			if (value){
				this.$element.find('.modal-body')
					.css('overflow', '')
					.css(prop, '');
			}

			this.$element
				.hide()
				.trigger('hidden');
		},

		removeLoading: function () {
			this.$loading.remove();
			this.$loading = null;
			this.isLoading = false;
		},

		loading: function (callback) {
			callback = callback || function () {};

			var animate = this.$element.hasClass('fade') ? 'fade' : '';

			if (!this.isLoading) {
				var doAnimate = $.support.transition && animate;

				this.$loading = $('<div class="loading-mask ' + animate + '">')
					.append(this.options.spinner)
					.appendTo(this.$element);

				if (doAnimate) this.$loading[0].offsetWidth; // force reflow

				this.$loading.addClass('in');

				this.isLoading = true;

				doAnimate ?
					this.$loading.one($.support.transition.end, callback) :
					callback();

			} else if (this.isLoading && this.$loading) {
				this.$loading.removeClass('in');

				var that = this;
				$.support.transition && this.$element.hasClass('fade')?
					this.$loading.one($.support.transition.end, function () { that.removeLoading() }) :
					that.removeLoading();

			} else if (callback) {
				callback(this.isLoading);
			}
		},

		focus: function () {
			var $focusElem = this.$element.find(this.options.focusOn);

			$focusElem = $focusElem.length ? $focusElem : this.$element;

			$focusElem.focus();
		},

		attention: function (){
			// NOTE: transitionEnd with keyframes causes odd behaviour

			if (this.options.attentionAnimation){
				this.$element
					.removeClass('animated')
					.removeClass(this.options.attentionAnimation);

				var that = this;

				setTimeout(function () {
					that.$element
						.addClass('animated')
						.addClass(that.options.attentionAnimation);
				}, 0);
			}


			this.focus();
		},


		destroy: function () {
			var e = $.Event('destroy');
			this.$element.trigger(e);
			if (e.isDefaultPrevented()) return;

			this.teardown();
		},

		teardown: function () {
			if (!this.$parent.length){
				this.$element.remove();
				this.$element = null;
				return;
			}

			if (this.$parent !== this.$element.parent()){
				this.$element.appendTo(this.$parent);
			}

			this.$element.off('.modal');
			this.$element.removeData('modal');
			this.$element
				.removeClass('in')
				.attr('aria-hidden', true);
		}
	};


	/* MODAL PLUGIN DEFINITION
	* ======================= */

	$.fn.modal = function (option, args) {
		return this.each(function () {
			var $this = $(this),
				data = $this.data('modal'),
				options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option);

			if (!data) $this.data('modal', (data = new Modal(this, options)));
			if (typeof option == 'string') data[option].apply(data, [].concat(args));
			else if (options.show) data.show()
		})
	};

	$.fn.modal.defaults = {
		keyboard: true,
		backdrop: true,
		loading: false,
		show: true,
		width: null,
		height: null,
		maxHeight: null,
		modalOverflow: false,
		consumeTab: true,
		focusOn: null,
		replace: false,
		resize: false,
		attentionAnimation: 'shake',
		manager: 'body',
		spinner: '<div class="loading-spinner" style="width: 200px; margin-left: -100px;"><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div></div>',
		backdropTemplate: '<div class="modal-backdrop" />'
	};

	$.fn.modal.Constructor = Modal;


	/* MODAL DATA-API
	* ============== */

	$(function () {
		$(document).off('click.modal').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
			var $this = $(this),
				href = $this.attr('href'),
				$target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))), //strip for ie7
				option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data());

			e.preventDefault();
			$target
				.modal(option)
				.one('hide', function () {
					$this.focus();
				})
		});
	});

}(window.jQuery);
;/*
  html2canvas 0.4.0 <http://html2canvas.hertzen.com>
  Copyright (c) 2013 Niklas von Hertzen (@niklasvh)

  Released under MIT License
*/
(function(window, document, undefined){

"use strict";

var _html2canvas = {},
previousElement,
computedCSS,
html2canvas;

function h2clog(a) {
  if (_html2canvas.logging && window.console && window.console.log) {
    window.console.log(a);
  }
}

_html2canvas.Util = {};

_html2canvas.Util.trimText = (function(isNative){
  return function(input){
    if(isNative) { return isNative.apply( input ); }
    else { return ((input || '') + '').replace( /^\s+|\s+$/g , '' ); }
  };
})( String.prototype.trim );

_html2canvas.Util.parseBackgroundImage = function (value) {
    var whitespace = ' \r\n\t',
        method, definition, prefix, prefix_i, block, results = [],
        c, mode = 0, numParen = 0, quote, args;

    var appendResult = function(){
        if(method) {
            if(definition.substr( 0, 1 ) === '"') {
                definition = definition.substr( 1, definition.length - 2 );
            }
            if(definition) {
                args.push(definition);
            }
            if(method.substr( 0, 1 ) === '-' &&
                    (prefix_i = method.indexOf( '-', 1 ) + 1) > 0) {
                prefix = method.substr( 0, prefix_i);
                method = method.substr( prefix_i );
            }
            results.push({
                prefix: prefix,
                method: method.toLowerCase(),
                value: block,
                args: args
            });
        }
        args = []; //for some odd reason, setting .length = 0 didn't work in safari
        method =
            prefix =
            definition =
            block = '';
    };

    appendResult();
    for(var i = 0, ii = value.length; i<ii; i++) {
        c = value[i];
        if(mode === 0 && whitespace.indexOf( c ) > -1){
            continue;
        }
        switch(c) {
            case '"':
                if(!quote) {
                    quote = c;
                }
                else if(quote === c) {
                    quote = null;
                }
                break;

            case '(':
                if(quote) { break; }
                else if(mode === 0) {
                    mode = 1;
                    block += c;
                    continue;
                } else {
                    numParen++;
                }
                break;

            case ')':
                if(quote) { break; }
                else if(mode === 1) {
                    if(numParen === 0) {
                        mode = 0;
                        block += c;
                        appendResult();
                        continue;
                    } else {
                        numParen--;
                    }
                }
                break;

            case ',':
                if(quote) { break; }
                else if(mode === 0) {
                    appendResult();
                    continue;
                }
                else if (mode === 1) {
                    if(numParen === 0 && !method.match(/^url$/i)) {
                        args.push(definition);
                        definition = '';
                        block += c;
                        continue;
                    }
                }
                break;
        }

        block += c;
        if(mode === 0) { method += c; }
        else { definition += c; }
    }
    appendResult();

    return results;
};

_html2canvas.Util.Bounds = function getBounds (el) {
  var clientRect,
  bounds = {};

  if (el.getBoundingClientRect){
    clientRect = el.getBoundingClientRect();


    // TODO add scroll position to bounds, so no scrolling of window necessary
    bounds.top = clientRect.top;
    bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
    bounds.left = clientRect.left;

    // older IE doesn't have width/height, but top/bottom instead
    bounds.width = clientRect.width || (clientRect.right - clientRect.left);
    bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);

    return bounds;

  }
};

_html2canvas.Util.getCSS = function (el, attribute, index) {
  // return $(el).css(attribute);

    var val,
    isBackgroundSizePosition = attribute.match( /^background(Size|Position)$/ );

  function toPX( attribute, val ) {
    var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
    left,
    style = el.style;

    // Check if we are not dealing with pixels, (Opera has issues with this)
    // Ported from jQuery css.js
    // From the awesome hack by Dean Edwards
    // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

    // If we're not dealing with a regular pixel number
    // but a number that has a weird ending, we need to convert it to pixels

    if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {

      // Remember the original values
      left = style.left;

      // Put in the new values to get a computed value out
      if ( rsLeft ) {
        el.runtimeStyle.left = el.currentStyle.left;
      }
      style.left = attribute === "fontSize" ? "1em" : (val || 0);
      val = style.pixelLeft + "px";

      // Revert the changed values
      style.left = left;
      if ( rsLeft ) {
        el.runtimeStyle.left = rsLeft;
      }

    }

    if (!/^(thin|medium|thick)$/i.test( val )) {
      return Math.round(parseFloat( val )) + "px";
    }

    return val;
  }

    if (previousElement !== el) {
      computedCSS = document.defaultView.getComputedStyle(el, null);
    }
    val = computedCSS[attribute];

    if (isBackgroundSizePosition) {
      val = (val || '').split( ',' );
      val = val[index || 0] || val[0] || 'auto';
      val = _html2canvas.Util.trimText(val).split(' ');

      if(attribute === 'backgroundSize' && (!val[ 0 ] || val[ 0 ].match( /cover|contain|auto/ ))) {
        //these values will be handled in the parent function

      } else {
        val[ 0 ] = ( val[ 0 ].indexOf( "%" ) === -1 ) ? toPX(  attribute + "X", val[ 0 ] ) : val[ 0 ];
        if(val[ 1 ] === undefined) {
          if(attribute === 'backgroundSize') {
            val[ 1 ] = 'auto';
            return val;
          }
          else {
            // IE 9 doesn't return double digit always
            val[ 1 ] = val[ 0 ];
          }
        }
        val[ 1 ] = ( val[ 1 ].indexOf( "%" ) === -1 ) ? toPX(  attribute + "Y", val[ 1 ] ) : val[ 1 ];
      }
    } else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) {
      var arr = val.split(" ");
      if ( arr.length <= 1 ) {
              arr[ 1 ] = arr[ 0 ];
      }
      arr[ 0 ] = parseInt( arr[ 0 ], 10 );
      arr[ 1 ] = parseInt( arr[ 1 ], 10 );
      val = arr;
    }

  return val;
};

_html2canvas.Util.resizeBounds = function( current_width, current_height, target_width, target_height, stretch_mode ){
  var target_ratio = target_width / target_height,
    current_ratio = current_width / current_height,
    output_width, output_height;

  if(!stretch_mode || stretch_mode === 'auto') {
    output_width = target_width;
    output_height = target_height;

  } else {
    if(target_ratio < current_ratio ^ stretch_mode === 'contain') {
      output_height = target_height;
      output_width = target_height * current_ratio;
    } else {
      output_width = target_width;
      output_height = target_width / current_ratio;
    }
  }

  return { width: output_width, height: output_height };
};

function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroundSize ) {
    var bgposition =  _html2canvas.Util.getCSS( el, prop, imageIndex ) ,
    topPos,
    left,
    percentage,
    val;

    if (bgposition.length === 1){
      val = bgposition[0];

      bgposition = [];

      bgposition[0] = val;
      bgposition[1] = val;
    }

    if (bgposition[0].toString().indexOf("%") !== -1){
      percentage = (parseFloat(bgposition[0])/100);
      left = bounds.width * percentage;
      if(prop !== 'backgroundSize') {
        left -= (backgroundSize || image).width*percentage;
      }

    } else {
      if(prop === 'backgroundSize') {
        if(bgposition[0] === 'auto') {
          left = image.width;

        } else {
          if(bgposition[0].match(/contain|cover/)) {
            var resized = _html2canvas.Util.resizeBounds( image.width, image.height, bounds.width, bounds.height, bgposition[0] );
            left = resized.width;
            topPos = resized.height;
          } else {
            left = parseInt (bgposition[0], 10 );
          }
        }

      } else {
        left = parseInt( bgposition[0], 10 );
      }
    }


    if(bgposition[1] === 'auto') {
      topPos = left / image.width * image.height;
    } else if (bgposition[1].toString().indexOf("%") !== -1){
      percentage = (parseFloat(bgposition[1])/100);
      topPos =  bounds.height * percentage;
      if(prop !== 'backgroundSize') {
        topPos -= (backgroundSize || image).height * percentage;
      }

    } else {
      topPos = parseInt(bgposition[1],10);
    }

    return [left, topPos];
}

_html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex, backgroundSize ) {
    var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize );
    return { left: result[0], top: result[1] };
};
_html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) {
    var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex );
    return { width: result[0], height: result[1] };
};

_html2canvas.Util.Extend = function (options, defaults) {
  for (var key in options) {
    if (options.hasOwnProperty(key)) {
      defaults[key] = options[key];
    }
  }
  return defaults;
};


/*
 * Derived from jQuery.contents()
 * Copyright 2010, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
_html2canvas.Util.Children = function( elem ) {


  var children;
  try {

    children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ?
    elem.contentDocument || elem.contentWindow.document : (function( array ){
      var ret = [];

      if ( array !== null ) {

        (function( first, second ) {
          var i = first.length,
          j = 0;

          if ( typeof second.length === "number" ) {
            for ( var l = second.length; j < l; j++ ) {
              first[ i++ ] = second[ j ];
            }

          } else {
            while ( second[j] !== undefined ) {
              first[ i++ ] = second[ j++ ];
            }
          }

          first.length = i;

          return first;
        })( ret, array );

      }

      return ret;
    })( elem.childNodes );

  } catch (ex) {
    h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
    children = [];
  }
  return children;
};

_html2canvas.Util.Font = (function () {

  var fontData = {};

  return function(font, fontSize, doc) {
    if (fontData[font + "-" + fontSize] !== undefined) {
      return fontData[font + "-" + fontSize];
    }

    var container = doc.createElement('div'),
    img = doc.createElement('img'),
    span = doc.createElement('span'),
    sampleText = 'Hidden Text',
    baseline,
    middle,
    metricsObj;

    container.style.visibility = "hidden";
    container.style.fontFamily = font;
    container.style.fontSize = fontSize;
    container.style.margin = 0;
    container.style.padding = 0;

    doc.body.appendChild(container);

    // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif)
    img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=";
    img.width = 1;
    img.height = 1;

    img.style.margin = 0;
    img.style.padding = 0;
    img.style.verticalAlign = "baseline";

    span.style.fontFamily = font;
    span.style.fontSize = fontSize;
    span.style.margin = 0;
    span.style.padding = 0;

    span.appendChild(doc.createTextNode(sampleText));
    container.appendChild(span);
    container.appendChild(img);
    baseline = (img.offsetTop - span.offsetTop) + 1;

    container.removeChild(span);
    container.appendChild(doc.createTextNode(sampleText));

    container.style.lineHeight = "normal";
    img.style.verticalAlign = "super";

    middle = (img.offsetTop-container.offsetTop) + 1;
    metricsObj = {
      baseline: baseline,
      lineWidth: 1,
      middle: middle
    };

    fontData[font + "-" + fontSize] = metricsObj;

    doc.body.removeChild(container);

    return metricsObj;
  };
})();

(function(){

  _html2canvas.Generate = {};

  var reGradients = [
  /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
  /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
  /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/,
  /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/,
  /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/,
  /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/,
  /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/
  ];

  /*
 * TODO: Add IE10 vendor prefix (-ms) support
 * TODO: Add W3C gradient (linear-gradient) support
 * TODO: Add old Webkit -webkit-gradient(radial, ...) support
 * TODO: Maybe some RegExp optimizations are possible ;o)
 */
  _html2canvas.Generate.parseGradient = function(css, bounds) {
    var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl;

    for(i = 0; i < len; i+=1){
      m1 = css.match(reGradients[i]);
      if(m1) {
        break;
      }
    }

    if(m1) {
      switch(m1[1]) {
        case '-webkit-linear-gradient':
        case '-o-linear-gradient':

          gradient = {
            type: 'linear',
            x0: null,
            y0: null,
            x1: null,
            y1: null,
            colorStops: []
          };

          // get coordinates
          m2 = m1[2].match(/\w+/g);
          if(m2){
            m2Len = m2.length;
            for(i = 0; i < m2Len; i+=1){
              switch(m2[i]) {
                case 'top':
                  gradient.y0 = 0;
                  gradient.y1 = bounds.height;
                  break;

                case 'right':
                  gradient.x0 = bounds.width;
                  gradient.x1 = 0;
                  break;

                case 'bottom':
                  gradient.y0 = bounds.height;
                  gradient.y1 = 0;
                  break;

                case 'left':
                  gradient.x0 = 0;
                  gradient.x1 = bounds.width;
                  break;
              }
            }
          }
          if(gradient.x0 === null && gradient.x1 === null){ // center
            gradient.x0 = gradient.x1 = bounds.width / 2;
          }
          if(gradient.y0 === null && gradient.y1 === null){ // center
            gradient.y0 = gradient.y1 = bounds.height / 2;
          }

          // get colors and stops
          m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
          if(m2){
            m2Len = m2.length;
            step = 1 / Math.max(m2Len - 1, 1);
            for(i = 0; i < m2Len; i+=1){
              m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
              if(m3[2]){
                stop = parseFloat(m3[2]);
                if(m3[3] === '%'){
                  stop /= 100;
                } else { // px - stupid opera
                  stop /= bounds.width;
                }
              } else {
                stop = i * step;
              }
              gradient.colorStops.push({
                color: m3[1],
                stop: stop
              });
            }
          }
          break;

        case '-webkit-gradient':

          gradient = {
            type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions
            x0: 0,
            y0: 0,
            x1: 0,
            y1: 0,
            colorStops: []
          };

          // get coordinates
          m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
          if(m2){
            gradient.x0 = (m2[1] * bounds.width) / 100;
            gradient.y0 = (m2[2] * bounds.height) / 100;
            gradient.x1 = (m2[3] * bounds.width) / 100;
            gradient.y1 = (m2[4] * bounds.height) / 100;
          }

          // get colors and stops
          m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
          if(m2){
            m2Len = m2.length;
            for(i = 0; i < m2Len; i+=1){
              m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
              stop = parseFloat(m3[2]);
              if(m3[1] === 'from') {
                stop = 0.0;
              }
              if(m3[1] === 'to') {
                stop = 1.0;
              }
              gradient.colorStops.push({
                color: m3[3],
                stop: stop
              });
            }
          }
          break;

        case '-moz-linear-gradient':

          gradient = {
            type: 'linear',
            x0: 0,
            y0: 0,
            x1: 0,
            y1: 0,
            colorStops: []
          };

          // get coordinates
          m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);

          // m2[1] == 0%   -> left
          // m2[1] == 50%  -> center
          // m2[1] == 100% -> right

          // m2[2] == 0%   -> top
          // m2[2] == 50%  -> center
          // m2[2] == 100% -> bottom

          if(m2){
            gradient.x0 = (m2[1] * bounds.width) / 100;
            gradient.y0 = (m2[2] * bounds.height) / 100;
            gradient.x1 = bounds.width - gradient.x0;
            gradient.y1 = bounds.height - gradient.y0;
          }

          // get colors and stops
          m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
          if(m2){
            m2Len = m2.length;
            step = 1 / Math.max(m2Len - 1, 1);
            for(i = 0; i < m2Len; i+=1){
              m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
              if(m3[2]){
                stop = parseFloat(m3[2]);
                if(m3[3]){ // percentage
                  stop /= 100;
                }
              } else {
                stop = i * step;
              }
              gradient.colorStops.push({
                color: m3[1],
                stop: stop
              });
            }
          }
          break;

        case '-webkit-radial-gradient':
        case '-moz-radial-gradient':
        case '-o-radial-gradient':

          gradient = {
            type: 'circle',
            x0: 0,
            y0: 0,
            x1: bounds.width,
            y1: bounds.height,
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            colorStops: []
          };

          // center
          m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
          if(m2){
            gradient.cx = (m2[1] * bounds.width) / 100;
            gradient.cy = (m2[2] * bounds.height) / 100;
          }

          // size
          m2 = m1[3].match(/\w+/);
          m3 = m1[4].match(/[a-z\-]*/);
          if(m2 && m3){
            switch(m3[0]){
              case 'farthest-corner':
              case 'cover': // is equivalent to farthest-corner
              case '': // mozilla removes "cover" from definition :(
                tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
                tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
                br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
                bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
                gradient.rx = gradient.ry = Math.max(tl, tr, br, bl);
                break;
              case 'closest-corner':
                tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
                tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
                br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
                bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
                gradient.rx = gradient.ry = Math.min(tl, tr, br, bl);
                break;
              case 'farthest-side':
                if(m2[0] === 'circle'){
                  gradient.rx = gradient.ry = Math.max(
                    gradient.cx,
                    gradient.cy,
                    gradient.x1 - gradient.cx,
                    gradient.y1 - gradient.cy
                    );
                } else { // ellipse

                  gradient.type = m2[0];

                  gradient.rx = Math.max(
                    gradient.cx,
                    gradient.x1 - gradient.cx
                    );
                  gradient.ry = Math.max(
                    gradient.cy,
                    gradient.y1 - gradient.cy
                    );
                }
                break;
              case 'closest-side':
              case 'contain': // is equivalent to closest-side
                if(m2[0] === 'circle'){
                  gradient.rx = gradient.ry = Math.min(
                    gradient.cx,
                    gradient.cy,
                    gradient.x1 - gradient.cx,
                    gradient.y1 - gradient.cy
                    );
                } else { // ellipse

                  gradient.type = m2[0];

                  gradient.rx = Math.min(
                    gradient.cx,
                    gradient.x1 - gradient.cx
                    );
                  gradient.ry = Math.min(
                    gradient.cy,
                    gradient.y1 - gradient.cy
                    );
                }
                break;

            // TODO: add support for "30px 40px" sizes (webkit only)
            }
          }

          // color stops
          m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
          if(m2){
            m2Len = m2.length;
            step = 1 / Math.max(m2Len - 1, 1);
            for(i = 0; i < m2Len; i+=1){
              m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
              if(m3[2]){
                stop = parseFloat(m3[2]);
                if(m3[3] === '%'){
                  stop /= 100;
                } else { // px - stupid opera
                  stop /= bounds.width;
                }
              } else {
                stop = i * step;
              }
              gradient.colorStops.push({
                color: m3[1],
                stop: stop
              });
            }
          }
          break;
      }
    }

    return gradient;
  };

  _html2canvas.Generate.Gradient = function(src, bounds) {
    if(bounds.width === 0 || bounds.height === 0) {
      return;
    }

    var canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d'),
    gradient, grad, i, len;

    canvas.width = bounds.width;
    canvas.height = bounds.height;

    // TODO: add support for multi defined background gradients
    gradient = _html2canvas.Generate.parseGradient(src, bounds);

    if(gradient) {
      if(gradient.type === 'linear') {
        grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);

        for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
          try {
            grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
          }
          catch(e) {
            h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
          }
        }

        ctx.fillStyle = grad;
        ctx.fillRect(0, 0, bounds.width, bounds.height);

      } else if(gradient.type === 'circle') {

        grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx);

        for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
          try {
            grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
          }
          catch(e) {
            h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
          }
        }

        ctx.fillStyle = grad;
        ctx.fillRect(0, 0, bounds.width, bounds.height);

      } else if(gradient.type === 'ellipse') {

        // draw circle
        var canvasRadial = document.createElement('canvas'),
        ctxRadial = canvasRadial.getContext('2d'),
        ri = Math.max(gradient.rx, gradient.ry),
        di = ri * 2, imgRadial;

        canvasRadial.width = canvasRadial.height = di;

        grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri);

        for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
          try {
            grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
          }
          catch(e) {
            h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
          }
        }

        ctxRadial.fillStyle = grad;
        ctxRadial.fillRect(0, 0, di, di);

        ctx.fillStyle = gradient.colorStops[i - 1].color;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);

      }
    }

    return canvas;
  };

  _html2canvas.Generate.ListAlpha = function(number) {
    var tmp = "",
    modulus;

    do {
      modulus = number % 26;
      tmp = String.fromCharCode((modulus) + 64) + tmp;
      number = number / 26;
    }while((number*26) > 26);

    return tmp;
  };

  _html2canvas.Generate.ListRoman = function(number) {
    var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
    decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
    roman = "",
    v,
    len = romanArray.length;

    if (number <= 0 || number >= 4000) {
      return number;
    }

    for (v=0; v < len; v+=1) {
      while (number >= decimal[v]) {
        number -= decimal[v];
        roman += romanArray[v];
      }
    }

    return roman;

  };

})();
function h2cRenderContext(width, height) {
  var storage = [];
  return {
    storage: storage,
    width: width,
    height: height,
    clip: function() {
      storage.push({
        type: "function",
        name: "clip",
        'arguments': arguments
      });
    },
    translate: function() {
      storage.push({
        type: "function",
        name: "translate",
        'arguments': arguments
      });
    },
    fill: function() {
      storage.push({
        type: "function",
        name: "fill",
        'arguments': arguments
      });
    },
    save: function() {
      storage.push({
        type: "function",
        name: "save",
        'arguments': arguments
      });
    },
    restore: function() {
      storage.push({
        type: "function",
        name: "restore",
        'arguments': arguments
      });
    },
    fillRect: function () {
      storage.push({
        type: "function",
        name: "fillRect",
        'arguments': arguments
      });
    },
    createPattern: function() {
      storage.push({
        type: "function",
        name: "createPattern",
        'arguments': arguments
      });
    },
    drawShape: function() {

      var shape = [];

      storage.push({
        type: "function",
        name: "drawShape",
        'arguments': shape
      });

      return {
        moveTo: function() {
          shape.push({
            name: "moveTo",
            'arguments': arguments
          });
        },
        lineTo: function() {
          shape.push({
            name: "lineTo",
            'arguments': arguments
          });
        },
        arcTo: function() {
          shape.push({
            name: "arcTo",
            'arguments': arguments
          });
        },
        bezierCurveTo: function() {
          shape.push({
            name: "bezierCurveTo",
            'arguments': arguments
          });
        },
        quadraticCurveTo: function() {
          shape.push({
            name: "quadraticCurveTo",
            'arguments': arguments
          });
        }
      };

    },
    drawImage: function () {
      storage.push({
        type: "function",
        name: "drawImage",
        'arguments': arguments
      });
    },
    fillText: function () {
      storage.push({
        type: "function",
        name: "fillText",
        'arguments': arguments
      });
    },
    setVariable: function (variable, value) {
      storage.push({
        type: "variable",
        name: variable,
        'arguments': value
      });
    }
  };
}
_html2canvas.Parse = function (images, options) {
  window.scroll(0,0);

  var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default
  numDraws = 0,
  doc = element.ownerDocument,
  support = _html2canvas.Util.Support(options, doc),
  ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"),
  body = doc.body,
  getCSS = _html2canvas.Util.getCSS,
  pseudoHide = "___html2canvas___pseudoelement",
  hidePseudoElements = doc.createElement('style');

  hidePseudoElements.innerHTML = '.' + pseudoHide + '-before:before { content: "" !important; display: none !important; }' +
  '.' + pseudoHide + '-after:after { content: "" !important; display: none !important; }';

  body.appendChild(hidePseudoElements);

  images = images || {};

  function documentWidth () {
    return Math.max(
      Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
      Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
      Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
      );
  }

  function documentHeight () {
    return Math.max(
      Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
      Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
      Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
      );
  }

  function getCSSInt(element, attribute) {
    var val = parseInt(getCSS(element, attribute), 10);
    return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html
  }

  function renderRect (ctx, x, y, w, h, bgcolor) {
    if (bgcolor !== "transparent"){
      ctx.setVariable("fillStyle", bgcolor);
      ctx.fillRect(x, y, w, h);
      numDraws+=1;
    }
  }

  function textTransform (text, transform) {
    switch(transform){
      case "lowercase":
        return text.toLowerCase();
      case "capitalize":
        return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) {
          if (m.length > 0) {
            return p1 + p2.toUpperCase();
          }
        } );
      case "uppercase":
        return text.toUpperCase();
      default:
        return text;
    }
  }

  function noLetterSpacing(letter_spacing) {
    return (/^(normal|none|0px)$/.test(letter_spacing));
  }

  function drawText(currentText, x, y, ctx){
    if (currentText !== null && _html2canvas.Util.trimText(currentText).length > 0) {
      ctx.fillText(currentText, x, y);
      numDraws+=1;
    }
  }

  function setTextVariables(ctx, el, text_decoration, color) {
    var align = false,
    bold = getCSS(el, "fontWeight"),
    family = getCSS(el, "fontFamily"),
    size = getCSS(el, "fontSize");

    switch(parseInt(bold, 10)){
      case 401:
        bold = "bold";
        break;
      case 400:
        bold = "normal";
        break;
    }

    ctx.setVariable("fillStyle", color);
    ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" "));
    ctx.setVariable("textAlign", (align) ? "right" : "left");

    if (text_decoration !== "none"){
      return _html2canvas.Util.Font(family, size, doc);
    }
  }

  function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) {
    switch(text_decoration) {
      case "underline":
        // Draws a line at the baseline of the font
        // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
        renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color);
        break;
      case "overline":
        renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color);
        break;
      case "line-through":
        // TODO try and find exact position for line-through
        renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color);
        break;
    }
  }

  function getTextBounds(state, text, textDecoration, isLast) {
    var bounds;
    if (support.rangeBounds) {
      if (textDecoration !== "none" || _html2canvas.Util.trimText(text).length !== 0) {
        bounds = textRangeBounds(text, state.node, state.textOffset);
      }
      state.textOffset += text.length;
    } else if (state.node && typeof state.node.nodeValue === "string" ){
      var newTextNode = (isLast) ? state.node.splitText(text.length) : null;
      bounds = textWrapperBounds(state.node);
      state.node = newTextNode;
    }
    return bounds;
  }

  function textRangeBounds(text, textNode, textOffset) {
    var range = doc.createRange();
    range.setStart(textNode, textOffset);
    range.setEnd(textNode, textOffset + text.length);
    return range.getBoundingClientRect();
  }

  function textWrapperBounds(oldTextNode) {
    var parent = oldTextNode.parentNode,
    wrapElement = doc.createElement('wrapper'),
    backupText = oldTextNode.cloneNode(true);

    wrapElement.appendChild(oldTextNode.cloneNode(true));
    parent.replaceChild(wrapElement, oldTextNode);

    var bounds = _html2canvas.Util.Bounds(wrapElement);
    parent.replaceChild(backupText, wrapElement);
    return bounds;
  }

  function renderText(el, textNode, stack) {
    var ctx = stack.ctx,
    color = getCSS(el, "color"),
    textDecoration = getCSS(el, "textDecoration"),
    textAlign = getCSS(el, "textAlign"),
    metrics,
    textList,
    state = {
      node: textNode,
      textOffset: 0
    };

    if (_html2canvas.Util.trimText(textNode.nodeValue).length > 0) {
      textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform"));
      textAlign = textAlign.replace(["-webkit-auto"],["auto"]);

      textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ?
      textNode.nodeValue.split(/(\b| )/)
      : textNode.nodeValue.split("");

      metrics = setTextVariables(ctx, el, textDecoration, color);

      if (options.chinese) {
        textList.forEach(function(word, index) {
          if (/.*[\u4E00-\u9FA5].*$/.test(word)) {
            word = word.split("");
            word.unshift(index, 1);
            textList.splice.apply(textList, word);
          }
        });
      }

      textList.forEach(function(text, index) {
        var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1));
        if (bounds) {
          drawText(text, bounds.left, bounds.bottom, ctx);
          renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
        }
      });
    }
  }

  function listPosition (element, val) {
    var boundElement = doc.createElement( "boundelement" ),
    originalType,
    bounds;

    boundElement.style.display = "inline";

    originalType = element.style.listStyleType;
    element.style.listStyleType = "none";

    boundElement.appendChild(doc.createTextNode(val));

    element.insertBefore(boundElement, element.firstChild);

    bounds = _html2canvas.Util.Bounds(boundElement);
    element.removeChild(boundElement);
    element.style.listStyleType = originalType;
    return bounds;
  }

  function elementIndex( el ) {
    var i = -1,
    count = 1,
    childs = el.parentNode.childNodes;

    if (el.parentNode) {
      while( childs[ ++i ] !== el ) {
        if ( childs[ i ].nodeType === 1 ) {
          count++;
        }
      }
      return count;
    } else {
      return -1;
    }
  }

  function listItemText(element, type) {
    var currentIndex = elementIndex(element),
    text;
    switch(type){
      case "decimal":
        text = currentIndex;
        break;
      case "decimal-leading-zero":
        text = (currentIndex.toString().length === 1) ? currentIndex = "0" + currentIndex.toString() : currentIndex.toString();
        break;
      case "upper-roman":
        text = _html2canvas.Generate.ListRoman( currentIndex );
        break;
      case "lower-roman":
        text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase();
        break;
      case "lower-alpha":
        text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase();
        break;
      case "upper-alpha":
        text = _html2canvas.Generate.ListAlpha( currentIndex );
        break;
    }

    text += ". ";
    return text;
  }

  function renderListItem(element, stack, elBounds) {
    var x,
    text,
    ctx = stack.ctx,
    type = getCSS(element, "listStyleType"),
    listBounds;

    if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) {
      text = listItemText(element, type);
      listBounds = listPosition(element, text);
      setTextVariables(ctx, element, "none", getCSS(element, "color"));

      if (getCSS(element, "listStylePosition") === "inside") {
        ctx.setVariable("textAlign", "left");
        x = elBounds.left;
      } else {
        return;
      }

      drawText(text, x, listBounds.bottom, ctx);
    }
  }

  function loadImage (src){
    var img = images[src];
    if (img && img.succeeded === true) {
      return img.img;
    } else {
      return false;
    }
  }

  function clipBounds(src, dst){
    var x = Math.max(src.left, dst.left),
    y = Math.max(src.top, dst.top),
    x2 = Math.min((src.left + src.width), (dst.left + dst.width)),
    y2 = Math.min((src.top + src.height), (dst.top + dst.height));

    return {
      left:x,
      top:y,
      width:x2-x,
      height:y2-y
    };
  }

  function setZ(zIndex, parentZ){
    // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them
    var newContext;
    if (!parentZ){
      newContext = h2czContext(0);
      return newContext;
    }

    if (zIndex !== "auto"){
      newContext = h2czContext(zIndex);
      parentZ.children.push(newContext);
      return newContext;

    }

    return parentZ;
  }

  function renderImage(ctx, element, image, bounds, borders) {

    var paddingLeft = getCSSInt(element, 'paddingLeft'),
    paddingTop = getCSSInt(element, 'paddingTop'),
    paddingRight = getCSSInt(element, 'paddingRight'),
    paddingBottom = getCSSInt(element, 'paddingBottom');

    drawImage(
      ctx,
      image,
      0, //sx
      0, //sy
      image.width, //sw
      image.height, //sh
      bounds.left + paddingLeft + borders[3].width, //dx
      bounds.top + paddingTop + borders[0].width, // dy
      bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw
      bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh
      );
  }

  function getBorderData(element) {
    return ["Top", "Right", "Bottom", "Left"].map(function(side) {
      return {
        width: getCSSInt(element, 'border' + side + 'Width'),
        color: getCSS(element, 'border' + side + 'Color')
      };
    });
  }

  function getBorderRadiusData(element) {
    return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
      return getCSS(element, 'border' + side + 'Radius');
    });
  }

  var getCurvePoints = (function(kappa) {

    return function(x, y, r1, r2) {
      var ox = (r1) * kappa, // control point offset horizontal
      oy = (r2) * kappa, // control point offset vertical
      xm = x + r1, // x-middle
      ym = y + r2; // y-middle
      return {
        topLeft: bezierCurve({
          x:x,
          y:ym
        }, {
          x:x,
          y:ym - oy
        }, {
          x:xm - ox,
          y:y
        }, {
          x:xm,
          y:y
        }),
        topRight: bezierCurve({
          x:x,
          y:y
        }, {
          x:x + ox,
          y:y
        }, {
          x:xm,
          y:ym - oy
        }, {
          x:xm,
          y:ym
        }),
        bottomRight: bezierCurve({
          x:xm,
          y:y
        }, {
          x:xm,
          y:y + oy
        }, {
          x:x + ox,
          y:ym
        }, {
          x:x,
          y:ym
        }),
        bottomLeft: bezierCurve({
          x:xm,
          y:ym
        }, {
          x:xm - ox,
          y:ym
        }, {
          x:x,
          y:y + oy
        }, {
          x:x,
          y:y
        })
      };
    };
  })(4 * ((Math.sqrt(2) - 1) / 3));

  function bezierCurve(start, startControl, endControl, end) {

    var lerp = function (a, b, t) {
      return {
        x:a.x + (b.x - a.x) * t,
        y:a.y + (b.y - a.y) * t
      };
    };

    return {
      start: start,
      startControl: startControl,
      endControl: endControl,
      end: end,
      subdivide: function(t) {
        var ab = lerp(start, startControl, t),
        bc = lerp(startControl, endControl, t),
        cd = lerp(endControl, end, t),
        abbc = lerp(ab, bc, t),
        bccd = lerp(bc, cd, t),
        dest = lerp(abbc, bccd, t);
        return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
      },
      curveTo: function(borderArgs) {
        borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
      },
      curveToReversed: function(borderArgs) {
        borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
      }
    };
  }

  function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
    if (radius1[0] > 0 || radius1[1] > 0) {
      borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
      corner1[0].curveTo(borderArgs);
      corner1[1].curveTo(borderArgs);
    } else {
      borderArgs.push(["line", x, y]);
    }

    if (radius2[0] > 0 || radius2[1] > 0) {
      borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
    }
  }

  function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
    var borderArgs = [];

    if (radius1[0] > 0 || radius1[1] > 0) {
      borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
      outer1[1].curveTo(borderArgs);
    } else {
      borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
    }

    if (radius2[0] > 0 || radius2[1] > 0) {
      borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
      outer2[0].curveTo(borderArgs);
      borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
      inner2[0].curveToReversed(borderArgs);
    } else {
      borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]);
      borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]);
    }

    if (radius1[0] > 0 || radius1[1] > 0) {
      borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
      inner1[1].curveToReversed(borderArgs);
    } else {
      borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]);
    }

    return borderArgs;
  }

  function calculateCurvePoints(bounds, borderRadius, borders) {

    var x = bounds.left,
    y = bounds.top,
    width = bounds.width,
    height = bounds.height,

    tlh = borderRadius[0][0],
    tlv = borderRadius[0][1],
    trh = borderRadius[1][0],
    trv = borderRadius[1][1],
    brv = borderRadius[2][0],
    brh = borderRadius[2][1],
    blh = borderRadius[3][0],
    blv = borderRadius[3][1],

    topWidth = width - trh,
    rightHeight = height - brv,
    bottomWidth = width - brh,
    leftHeight = height - blv;

    return {
      topLeftOuter: getCurvePoints(
        x,
        y,
        tlh,
        tlv
        ).topLeft.subdivide(0.5),

      topLeftInner: getCurvePoints(
        x + borders[3].width,
        y + borders[0].width,
        Math.max(0, tlh - borders[3].width),
        Math.max(0, tlv - borders[0].width)
        ).topLeft.subdivide(0.5),

      topRightOuter: getCurvePoints(
        x + topWidth,
        y,
        trh,
        trv
        ).topRight.subdivide(0.5),

      topRightInner: getCurvePoints(
        x + Math.min(topWidth, width + borders[3].width),
        y + borders[0].width,
        (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width,
        trv - borders[0].width
        ).topRight.subdivide(0.5),

      bottomRightOuter: getCurvePoints(
        x + bottomWidth,
        y + rightHeight,
        brh,
        brv
        ).bottomRight.subdivide(0.5),

      bottomRightInner: getCurvePoints(
        x + Math.min(bottomWidth, width + borders[3].width),
        y + Math.min(rightHeight, height + borders[0].width),
        Math.max(0, brh - borders[1].width),
        Math.max(0, brv - borders[2].width)
        ).bottomRight.subdivide(0.5),

      bottomLeftOuter: getCurvePoints(
        x,
        y + leftHeight,
        blh,
        blv
        ).bottomLeft.subdivide(0.5),

      bottomLeftInner: getCurvePoints(
        x + borders[3].width,
        y + leftHeight,
        Math.max(0, blh - borders[3].width),
        Math.max(0, blv - borders[2].width)
        ).bottomLeft.subdivide(0.5)
    };
  }

  function getBorderClip(element, borderPoints, borders, radius, bounds) {
    var backgroundClip = getCSS(element, 'backgroundClip'),
    borderArgs = [];

    switch(backgroundClip) {
      case "content-box":
      case "padding-box":
        parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
        parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
        parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
        parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
        break;

      default:
        parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
        parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
        parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
        parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
        break;
    }

    return borderArgs;
  }

  function parseBorders(element, bounds, borders){
    var x = bounds.left,
    y = bounds.top,
    width = bounds.width,
    height = bounds.height,
    borderSide,
    bx,
    by,
    bw,
    bh,
    borderArgs,
    // http://www.w3.org/TR/css3-background/#the-border-radius
    borderRadius = getBorderRadiusData(element),
    borderPoints = calculateCurvePoints(bounds, borderRadius, borders),
    borderData = {
      clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds),
      borders: []
    };

    for (borderSide = 0; borderSide < 4; borderSide++) {

      if (borders[borderSide].width > 0) {
        bx = x;
        by = y;
        bw = width;
        bh = height - (borders[2].width);

        switch(borderSide) {
          case 0:
            // top border
            bh = borders[0].width;

            borderArgs = drawSide({
              c1: [bx, by],
              c2: [bx + bw, by],
              c3: [bx + bw - borders[1].width, by + bh],
              c4: [bx + borders[3].width, by + bh]
            }, borderRadius[0], borderRadius[1],
            borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
            break;
          case 1:
            // right border
            bx = x + width - (borders[1].width);
            bw = borders[1].width;

            borderArgs = drawSide({
              c1: [bx + bw, by],
              c2: [bx + bw, by + bh + borders[2].width],
              c3: [bx, by + bh],
              c4: [bx, by + borders[0].width]
            }, borderRadius[1], borderRadius[2],
            borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
            break;
          case 2:
            // bottom border
            by = (by + height) - (borders[2].width);
            bh = borders[2].width;

            borderArgs = drawSide({
              c1: [bx + bw, by + bh],
              c2: [bx, by + bh],
              c3: [bx + borders[3].width, by],
              c4: [bx + bw - borders[2].width, by]
            }, borderRadius[2], borderRadius[3],
            borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
            break;
          case 3:
            // left border
            bw = borders[3].width;

            borderArgs = drawSide({
              c1: [bx, by + bh + borders[2].width],
              c2: [bx, by],
              c3: [bx + bw, by + borders[0].width],
              c4: [bx + bw, by + bh]
            }, borderRadius[3], borderRadius[0],
            borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
            break;
        }

        borderData.borders.push({
          args: borderArgs,
          color: borders[borderSide].color
        });

      }
    }

    return borderData;
  }

  function createShape(ctx, args) {
    var shape = ctx.drawShape();
    args.forEach(function(border, index) {
      shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1));
    });
    return shape;
  }

  function renderBorders(ctx, borderArgs, color) {
    if (color !== "transparent") {
      ctx.setVariable( "fillStyle", color);
      createShape(ctx, borderArgs);
      ctx.fill();
      numDraws+=1;
    }
  }

  function renderFormValue (el, bounds, stack){

    var valueWrap = doc.createElement('valuewrap'),
    cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'],
    textValue,
    textNode;

    cssPropertyArray.forEach(function(property) {
      try {
        valueWrap.style[property] = getCSS(el, property);
      } catch(e) {
        // Older IE has issues with "border"
        h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
      }
    });

    valueWrap.style.borderColor = "black";
    valueWrap.style.borderStyle = "solid";
    valueWrap.style.display = "block";
    valueWrap.style.position = "absolute";

    if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){
      valueWrap.style.lineHeight = getCSS(el, "height");
    }

    valueWrap.style.top = bounds.top + "px";
    valueWrap.style.left = bounds.left + "px";

    textValue = (el.nodeName === "SELECT") ? (el.options[el.selectedIndex] || 0).text : el.value;
    if(!textValue) {
      textValue = el.placeholder;
    }

    textNode = doc.createTextNode(textValue);

    valueWrap.appendChild(textNode);
    body.appendChild(valueWrap);

    renderText(el, textNode, stack);
    body.removeChild(valueWrap);
  }

  function drawImage (ctx) {
    ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1));
    numDraws+=1;
  }

  function getPseudoElement(el, which) {
    var elStyle = window.getComputedStyle(el, which);
    if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content") {
      return;
    }
    var content = elStyle.content + '',
    first = content.substr( 0, 1 );
    //strips quotes
    if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) {
      content = content.substr( 1, content.length - 2 );
    }

    var isImage = content.substr( 0, 3 ) === 'url',
    elps = document.createElement( isImage ? 'img' : 'span' );

    elps.className = pseudoHide + "-before " + pseudoHide + "-after";

    Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) {
      // Prevent assigning of read only CSS Rules, ex. length, parentRule
      try {
        elps.style[prop] = elStyle[prop];
      } catch (e) {
        h2clog(['Tried to assign readonly property ', prop, 'Error:', e]);
      }
    });

    if(isImage) {
      elps.src = _html2canvas.Util.parseBackgroundImage(content)[0].args[0];
    } else {
      elps.innerHTML = content;
    }
    return elps;
  }

  function indexedProperty(property) {
    return (isNaN(window.parseInt(property, 10)));
  }

  function injectPseudoElements(el, stack) {
    var before = getPseudoElement(el, ':before'),
    after = getPseudoElement(el, ':after');
    if(!before && !after) {
      return;
    }

    if(before) {
      el.className += " " + pseudoHide + "-before";
      el.parentNode.insertBefore(before, el);
      parseElement(before, stack, true);
      el.parentNode.removeChild(before);
      el.className = el.className.replace(pseudoHide + "-before", "").trim();
    }

    if (after) {
      el.className += " " + pseudoHide + "-after";
      el.appendChild(after);
      parseElement(after, stack, true);
      el.removeChild(after);
      el.className = el.className.replace(pseudoHide + "-after", "").trim();
    }

  }

  function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) {
    var offsetX = Math.round(bounds.left + backgroundPosition.left),
    offsetY = Math.round(bounds.top + backgroundPosition.top);

    ctx.createPattern(image);
    ctx.translate(offsetX, offsetY);
    ctx.fill();
    ctx.translate(-offsetX, -offsetY);
  }

  function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) {
    var args = [];
    args.push(["line", Math.round(left), Math.round(top)]);
    args.push(["line", Math.round(left + width), Math.round(top)]);
    args.push(["line", Math.round(left + width), Math.round(height + top)]);
    args.push(["line", Math.round(left), Math.round(height + top)]);
    createShape(ctx, args);
    ctx.save();
    ctx.clip();
    renderBackgroundRepeat(ctx, image, backgroundPosition, bounds);
    ctx.restore();
  }

  function renderBackgroundColor(ctx, backgroundBounds, bgcolor) {
    renderRect(
      ctx,
      backgroundBounds.left,
      backgroundBounds.top,
      backgroundBounds.width,
      backgroundBounds.height,
      bgcolor
      );
  }

  function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) {
    var backgroundSize = _html2canvas.Util.BackgroundSize(el, bounds, image, imageIndex),
    backgroundPosition = _html2canvas.Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize),
    backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(function(value) {
      return value.trim();
    });

    image = resizeImage(image, backgroundSize);

    backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0];

    switch (backgroundRepeat) {
      case "repeat-x":
        backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
          bounds.left, bounds.top + backgroundPosition.top, 99999, image.height);
        break;

      case "repeat-y":
        backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
          bounds.left + backgroundPosition.left, bounds.top, image.width, 99999);
        break;

      case "no-repeat":
        backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
          bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height);
        break;

      default:
        renderBackgroundRepeat(ctx, image, backgroundPosition, {
          top: bounds.top,
          left: bounds.left,
          width: image.width,
          height: image.height
        });
        break;
    }
  }

  function renderBackgroundImage(element, bounds, ctx) {
    var backgroundImage = getCSS(element, "backgroundImage"),
    backgroundImages = _html2canvas.Util.parseBackgroundImage(backgroundImage),
    image,
    imageIndex = backgroundImages.length;

    while(imageIndex--) {
      backgroundImage = backgroundImages[imageIndex];

      if (!backgroundImage.args || backgroundImage.args.length === 0) {
        continue;
      }

      var key = backgroundImage.method === 'url' ?
      backgroundImage.args[0] :
      backgroundImage.value;

      image = loadImage(key);

      // TODO add support for background-origin
      if (image) {
        renderBackgroundRepeating(element, bounds, ctx, image, imageIndex);
      } else {
        h2clog("html2canvas: Error loading background:", backgroundImage);
      }
    }
  }

  function resizeImage(image, bounds) {
    if(image.width === bounds.width && image.height === bounds.height) {
      return image;
    }

    var ctx, canvas = doc.createElement('canvas');
    canvas.width = bounds.width;
    canvas.height = bounds.height;
    ctx = canvas.getContext("2d");
    drawImage(ctx, image, 0, 0, image.width, image.height, 0, 0, bounds.width, bounds.height );
    return canvas;
  }

  function setOpacity(ctx, element, parentStack) {
    var opacity = getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1);
    ctx.setVariable("globalAlpha", opacity);
    return opacity;
  }

  function createStack(element, parentStack, bounds) {

    var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height),
    stack = {
      ctx: ctx,
      zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null),
      opacity: setOpacity(ctx, element, parentStack),
      cssPosition: getCSS(element, "position"),
      borders: getBorderData(element),
      clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null
    };

    // TODO correct overflow for absolute content residing under a static position
    if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){
      stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds;
    }

    stack.zIndex.children.push(stack);

    return stack;
  }

  function getBackgroundBounds(borders, bounds, clip) {
    var backgroundBounds = {
      left: bounds.left + borders[3].width,
      top: bounds.top + borders[0].width,
      width: bounds.width - (borders[1].width + borders[3].width),
      height: bounds.height - (borders[0].width + borders[2].width)
    };

    if (clip) {
      backgroundBounds = clipBounds(backgroundBounds, clip);
    }

    return backgroundBounds;
  }

  function renderElement(element, parentStack, pseudoElement){
    var bounds = _html2canvas.Util.Bounds(element),
    image,
    bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"),
    stack = createStack(element, parentStack, bounds),
    borders = stack.borders,
    ctx = stack.ctx,
    backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip),
    borderData = parseBorders(element, bounds, borders);

    createShape(ctx, borderData.clip);

    ctx.save();
    ctx.clip();

    if (backgroundBounds.height > 0 && backgroundBounds.width > 0){
      renderBackgroundColor(ctx, bounds, bgcolor);
      renderBackgroundImage(element, backgroundBounds, ctx);
    }

    ctx.restore();

    borderData.borders.forEach(function(border) {
      renderBorders(ctx, border.args, border.color);
    });

    if (!pseudoElement) {
      injectPseudoElements(element, stack);
    }

    switch(element.nodeName){
      case "IMG":
        if ((image = loadImage(element.getAttribute('src')))) {
          renderImage(ctx, element, image, bounds, borders);
        } else {
          h2clog("html2canvas: Error loading <img>:" + element.getAttribute('src'));
        }
        break;
      case "INPUT":
        // TODO add all relevant type's, i.e. HTML5 new stuff
        // todo add support for placeholder attribute for browsers which support it
        if (/^(text|url|email|submit|button|reset)$/.test(element.type) && (element.value || element.placeholder).length > 0){
          renderFormValue(element, bounds, stack);
        }
        break;
      case "TEXTAREA":
        if ((element.value || element.placeholder || "").length > 0){
          renderFormValue(element, bounds, stack);
        }
        break;
      case "SELECT":
        if ((element.options||element.placeholder || "").length > 0){
          renderFormValue(element, bounds, stack);
        }
        break;
      case "LI":
        renderListItem(element, stack, backgroundBounds);
        break;
      case "CANVAS":
        renderImage(ctx, element, element, bounds, borders);
        break;
    }

    return stack;
  }

  function isElementVisible(element) {
    return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore"));
  }

  function parseElement (el, stack, pseudoElement) {

    if (isElementVisible(el)) {
      stack = renderElement(el, stack, pseudoElement) || stack;
      if (!ignoreElementsRegExp.test(el.nodeName)) {
        _html2canvas.Util.Children(el).forEach(function(node) {
          if (node.nodeType === 1) {
            parseElement(node, stack, pseudoElement);
          } else if (node.nodeType === 3) {
            renderText(el, node, stack);
          }
        });
      }
    }
  }

  function svgDOMRender(body, stack) {
    var img = new Image(),
    docWidth = documentWidth(),
    docHeight = documentHeight(),
    html = "";

    function parseDOM(el) {
      var children = _html2canvas.Util.Children( el ),
      len = children.length,
      attr,
      a,
      alen,
      elm,
      i;
      for ( i = 0; i < len; i+=1 ) {
        elm = children[ i ];
        if ( elm.nodeType === 3 ) {
          // Text node
          html += elm.nodeValue.replace(/</g,"&lt;").replace(/>/g,"&gt;");
        } else if ( elm.nodeType === 1 ) {
          // Element
          if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) {

            html += "<" + elm.nodeName.toLowerCase();

            // add attributes
            if ( elm.hasAttributes() ) {
              attr = elm.attributes;
              alen = attr.length;
              for ( a = 0; a < alen; a+=1 ) {
                html += " " + attr[ a ].name + '="' + attr[ a ].value + '"';
              }
            }


            html += '>';

            parseDOM( elm );


            html += "</" + elm.nodeName.toLowerCase() + ">";
          }
        }

      }

    }

    parseDOM(body);
    img.src = [
    "data:image/svg+xml,",
    "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" + docWidth + "' height='" + docHeight + "'>",
    "<foreignObject width='" + docWidth + "' height='" + docHeight + "'>",
    "<html xmlns='http://www.w3.org/1999/xhtml' style='margin:0;'>",
    html.replace(/\#/g,"%23"),
    "</html>",
    "</foreignObject>",
    "</svg>"
    ].join("");

    img.onload = function() {
      stack.svgRender = img;
    };

  }

  function init() {
    var stack = renderElement(element, null);

    if (support.svgRendering) {
      svgDOMRender(document.documentElement, stack);
    }

    Array.prototype.slice.call(element.children, 0).forEach(function(childElement) {
      parseElement(childElement, stack);
    });

    stack.backgroundColor = getCSS(document.documentElement, "backgroundColor");
    body.removeChild(hidePseudoElements);
    return stack;
  }

  return init();
};

function h2czContext(zindex) {
  return {
    zindex: zindex,
    children: []
  };
}
_html2canvas.Preload = function( options ) {

  var images = {
    numLoaded: 0,   // also failed are counted here
    numFailed: 0,
    numTotal: 0,
    cleanupDone: false
  },
  pageOrigin,
  methods,
  i,
  count = 0,
  element = options.elements[0] || document.body,
  doc = element.ownerDocument,
  domImages = doc.images, // TODO probably should limit it to images present in the element only
  imgLen = domImages.length,
  link = doc.createElement("a"),
  supportCORS = (function( img ){
    return (img.crossOrigin !== undefined);
  })(new Image()),
  timeoutTimer;

  link.href = window.location.href;
  pageOrigin  = link.protocol + link.host;

  function isSameOrigin(url){
    link.href = url;
    link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
    var origin = link.protocol + link.host;
    return (origin === pageOrigin);
  }

  function start(){
    h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")");
    if (!images.firstRun && images.numLoaded >= images.numTotal){
      h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")");

      if (typeof options.complete === "function"){
        options.complete(images);
      }

    }
  }

  // TODO modify proxy to serve images with CORS enabled, where available
  function proxyGetImage(url, img, imageObj){
    var callback_name,
    scriptUrl = options.proxy,
    script;

    link.href = url;
    url = link.href; // work around for pages with base href="" set - WARNING: this may change the url

    callback_name = 'html2canvas_' + (count++);
    imageObj.callbackname = callback_name;

    if (scriptUrl.indexOf("?") > -1) {
      scriptUrl += "&";
    } else {
      scriptUrl += "?";
    }
    scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name;
    script = doc.createElement("script");

    window[callback_name] = function(a){
      if (a.substring(0,6) === "error:"){
        imageObj.succeeded = false;
        images.numLoaded++;
        images.numFailed++;
        start();
      } else {
        setImageLoadHandlers(img, imageObj);
        img.src = a;
      }
      window[callback_name] = undefined; // to work with IE<9  // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
      try {
        delete window[callback_name];  // for all browser that support this
      } catch(ex) {}
      script.parentNode.removeChild(script);
      script = null;
      delete imageObj.script;
      delete imageObj.callbackname;
    };

    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", scriptUrl);
    imageObj.script = script;
    window.document.body.appendChild(script);

  }

  function loadPseudoElement(element, type) {
    var style = window.getComputedStyle(element, type),
    content = style.content;
    if (content.substr(0, 3) === 'url') {
      methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]);
    }
    loadBackgroundImages(style.backgroundImage, element);
  }

  function loadPseudoElementImages(element) {
    loadPseudoElement(element, ":before");
    loadPseudoElement(element, ":after");
  }

  function loadGradientImage(backgroundImage, bounds) {
    var img = _html2canvas.Generate.Gradient(backgroundImage, bounds);

    if (img !== undefined){
      images[backgroundImage] = {
        img: img,
        succeeded: true
      };
      images.numTotal++;
      images.numLoaded++;
      start();
    }
  }

  function invalidBackgrounds(background_image) {
    return (background_image && background_image.method && background_image.args && background_image.args.length > 0 );
  }

  function loadBackgroundImages(background_image, el) {
    var bounds;

    _html2canvas.Util.parseBackgroundImage(background_image).filter(invalidBackgrounds).forEach(function(background_image) {
      if (background_image.method === 'url') {
        methods.loadImage(background_image.args[0]);
      } else if(background_image.method.match(/\-?gradient$/)) {
        if(bounds === undefined) {
          bounds = _html2canvas.Util.Bounds(el);
        }
        loadGradientImage(background_image.value, bounds);
      }
    });
  }

  function getImages (el) {
    var elNodeType = false;

    // Firefox fails with permission denied on pages with iframes
    try {
      _html2canvas.Util.Children(el).forEach(function(img) {
        getImages(img);
      });
    }
    catch( e ) {}

    try {
      elNodeType = el.nodeType;
    } catch (ex) {
      elNodeType = false;
      h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
    }

    if (elNodeType === 1 || elNodeType === undefined) {
      loadPseudoElementImages(el);
      try {
        loadBackgroundImages(_html2canvas.Util.getCSS(el, 'backgroundImage'), el);
      } catch(e) {
        h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
      }
      loadBackgroundImages(el);
    }
  }

  function setImageLoadHandlers(img, imageObj) {
    img.onload = function() {
      if ( imageObj.timer !== undefined ) {
        // CORS succeeded
        window.clearTimeout( imageObj.timer );
      }

      images.numLoaded++;
      imageObj.succeeded = true;
      img.onerror = img.onload = null;
      start();
    };
    img.onerror = function() {
      if (img.crossOrigin === "anonymous") {
        // CORS failed
        window.clearTimeout( imageObj.timer );

        // let's try with proxy instead
        if ( options.proxy ) {
          var src = img.src;
          img = new Image();
          imageObj.img = img;
          img.src = src;

          proxyGetImage( img.src, img, imageObj );
          return;
        }
      }

      images.numLoaded++;
      images.numFailed++;
      imageObj.succeeded = false;
      img.onerror = img.onload = null;
      start();
    };
  }

  methods = {
    loadImage: function( src ) {
      var img, imageObj;
      if ( src && images[src] === undefined ) {
        img = new Image();
        if ( src.match(/data:image\/.*;base64,/i) ) {
          img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
          imageObj = images[src] = {
            img: img
          };
          images.numTotal++;
          setImageLoadHandlers(img, imageObj);
        } else if ( isSameOrigin( src ) || options.allowTaint ===  true ) {
          imageObj = images[src] = {
            img: img
          };
          images.numTotal++;
          setImageLoadHandlers(img, imageObj);
          img.src = src;
        } else if ( supportCORS && !options.allowTaint && options.useCORS ) {
          // attempt to load with CORS

          img.crossOrigin = "anonymous";
          imageObj = images[src] = {
            img: img
          };
          images.numTotal++;
          setImageLoadHandlers(img, imageObj);
          img.src = src;

          // work around for https://bugs.webkit.org/show_bug.cgi?id=80028
          img.customComplete = function () {
            if (!this.img.complete) {
              this.timer = window.setTimeout(this.img.customComplete, 100);
            } else {
              this.img.onerror();
            }
          }.bind(imageObj);
          img.customComplete();

        } else if ( options.proxy ) {
          imageObj = images[src] = {
            img: img
          };
          images.numTotal++;
          proxyGetImage( src, img, imageObj );
        }
      }

    },
    cleanupDOM: function(cause) {
      var img, src;
      if (!images.cleanupDone) {
        if (cause && typeof cause === "string") {
          h2clog("html2canvas: Cleanup because: " + cause);
        } else {
          h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
        }

        for (src in images) {
          if (images.hasOwnProperty(src)) {
            img = images[src];
            if (typeof img === "object" && img.callbackname && img.succeeded === undefined) {
              // cancel proxy image request
              window[img.callbackname] = undefined; // to work with IE<9  // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
              try {
                delete window[img.callbackname];  // for all browser that support this
              } catch(ex) {}
              if (img.script && img.script.parentNode) {
                img.script.setAttribute("src", "about:blank");  // try to cancel running request
                img.script.parentNode.removeChild(img.script);
              }
              images.numLoaded++;
              images.numFailed++;
              h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);
            }
          }
        }

        // cancel any pending requests
        if(window.stop !== undefined) {
          window.stop();
        } else if(document.execCommand !== undefined) {
          document.execCommand("Stop", false);
        }
        if (document.close !== undefined) {
          document.close();
        }
        images.cleanupDone = true;
        if (!(cause && typeof cause === "string")) {
          start();
        }
      }
    },

    renderingDone: function() {
      if (timeoutTimer) {
        window.clearTimeout(timeoutTimer);
      }
    }
  };

  if (options.timeout > 0) {
    timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout);
  }

  h2clog('html2canvas: Preload starts: finding background-images');
  images.firstRun = true;

  getImages(element);

  h2clog('html2canvas: Preload: Finding images');
  // load <img> images
  for (i = 0; i < imgLen; i+=1){
    methods.loadImage( domImages[i].getAttribute( "src" ) );
  }

  images.firstRun = false;
  h2clog('html2canvas: Preload: Done.');
  if ( images.numTotal === images.numLoaded ) {
    start();
  }

  return methods;

};
_html2canvas.Renderer = function(parseQueue, options){

  function createRenderQueue(parseQueue) {
    var queue = [];

    var sortZ = function(zStack){
      var subStacks = [],
      stackValues = [];

      zStack.children.forEach(function(stackChild) {
        if (stackChild.children && stackChild.children.length > 0){
          subStacks.push(stackChild);
          stackValues.push(stackChild.zindex);
        } else {
          queue.push(stackChild);
        }
      });

      stackValues.sort(function(a, b) {
        return a - b;
      });

      stackValues.forEach(function(zValue) {
        var index;

        subStacks.some(function(stack, i){
          index = i;
          return (stack.zindex === zValue);
        });
        sortZ(subStacks.splice(index, 1)[0]);

      });
    };

    sortZ(parseQueue.zIndex);

    return queue;
  }

  function getRenderer(rendererName) {
    var renderer;

    if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) {
      renderer = _html2canvas.Renderer[rendererName](options);
    } else if (typeof rendererName === "function") {
      renderer = rendererName(options);
    } else {
      throw new Error("Unknown renderer");
    }

    if ( typeof renderer !== "function" ) {
      throw new Error("Invalid renderer defined");
    }
    return renderer;
  }

  return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas);
};

_html2canvas.Util.Support = function (options, doc) {

  function supportSVGRendering() {
    var img = new Image(),
    canvas = doc.createElement("canvas"),
    ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d");
    if (ctx === false) {
      return false;
    }
    canvas.width = canvas.height = 10;
    img.src = [
    "data:image/svg+xml,",
    "<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>",
    "<foreignObject width='10' height='10'>",
    "<div xmlns='http://www.w3.org/1999/xhtml' style='width:10;height:10;'>",
    "sup",
    "</div>",
    "</foreignObject>",
    "</svg>"
    ].join("");
    try {
      ctx.drawImage(img, 0, 0);
      canvas.toDataURL();
    } catch(e) {
      return false;
    }
    h2clog('html2canvas: Parse: SVG powered rendering available');
    return true;
  }

  // Test whether we can use ranges to measure bounding boxes
  // Opera doesn't provide valid bounds.height/bottom even though it supports the method.

  function supportRangeBounds() {
    var r, testElement, rangeBounds, rangeHeight, support = false;

    if (doc.createRange) {
      r = doc.createRange();
      if (r.getBoundingClientRect) {
        testElement = doc.createElement('boundtest');
        testElement.style.height = "123px";
        testElement.style.display = "block";
        doc.body.appendChild(testElement);

        r.selectNode(testElement);
        rangeBounds = r.getBoundingClientRect();
        rangeHeight = rangeBounds.height;

        if (rangeHeight === 123) {
          support = true;
        }
        doc.body.removeChild(testElement);
      }
    }

    return support;
  }

  return {
    rangeBounds: supportRangeBounds(),
    svgRendering: options.svgRendering && supportSVGRendering()
  };
};
window.html2canvas = function(elements, opts) {
  elements = (elements.length) ? elements : [elements];
  var queue,
  canvas,
  options = {
    // general
    logging: false,
    elements: elements,
    background: "#fff",

    // preload options
    proxy: null,
    timeout: 0,    // no timeout
    useCORS: false, // try to load images as CORS (where available), before falling back to proxy
    allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true

    // parse options
    svgRendering: false, // use svg powered rendering where available (FF11+)
    ignoreElements: "IFRAME|OBJECT|PARAM",
    useOverflow: true,
    letterRendering: false,
    chinese: false,

    // render options

    width: null,
    height: null,
    taintTest: true, // do a taint test with all images before applying to canvas
    renderer: "Canvas"
  };

  options = _html2canvas.Util.Extend(opts, options);

  _html2canvas.logging = options.logging;
  options.complete = function( images ) {

    if (typeof options.onpreloaded === "function") {
      if ( options.onpreloaded( images ) === false ) {
        return;
      }
    }
    queue = _html2canvas.Parse( images, options );

    if (typeof options.onparsed === "function") {
      if ( options.onparsed( queue ) === false ) {
        return;
      }
    }

    canvas = _html2canvas.Renderer( queue, options );

    if (typeof options.onrendered === "function") {
      options.onrendered( canvas );
    }


  };

  // for pages without images, we still want this to be async, i.e. return methods before executing
  window.setTimeout( function(){
    _html2canvas.Preload( options );
  }, 0 );

  return {
    render: function( queue, opts ) {
      return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
    },
    parse: function( images, opts ) {
      return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) );
    },
    preload: function( opts ) {
      return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) );
    },
    log: h2clog
  };
};

window.html2canvas.log = h2clog; // for renderers
window.html2canvas.Renderer = {
  Canvas: undefined // We are assuming this will be used
};
_html2canvas.Renderer.Canvas = function(options) {

  options = options || {};

  var doc = document,
  safeImages = [],
  testCanvas = document.createElement("canvas"),
  testctx = testCanvas.getContext("2d"),
  canvas = options.canvas || doc.createElement('canvas');


  function createShape(ctx, args) {
    ctx.beginPath();
    args.forEach(function(arg) {
      ctx[arg.name].apply(ctx, arg['arguments']);
    });
    ctx.closePath();
  }

  function safeImage(item) {
    if (safeImages.indexOf(item['arguments'][0].src ) === -1) {
      testctx.drawImage(item['arguments'][0], 0, 0);
      try {
        testctx.getImageData(0, 0, 1, 1);
      } catch(e) {
        testCanvas = doc.createElement("canvas");
        testctx = testCanvas.getContext("2d");
        return false;
      }
      safeImages.push(item['arguments'][0].src);
    }
    return true;
  }

  function isTransparent(backgroundColor) {
    return (backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)");
  }

  function renderItem(ctx, item) {
    switch(item.type){
      case "variable":
        ctx[item.name] = item['arguments'];
        break;
      case "function":
        if (item.name === "createPattern") {
          if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) {
            try {
              ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat");
            }
            catch(e) {
              h2clog("html2canvas: Renderer: Error creating pattern", e.message);
            }
          }
        } else if (item.name === "drawShape") {
          createShape(ctx, item['arguments']);
        } else if (item.name === "drawImage") {
          if (item['arguments'][8] > 0 && item['arguments'][7] > 0) {
            if (!options.taintTest || (options.taintTest && safeImage(item))) {
              ctx.drawImage.apply( ctx, item['arguments'] );
            }
          }
        } else {
          ctx[item.name].apply(ctx, item['arguments']);
        }
        break;
    }
  }

  return function(zStack, options, doc, queue, _html2canvas) {

    var ctx = canvas.getContext("2d"),
    storageContext,
    i,
    queueLen,
    newCanvas,
    bounds,
    fstyle;

    canvas.width = canvas.style.width =  options.width || zStack.ctx.width;
    canvas.height = canvas.style.height = options.height || zStack.ctx.height;

    fstyle = ctx.fillStyle;
    ctx.fillStyle = (isTransparent(zStack.backgroundColor) && options.background !== undefined) ? options.background : zStack.backgroundColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = fstyle;


    if ( options.svgRendering && zStack.svgRender !== undefined ) {
      // TODO: enable async rendering to support this
      ctx.drawImage( zStack.svgRender, 0, 0 );
    } else {
      for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) {
        storageContext = queue.splice(0, 1)[0];
        storageContext.canvasPosition = storageContext.canvasPosition || {};

        // set common settings for canvas
        ctx.textBaseline = "bottom";

        if (storageContext.clip){
          ctx.save();
          ctx.beginPath();
          // console.log(storageContext);
          ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
          ctx.clip();
        }

        if (storageContext.ctx.storage) {
          storageContext.ctx.storage.forEach(renderItem.bind(null, ctx));
        }

        if (storageContext.clip){
          ctx.restore();
        }
      }
    }

    h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");

    queueLen = options.elements.length;

    if (queueLen === 1) {
      if (typeof options.elements[0] === "object" && options.elements[0].nodeName !== "BODY") {
        // crop image to the bounds of selected (single) element
        bounds = _html2canvas.Util.Bounds(options.elements[0]);
        newCanvas = doc.createElement('canvas');
        newCanvas.width = bounds.width;
        newCanvas.height = bounds.height;
        ctx = newCanvas.getContext("2d");

        ctx.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height);
        canvas = null;
        return newCanvas;
      }
    }

    return canvas;
  };
};
})(window,document);;var translateAble = translateAble || {};

translateAble.Langs = ['pl', 'en'];
translateAble.Separator = '#LANG_TRANS#';
translateAble.LangIdent = '#LANG_IDENT#';
//translateAble.DefLang = 'pl'; translations.lang

translateAble.Text = function (defaultValue, text) {
    var self = this;
    
    self.initMode = true;


    self.defaultValue = defaultValue != null ? defaultValue : '';

    self.transTextes = [];
    for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
        self.transTextes[translateAble.Langs[i]] = ko.observable(self.defaultValue);
    }

    self.currentLang = ko.observable(translations.lang);

    if (text) self.initByModel();

    

    self.currentValue = ko.observable(self.transTextes[translations.lang]());
    self.currentValue.subscribe(function (newValue) {
        if (!self.initMode) {
            self.transTextes[self.currentLang()](newValue);
        }
    });

    self.currentLang.subscribe(function (newValue) {
        self.initMode = true;
        self.currentValue(self.transTextes[newValue]());
        self.initMode = false;
    });

    self.someValue = ko.computed({
        read: function () {
            return self.getSome();
        },
        write: function (value) {
            self.currentValue(value);
        },
        owner: self
    }); 

    self.initMode = false;
}
translateAble.Text.prototype = {
    getClearData: function(){
        var self = this;
        if (self.defaultValue === '') {
            return self.getClearText();
        }
        if (self.defaultValue === 0) {
            return self.getStructData();
        }
        return null;
    },
    getClearText: function(){
        var self = this;
        var result = '';
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            var txt = self.transTextes[translateAble.Langs[i]]();

            if (result) result += translateAble.Separator;

            result += translateAble.Langs[i];
            result += translateAble.LangIdent;
            result += txt;
        }
        return result;
    },
    getStructData: function(){
        var self = this;
        var result = [];
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            var txt = self.transTextes[translateAble.Langs[i]]();
            result.push({ Lang: translateAble.Langs[i], Id: txt });
        }
        return result;
    },
    getSome: function(){
        var self = this;
        var current = self.defaultValue;
        var def = self.defaultValue;
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            var txt = self.transTextes[translateAble.Langs[i]]();
            if (translations.lang == translateAble.Langs[i]) {
                current = txt;
            } else {
                def = txt;
            }
        }
        if (current) return current;
        return def;
    },
    initByModel: function (model) {
        var self = this;
        if (self.defaultValue === '') {
            self.initByText(model);
        }
        if (self.defaultValue === 0) {
            self.initByStruct(model);
        }
    },
    initByText: function (text) {
        var self = this;

        self.initMode = true;

        if (_.isFunction(text)) {
            self.sourceText = text;
        } else {
            self.sourceText = ko.observable(text);
        }        

        var toSplit = self.sourceText();
        var splits = toSplit?toSplit.split(translateAble.Separator):[''];
        if (splits.length==1) {
            var langTouple = splits[0].split(translateAble.LangIdent);
            if (langTouple.length == 2) {
                self.transTextes[langTouple[0]](langTouple[1]);
            } else {
                self.transTextes[self.currentLang()](langTouple[0]);
            }
        } else {
            for (var i = 0, len = splits.length; i < len; i++) {
                var lnText = splits[i];
                var langTouple = lnText.split(translateAble.LangIdent);
                if (langTouple.length == 2) {
                    self.transTextes[langTouple[0]](langTouple[1]);
                }
            }
        }
        self.currentValue(self.transTextes[self.currentLang()]());

        self.initMode = false;
    },
    initByStruct: function (text) {
        var self = this;

        self.initMode = true;

        if (_.isFunction(text)) {
            self.sourceArr = text();
        } else {
            self.sourceArr = text;
        }

        _(self.sourceArr).each(function (elem) {
            self.transTextes[elem.Lang ? elem.Lang : translations.lang](elem.Id);
        });

        
        self.currentValue(self.transTextes[self.currentLang()]());

        self.initMode = false;
    },
    init: function () {
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            self.transTextes[translateAble.Langs[i]](self.defaultValue);
        }
    }
}

ko.components.register('trans-txt-langs', {
    viewModel: function (params) {
        var self = this;
        self.textObj = params.txt;
        self.langs = [];
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            var l =new translateAble.LangComp( translateAble.Langs[i], self.textObj );           
            self.langs.push(l);
        }
    },
    template:
        '<div class="transTxtLangs" data-bind="foreach: langs">' +
        '    <div class="lang" data-bind="text: lang, css: {selected:selected, hasContent:hasContent} , click: select"></div>' +
        '</div>'
});
ko.components.register('trans-txt-langs-flg', {
    viewModel: function (params) {
        var self = this;
        self.textObj = params.txt;
        self.langs = [];
        for (var i = 0, len = translateAble.Langs.length; i < len; i++) {
            var l =new translateAble.LangComp( translateAble.Langs[i], self.textObj );           
            self.langs.push(l);
        }
    },
    template:
        '<div class="transTxtLangs flg" data-bind="foreach: langs">' +
        '    <div class="lang" data-bind="title: lang, css: cssStyle, click: select"></div>' +
        '</div>'
});
translateAble.LangComp = function (lang, textObj) {
    var self = this;
    self.lang = lang;
    self.textObj = textObj;
    self.selected = ko.computed(function () {
        return self.textObj.currentLang() == self.lang;
    });
    self.hasContent = ko.computed(function () {
        var t = self.textObj.transTextes[self.lang]();
        return t!=null && t != '' &&  t!=0;
    });
    self.select = function () {
        self.textObj.currentLang(self.lang);
    };
	self.cssStyle = ko.computed(function() {
		var result = self.lang;		
		result += self.selected() ? ' selected ' : '';
		result += self.hasContent() ? ' hasContent' : '';
		return result;		
	}, self);
};var downsampleTreshold = 300;
var useCurves = true;

var monthNames = [
        shortName(translations.January),
        shortName(translations.February),
        shortName(translations.March),
        shortName(translations.April),
        shortName(translations.May),
        shortName(translations.June),
        shortName(translations.July),
        shortName(translations.August),
        shortName(translations.September),
        shortName(translations.October),
        shortName(translations.November),
        shortName(translations.December)
];

var dayNames = [
   shortName(translations.Monday),
   shortName(translations.Tuesday),
   shortName(translations.Wednesday),
   shortName(translations.Thursday),
   shortName(translations.Friday),
   shortName(translations.Saturday),
   shortName(translations.Sunday)
];

function shortName(name) {
    return name.toLowerCase().substr(0, 3);
}

//#region axes

function getTimeXaxis(maxX) {
    var data = {
        mode: "time", tickLength: 4, font: { size: 10, color: 'black', family: 'aller_lightregular' }, tickFormatter: MsToString
    };
    if (maxX) {
        data.max = maxX;
    }
    return data;
}
function getDistanceXaxis(maxX) {
    var data = { mode: null, tickLength: 4, font: { size: 10, color: 'black', family: 'aller_lightregular' } };
    if (maxX) {
        data.max = maxX;
    }
    return data;
}
function getZonePercentYaxis(ticks, side, max, showPercSign) {
    var rT = ticks?ticks.slice(0, -1):null;
    return { tickLength: 0, tickFormatter: function (v) { return v + (showPercSign?"%":""); }, ticks: rT, min: ticks[0], max: ticks[ticks.length - 1], position: side, font: { size: 10, color: 'black', family: 'aller_lightregular' } };
}
function getZoneValueYaxis(ticks, side, max) {
    var rT = ticks?ticks.slice(0, -1):null;
    return { tickLength: 0, ticks: rT, min: ticks[0], max: ticks[ticks.length - 1], position: side, show: true, font: { size: 10, color: 'black', family: 'aller_lightregular' } };
}
function getCustomValueYaxis(side, unit, invertFactor, min, withUnit, forceMin) {
    var vunction;
    //var min= null;
    if (invertFactor) {
        //min = 0;
        vunction = function (v) {
            return calculatePaceString(v, unit, invertFactor) + (withUnit ? ('&nbsp;' + unit) : '');
        };
    } else {
        vunction = function (v) {
            if (v < 0) return '';
            return Number((v).toFixed(2).toString()) + (withUnit? ( '&nbsp;' + unit):'');
        };
    }

    return {
        tickLength: 0,
        tickFormatter: vunction,
        position: side,
        show: true,
        min: min <= 0 ? 0 : (forceMin?min:null),
        font: { size: 10, color: 'black', family: 'aller_lightregular' }
    };
}

//#region FB
function getTimeXaxisFB(data, fontSize, allowedNumberOfTicks, maxX) {
    if (!fontSize) fontSize = 25;

    var data = { mode: "time", tickSize: getTicks(data, true, '', allowedNumberOfTicks), tickLength: 4, font: { size: fontSize, color: 'black', family: 'aller_lightregular', weight: 'bold' }, label: { pad: 10 } };
    if (maxX) {
        data.max = maxX;
    }
    return data;
}
function getTimeXaxisFBSpeed(data, maxX) {
    var data = { mode: "time", tickLength: 4, tickSize: getTicks(data, true), font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' }, label: { pad: 10 } };
    //    return { mode: "time", tickLength: 4, ticks: getTicks(result, true), font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' }, label: {pad: 10} };
    if (maxX) {
        data.max = maxX;
    }
    return data;
}

function getTicks(data, xAxis, icon, allowedNumberOfTicks) {
    var chart = $('body > .fbPreview ').find('.chart');
    var chartWidth = chart.width();
    var chartHeight = chart.height();

    var ticks = [];

    if (xAxis) {
        if (!allowedNumberOfTicks) allowedNumberOfTicks = 8;
        var allowedTimeDistances = [1, 2, 5, 10, 30, 60, 120];
        var lastTime = data[data.length - 1][0];
        var allMinutes = (lastTime / (60 * 1000));
        var minutes = (allMinutes / allowedNumberOfTicks);
        
        var closestValue = 10000, closestIndex = 0;
        for (var i = 0, len = allowedTimeDistances.length; i < len; i++) {
            var allowedTimeDistance = allowedTimeDistances[i];
            var value = Math.abs(minutes - allowedTimeDistance);

            if (value < closestValue) {
                closestValue = value;
                closestIndex = i;
            }
        }       

        return [allowedTimeDistances[closestIndex], "minute"];       
    } else {
        var allowedValueDistances = [0.5, 1, 2, 5, 10, 20, 30, 60, 120];

        if (icon == 'run') {
            allowedValueDistances = [1, 2, 5, 10, 20, 30, 60, 120];
        }

        if (!allowedNumberOfTicks) allowedNumberOfTicks = 6;

        var maxValue = 0, minValue = 100000;
        for (var i = 0, len = data.length; i < len; i++) {
            var value = data[i][1];

            if (value > maxValue) maxValue = value;
            if (value < minValue) minValue = value;
        }

        var allowedValueDistance = (maxValue - minValue) / allowedNumberOfTicks;

        var closestValue = 10000, closestIndex = 0;
        for (var i = 0, len = allowedValueDistances.length; i < len; i++) {
            var allowedDistance = allowedValueDistances[i];
            var value = Math.abs(allowedValueDistance - allowedDistance);

            if (value < closestValue) {
                closestValue = value;
                closestIndex = i;
            }
        }
       
        return allowedValueDistances[closestIndex];
    }

    return ticks;
}
function getZonePercentYaxisFB(ticks, side, fontSize) {
    if (!fontSize) fontSize = 25;
    var rT = ticks.slice(0, -1);
    return { tickLength: 0, tickFormatter: function (v) { return v + "%"; }, ticks: rT, min: ticks[0], max: ticks[ticks.length - 1], font: { size: fontSize, color: 'black', family: 'aller_lightregular', weight: 'bold' } };
}
function getZoneValueYaxisFB(ticks, side, fontSize) {
    if (!fontSize) fontSize = 25;
    var rT = ticks.slice(0, -1);
    return { tickLength: 0, ticks: rT, min: ticks[0], max: ticks[ticks.length - 1], position: side, show: true, font: { size: fontSize, color: 'black', family: 'aller_lightregular', weight: 'bold' } };
}
function getCustomValueYaxisFB(side, unit) {
    return { tickLength: 0, tickFormatter: function (v) { return Number((v).toFixed(2)) + unit; }, position: side, show: true, font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' } };
}

function getCustomValueYaxisFBSpeed(side, unit, data, icon) {
    return {
        tickLength: 0,
        tickSize: getTicks(data, false, icon),
        tickFormatter: function (v) { return Number((v).toFixed(2)) + unit; },
        position: side,
        show: true,
        font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' }
    };
    //    return { tickLength: 0, ticks: getTicks(result), tickFormatter: function (v) { return Number((v).toFixed(2)) + unit; }, position: side, show: true, font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' } };
}

function getCustomValueYaxisFB(side, unit, invertFactor) {
    var vunction;
    if (invertFactor) {
        vunction = function (v) {
            return calculatePaceString(v, unit, invertFactor);
        };
    } else {
        vunction = function (v) {
            if (v < 0) return '';
            return Number((v).toFixed(2).toString());// + '&nbsp;' + unit;
        };
    }

    return {
        tickLength: 0,
        tickFormatter: vunction,
        position: side,
        show: true,
        font: { size: 25, color: 'black', family: 'aller_lightregular', weight: 'bold' }
    };
}

//#endregion
//#endregion

function getChartColors(multisession) {
    return multisession ? ['#393939', '#393939', '#393939', '#393939', '#393939', '#393939', '#393939', '#393939', '#393939', '#393939']
                : ['#393939', '#F04644', '#214A9E', '#2BB994', '#5CA9E5', '#EF7339', '#FFC253', '#BAD67C', '#A187B9', '#F2DE67'];
}

function showChartTooltip(x, y, contents) {
    if (contents) {
        $('<div id="tooltip">' + contents + '</div>').css({
            position: 'absolute',
            display: 'none',
            top: y - 30,
            left: x + 5,
            border: '1px solid #fdd',
            padding: '2px',
            'background-color': '#fee',
            opacity: 0.80,
            'z-index': 9999
        }).appendTo("body").fadeIn(200);
    }
}

function fixColors(d) {
    // hard-code color indices to prevent them from shifting as
    // countries are turned on/off
    var i = 0;
    $.each(d, function (key, val) {
        val.color = i;
        val.visible = true;
        ++i;
    });
}
function fixColors2(chart, d) {
    var dPars = chart.getData();
    for (var i = 0, len = d.length; i < len; i++) {
        var data = d[i];
        var data2 = dPars[i];
        data.color = data2.color;
    }
}

function fireRefreshMapCurrentPositionCallback(e, refreshMapCallback) {
    var distance = $(e.currentTarget).data("distance")
    if (refreshMapCallback)
        refreshMapCallback(distance);
}

function setLaps(result, singleTraindChart, placeholder, x_distance, refreshMapCallback) {
    //var sideTresh = result.maxX / 2;
    var maxLeft = singleTraindChart.pointOffset({ x: result.maxX, y: 0 }).left;
    var minLeft = singleTraindChart.pointOffset({ x: result.minX, y: 0 }).left;
    if (!minLeft) minLeft = 0;
    var sideTresh = maxLeft - 200;

    for (var i = 0, len = result.laps.length /*- 1*/; i < len; i++) {
        var lap = result.laps[i];
        var o = singleTraindChart.pointOffset({ x: x_distance ? lap.xaxisDist : lap.xaxis, y: 0 });

        if (o.left >= minLeft && o.left <= maxLeft) {

            var lapHtml = "<div class='chartLap ";
            lapHtml += lap.Auto ? ' autoLap ' : ' manualLap ';
            lapHtml += (o.left < sideTresh) ? 'right' : 'left';
            lapHtml += "' style='left:" + o.left + "px;' ";

            lapHtml += "><span class='icon'></span><div class='info'>";

            if (lap.Duration) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/cz_trwa_32.png" /></div><div class="value">' + lap.Duration + '</div></div>';
            }
            if (lap.Distance) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/dystans_32.png" /></div><div class="value">' + lap.Distance + '</div></div>';
            }
            if (lap.AvgHr) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/serce_przerywane_32.png" /></div><div class="value">' + lap.AvgHr + ' bpm</div></div>';
            }
            if (lap.AvgSpeed && (!lap.AvgPace || lap.AvgPace == '0')) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/predkosc_30.png" /></div><div class="value">' + lap.AvgSpeed + '</div></div>';
            }
            if (lap.AvgPace && lap.AvgPace != '0') {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/tempo_30.png" /></div><div class="value">' + lap.AvgPace + '</div></div>';
            }
            if (lap.AvgCadence && (!lap.Auto || !lap.AvgHr)) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/kadencja_30.png" /></div><div class="value">' + lap.AvgCadence + ' rpm</div></div>';
            }
            if (lap.Start) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/cz_rozpocz_32.png" /></div><div class="value">' + lap.Start + '</div></div>';
            }
            if (lap.SumDistance) {
                lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/dystans_okrazenie_32.png" /></div><div class="value">' + lap.SumDistance + '</div></div>';
            }

            lapHtml += "</div></div>";

            placeholder.append(lapHtml);
            var lapElements = placeholder.find('.chartLap');
            if (refreshMapCallback && lapElements) {
                var lastLapElement = lapElements.last()
                if (lastLapElement) {
                    var lapDistance = lap.xaxisDist;
                    lastLapElement.data("distance", lapDistance);
                    lastLapElement.mouseover(function (e) { fireRefreshMapCurrentPositionCallback(e, refreshMapCallback); });
                }
            }
        }
    }
}

function loadCurrentChart(trainingId, multi, container, callBackFunction, withLaps, type) {

    if (!type) type = 'hr';

    var multisession = multi;
    if (!trainingId) return;
    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetCurrentData",
        data: { id: trainingId, multi: multi, type: type }
    })
    .done(function (result) {

        var hiddenData = result.hiddenData;

        var markings = result.markings;

        var unit, lUnit;
        if (type == 'hr') { unit = 'bpm'; lUnit = '%' }
        if (type == 'power') { unit = 'W'; lUnit = '%FTP' }

        var yaxes = [];
        if (result.ticks[0] && result.ticks[0].length) {
            yaxes.push(getZonePercentYaxis(result.ticks[0], 'left'));
        }
        if (result.ticks[1] && result.ticks[1].length) {
            yaxes.push(getZoneValueYaxis(result.ticks[1], 'right'));
        } else {
            yaxes.push(getCustomValueYaxis('right', '', 0, 0, null, type == 'power'));
            if (yaxes.length == 1) {
                yaxes.push(getZonePercentYaxis(result.ticks[0], 'left'));
                yaxes[1].show = false;
                lUnit = '';
            }
        }

        var options = {
            xaxis: getTimeXaxis(result.maxX),
            yaxes: yaxes,
            grid: {
                backgroundColor: '#ffffff',
                borderWidth: 0, markings: result.markings, hoverable: true
            },
            legend: {
                show: false //noColumns: result.zoneData.length, position: "se"
            },
            colors: getChartColors(multisession),
            series: {
                shadowSize: 0,
                downsample: { threshold: downsampleTreshold }, // 0 disables downsampling for this series.                
                curvedLines: { active: useCurves }
            }
        };

        var d = result.zoneData;

        var placeholderId = '';
        if (type == 'hr') placeholderId=".hrChart";
        if (type == 'power') placeholderId=".powerChart";
        var placeholder =$(container).find(placeholderId);
        if (!placeholder.length) return;

        var singleTraindChart = $.plot(placeholder, d, options);

        for (var i = 0, len = result.icons.length; i < len; i++) {
            var o = singleTraindChart.pointOffset({ x: result.icons[i].Time, y: 0 });
            placeholder.append("<span class='" + result.icons[i].Icon + " disc_30' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
        }

        if (withLaps) setLaps(result, singleTraindChart, placeholder);

        

        placeholder.append('<span class="chartUnitLabel">' + unit + '</span>');
        placeholder.append('<span class="chartLeftUnitLabel">' + lUnit + '</span>');

        var previousPoint = null;
        placeholder.bind("plothover", function (event, pos, item) {
            if (item) {
                if (previousPoint != item.dataIndex) {
                    previousPoint = item.dataIndex;

                    $("#tooltip").remove();
                    var y = item.datapoint[1].toFixed(2);

                    var person = hiddenData.lenght == 1 ? hiddenData[0] : hiddenData[item.seriesIndex];
                    //var nick = person?person.Name:'';
                    var value = 0;
                    if (person) {
                        if (type == 'hr') value = person.MaxHr;
                        if (type == 'power') value = person.BaseFTP;
                    }

                    var unit = '';
                    if (type == 'hr') unit = 'bpm';
                    if (type == 'power') unit = 'W';

                    if (value) {
                        showChartTooltip(item.pageX, item.pageY,
                                     (item.datapoint[1] * value / 100).toFixed(0) + " " + unit + " (" + y + "%)");
                    } else {
                        if (type == 'hr') {
                            showChartTooltip(item.pageX, item.pageY, y + "%");
                        } else if (type == 'power') {
                            showChartTooltip(item.pageX, item.pageY, item.datapoint[1].toFixed(0) + "W");
                        }
                    }
                }
            }
            else {
                $("#tooltip").remove();
                previousPoint = null;

                if (markings) {
                    var x = pos.x;
                    for (var i = 0, len = markings.length; i < len; i++) {
                        var mark = markings[i];
                        if (typeof (showChartTooltip) != 'undefined' && mark.xaxis && mark.xaxis.from < x && mark.xaxis.to > x) {
                            showChartTooltip(pos.pageX, pos.pageY, mark.msg);
                        }
                    }
                }
            }
        });

        if (callBackFunction != null) {
            callBackFunction();
        }
    });
}

function loadCurrentChartFB(trainingId, multi, container, callBackFunction, fontSize, allowedNumberOfTicks, type) {

    var multisession = multi;
    if (!type) type = 'hr';

    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetCurrentData",
        data: { id: trainingId, multi: multi, pLineWidth: 2, type: type }
    })
    .done(function (result) {

        var hiddenData = result.hiddenData;

        var markings = result.markings;

        var zonesLength = result.zoneData.length;
        var zonesDataIndex = 0;

        if (zonesLength > 1) zonesDataIndex = zonesLength - 2;

        var yaxes = [];
        if (result.ticks[0] && result.ticks[0].length) {
            yaxes.push(getZonePercentYaxisFB(result.ticks[0], 'left', fontSize));
        }
        if (result.ticks[1] && result.ticks[1].length) {
            yaxes.push(getZoneValueYaxisFB(result.ticks[1], 'right', fontSize));
        } else {
            yaxes.push(getCustomValueYaxisFB('right', '', 0));
            if (yaxes.length == 1) {
                yaxes.push(getZonePercentYaxisFB(result.ticks[0], 'left', fontSize));
                yaxes[1].show = false;
            }
        }

        var options = {
            xaxis: getTimeXaxisFB(result.zoneData[zonesDataIndex].data, fontSize, allowedNumberOfTicks, result.maxX),
            yaxes: yaxes,
            grid: {
                backgroundColor: '#ffffff',
                borderWidth: 0, markings: result.markings, hoverable: true
                //                 labelMargin: 10,
                //                 margin: {
                //                     top: 8,
                //                     bottom: 20,
                //                     left: 20
                //                 }                
            },
            legend: {
                show: false //noColumns: result.zoneData.length, position: "se"
            },
            colors: getChartColors(multisession),
            series: {
                shadowSize: 0,
                downsample: { threshold: downsampleTreshold }, // 0 disables downsampling for this series.                
                curvedLines: { active: useCurves }
            }
            //                             margin: {
            //                     top: 8,
            //                     bottom: 20,
            //                     left: 20
            //                 }
        };

        var d = result.zoneData;

        var placeholderId = '';
        if (type == 'hr') placeholderId = ".hrChart";
        if (type == 'power') placeholderId = ".powerChart";
        var placeholder = $(container).find(placeholderId);
        if (!placeholder.length) return;

        var singleTraindChart = $.plot(placeholder, d, options);

        //setLaps(result, singleTraindChart, placeholder);

        for (var i = 0, len = result.icons.length; i < len; i++) {
            var o = singleTraindChart.pointOffset({ x: result.icons[i].Time, y: 0 });
            placeholder.append("<span class='" + result.icons[i].Icon + " disc_80' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
        }

        if (callBackFunction != null) {
            callBackFunction();
        }
    });
}



/***************************************
****************************************
***************************************/

function getSeveralChart(eventId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetEventData",
        data: { eventId: eventId }
    })
    .done(function (result) {
        drawSeveralChart(result);
    });
}
function loadSeveralHrChart(trainingIds, container) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetTrainingsHrData",
        data: { ids: trainingIds, distance: false }
    })
    .done(function (result) {
        drawSeveralChart(result, container);
        /*
        eventTableExist = true;
        if (bindEventLegend) {
            bindEventLegend(container);
        }
        */
    });
}
function drawSeveralChart(result, container) {
    var hiddenData = result.hiddenData;

    var options = {
        xaxis: getTimeXaxis(),
        yaxis: getZonePercentYaxis(result.ticks, 'left'),
        grid: {
            backgroundColor: '#ffffff', borderWidth: 0, markings: result.markings, hoverable: true
        },
        legend: {
            show: false //noColumns: result.zoneData.length, position: "se"
        },
        colors: getChartColors(false),
        series: {
            shadowSize: 0,
            downsample: { threshold: downsampleTreshold }, // 0 disables downsampling for this series.                
            curvedLines: { active: useCurves }
        }
    };

    var d = result.zoneData;

    fixColors(d);

    var elem;
    if (container) {
        elem = $(".hrChart", container);
    } else {
        elem = $("#severalChartContainer");
    }

    var severalTraindChart = $.plot(elem, d, options);

    fixColors2(severalTraindChart, d);

    var previousPoint = null;
    elem.bind("plothover", function (event, pos, item) {
        if (item) {
            if (previousPoint != item.dataIndex) {
                previousPoint = item.dataIndex;

                $("#tooltip").remove();
                //var x = item.datapoint[0].toFixed(2),
                var y = item.datapoint[1].toFixed(2);

                var person = hiddenData.lenght == 1 ? hiddenData[0] : hiddenData[item.seriesIndex];
                var nick = person ? person.Name : '';
                var value = person ? person.MaxHr : 0;//.Values[item.dataIndex] : '';


                if (value) {
                    showChartTooltip(item.pageX, item.pageY,
                                 (item.datapoint[1] * value / 100).toFixed(0) + " " + translations.bpm + " (" + y + "%)");
                } else {
                    showChartTooltip(item.pageX, item.pageY, y + "%");
                }
            }
        }
        else {
            $("#tooltip").remove();
            previousPoint = null;
        }
    });

    /*
    rebuildChart = function () {
        var newData = [];
        for (var i = 0, len = d.length; i < len; i++) {
            if (d[i].visible) {
                newData.push(d[i]);
            }
        }
        severalTraindChart = $.plot(elem, newData, options);
    }
    */

    bindXAxisToggle = function (container) {
        var btn = $('.xAxisToggle a', container);
        btn.click(function () {
            var isDistance = !result.isDistance;
            $.ajax({
                type: "POST",
                url: ContextPath + "Chart/GetTrainingsHrData",
                data: { ids: trainingIds, distance: isDistance }
            })
            .done(function (newResult) {
                result = newResult;
                var d2 = result.zoneData;
                for (var i = 0, len = d.length; i < len; i++) {
                    for (var i2 = 0, len2 = d2.length; i2 < len2; i2++) {
                        var data2 = d2[i2];
                        var data = d[i];
                        if (data2.serieId == data.serieId) {
                            data2.visible = data.visible;
                            data2.color = data.color;
                        }
                    }
                }
                d = d2;
                var newData = [];
                for (var i = 0, len = d.length; i < len; i++) {
                    if (d[i].visible) {
                        newData.push(d[i]);
                    }
                }

                options.xaxis = isDistance ? getDistanceXaxis() : getTimeXaxis();
                severalTraindChart = $.plot(elem, newData, options);

                if (isDistance) {
                    elem.append($("<span class='distanceXLegend'>km</span>"));
                } else {
                    elem.remove('.distanceXLegend');
                }
            });
        });
    }

    bindEventLegend = function (container) {

        if (eventTableExist) {
            var divs = $('.toggleOnChart', container);
            $.each(divs, function (key, el) {
                var div = $(el);
                for (var i = 0, len = d.length; i < len; i++) {
                    var id = div.data("id");
                    for (var i = 0, len = d.length; i < len; i++) {
                        var data = d[i];
                        if (data.serieId == id) {
                            var chb; // = $('input', div);

                            chb = $(document.createElement("input"));
                            chb.attr("type", "checkbox");
                            if (data.visible) {
                                chb.attr('checked', 'checked');
                            }
                            chb.data("serieId", data.serieId);
                            div.append(chb);

                            chb.change(function () {
                                var serieId = chb.data("serieId");
                                for (var i = 0, len = d.length; i < len; i++) {
                                    if (d[i].serieId == serieId) {
                                        d[i].visible = !d[i].visible;
                                    }
                                }
                                //rebuildChart();
                                var newData = [];
                                for (var i = 0, len = d.length; i < len; i++) {
                                    if (d[i].visible) {
                                        newData.push(d[i]);
                                    }
                                }
                                severalTraindChart = $.plot(elem, newData, options);
                            });

                            div.css("background-color", data.color);
                            div.css("width", "100%");
                            div.css("height", "100%");
                            div.css("text-align", "center");
                        }
                    }
                }
            });
        }
    }

    bindEventLegend(container);

    bindXAxisToggle(container);
}

//var rebuildChart = null;




/***************************************
************* SPEED CHARTS *************
***************************************/

function loadSpeedChart(trainingIds, multi, container, withLaps, paceParams) {
    var type = "speed";
    var contSelector = ".speedChart";
    var unit = "km/h";
    var invertFactor = 0;
    if (paceParams && paceParams.unit) {
        unit = paceParams.unit;
        invertFactor = paceParams.factor;
    }

    loadTrainingDataChart(trainingIds, multi, container, type, contSelector, unit, withLaps, invertFactor);
}
function loadCadenceChart(trainingIds, multi, container, withLaps) {
    var type = "cadence";
    var contSelector = ".cadenceChart";
    var unit = "rpm";

    loadTrainingDataChart(trainingIds, multi, container, type, contSelector, unit, withLaps);
}

function loadPowerChart(trainingIds, multi, container, withLaps) {
    var type = "power";
    var contSelector = ".powerChart";
    var unit = "W";

    //loadTrainingDataChart(trainingIds, multi, container, type, contSelector, unit, withLaps);
    loadCurrentChart(trainingIds, multi, container, null, withLaps, 'power');
}

function loadAltitudeChart(trainingIds, multi, container, withLaps) {
    var type = "altitude";
    var contSelector = ".altitudeChart";
    var unit = "M";

    loadTrainingDataChart(trainingIds, multi, container, type, contSelector, unit, withLaps);
}



function loadTrainingDataChart(trainingIds, multi, container, type, contSelector, unit, withLaps, invertFactor) {

    var multisession = multi;

    if (!trainingIds) return;

    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetTrainingData",
        data: { ids: trainingIds, multi: multi, type: type }
    })
    .done(function (result) {
        loadTrainingDataChartResult(result, trainingIds, multi, container, type, contSelector, unit, withLaps, invertFactor);
    });
}

function loadTrainingDataChartResult(result, trainingIds, multi, container, type, contSelector, unit, withLaps, invertFactor) {
    var hiddenData = result.hiddenData;

    var laps = result.laps;

    var d = result.data;

    var minValue = 0;
    if (d[0] && d[0].data && d[0].data.length) {
        minValue = _(d[0].data).min(function (elem) { return elem[1]; })[1];
    }

    var options = {
        xaxis: getTimeXaxis(result.maxX),
        yaxes: [getCustomValueYaxis("right", unit, invertFactor, minValue)
        ],
        grid: {
            backgroundColor: '#ffffff', borderWidth: 0, hoverable: true, markings: result.markings
        },
        legend: {
            show: false
        },
        colors: getChartColors(multi),
        series: {
            shadowSize: 0,
            downsample: { threshold: downsampleTreshold }, // 0 disables downsampling for this series.                
            curvedLines: { active: useCurves }
        }
    };

    fixColors(d);

    var placeholder = contSelector ? $(contSelector, $(container)) : $(container);
    if (!placeholder.length) return;
    var singleTraindChart = $.plot(placeholder, d, options);

    fixColors2(singleTraindChart, d);

    if (withLaps) setLaps(result, singleTraindChart, placeholder);

    for (var i = 0, len = result.icons.length; i < len; i++) {
        var o = singleTraindChart.pointOffset({ x: result.icons[i].Time, y: 0 });
        placeholder.append("<span class='" + result.icons[i].Icon + " disc_30' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
    }

    placeholder.append('<span class="chartUnitLabel">' + unit + '</span>');


    var previousPoint = null;
    placeholder.bind("plothover", function (event, pos, item) {
        if (item) {
            if (previousPoint != item.dataIndex) {
                previousPoint = item.dataIndex;

                $("#tooltip").remove();
                var y = item.datapoint[1].toFixed(2);
                if (y < 0) return;

                if (invertFactor) {
                    if (y <= 0) return;
                    y = calculatePaceString(item.datapoint[1], unit, invertFactor, true, false);
                } else {
                    y = y + '&nbsp;' + unit
                }

                showChartTooltip(item.pageX, item.pageY, y);
            }
        }
        else {
            $("#tooltip").remove();
            previousPoint = null;
        }
    });

    bindXAxisToggle = function (container) {
        var btn = $('.xAxisToggle a', container);
        btn.click(function () {
            var isDistance = !result.isDistance;
            $.ajax({
                type: "POST",
                url: ContextPath + "Chart/GetTrainingData",
                data: { ids: trainingIds, multi: multi, type: type, distance: isDistance }
            })
            .done(function (newResult) {
                result = newResult;
                var d2 = result.data;
                for (var i = 0, len = d.length; i < len; i++) {
                    for (var i2 = 0, len2 = d2.length; i2 < len2; i2++) {
                        var data2 = d2[i2];
                        var data = d[i];
                        if (data2.serieId == data.serieId) {
                            data2.visible = data.visible;
                            data2.color = data.color;
                        }
                    }
                }
                d = d2;
                var newData = [];
                for (var i = 0, len = d.length; i < len; i++) {
                    if (d[i].visible) {
                        newData.push(d[i]);
                    }
                }

                var minValue = 0;
                if (d[0] && d[0].data && d[0].data.length) {
                    minValue = _(d[0].data).min(function (elem) { return elem[1]; })[1];
                }

                options.xaxis = isDistance ? getDistanceXaxis() : getTimeXaxis();
                singleTraindChart = $.plot(placeholder, newData, options);

                if (isDistance) {
                    placeholder.append($("<span class='distanceXLegend'>km</span>"));
                } else {
                    placeholder.remove('.distanceXLegend');
                }
            });
        });
    }

    bindEventLegend = function (container) {

        if (eventTableExist) {
            var divs = $('.toggleOnChart', container);
            $.each(divs, function (key, el) {
                var div = $(el);
                if ($('input', div).length == 0) {
                    var id = div.data("id");
                    for (var i = 0, len = d.length; i < len; i++) {
                        var data = d[i];
                        if (data.serieId == id) {
                            var chb; // = $('input', div);

                            chb = $(document.createElement("input"));
                            chb.attr("type", "checkbox");
                            if (data.visible) {
                                chb.attr('checked', 'checked');
                            }
                            chb.data("serieId", data.serieId);
                            div.append(chb);

                            chb.change(function () {
                                var serieId = chb.data("serieId");
                                for (var i = 0, len = d.length; i < len; i++) {
                                    if (d[i].serieId == serieId) {
                                        d[i].visible = !d[i].visible;
                                    }
                                }
                                //rebuildChart();
                                var newData = [];
                                for (var i = 0, len = d.length; i < len; i++) {
                                    if (d[i].visible) {
                                        newData.push(d[i]);
                                    }
                                }
                                singleTraindChart = $.plot(placeholder, newData, options);
                            });

                            div.css("background-color", data.color);
                            div.css("width", "100%");
                            div.css("height", "100%");
                            div.css("text-align", "center");
                        }
                    }
                }
            });
        }
    }

    bindEventLegend(container);

    bindXAxisToggle(container);
}

function loadTrainingDataChartFB(trainingIds, multi, container, type, contSelector, unit, callBackFunction, invertFactor) {

    var multisession = multi;

    $.ajax({
        type: "POST",
        url: ContextPath + "Chart/GetTrainingData",
        data: { ids: trainingIds, multi: multi, type: type, forFB: true }
    })
    .done(function (result) {

        var hiddenData = result.hiddenData;

        var options = {
            xaxis: getTimeXaxisFBSpeed(result.data[0].data, result.maxX),
            yaxes: [getCustomValueYaxisFB("right", unit, invertFactor)
//             yaxes: [getCustomValueYaxisFBSpeed("right", unit, result.data[0].data, result.icons[0].Icon)
            ],
            grid: {
                backgroundColor: '#ffffff', borderWidth: 0, hoverable: true, markings: result.markings
            },
            legend: {
                show: false
            },
            colors: getChartColors(multi),
            series: {
                shadowSize: 0,
                downsample: { threshold: downsampleTreshold }, // 0 disables downsampling for this series.                
                curvedLines: { active: useCurves }
            }
        };

        var d = result.data;

        fixColors(d);

        var placeholder = $(contSelector, $(container));
        if (!placeholder.length) return;
        var singleTraindChart = $.plot(placeholder, d, options);

        fixColors2(singleTraindChart, d);

        for (var i = 0, len = result.icons.length; i < len; i++) {
            var o = singleTraindChart.pointOffset({ x: result.icons[i].Time, y: 0 });
            placeholder.append("<span class='" + result.icons[i].Icon + " disc_80' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
        }

        placeholder.append('<span class="chartUnitLabel">' + unit + '</span>');

        setLaps(result, singleTraindChart, placeholder);

        if (callBackFunction != null) {
            callBackFunction();
        }
    });
}


function LapsVisibilityModel() {
    var self = this;
    self.ShowManualLaps = ko.observable(true);
    self.ShowAutoLaps = ko.observable(true);    

    self.ToggleManual = function (data, event) {
        self.ShowManualLaps(!self.ShowManualLaps());
    }
    self.ToggleAuto = function (data, event) {
        self.ShowAutoLaps(!self.ShowAutoLaps());
    }

    self.ManualText = ko.computed(function () {
        if (self.ShowManualLaps()) {
            return translations.Show + ' ' + translations.Laps;
        }
        else {
            return translations.Hide + ' ' + translations.Laps;
        }
    });
    self.AutoText = ko.computed(function () {
        if (self.ShowAutoLaps()) {
            return translations.Show + ' ' + translations.LapsAuto;
        }
        else {
            return translations.Hide + ' ' + translations.LapsAuto;
        }
    });
}
var lapsVisibilityModel = new LapsVisibilityModel();
GeneralModel.addProperty("LapsVisibilityModel", lapsVisibilityModel);

function toggleLapsVisibility(elem, lapsType) {
    var elem = $(elem);
    var cCont = $(elem).parents('.lapsSwicher').parent().find('.chartContainer');
    if (elem.is(':checked')) {
        cCont.addClass(lapsType);
    } else {
        cCont.removeClass(lapsType);
    }
}



ko.bindingHandlers.SingleDataChart = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var dataName = ko.unwrap(value);

        var container = $(element);
        container.html('');
        container.show();

        var type = dataName;
        var contSelector = ".cadenceChart";
        var unit = '';

        var trainingId = allBindings.get('TrainingId')();

        setTimeout(function () { loadTrainingDataChart(trainingId, false, container, type, null, unit, false); }, 1000);
        
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever any observables/computeds that are accessed change
        // Update the DOM element based on the supplied values here.
    }
};;function TimeWindowStats(multiChart) {
    var self = this;

    self.MultiChart = multiChart;

    self.invertFactor = null;
    self.unit = null;

    self.Start = null;
    self.End = null;

    self.FTP = multiChart.FTP;

    self.avg = ko.observable(null);
    self.max = ko.observable(null);
    self.min = ko.observable(null);
    self.avgStartVal = ko.observable(null);
    self.avgEndVal = ko.observable(null);
    self.TrendA = ko.observable(null);
    self.TrendB = ko.observable(null);
    self.chartZonesTools = {};

    self.powerNP = ko.observable(null);
    self.duration = ko.observable(null);

    self.lineStartY = ko.observable(null);
    self.lineEndY = ko.observable(null);

    self.RoundOrPace = function (val, to) {
        if (self.invertFactor !== null) {
            return calculatePaceString(val, self.unit, self.invertFactor, false, false)
        } else {
            return MathRoundTo(val, (typeof to !== "undefined") ? to : 1);
        }
    }
    self.RoundOrPace2 = function (val2, val1) {
        if (self.invertFactor !== null) {
            var p1 = self.invertFactor / val1;
            var p2 = self.invertFactor / val2;
            var p = p1 - p2;
            var minus = p < 0;
            if (minus) p = -p;

            var result = formatPace(p, self.unit, self.invertFactor, false, false);
            if (minus) result = '-' + result;
            return result;
        } else {
            return MathRoundTo(val2 - val1, 1);
        }
    }

    self.Round = function (val, to) {
        return MathRoundTo(val, (typeof to !== "undefined") ? to : 1);
    }

    self.Avg = ko.computed(function () {
        return self.RoundOrPace(self.avg());
    });
    self.Max = ko.computed(function () {
        return self.RoundOrPace(self.max());
    });
    self.Min = ko.computed(function () {
        return self.RoundOrPace(self.min());
    });
    self.AvgStartVal = ko.computed(function () {
        return self.RoundOrPace(self.avgStartVal());
    });
    self.AvgEndVal = ko.computed(function () {
        return self.RoundOrPace(self.avgEndVal());
    });

    self.MinMax = ko.computed(function () {
        return self.RoundOrPace2(self.max(), self.min());
    });

    self.StartEnd = ko.computed(function () {
        return self.RoundOrPace2(self.avgEndVal(), self.avgStartVal());
    });

    self.LineStartY = ko.computed(function () {
        return self.RoundOrPace(self.lineStartY());
    });
    self.LineEndY = ko.computed(function () {
        return self.RoundOrPace(self.lineEndY());
    });

    self.PowerNP = ko.computed(function () {
        return self.Round(self.powerNP(), 2);
    });
    self.PowerVI = ko.computed(function () {
        return self.Round(self.powerNP() / self.avg(), 2);
    });
    self.powerIF = ko.computed(function () {
        if (self.FTP()) {
            return self.powerNP() / self.FTP();
        } else {
            return 0;
        }
    });
    self.PowerIF = ko.computed(function () {
        if (self.FTP()) {
            return self.Round(self.powerIF(), 2);
        } else {
            return 'no FTP!';
        }
    });
    self.PowerTSS = ko.computed(function () {
        if (self.FTP()) {
            return self.Round(((self.duration() * self.powerNP() * self.powerIF()) / (self.FTP() * 3600)) * 100, 2);
        } else {
            return 'no FTP!';
        }
    });
    self.Work = ko.computed(function () {
        return self.Round((self.duration() * self.avg() / 1000), 0);
    });
}

function WindowCalculator(windowLength) {
    var self = this;

    self.windowLength = windowLength;

    self.deltaTimes = [];
    self.values = [];

    self.sumOfDeltaTimes = 0.0;
    self.sumOfValues = 0.0;

    self.maxAvgOfValues = 0.0;

    self.Update = function (deltaTime, value) {

        self.deltaTimes.push(deltaTime);
        self.values.push(value);

        self.sumOfDeltaTimes += deltaTime;
        self.sumOfValues += value;

        if (self.sumOfDeltaTimes >= 1000.0 * windowLength) {
            self.maxAvgOfValues = Math.max(self.maxAvgOfValues, self.sumOfValues / self.values.length);
        }

        while (self.sumOfDeltaTimes >= 1000.0 * windowLength) {
            // wyrzucamy tak dlugo az jest mniej niz windowLength
            var deltaTimeRemoved = self.deltaTimes.splice(0, 1);
            self.sumOfDeltaTimes -= deltaTimeRemoved[0];
            var valueRemoved = self.values.splice(0, 1);
            self.sumOfValues -= valueRemoved[0];
        }

    };

}

function ChartZonesToolForMMP(parent) {
    var self = this;

    self.parent = parent;
    self.windowLengths = [2, 5, 10, 30, 60, 120, 180, 300, 480, 1200, 3600, 5400];
    self.windows = [];

    self.Initialize = function (parent, zoneChartType, chartType, avg, stddev, numberOfPoints, duration, forceZeroStart, invertFactor, unit) {

        self.parent = parent;
        self.zoneChartType = zoneChartType;
        self.chartType = chartType;
        self.invertFactor = invertFactor;
        self.unit = unit;
        self.numberOfPoints = numberOfPoints;
        self.duration = duration;

        $(self.windowLengths).each(function (index, windowLength) {
            self.windows.push(new WindowCalculator(windowLength));
        });
    };

    self.Update = function (deltaTime, value) {
        $(self.windows).each(function (index, window) {
            window.Update(deltaTime, value);
        });
    };

    self.PostProcessing = function () {
    }

    self.GetSeries = function (dt, split) {
        var dataSeries = [];
        var xTickSeries = [];
        var minY = Number.MAX_VALUE;
        var maxY = 0.0;
        var minX = 0.0;
        var maxX = self.windows.length - 1;

        var xPosition = 0.0;
        $(self.windows).each(function (index, window) {
            xTickSeries.push([xPosition, secondsToShortDescFromat(window.windowLength)]);
            dataSeries.push([xPosition, window.maxAvgOfValues]);
            maxY = Math.max(maxY, window.maxAvgOfValues);
            minY = Math.min(minY, window.maxAvgOfValues);
            xPosition += 1.0;
        });

        return { series: [dataSeries], xTicks: xTickSeries, minY: minY, maxY: maxY, minX: minX, maxX: maxX };
    }

}

function ChartZonesTool(parent) {
    var self = this;

    self.parent = parent;

    self.isInvert = false;
    self.min = 0.0;
    self.max = 0.0;
    self.numberOfPoints = 0;
    self.invertFactor = -1.0;
    self.unit = 'min';
    self.N = null;
    self.gridMax = null;
    self.gridMin = null;
    self.zones = [];

    self.duration = null;

    self.belowZones = 0.0;
    self.aboveZones = 0.0;

    self.parent = null;

    self.chartType = null;

    self.zoneChartType = null;

    self.zoneNames = [];
    self.zoneColors = [];

    self.gridMode = 'normal';

    self.Initialize = function (parent, zoneChartType, chartType, avg, stddev, numberOfPoints, duration, forceZeroStart, invertFactor, unit) {
        self.parent = parent;
        self.zoneChartType = zoneChartType;
        self.chartType = chartType;
        self.invertFactor = invertFactor;
        self.unit = unit;
        self.numberOfPoints = numberOfPoints;
        self.duration = duration;
        self.isInvert = (typeof invertFactor !== "undefined") && (invertFactor > 0);
        if (self.isInvert) {
            self.scales = [0.25, 0.5, 1.0];
            var paceAvg = invertFactor / avg;
            var paceDlt = (stddev / avg) * paceAvg;
            self.min = forceZeroStart ? 0 : Math.max(0, paceAvg - 2 * paceDlt);
            self.max = Math.min(200, paceAvg + 2 * paceDlt);
        } else {
            self.scales = [0.2, 0.25, 0.5, 1.0];
            var min = forceZeroStart ? 0 : Math.max(0, avg - 2 * stddev);
            var max = avg + 2 * stddev;
            self.max = max;
            self.min = min;
        }
        if (self.zoneChartType.Mode == 'custom') {
            self.createCustomZonesGrid();
        } else {
            self.createDefaultZonesGrid(self.min, self.max, self.numberOfPoints, invertFactor);
        }

    };

    self.createCustomZonesGrid = function () {
        var zones;
        if (self.zoneChartType.DataType.type == 1) {
            zones = self.parent.ServerModel.PaceZones;
        } else if (self.zoneChartType.DataType.type == 4) {
            zones = trainingDetailsManager.currentTraining.PowerZones();
        } else if (self.zoneChartType.DataType.type == 0) {
            zones = trainingDetailsManager.currentTraining.Zones();
        }
        if (self.isInvert) {
            zones.sort(function (a, b) { return -(a.LowerBound - b.LowerBound); });
        } else {
            zones.sort(function (a, b) { return a.LowerBound - b.LowerBound; });
        }

        var scale = 1.0;
        if (self.zoneChartType.DataType.type == 4) {
            scale = self.parent.FTP();
            if (typeof scale === 'undefined') {
                scale = 1.0;
            } else {
                scale = scale / 100.0;
            }
        }
        else if (self.zoneChartType.DataType.type == 0) {
            scale = self.parent.maxHr;
            if (typeof scale === 'undefined') {
                scale = 1.0;
            } else {
                scale = scale / 100.0;
            }
        }

        self.zoneNames = [];

        self.gridLowerBound = [];
        $.each(zones, function (index, zone) {
            var lb = zone.LowerBound * scale;
            if (self.isInvert) {
                lbInv = self.invertFactor / lb;
                self.gridLowerBound.push(lbInv);
            } else {
                self.gridLowerBound.push(lb);
            }
            self.zoneNames.push(zone.ZoneName);
            self.zoneColors.push(zone.Color);
        });

        // remove first (with 0 as limit)
        self.gridLowerBound.shift();

        self.gridMin = self.gridLowerBound[0];
        self.gridMax = self.gridLowerBound[self.gridLowerBound.length - 1];

        self.N = self.gridLowerBound.length - 1;
        self.gridMode = "nonlinear";

        for (var i = 0; i < self.N; i++) {
            self.zones[i] = 0;
        }
    };

    self.createDefaultZonesGrid = function (min, max, numberOfPoints, invertFactor) {
        var target = Math.min(8, 0.1 * numberOfPoints + 4);
        var bestError = 100000;
        var bestGrid = 0.0;

        var exp = Math.max(3, Math.log10(Math.abs(max - min)));
        for (var i = -3; i < exp; i++) {
            $.each(self.scales, function (index, scale) {
                var grid = scale * Math.pow(10, i);
                if (grid < max - min) {
                    var N = Math.ceil(max / grid) + 1 - Math.floor(min / grid);
                    var error = Math.abs(N - target);
                    if (error < bestError) {
                        bestGrid = grid;
                        bestError = error;
                    }
                }
            });
        }
        var kMin = Math.floor(min / bestGrid);
        var kMax = Math.ceil(max / bestGrid);
        self.N = kMax - kMin;
        self.gridMin = kMin * bestGrid;
        self.gridMax = kMax * bestGrid;
        for (var i = 0; i < self.N; i++) {
            self.zones[i] = 0;
        }
    };

    self.sumTime = 0.0;

    self.Update = function (deltaTime, value) {

        if (self.zoneChartType.DataType.type == 1 && self.gridMode == 'nonlinear') {
            self.qq = 'ok';
        }

        self.sumTime += deltaTime;
        if (self.isInvert) {
            value = self.invertFactor / value;
        }
        var valNrm = self.valueToNrm(value);
        if (valNrm < 0.0) {
            self.belowZones += deltaTime;
        } else if (valNrm > 1.0) {
            self.aboveZones += deltaTime;
        } else {
            var index = Math.floor(self.N * valNrm);
            self.zones[index] += deltaTime;
        }
    }

    self.PostProcessing = function () {
        self.belowZones = 100.0 * (self.belowZones / self.sumTime);
        self.aboveZones = 100.0 * (self.aboveZones / self.sumTime);
        for (var i = 0; i < self.zones.length; i++) {
            self.zones[i] = 100.0 * (self.zones[i] / self.sumTime);
        }
    }

    self.GetSeries = function (dt, split) {
        var dataSeries = [];
        var xTickSeries = [];
        var valueBelow = self.nrmToValue(0.0 / self.N);
        var valueAbove = self.nrmToValue(1.0);
        var maxY = Math.max(self.belowZones, self.aboveZones);
        var minX = 0.0;
        var maxX = self.N - 1;
        var reverse = 0;
        if (self.gridMode == 'nonlinear' || (self.min > 1e-3 && self.belowZones > 0.1)) {
            var position = self.isInvert ? self.N : -1;
            if (self.isInvert) {
                position = self.N;
                maxX = self.N;
            } else {
                position = -1;
                minX = -1;
            }
            dataSeries.push([position, self.belowZones]);
            xTickSeries.push([position, "<" + self.convertValueToText(valueBelow)]);
        }
        $.each(self.zones, function (index, zn) {
            var znValue = zn;
            var valueStart = self.nrmToValue(index / self.N);
            var valueEnd = self.nrmToValue((index + 1) / self.N);
            var position = self.isInvert ? self.N - index - 1 : index;
            dataSeries.push([position, znValue]);
            if (self.isInvert) {
                xTickSeries.push([position, self.convertValueToText(valueEnd) + "<br/>" + self.convertValueToText(valueStart)]);
            } else {
                var sep = (self.N > 7) ? "<br/>" : "-";
                xTickSeries.push([position, self.convertValueToText(valueStart) + sep + self.convertValueToText(valueEnd)]);
            }
            if (maxY < znValue) {
                maxY = znValue;
            }
        });
        if (self.gridMode == 'nonlinear' || (self.aboveZones > 0.1)) {
            var position;
            if (self.isInvert) {
                position = -1;
                minX = -1;
            } else {
                position = self.N;
                maxX = self.N;
            }
            dataSeries.push([position, self.aboveZones]);
            xTickSeries.push([position, ">" + self.convertValueToText(valueAbove)]);
        }
        if (split) {
            // Convert to each bar to new data series
            // This enables to set a different color for each bar
            var splitDataSeries = [];
            $(dataSeries).each(function (index, item) {
                splitDataSeries.push([item]);
            });
            dataSeries = splitDataSeries;
        } else {
            var splitDataSeries = [dataSeries];
            dataSeries = splitDataSeries;
        }
        return { series: dataSeries, xTicks: xTickSeries, minY: 0.0, maxY: maxY, minX: minX, maxX: maxX };
    }

    self.nrmToValue = function (val) {
        if (self.gridMode == "nonlinear") {
            if (val < 0.0) {
                return val + self.gridLowerBound[0];
            }
            if (val >= 1.0) {
                return val * self.gridLowerBound[self.N];
            }

            var valN = val * self.N;
            var index = Math.floor(valN);
            var levelMin = self.gridLowerBound[index];
            var levelMax = self.gridLowerBound[index + 1];
            var valRel = valN - index;
            return valRel * (levelMax - levelMin) + levelMin;
        } else {
            return val * (self.gridMax - self.gridMin) + self.gridMin;
        }
    }

    self.valueToNrm = function (val) {
        if (self.gridMode == "nonlinear") {
            if (val < self.gridLowerBound[0]) {
                return val - self.gridLowerBound[0];
            }
            if (val >= self.gridLowerBound[self.N]) {
                return val / self.gridLowerBound[self.N];
            }

            var index = 0;
            while (index < self.N && !(self.gridLowerBound[index] <= val && val < self.gridLowerBound[index + 1])) {
                index++;
            }
            var levelMin = self.gridLowerBound[index];
            var levelMax = self.gridLowerBound[index + 1];

            return ((val - levelMin) / (levelMax - levelMin)) / self.N + index / self.N;
        } else {
            return (val - self.gridMin) / (self.gridMax - self.gridMin);
        }
    }

    self.convertValueToText = function (value) {
        if (self.isInvert) {
            return formatPace(value, self.unit, self.invertFactor, false, false);
        } else {
            return value.toFixed(0);
        }

        /*else if (self.parent.isCadenceType(self.chartType) || self.parent.isHrType(self.chartType)) {
            return value.toFixed(0);
        } else {
            return value.toFixed(1);
        }*/
    }
}

function ZonesChartType(options, parent) {
    var self = this;

    self.Parent = parent;

    self.DataType = options.dataType;
    self.Mode = options.mode;

    self.Available = ko.observable(false);

    self.showInZones = ko.observable(false);

    self.name = ko.observable(options.name);
    self.IconName = ko.observable('');

    self.showInZonesEvent = function () {
        $.each(self.Parent.ZonesChartTypes, function (i, dt) {
            dt.showInZones(false);
        });
        self.showInZones(true);
        self.Parent.DrawZonesCharts();
    };

    self.CheckAvailable = function () {
        var checkZones = true;
        if (self.DataType.type == 1 && self.Mode == "custom") {
            checkZones = self.Parent
            && self.Parent.ServerModel
            && self.Parent.ServerModel.PaceZones
            && self.Parent.ServerModel.PaceZones.length > 0;
        }
        if (self.DataType.type == 4 && self.Mode == "custom") {
            checkZones = trainingDetailsManager
            && trainingDetailsManager.currentTraining
            && trainingDetailsManager.currentTraining.PowerZones()
            && trainingDetailsManager.currentTraining.PowerZones().length > 0;
        }
        if (self.DataType.type == 0 && self.Mode == "custom") {
            checkZones = trainingDetailsManager
            && trainingDetailsManager.currentTraining
            && trainingDetailsManager.currentTraining.Zones()
            && trainingDetailsManager.currentTraining.Zones().length > 0;
        }
        self.Available(self.DataType.Available()
            && checkZones
            && self.DataType.CurrentTimeWindowStats()
            && self.DataType.CurrentTimeWindowStats().chartZonesTools
            && self.DataType.CurrentTimeWindowStats().chartZonesTools[self.Mode]);
    };

    self.setIconName = function () {
        if (self.Mode == "custom" && self.DataType.type == 1) {
            if (self.Parent.paceParams && self.Parent.paceParams.unit) {
                self.IconName("paceInZones");
            } else {
                self.IconName("speedInZones");
            }
        } else if (self.Mode == "custom" && self.DataType.type == 4) {
            self.IconName("powerInZones");
        } else if (self.Mode == "mmp" && self.DataType.type == 4) {
            self.IconName("powerAsMMP");
        } else if (self.Mode == "custom" && self.DataType.type == 0) {
            self.IconName("hrInZones");
        } else {
            self.IconName(self.DataType.IconName());
        }
    };

    self.DataType.name.subscribe(function (newValue) {
        if (self.DataType.type == 1) {
            self.setIconName();
            if (self.Mode == "custom" && self.DataType.type == 1) {
                if (self.Parent.paceParams && self.Parent.paceParams.unit) {
                    self.name(translations.PaceInZones);
                } else {
                    self.name(translations.SpeedInZones);
                }
            } else {
                self.name(newValue);
            }

        }
    })

    self.setIconName();
}

function MultiChartDataType(options, parent) {

    var self = this;

    self.Parent = parent;

    self.type = options.type;
    self.typeAvg = options.typeAvg;
    self.prevType = options.prevType;
    self.name = ko.observable(options.name);
    self.Available = ko.observable(false);
    self.Show = ko.observable(true);
    self.ShowAvg = ko.observable(false);
    self.ShowAutoLaps = ko.observable(false);
    self.Color = ko.observable('');
    self.axeNumber = 0;
    self.exeBlockType = options.exeBlockType;

    self.IconName = ko.observable('');
    self.unit = ko.observable('');

    self.MaxVal = null;
    self.MinVal = null;

    self.ExeMaxVal = null;
    self.ExeMinVal = null;

    self.timeWindowStats = [];
    self.CurrentTimeWindowStats = ko.observable(null);

    self.ShowState = ko.computed(function () {
        if (self.Show()) {
            if (self.ShowAvg()) {
                return 'NS';
            } else {
                return 'N';
            }
        } else {
            if (self.ShowAvg()) {
                return 'S';
            } else {
                return 'O';
            }
        }
    });
    self.ToggleShowState = function () {
        if (self.Show()) {
            if (self.ShowAvg()) {
                self.Show(false);
                self.ShowAvg(false);
            } else {
                self.ShowAvg(true);
                self.Show(false);
            }
        } else {
            if (self.ShowAvg()) {
                self.Show(true);
                self.ShowAvg(true);
            } else {
                self.Show(true);
                self.ShowAvg(false);
            }
        }
        self.Parent.MiniIsValid = false;
    };

    self.GetTimeWindowStats = function (start, end) {
        self.CurrentTimeWindowStats(_(self.timeWindowStats).findWhere({ Start: start, End: end }));
        return self.CurrentTimeWindowStats();
    }
    self.SetTimeWindowStats = function (stats) {
        self.timeWindowStats.push(stats);
        self.CurrentTimeWindowStats(stats);
    }

    self.setIconName = function () {
        switch (self.type) {
            case 0: self.IconName('hr'); break;
            case 2: self.IconName('altitude'); break;
            case 3: self.IconName('cadence'); break;
            case 4: self.IconName('power'); break;
            case 1: self.IconName((self.Parent.paceParams && self.Parent.paceParams.unit) ? 'pace' : 'speed'); break;
        }
    }
    self.setUnit = function () {
        switch (self.type) {
            case 0: self.unit('bpm'); break;
            case 2: self.unit('m'); break;
            case 3: self.unit('rpm'); break;
            case 4: self.unit('W'); break;
            case 1: self.unit((self.Parent.paceParams && self.Parent.paceParams.unit) ? self.Parent.paceParams.unit : 'km/h'); break;
        }
    }
    self.setIconName();
    self.setUnit();
    self.name.subscribe(function (newValue) {
        if (self.type == 1) {
            self.setIconName();
            self.setUnit();
        }
    });

}

function MultiChart(options) {
    var self = this;

    //#region data indexes

    var chartType_hr = 0;
    var chartType_speed = 1;
    var chartType_altitude = 2;
    var chartType_cadence = 3;
    var chartType_power = 4;

    var chartType_hrAvg = 9;
    var chartType_speedAvg = 10;
    var chartType_altitudeAvg = 11;
    var chartType_cadenceAvg = 12;
    var chartType_powerAvg = 13;

    var chartType_Time = 7;
    var chartType_Distance = 8

    var chartType_lastDist = 14;

    var chartType_prevHr = 15;
    var chartType_prevSpeed = 16;
    var chartType_prevAltitude = 17;
    var chartType_prevCadence = 18;
    var chartType_prevPower = 19;

    var chartType_power4 = 20;

    var maxChartTypeIndex = 20;



    //#endregion

    //#region params

    self.zonesChart = [chartType_speed, chartType_cadence, chartType_power, chartType_hr];

    self.useGaussAvg = true;

    self.id = 0;
    self.ids = [];
    self.multi = null;
    self.types = null;
    self.doNotShift = null;
    self.forFB = null;

    self.paceParams = null;
    self.placeholder = null;
    self.miniPlaceholder = null;

    self.baseDownsampleTreshold = ko.observable('auto');
    self.useCurves = ko.observable(useCurves);
    self.medianRadius = ko.observable(100);

    self.ShowZoomReset = ko.observable(false);

    self.colorsMode = 'chartType'; //chartType, multiPerson

    self.showMini = true;

    self.maxHr = null;
    self.duration = null;

    self.rangeFrom = null;
    self.rangeTo = null;
    self.ranges = null;

    //#region timeWindow params
    self.timeWindowStatsCalculated = false;
    self.timeWindowStatsStart = null;
    self.timeWindowStatsEnd = null;

    self.timeWindowStatsTime = ko.observable(null);
    self.timeWindowStatsDistance = ko.observable(null);

    //#endregion

    self.ServerModel = null;
    self.ServerModelPreprocessed = false;

    self.ChartOptions = null;
    self.ChartData = null;
    self.Chart = null;
    self.MiniChart = null;
    self.MiniChartSelecting = false;
    self.MiniIsValid = false;

    self.downsampleTreshold = ko.observable(300);

    self.X_time = ko.observable(true);
    self.X_distance = ko.observable(false);
    self.X_time.subscribe(function (newValue) {
        self.rangeFrom = null;
        self.rangeTo = null;
        self.ranges = null;
        self.MiniIsValid = false;
        self.X_distance(!self.X_time());
    });
    self.X_distance.subscribe(function (newValue) {
        self.X_time(!self.X_distance());
    });

    self.ShowSettings = ko.observable(false);
    self.ShowStats = ko.observable(false);

    self.ShowHrZones = ko.observable(false);
    self.ShowPowerZones = ko.observable(false);
    self.FTP = ko.observable(0);

    self.labelWidth = 20;

    self.AxesLabelsLeft = ko.observableArray([]);
    self.AxesLabelsRight = ko.observableArray([]);

    self.DataTypesOrder = [chartType_speed, chartType_cadence, chartType_hr, chartType_power, chartType_altitude];

    self.DataTypes = [
        new MultiChartDataType({ type: chartType_hr, typeAvg: chartType_hrAvg, prevType: chartType_prevHr, name: translations.HeartRate, exeBlockType: 'hr' }, self),
        new MultiChartDataType({ type: chartType_speed, typeAvg: chartType_speedAvg, prevType: chartType_prevSpeed, name: translations.Speed, exeBlockType: 'speed' }, self),
        new MultiChartDataType({ type: chartType_altitude, typeAvg: chartType_altitudeAvg, prevType: chartType_prevAltitude, name: translations.Altitude, exeBlockType: '' }, self),
        new MultiChartDataType({ type: chartType_cadence, typeAvg: chartType_cadenceAvg, prevType: chartType_prevCadence, name: translations.Cadence, exeBlockType: 'cadence' }, self),
        new MultiChartDataType({ type: chartType_power, typeAvg: chartType_powerAvg, prevType: chartType_prevPower, name: translations.Power, exeBlockType: 'power' }, self)
    ];
    self.DataTypesLength = self.DataTypes.length;

    self.DataTypesOrdered = [
        self.DataTypes[self.DataTypesOrder[0]],
        self.DataTypes[self.DataTypesOrder[1]],
        self.DataTypes[self.DataTypesOrder[2]],
        self.DataTypes[self.DataTypesOrder[3]],
        self.DataTypes[self.DataTypesOrder[4]]
    ];

    self.ZonesChartTypes = [
        new ZonesChartType({ dataType: self.DataTypes[chartType_hr], mode: 'default', name: translations.HeartRate }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_hr], mode: 'custom', name: translations.TimeInZonesShort }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_speed], mode: 'default', name: translations.Speed }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_speed], mode: 'custom', name: translations.SpeedInZones }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_altitude], mode: 'default', name: translations.Altitude }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_cadence], mode: 'default', name: translations.Cadence }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_power], mode: 'default', name: translations.Power }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_power], mode: 'custom', name: translations.PowerInZones }, self),
        new ZonesChartType({ dataType: self.DataTypes[chartType_power], mode: 'mmp', name: translations.PowerLevel }, self)
    ];
    self.ZonesChartTypesLength = self.ZonesChartTypes.length;

    //self.TrainingExcercisesChart = null;
    //self.PlanExcercisesChart = null;
    self.TrainingExcercisesChartVisible = ko.observable(false);
    self.PlanExcercisesChartVisible = ko.observable(false);

    self.CanBeDistance = ko.computed(function () {
        return self.DataTypes[chartType_speed].Available();
    });

    self.medianRadius.subscribe(function (newValue) {
        if (newValue && self.ServerModel) {
            for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
                var serie = self.ServerModel.Series[i_serie];
                for (var i_flotSerie = 0, len_flotSerie = serie.Series.length; i_flotSerie < len_flotSerie; i_flotSerie++) {
                    var flotSerie = serie.Series[i_flotSerie];
                    flotSerie.processedTime = false;
                    flotSerie.processedDist = false;
                }
            }
        }
    });

    self.MaxX = function () {
        if (!self.ServerModel) return 0;
        return self.X_time() ? self.ServerModel.MaxXTime : self.ServerModel.MaxXDistance;
    };

    self.MeasureMode = ko.observable('none');
    self.MeasureStartX = ko.observable(0);
    self.MeasureStartY = ko.observable(0);
    self.MeasureEndX = ko.observable(0);
    self.MeasureEndY = ko.observable(0);

    self.isCadenceType = function (chartType) {
        return chartType_cadence == chartType || chartType_cadenceAvg == chartType;
    };
    self.isHrType = function (chartType) {
        return chartType_hr == chartType || chartType_hrAvg == chartType;
    };

    //#endregion

    //#region init
    self.init = function (options) {
        self.id = options && typeof (options.id) != 'undefined' ? options.id : 0;
        self.ids = options && typeof (options.ids) != 'undefined' ? options.ids : [];
        self.multi = options && typeof (options.multi) != 'undefined' ? options.multi : null;
        self.types = options && typeof (options.types) != 'undefined' ? options.types : null;
        self.doNotShift = options && typeof (options.doNotShift) != 'undefined' ? options.doNotShift : null;
        self.forFB = options && typeof (options.forFB) != 'undefined' ? options.forFB : null;

        self.paceParams = options && typeof (options.paceParams) != 'undefined' ? options.paceParams : null;
        self.placeholder = options && typeof (options.placeholder) != 'undefined' ? options.placeholder : null;
        self.miniPlaceholder = options && typeof (options.miniPlaceholder) != 'undefined' ? options.miniPlaceholder : null;

        self.baseDownsampleTreshold(options && typeof (options.downsampleTreshold) != 'undefined' ? options.downsampleTreshold : 'auto');
        self.useCurves(options && typeof (options.useCurves) != 'undefined' ? options.useCurves : useCurves);
        self.medianRadius(options && typeof (options.medianRadius) != 'undefined' ? options.medianRadius : 100);

        self.colorsMode = options && typeof (options.colorsMode) != 'undefined' ? options.colorsMode : 'chartType'; //chartType, multiPerson

        self.showMini = options && typeof (options.showMini) != 'undefined' ? options.showMini : true;

        self.ServerModelPreprocessed = false;

        self.AxesLabelsLeft.removeAll();
        self.AxesLabelsLeft.removeAll();

        delete self.ServerModel;
        self.ServerModel = null;
        delete self.ChartData;
        self.ChartData = null;

        self.rangeFrom = null;
        self.rangeTo = null;
        self.ranges = null;
        self.ShowZoomReset(false);
        self.MiniIsValid = false;

        for (var i_yAxe = 0, len_yAxe = self.DataTypesLength; i_yAxe < len_yAxe; i_yAxe++) {
            var iyAxe = self.DataTypesOrder[i_yAxe];
            var selfDataType = self.DataTypes[iyAxe];
            selfDataType.MaxVal = null;
            selfDataType.MinVal = null;
        }

        self.maxHr = null;

        self.X_time(true);

        if (self.Chart) self.Chart.shutdown();
        if (self.Chart) self.MiniChart.shutdown();
    }
    if (options) {
        self.init(options);
    }
    //#endregion

    //#region refresh 

    self.drawTimeout = null;
    self.DefferedDraw = function () {
        if (!self.building) {
            if (self.drawTimeout) clearTimeout(self.drawTimeout);
            $('body').css('cursor', 'wait');
            self.drawTimeout = setTimeout(self.DrawChart, 750);
        }
    }
    self.RefreshCompute = ko.computed(function () {
        var compute = self.downsampleTreshold() + self.medianRadius() + self.useCurves() + self.X_time() + self.ShowHrZones() + self.ShowPowerZones() + self.TrainingExcercisesChartVisible() + self.PlanExcercisesChartVisible();
        var anyChartShow = false;
        for (var i_dt = 0; i_dt < self.DataTypesLength; i_dt++) {
            var dt = self.DataTypes[i_dt];
            compute += dt.Show() + dt.ShowAvg() + dt.ShowAutoLaps();
            if ((dt.Show() || dt.ShowAvg()) && dt.Available()) anyChartShow = true;
        }
        /*
        if (!anyChartShow) {
            for (var i_dt = 0; i_dt < self.DataTypesLength; i_dt++) {
                if (self.DataTypes[i_dt].Available()) {
                    self.DataTypes[i_dt].Show(true);
                    break;
                }
            }
        }
*/
        self.DefferedDraw();
    });

    //#endregion

    //#region data process 

    self.AutoDownsampleTreshold = function () {
        if (self.ChartData && self.ChartOptions && self.baseDownsampleTreshold() === 'auto') {
            var nrOfLines = self.ChartData.length;
            if (nrOfLines > 1) nrOfLines = nrOfLines * 3 / 5;
            var baseSample = downsampleTreshold;
            self.downsampleTreshold(baseSample / (nrOfLines));
        } else {
            self.downsampleTreshold(self.baseDownsampleTreshold());
        }
        var downsample = self.downsampleTreshold();
        var maxX = self.MaxX();
        if (self.rangeFrom && self.rangeTo && maxX) {
            downsample = downsample * maxX / (self.rangeTo - self.rangeFrom);
        }

        self.ChartOptions.series.downsample = { threshold: downsample }; // 0 disables downsampling for this series.                
        self.ChartOptions.series.curvedLines = { active: self.useCurves() };
    };

    self.GetModel = function (callback) {
        var data = {};
        if (self.id) {
            data.ids = '' + self.id;
        } else if (self.ids && self.ids.length) {
            data.ids = self.ids.join(';');
        } else {
            return;
        }
        if (self.multi !== null) data.multi = self.multi;
        if (self.types !== null) data.types = self.types;
        if (self.doNotShift !== null) data.doNotShift = self.doNotShift;
        if (self.forFB !== null) data.forFB = self.forFB;

        $.ajax({
            type: "POST",
            url: ContextPath + "Chart/GetMultiChartModel",
            data: data
        })
        .done(function (result) {
            if (result && _.isString(result)) {
                result = JSON.parse(result);
            }
            self.ServerModel = result;
            self.preprocessServerModel(function () {
                self.MiniIsValid = false;
                callback();
            });
        });
    };

    self.defaultZoneChart = 0;

    var blurDist3x = 0.150;
    self.preprocessServerModel = function (calback) {
        if (self.ServerModelPreprocessed) return;

        self.DataTypes[chartType_speed].name((self.paceParams && self.paceParams.unit) ? translations.Pace : translations.Speed);

        self.FTP(self.ServerModel.FTP);

        for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
            var serie = self.ServerModel.Series[i_serie];
            if (serie.ChartType == chartType_altitude) {
                _.each(serie.Series, function (s) { s.lines.fill = true; });
            }
        }

        _.each(self.DataTypes, function (dt) { dt.Available(false); dt.MaxVal = null; });

        var worker = new Worker(ContextPath + 'Scripts/coreMultiChartPreProcess.js?version=2');

        worker.onmessage = function (event) {
            self.ServerModel.DataPoints = event.data.dataPoints;
            self.copyDataTypes(event.data.DataTypes, self.DataTypes);

            self.ServerModelPreprocessed = true;
            self.X_time.notifySubscribers(); //referesh self.MaxX 
            if (calback) calback();
        };
        worker.onerror = function (event) {
            self.X_time.notifySubscribers(); //referesh self.MaxX 
            if (calback) calback();
        };

        var preparedDataTypes = self.copyDataTypes(self.DataTypes, []);

        worker.postMessage({
            dataPoints: self.ServerModel.DataPoints,
            DataTypesLength: self.DataTypesLength,
            useGaussAvg: self.useGaussAvg,
            DataTypes: preparedDataTypes,
            maxChartTypeIndex: maxChartTypeIndex,
            chartType_hr: chartType_hr,
            chartType_Time: chartType_Time,
            chartType_Distance: chartType_Distance,
            chartType_lastDist: chartType_lastDist,
            chartType_power: chartType_power,
            chartType_power4: chartType_power4,
            chartType_prevPower: chartType_prevPower,
            chartType_altitude: chartType_altitude
        });
    }

    self.copyDataTypes = function (from, to) {

        for (var i = 0, len = from.length; i < len; i++) {
            if (to.length <= i) to.push({});
            toi = to[i];
            fromi = from[i];

            toi.type = fromi.type;
            toi.typeAvg = fromi.typeAvg;
            toi.prevType = fromi.prevType;
            toi.MaxVal = fromi.MaxVal;
            toi.MinVal = fromi.MinVal;
        }

        return to;
    }

    self.dataCopyFunc = function (avg, dt, serie, flotSeries, xTime) {
        var chartTypeIndex = avg ? dt.typeAvg : dt.type;
        var baseChartTypeIndex = dt.type;
        var hr = baseChartTypeIndex == chartType_hr;
        if ((!avg && dt.Show()) || (avg && dt.ShowAvg())) {
            //dla kazdej FlotSerie w  SingleChartModel (na ogol tylko jedna)
            for (var i_flotSerie = 0, len_flotSerie = serie.Series.length; i_flotSerie < len_flotSerie; i_flotSerie++) {
                var flotSerie = serie.Series[i_flotSerie];
                if (avg) {
                    var avgFlotSerie;
                    if (typeof (serie.AvgSeries) == 'undefined') {
                        serie.AvgSeries = [];
                    }
                    while (serie.AvgSeries.length <= i_flotSerie) {
                        serie.AvgSeries.push(null);
                    }
                    avgFlotSerie = serie.AvgSeries[i_flotSerie];
                    if (avgFlotSerie === null) {
                        avgFlotSerie = jQuery.extend(true, {}, flotSerie);
                        avgFlotSerie.processedTime = false;
                        avgFlotSerie.processedDist = false;
                        avgFlotSerie.avg = true;
                        serie.AvgSeries[i_flotSerie] = avgFlotSerie;
                    }
                    flotSerie = avgFlotSerie;
                    flotSerie.points.show = true;
                    flotSerie.points.radius = 3;
                    flotSerie.lines.show = false;
                    flotSerie.downsample = { threshold: 0 }; // 0 disables downsampling for this series. 
                }

                if ((typeof (flotSerie.color) == 'undefined' || !flotSerie.color) && self.colorsMode === 'chartType') {
                    flotSerie.color = baseChartTypeIndex;
                }
                var trainingId = serie.TrainingId;
                var data = null;

                if (xTime) {
                    if (typeof (flotSerie.processedTime) != 'undefined' && flotSerie.processedTime) {
                        data = serie.TimeSeriesData[avg ? 't' : 'f'][i_flotSerie];
                    }
                } else {
                    if (typeof (flotSerie.processedDist) != 'undefined' && flotSerie.processedDist) {
                        data = serie.DistSeriesData[avg ? 't' : 'f'][i_flotSerie];
                    }
                }

                if (typeof (data) == 'undefined' || data === null) {
                    data = [];
                    //pobierz dane z DataPoints (odpowiedni trening i typ danych/wykresu)
                    var prexX = -1;
                    for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                        var dps = self.ServerModel.DataPoints[i_dps];
                        if (dps.TrainingId == trainingId) {
                            for (var i_dp = 0, len_dp = dps.Points.length; i_dp < len_dp; i_dp++) {
                                var point = dps.Points[i_dp];
                                var x = xTime ? point[chartType_Time] : point[chartType_Distance];
                                var value = point[chartTypeIndex];
                                if (x && x >= 0 && (avg || value !== null) && x != prexX && (!hr || value > 0)) {
                                    if (hr) {
                                        value = 100 * value / serie.MaxHr;
                                    }
                                    if (value == 0) {
                                        value = 0.0001;
                                    }
                                    data.push([x, value]);
                                    prexX = x;
                                }
                            }
                        }
                    }

                    //#region mediana dla kazdego punktu
                    var dataLen = data.length;
                    var medianRadius = Math.round(47 - Math.log(self.medianRadius()) * 10);
                    if (medianRadius > 1 && dataLen > 1) {
                        var mData = [];
                        var bucket = [];
                        var cVal = data[0];


                        //#region inicjalizacja pierwszego elementu
                        for (var i = 0; i <= medianRadius && i < dataLen; i++) {
                            bucket.push(data[i][1]);
                        }
                        mData.push([data[0][0], getMedianFunc(bucket)]);
                        //#endregion

                        var nextValIdx;
                        for (var i_d = 1; i_d < dataLen; i_d++) {
                            cVal = data[i_d];
                            nextValIdx = i_d + medianRadius;
                            if (nextValIdx < dataLen) {
                                if (!data[nextValIdx]) {
                                    alert('wtf!!!');
                                }
                                bucket.push(data[nextValIdx][1]);
                            }
                            if (i_d > medianRadius) {
                                bucket.shift();
                            }
                            mData.push([data[i_d][0], getMedianFunc(bucket)]);
                        }
                        data = mData;
                    }
                    //#endregion

                    if (xTime) {
                        flotSerie.processedTime = true;
                        serie.TimeSeriesData = self.initSeriesData(serie.TimeSeriesData, i_flotSerie);
                        serie.TimeSeriesData[avg ? 't' : 'f'][i_flotSerie] = data;
                    } else {
                        flotSerie.processedDist = true;
                        serie.DistSeriesData = self.initSeriesData(serie.DistSeriesData, i_flotSerie);
                        serie.DistSeriesData[avg ? 't' : 'f'][i_flotSerie] = data;
                    }
                }
                flotSerie.data = data;
                flotSerie.tempYaxis = baseChartTypeIndex;
                flotSerie.chartTypeIndex = chartTypeIndex;
                if (flotSerie.points && flotSerie.points.show) {
                    flotSerie.curvedLines = { active: false };
                    flotSerie.downsample = { threshold: 0 }; // 0 disables downsampling for this series. 
                }

                flotSeries.push(flotSerie);
            }
        }
    }

    self.initSeriesData = function (data, i_flotSerie) {
        if (typeof (data) == 'undefined') {
            data = { t: [], f: [], al: [] };
        }
        while (data.length <= i_flotSerie) {
            data['t'].push(null);
            data['f'].push(null);
            data['al'].push(null);
        }
        return data;
    }
    self.lapDataCopyFunc = function (dt, serie, flotSeries, xTime, lapData) {
        if (!lapData || !lapData.length) return;
        //lapData = _.where(lapData, { Auto: true });
        lapData = _.sortBy(lapData, function (elem) { return elem.xaxis; });
        var chartTypeIndex = dt.type;
        var baseChartTypeIndex = dt.type;
        var hr = baseChartTypeIndex == chartType_hr;
        if (dt.ShowAutoLaps()) {
            //dla kazdej FlotSerie w  SingleChartModel (na ogol tylko jedna)
            for (var i_flotSerie = 0, len_flotSerie = serie.Series.length; i_flotSerie < len_flotSerie; i_flotSerie++) {
                var flotSerie = serie.Series[i_flotSerie];

                var autoLapFlotSerie;
                if (typeof (serie.AutoLapFlotSeries) == 'undefined') {
                    serie.AutoLapFlotSeries = [];
                }
                while (serie.AutoLapFlotSeries.length <= i_flotSerie) {
                    serie.AutoLapFlotSeries.push(null);
                }
                autoLapFlotSerie = serie.AutoLapFlotSeries[i_flotSerie];
                if (autoLapFlotSerie === null) {
                    autoLapFlotSerie = jQuery.extend(true, {}, flotSerie);
                    autoLapFlotSerie.processedTime = false;
                    autoLapFlotSerie.processedDist = false;
                    autoLapFlotSerie.avg = true;
                    serie.AutoLapFlotSeries[i_flotSerie] = autoLapFlotSerie;
                }
                flotSerie = autoLapFlotSerie;
                flotSerie.lines.show = true;
                flotSerie.points.show = false;
                flotSerie.lines.lineWidth = 1;
                flotSerie.curvedLines = { active: false };
                flotSerie.downsample = { threshold: 0 }; // 0 disables downsampling for this series. 


                if ((typeof (flotSerie.color) == 'undefined' || !flotSerie.color) && self.colorsMode === 'chartType') {
                    flotSerie.color = baseChartTypeIndex;
                }
                var trainingId = serie.TrainingId;
                var data = null;

                if (xTime) {
                    if (typeof (flotSerie.processedTime) != 'undefined' && flotSerie.processedTime) {
                        data = serie.TimeSeriesData['al'][i_flotSerie];
                    }
                } else {
                    if (typeof (flotSerie.processedDist) != 'undefined' && flotSerie.processedDist) {
                        data = serie.DistSeriesData['al'][i_flotSerie];
                    }
                }

                if (typeof (data) == 'undefined' || data === null) {
                    data = [];
                    prevX = 0;
                    //#region pobranie danych o lapach
                    for (var i = 0, len = lapData.length; i < len; i++) {
                        var lap = lapData[i];
                        var x1 = xTime ? lap.xaxis : lap.xaxisDist;
                        var x2 = 0;
                        switch (chartTypeIndex) {
                            case 0: x2 = lap.AvgHrPrec; break;  //chartType_hr = 0;
                            case 1: x2 = lap.AvgSpeedVal; break;//chartType_speed = 1;
                                // case 2: x2= lap.AvgHr; break;//chartType_altitude = 2;
                            case 3: x2 = lap.AvgCadence; break;//chartType_cadence = 3;
                            case 4: x2 = lap.AvgPowerVal; break;//chartType_power = 4;
                        }
                        var prevXplus = prevX + (xTime ? 1 : 0.0001);
                        data.push([prevXplus, x2]);
                        data.push([x1, x2]);
                        prevX = x1;
                    }
                    //#endregion

                    if (xTime) {
                        flotSerie.processedTime = true;
                        serie.TimeSeriesData = self.initSeriesData(serie.TimeSeriesData, i_flotSerie);
                        serie.TimeSeriesData['al'][i_flotSerie] = data;
                    } else {
                        flotSerie.processedDist = true;
                        serie.DistSeriesData = self.initSeriesData(serie.DistSeriesData, i_flotSerie);
                        serie.DistSeriesData['al'][i_flotSerie] = data;
                    }
                }
                flotSerie.data = data;
                flotSerie.tempYaxis = baseChartTypeIndex;
                flotSerie.chartTypeIndex = chartTypeIndex;

                flotSeries.push(flotSerie);
            }
        }
    }

    self.excerciseDataCopyFunc = function (dt, flotSeries, xTime, trainingExeBlocks, planExeBlocks) {
        //if (!dt.Show() && !dt.ShowAvg()) return;
        if (!trainingExeBlocks && !planExeBlocks && !trainingExeBlocks.length && !planExeBlocks.length) return false;
        var baseChartTypeIndex = dt.type;
        var hr = baseChartTypeIndex == chartType_hr;

        var allBlocs = [];
        if (self.TrainingExcercisesChartVisible()) allBlocs = allBlocs.concat(trainingExeBlocks);
        if (self.PlanExcercisesChartVisible()) allBlocs = allBlocs.concat(planExeBlocks);

        if (!allBlocs.length) return false;

        var typeBlocks = _.filter(allBlocs, function (block) { return block.Type == dt.exeBlockType });
        if (!typeBlocks.length) return false;

        var block, x1, x2, y1, y2;
        var prevY = 0;
        var epsilon = xTime ? 1 : 0.0001;
        var flotSerie = {
            color: baseChartTypeIndex //dt.Color()
           , data: []
            //,label: string
            , lines: {
                show: true
                , fill: true
                , zero: false
                , lineWidth: 0
            }
            //,bars: specific bars options
            //,points: specific points options
            //,xaxis: number
            //, yaxis: dt.axeNumber
            //,clickable: boolean
            //,hoverable: boolean
            , shadowSize: 0
            //,highlightColor: color or number
            , tempYaxis: baseChartTypeIndex
            , chartTypeIndex: baseChartTypeIndex
            , downsample: { threshold: 0 }
            , curvedLines: false
        };

        if (baseChartTypeIndex == chartType_speed) flotSerie.color = '#888';// flotSerie.lines.fill = 0.9;

        for (var i = 0, len = typeBlocks.length; i < len; i++) {
            block = typeBlocks[i];

            x1 = xTime ? (block.FlotData.t1 * 1000) : (block.FlotData.d1);
            x2 = xTime ? (block.FlotData.t2 * 1000) : (block.FlotData.d2);

            y2 = hr ? block.UpperPercentValue : block.UpperValue;
            if (block.LoadChangeMode == 'upto' || block.LoadChangeMode == 'downto') {
                y1 = prevY;
            } else {
                y1 = y2;
            }
            prevY = y2;

            flotSerie.data.push([x1 - epsilon, 0]);
            flotSerie.data.push([x1, y1]);
            flotSerie.data.push([x2, y2]);
            flotSerie.data.push([x2 + epsilon, 0]);

            if (!dt.ExeMaxVal || y1 > dt.ExeMaxVal) dt.ExeMaxVal = y1;
            if (!dt.ExeMaxVal || y2 > dt.ExeMaxVal) dt.ExeMaxVal = y2;
            if (y1 && (!dt.ExeMinVal || y1 < dt.ExeMinVal)) dt.ExeMinVal = y1;
            if (y1 && (!dt.ExeMinVal || y2 < dt.ExeMinVal)) dt.ExeMinVal = y2;
        }
        flotSeries.push(flotSerie);
        return true;
    }

    //#endregion

    //#region timeWindowStats

    self.recalculateTimeWindowStats = function (xStart, xEnd, yRanges) {
        if (self.ServerModel) {
            if (!self.timeWindowStatsCalculated || self.timeWindowStatsStart !== xStart || self.timeWindowStatsEnd !== xEnd) {
                //popraw poczatek i koniec przedzialu
                if (!xStart) xStart = 0;
                if (!xEnd) xEnd = self.MaxX();

                var xTime = self.X_time();

                var startTime = xTime ? xStart : null;
                var endTime = xTime ? xEnd : null;
                var startDist = xTime ? null : xStart;
                var endDist = xTime ? null : xEnd;

                //wybierz typy, ktore trzeba obliczyc
                var dts = [];
                var dt, dts_len;
                for (var i_dt = 0; i_dt < self.DataTypesLength; i_dt++) {
                    dt = self.DataTypes[i_dt];
                    //obliczamy te ktore sa dostepne i nie byly jeszcze obliczane dla danego przedzialu
                    if (dt.Available() && !dt.GetTimeWindowStats(xStart, xEnd)) {
                        var dtPush = {
                            min: null,
                            max: null,
                            sum: 0,
                            sumSqr: 0,
                            count: 0,
                            sumW: 0,
                            countW: 0,
                            avg: null,
                            start: [],
                            end: [],
                            startVal: null,
                            endVal: null,
                            dt: dt,
                            t_sum1: 0,
                            t_sum2: 0,
                            endIdx: 0,

                            chartZonesTools: {}
                        };
                        if (dt.type == chartType_power) {
                            dtPush.powerNP = { sum: 0, count: 0 };
                        }
                        dts.push(dtPush);
                    }
                }
                dts_len = dts.length;

                //przygotuj min, max, start, end i dane do sredniej               
                var val, prevIdx, midVal, preVal, avgX, dps, point, x, x2, xspan;
                var prexX = 0;
                var startEndRadius = 10;
                for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                    dps = self.ServerModel.DataPoints[i_dps];
                    prexX = 0;
                    preVal = 0;
                    for (var i_dp = 0, len_dp = dps.Points.length; i_dp < len_dp; i_dp++) {
                        point = dps.Points[i_dp];
                        x = xTime ? point[chartType_Time] : point[chartType_lastDist];
                        if (x >= xStart && x <= xEnd) {
                            x2 = xTime ? point[chartType_lastDist] : point[chartType_Time];
                            if (x2 != null) {
                                if (xTime) {
                                    if (startDist == null) startDist = x2;
                                    endDist = x2;
                                } else {
                                    if (startTime == null) startTime = x2;
                                    endTime = x2;
                                }
                            }
                            avgX = xTime ? x : x2;

                            for (i_dt = 0; i_dt < dts_len; i_dt++) {
                                dt = dts[i_dt];
                                val = point[dt.dt.type];
                                if (val !== null && (dt.dt.type != chartType_hr || val > 0)) {
                                    if (val && (dt.min === null || dt.min > val)) dt.min = val;
                                    if (dt.max === null || dt.max < val) dt.max = val;

                                    prevIdx = point[dt.dt.prevType];
                                    if (prevIdx !== null) {
                                        prexX = dps.Points[prevIdx][chartType_Time];
                                        preVal = dps.Points[prevIdx][dt.dt.type];
                                    }
                                    else {
                                        preVal = val;
                                    }
                                    xspan = avgX - prexX;
                                    midVal = 0.5 * (preVal + val);
                                    dt.sumW += midVal * xspan;
                                    dt.countW += xspan;
                                    dt.sum += val;
                                    dt.count += 1;
                                    /*
                                    if (dt.dt.type == 4) {
                                        $('body').append(preVal + ';' + val + ';' + x + ';' + prexX + '<br/>');
                                    }
    */
                                    dt.sumSqr += val * val;

                                    if (dt.dt.type == chartType_power) {
                                        var power4 = point[chartType_power4];
                                        if (power4 != null) {
                                            dt.powerNP.sum += power4;
                                            dt.powerNP.count++;
                                        }
                                    }

                                    if (dt.start.length < startEndRadius) dt.start.push(val);
                                    if (dt.end.length < startEndRadius) {
                                        dt.end.push(val);
                                    } else {
                                        dt.end[dt.endIdx++] = val;
                                        if (dt.endIdx >= dt.end.length) dt.endIdx = 0;
                                    }
                                }
                            }
                        }
                    }
                }

                var anyAvg = false;
                //oblicz srednia i mediane dla start i end
                for (i_dt = 0; i_dt < dts_len; i_dt++) {
                    dt = dts[i_dt];
                    if (dt.count > 0) {
                        if (dt.dt.type == chartType_speed) {
                            dt.avg = 3600000.0 * (endDist - startDist) / (endTime - startTime)
                        } else {
                            dt.avg = dt.sumW / dt.countW;
                        }
                        dt.startVal = getMedianFunc(dt.start);
                        dt.endVal = getMedianFunc(dt.end);
                        if ($.inArray(dt.dt.type, self.zonesChart) >= 0) {
                            var atrAvg = dt.sum / dt.count;
                            var stddev = Math.sqrt(dt.sumSqr / dt.count - atrAvg * atrAvg);
                            var sDur = (endTime - startTime) / 1000;

                            $.each(self.ZonesChartTypes, function (index, zonesChartType) {
                                if (zonesChartType.DataType.type == dt.dt.type) {
                                    if (dt.dt.type == chartType_speed && self.paceParams) {
                                        var chartZonesTool = (zonesChartType.Mode == 'mmp') ? new ChartZonesToolForMMP(self) : new ChartZonesTool(self);
                                        chartZonesTool.Initialize(self, zonesChartType, dt.dt.type, dt.avg, stddev, dt.count, sDur, false, self.paceParams.factor, self.paceParams.unit);
                                        dt.chartZonesTools[zonesChartType.Mode] = chartZonesTool;
                                    } else {
                                        var chartZonesTool = (zonesChartType.Mode == 'mmp') ? new ChartZonesToolForMMP(self) : new ChartZonesTool(self);
                                        chartZonesTool.Initialize(self, zonesChartType, dt.dt.type, dt.avg, stddev, dt.count, sDur, false);
                                        dt.chartZonesTools[zonesChartType.Mode] = chartZonesTool;
                                    }
                                }
                            });


                        }
                        anyAvg = true;
                    }
                }

                if (anyAvg) {
                    var avgX = (xStart + xEnd) / 2;

                    for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                        var dps = self.ServerModel.DataPoints[i_dps];
                        for (var i_dp = 0, len_dp = dps.Points.length; i_dp < len_dp; i_dp++) {
                            var point = dps.Points[i_dp];
                            var x = xTime ? point[chartType_Time] : point[chartType_lastDist];
                            if (x >= xStart && x <= xEnd) {
                                for (i_dt = 0; i_dt < dts_len; i_dt++) {
                                    dt = dts[i_dt];
                                    if (dt.avg !== null) {
                                        val = point[dt.dt.type];
                                        if (val !== null && (dt.dt.type != chartType_hr || val > 0)) {
                                            dt.t_sum1 += (x - avgX) * (val - dt.avg);
                                            dt.t_sum2 += Math.pow((x - avgX), 2);
                                            var prevPoint = dps.Points[i_dp - 1];
                                            var deltaTime;
                                            if (prevPoint) {
                                                deltaTime = point[chartType_Time] - prevPoint[chartType_Time];
                                            } else {
                                                deltaTime = point[chartType_Time];
                                            }
                                            $.each(dt.chartZonesTools, function (key, chartZonesTool) {
                                                chartZonesTool.Update(deltaTime, val);
                                            });
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                //ustaw dane o przedziale
                for (i_dt = 0; i_dt < dts_len; i_dt++) {
                    dt = dts[i_dt];
                    var stats = new TimeWindowStats(self);
                    if (dt.dt.type == chartType_speed && self.paceParams && self.paceParams.unit) {
                        stats.invertFactor = self.paceParams.factor;
                        stats.unit = self.paceParams.unit;
                    }
                    $.each(dt.chartZonesTools, function (key, chartZonesTool) {
                        chartZonesTool.PostProcessing();
                    });

                    stats.Start = xStart;
                    stats.End = xEnd;
                    stats.avg(dt.avg);
                    stats.max(dt.max);
                    stats.min(dt.min);
                    stats.avgStartVal(dt.startVal);
                    stats.avgEndVal(dt.endVal);
                    stats.chartZonesTools = dt.chartZonesTools;

                    if (yRanges) {
                        var n = 'y' + (dt.dt.axeNumber == 1 ? '' : dt.dt.axeNumber) + 'axis';
                        var yRange = yRanges[n];

                        if (dt.dt.type == chartType_hr) {
                            var max = self.ServerModel.Series[0].MaxHr;
                            stats.lineStartY(max * yRange.from / 100);
                            stats.lineEndY(max * yRange.to / 100);
                        } else {
                            stats.lineStartY(yRange.from);
                            stats.lineEndY(yRange.to);
                        }
                    }

                    if (dt.t_sum2 > 0) {
                        var a = dt.t_sum1 / dt.t_sum2;
                        var aToStore = MathRoundTo(a * (xTime ? 60 * 1000 : 1), 2);
                        stats.TrendA(aToStore);
                        stats.TrendB(MathRoundTo(dt.avg - a * (xStart + xEnd) / 2, 1));
                    }

                    if (dt.dt.type == chartType_power) {
                        var powerAvg = dt.powerNP.sum / dt.powerNP.count;
                        stats.powerNP(Math.sqrt(Math.sqrt(powerAvg)));
                        stats.duration((endTime - startTime) / 1000);
                    }

                    dt.dt.SetTimeWindowStats(stats);
                }

                self.timeWindowStatsCalculated = true;
                self.timeWindowStatsStart = xStart;
                self.timeWindowStatsEnd = xEnd;

                self.duration = (endTime - startTime) / 1000;
                self.timeWindowStatsTime(self.formatTime(self.duration));
                self.timeWindowStatsDistance(MathRoundTo(endDist - startDist, 2));

                $.each(self.ZonesChartTypes, function (index, zonesChartType) {
                    zonesChartType.CheckAvailable();
                });
            }
        }
    }
    self.formatTime = function (seconds) {
        seconds = Math.round(seconds);
        var rs = seconds % 60;
        var s = rs;
        if (s < 10) s = '0' + s;
        var m = ((seconds - rs) / 60) % 60;
        if (m < 10) m = '0' + m;
        var h = (seconds - rs - 60 * m) / (60 * 60);
        if (h < 10) h = '0' + h;
        return h + ":" + m + ":" + s;
    }

    //#endregion

    self.building = true;
    self.BuildChartOptionsAndData = function () {
        if (self.ServerModel) {
            self.building = true;

            var properChartColors = getChartColors(self.multi);
            if (!self.multi) {
                var c0 = properChartColors[0];
                properChartColors[0] = properChartColors[1];
                properChartColors[1] = c0;
            }

            self.ChartOptions = {
                xaxis: self.X_time() ? getTimeXaxis(self.ServerModel.MaxXTime) : getDistanceXaxis(self.ServerModel.MaxXDistance),
                yaxes: [],
                grid: {
                    backgroundColor: '#ffffff',
                    // borderColor: { 'top': 'rgb(57, 57, 57)', 'right': 'rgb(57, 57, 57)', 'bottom': 'rgb(57, 57, 57)', 'left': '#ffffff' },
                    borderWidth: 1,
                    markings: [],
                    hoverable: true
                },
                legend: {
                    show: false //noColumns: result.zoneData.length, position: "se"
                },
                crosshair: {
                    mode: "x"//"y",
    , color: '#ddd'
                },
                colors: properChartColors,
                series: {
                    shadowSize: 0
                }
            };
            self.ChartOptions.xaxis.color = 'rgb(57, 57, 57)';

            if (self.showMini) {
                self.ChartOptions.selection = { mode: "xy", minSize: 1 };
            }

            //#region przygotowanie danych

            var autoLapData = _.where(self.ServerModel.Laps, { Auto: true });
            var manualLapData = _.where(self.ServerModel.Laps, { Auto: false });
            var lapData = (manualLapData && manualLapData.length) ? manualLapData : autoLapData

            var flotSeries = [];
            var xTime = self.X_time();
            //dla kazdego SingleChartModel
            for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
                var serie = self.ServerModel.Series[i_serie];
                if (serie.MaxHr) {
                    self.maxHr = serie.MaxHr;
                }
                var chartTypeIndex = serie.ChartType;
                var dt = _.findWhere(self.DataTypes, { type: chartTypeIndex });
                if (dt) {
                    dt.Available(true);
                    self.dataCopyFunc(false, dt, serie, flotSeries, xTime);
                    self.dataCopyFunc(true, dt, serie, flotSeries, xTime);
                    self.lapDataCopyFunc(dt, serie, flotSeries, xTime, lapData);
                }

            }
            for (var iyAxe = 0; iyAxe < self.DataTypesLength; iyAxe++) {
                if (self.excerciseDataCopyFunc(self.DataTypes[iyAxe], flotSeries, xTime, self.ServerModel.TrainingExcerciseBlocks, self.ServerModel.TrainingPlanExcerciseBlocks)) {
                    self.DataTypes[iyAxe].Available(true);
                }
            }

            self.ChartData = flotSeries;
            //#endregion

            self.AutoDownsampleTreshold();

            //#region przygotowanie osi y
            self.AxesLabelsLeft.removeAll();
            self.AxesLabelsRight.removeAll();


            //underscore do wyszukiwania maxValue i minValue
            var _DataPoints = _(self.ServerModel.DataPoints);
            for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                var dps = self.ServerModel.DataPoints[i_dps];
                dps._Points = _(dps.Points);
                dps.Maxes = [];
                dps.Mins = [];
            }


            var yAxeNumber = 1;//indexer do wskazywania osi dla serii danych

            var dummyAxe = getZonePercentYaxis([0, 100], 'left', null, false);
            dummyAxe.show = false;
            self.ChartOptions.yaxes.push(dummyAxe);
            yAxeNumber++;

            for (var i_yAxe = 0, len_yAxe = self.DataTypesLength; i_yAxe < len_yAxe; i_yAxe++) {
                var iyAxe = self.DataTypesOrder[i_yAxe];
                var selfDataType = self.DataTypes[iyAxe];
                var chartTypeIndex = selfDataType.type;
                if (selfDataType.Available() /*&& self.DataTypes[i_yAxe].Show()*/) {

                    var leftTicks = null;
                    var rightTicks = null;
                    //sprawdzenie tickow
                    for (var i_tiks = 0, len_tiks = self.ServerModel.Ticks.length; i_tiks < len_tiks; i_tiks++) {
                        var tiks = self.ServerModel.Ticks[i_tiks];
                        if (tiks.ChartType == chartTypeIndex) {
                            leftTicks = tiks.Left;
                            rightTicks = tiks.Right;
                        }
                    }


                    if (chartTypeIndex == chartType_hr) {
                        var left = getZonePercentYaxis(leftTicks, 'left', null, false);
                        var right = getZoneValueYaxis(rightTicks, 'right');
                        left.labelWidth = self.labelWidth;
                        right.labelWidth = self.labelWidth;
                        left.color = '#ffffff';// self.ChartOptions.colors[chartType_hr];
                        right.color = self.ChartOptions.colors[chartType_hr];
                        left.font = { color: right.color };
                        right.font = { color: right.color };
                        left.chartTypeIndex = chartType_hr;
                        right.chartTypeIndex = chartType_hr;
                        left.recalculatedHr = true;

                        self.ChartOptions.yaxes.push(left);
                        self.ChartOptions.yaxes.push(right);
                        self.DataTypes[chartType_hr].axeNumber = yAxeNumber;

                        flotSeries.push({ data: [], yaxis: yAxeNumber, tempYaxis: -1 });
                        flotSeries.push({ data: [], yaxis: yAxeNumber + 1, tempYaxis: -1 });

                        yAxeNumber++;
                        yAxeNumber++;

                        self.AxesLabelsLeft.push({ name: translations.HeartRate + ' (%)', color: right.color, dt: selfDataType, ShowZones: self.ShowHrZones });
                        self.AxesLabelsRight.push({ name: translations.HeartRate + ' (bpm)', color: right.color, dt: selfDataType });



                    }
                    else if (chartTypeIndex == chartType_power && leftTicks && leftTicks.length) {
                        var color = self.ChartOptions.colors[chartType_power];
                        var left = getZonePercentYaxis(leftTicks, 'left', null, false);
                        var right = getZoneValueYaxis(rightTicks, 'right');
                        //if (right.min < 0) right.min = 0;
                        //if (left.min < 0) left.min = 0;
                        left.labelWidth = self.labelWidth;
                        right.labelWidth = self.labelWidth;
                        left.color = color;
                        right.color = color;
                        left.font = { color: color };
                        right.font = { color: color };
                        left.chartTypeIndex = chartType_power;
                        right.chartTypeIndex = chartType_power;
                        //left.recalculatedHr = true;

                        self.ChartOptions.yaxes.push(left);
                        self.ChartOptions.yaxes.push(right);
                        self.DataTypes[chartType_power].axeNumber = yAxeNumber + 1;

                        flotSeries.push({ data: [], yaxis: yAxeNumber, tempYaxis: -1 });
                        flotSeries.push({ data: [], yaxis: yAxeNumber + 1, tempYaxis: -1 });

                        yAxeNumber++;
                        yAxeNumber++;

                        self.AxesLabelsLeft.unshift({ name: '%FTP', color: color, dt: selfDataType, ShowZones: self.ShowPowerZones });
                        self.AxesLabelsRight.push({ name: translations.Power + ' (W)', color: color, dt: selfDataType });

                    }
                    else {
                        var unit;
                        var invertFactor = null;
                        switch (chartTypeIndex) {
                            case chartType_speed:
                                unit = "km/h";
                                invertFactor = 0;
                                if (self.paceParams && self.paceParams.unit) {
                                    // axeName = translations.Pace;
                                    unit = self.paceParams.unit;
                                    invertFactor = self.paceParams.factor;
                                }
                                break;
                            case chartType_altitude:
                                unit = "m";
                                break;
                            case chartType_cadence:
                                unit = "rpm";
                                break;
                            case chartType_power:
                                unit = "W";
                                break;
                        }


                        //znajdz max value
                        if (selfDataType.MaxVal === null) {
                            for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                                var dps = self.ServerModel.DataPoints[i_dps];
                                dps.Maxes[iyAxe] = dps._Points.max(function (multiPoint) { return multiPoint[iyAxe] })[iyAxe];
                            }
                            selfDataType.MaxVal = _DataPoints.max(function (dp) { return dp.Maxes[iyAxe] }).Maxes[iyAxe];
                            if (!selfDataType.MaxVal || selfDataType.MaxVal < selfDataType.ExeMaxVal) selfDataType.MaxVal = selfDataType.ExeMaxVal;
                        }
                        var minValue = null;
                        if (chartTypeIndex != chartType_hr && chartTypeIndex != chartType_power) {
                            if (selfDataType.MinVal === null) {
                                var allowMinus = chartTypeIndex == chartType_altitude;
                                for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                                    var dps = self.ServerModel.DataPoints[i_dps];
                                    dps.Mins[iyAxe] = dps._Points.min(function (multiPoint) {
                                        var v = multiPoint[iyAxe];
                                        return (v > 0 || allowMinus && v) ? v : Number.POSITIVE_INFINITY
                                    })[iyAxe];
                                }
                                selfDataType.MinVal = _DataPoints.min(function (dp) { return dp.Mins[iyAxe] }).Mins[iyAxe];
                                if (selfDataType.MinVal === null || selfDataType.MinVal < selfDataType.ExeMaxVal) selfDataType.MinVal = selfDataType.ExeMinVal;
                            }
                            if (selfDataType.MinVal < selfDataType.MaxVal) {
                                var minMultip = 0.3;
                                if (chartTypeIndex == chartType_altitude) {
                                    minMultip = 0.05;
                                }
                                minValue = selfDataType.MinVal - minMultip * (selfDataType.MaxVal - selfDataType.MinVal);
                                if (!allowMinus && selfDataType.MinVal > 0 && minValue < 0) minValue = 0;
                            }

                            //console.log('chartTypeIndex: ' + chartTypeIndex + '; minValue: ' + minValue);
                        } else if (chartTypeIndex == chartType_power) {
                            minValue = 0;
                        }

                        var right = getCustomValueYaxis("right", unit, invertFactor, minValue);
                        right.min = minValue;
                        //console.log('chartTypeIndex: ' + chartTypeIndex + '; minValue: ' + minValue + '; maxValue: ' + selfDataType.MaxVal);
                        var maxMultip = 1.05;
                        if (chartTypeIndex == chartType_altitude) {
                            maxMultip = 3;
                        }
                        if (minValue) {
                            right.max = selfDataType.MaxVal + (selfDataType.MaxVal - minValue) * maxMultip;
                        } else {
                            right.max = selfDataType.MaxVal * maxMultip;
                        }

                        right.labelWidth = self.labelWidth;
                        right.color = self.ChartOptions.colors[selfDataType.type];
                        right.font = { color: right.color };
                        right.chartTypeIndex = selfDataType.type;
                        self.ChartOptions.yaxes.push(right);
                        selfDataType.axeNumber = yAxeNumber;
                        yAxeNumber++;


                        self.AxesLabelsRight.push({ name: selfDataType.name() + (unit ? (' (' + unit + ')') : ''), color: right.color, dt: selfDataType });
                    }

                }
            }
            //poprawienie wskazania osi dla serii danych
            for (var i_serie = 0, len_serie = flotSeries.length; i_serie < len_serie; i_serie++) {
                var serie = flotSeries[i_serie];
                if (serie.tempYaxis >= 0) {
                    serie.yaxis = _.findWhere(self.DataTypes, { type: serie.tempYaxis }).axeNumber;
                }
            }
        }
        //#endregion

        var colors = self.ChartOptions.colors;
        for (var i_dt = 0; i_dt < self.DataTypesLength; i_dt++) {
            self.DataTypes[i_dt].Color(colors.length > i_dt ? colors[i_dt] : colors[0]);
        }
        self.building = false;
    };

    self.SelectZoneChart = function () {
        var currentSelection = -1;
        var firstAvailable = -1;
        for (var i_dt = 0; i_dt < self.ZonesChartTypesLength; i_dt++) {
            var dt = self.ZonesChartTypes[i_dt];
            if (dt.Available()) {
                if (firstAvailable < 0) {
                    firstAvailable = i_dt;
                }
                if (dt.showInZones()) {
                    currentSelection = i_dt;
                }
            }
            dt.showInZones(false);
        }
        if (currentSelection >= 0) {
            self.ZonesChartTypes[currentSelection].showInZones(true);
        } else if (firstAvailable >= 0) {
            self.ZonesChartTypes[firstAvailable].showInZones(true);
        }
    }

    self.DrawChart = function () {
        if (self.ServerModel && self.ServerModelPreprocessed) {

            if (!self.placeholder) {
                self.placeholder = $('.multiChartContainer');
            } else {
                if (_.isString(self.placeholder)) self.placeholder = $(self.placeholder);
            }
            if (!self.placeholder.length) {
                $('body').css('cursor', 'default');
                return;
            }

            if (self.showMini && !self.MiniIsValid) {
                if (!self.miniPlaceholder) {
                    self.miniPlaceholder = $('.miniChartContainer');
                    if (!self.miniPlaceholder.length) {
                        self.miniPlaceholder = $('<div class="miniChartContainer"></div>');
                        self.miniPlaceholder.insertAfter(self.placeholder);
                    }
                } else {
                    if (_.isString(self.miniPlaceholder)) self.miniPlaceholder = $(self.miniPlaceholder);
                }
                if (!self.miniPlaceholder.length) self.showMini = false;
            }

            self.BuildChartOptionsAndData();

            //fixColors(self.ChartData);

            self.BuildHrZones();
            self.BuildPowerZones();

            self.recalculateTimeWindowStats();
            self.SelectZoneChart();

            self.Chart = $.plot(self.placeholder, self.ChartData, self.ChartOptions);

            fixColors2(self.Chart, self.ChartData);

            self.SetIcons();

            self.AddXButtons();

            self.BindHover();

            self.SetMiniChart();

            self.MeasureMode('none');

            self.DrawZonesCharts();

            $('body').css('cursor', 'default');
        }
    };


    self.DrawZonesCharts = function () {
        self.DrawZonesChart('.zonesChartContainer');
    };

    self.zoneBarNames = [];

    self.zoneBarTextType = 'time';

    self.DrawZonesChart = function (placeholderName) {
        var zonesPlaceholder = $(placeholderName);
        for (var dataSeriesIndex = 0; dataSeriesIndex < self.ZonesChartTypesLength; dataSeriesIndex++) {
            var zn = self.ZonesChartTypes[dataSeriesIndex];
            var dt = zn.DataType;
            if (typeof dt !== "undefined"
                && dt.Available()
                && zn.showInZones()) {

                self.internalDrawZoneChart(zonesPlaceholder, zn, dt);
            }
        }
    };

    self.internalDrawZoneChart = function (zonesPlaceholder, zn, dt) {

        var chartZonesTool = dt.CurrentTimeWindowStats().chartZonesTools[zn.Mode];
        self.zoneBarNames = chartZonesTool.zoneNames;
        self.zoneBarTextType = (zn.Mode == 'mmp') ? 'watt' : 'time';

        var data = chartZonesTool.GetSeries(dt, chartZonesTool.zoneChartType.Mode == 'custom');
        if (data.series.length > 0) {

            var minX = (zn.Mode == 'mmp') ? data.minX : data.minX - 0.4;
            var maxX = (zn.Mode == 'mmp') ? data.maxX : data.maxX + 0.4;
            var markings = [];
            if (dt.type == chartType_hr && chartZonesTool.zoneChartType.Mode != 'custom') {
                markings = self.BuildHrZonesMarkings(chartZonesTool, minX, maxX);
            } else if (dt.type == chartType_power && chartZonesTool.zoneChartType.Mode != 'custom') {
                markings = self.BuildPowerZonesMarkings(chartZonesTool, minX, maxX);
            }

            var colors = self.ChartOptions.colors;
            var labelColor = (colors.length > dt.type) ? colors[dt.type] : colors[0];

            var colorVct = [];
            if (chartZonesTool.zoneChartType.Mode == 'custom') {
                colorVct = chartZonesTool.zoneColors;
            } else {
                colorVct.push(labelColor);
            }

            var seriesOptions = { shadowSize: 0 };
            if (zn.Mode == 'mmp') {
                seriesOptions['lines'] = {
                    show: true,
                    lineWidth: 1
                };
                seriesOptions['points'] = {
                    show: true,
                    radius: 3,
                    symbol: "circle"
                }
            } else {
                seriesOptions['bars'] = {
                    show: true,
                    align: "center",
                    barWidth: 0.9,
                    lineWidth: 0,
                    labelWidth: 30,
                    fill: true,
                    fillColor: { colors: [{ opacity: 1.0 }, { opacity: 1.0 }] }
                };
            }

            var options = {
                xaxis: {
                    ticks: data.xTicks,
                    min: minX,
                    max: maxX,
                    tickLength: 0,
                    font: { size: 10, color: 'black', family: 'aller_lightregular' }
                },
                yaxis: {
                    min: 0.9 * data.minY,
                    max: 1.1 * data.maxY,
                    labelWidth: 30,
                    tickLength: 0,
                    tickFormatter: function (val, axis) {
                        if (zn.Mode == 'mmp') {
                            return Math.round(val, 0) + "(W)";
                        } else {
                            return Math.round(val, 0) + "%";
                        }

                    },
                    font: { size: 10, color: 'black', family: 'aller_lightregular' }
                },
                series: seriesOptions,
                legend: {
                    show: false
                },
                grid: {
                    show: true,
                    backgroundColor: '#ffffff',
                    borderWidth: 1,
                    hoverable: true,
                    marking: [],
                    markings: markings,
                },
                colors: colorVct,
                hooks: { drawOverlay: [self.DrawLabels] }
            };
            $.plot(zonesPlaceholder, data.series, options);

            if (zn.Mode != 'mmp') {
                var labelText = dt.name() + ' (' + dt.unit() + ')';
                $('.zoneChartUnitLabel span').text(labelText);
            } else {
                $('.zoneChartUnitLabel span').text('');
                var yaxisLabelMain = $("<div class='axisLabel yaxisLabel labelMain'></div>")
                    .text(translations.PowerLevel)
                    .appendTo(zonesPlaceholder);
                // yaxisLabelMain.css("margin-top", yaxisLabelMain.width() / 2 - 20);
            }
            $('.zoneChartUnitLabel').css('color', labelColor);
            zonesPlaceholder.bind("plothover", function (event, pos, item) {

                var tooltipPlaceholder = $(".zonesChart div.tooltip");
                if (item) {
                    if (self.zoneBarTextType == 'watt') {
                        tooltipPlaceholder.text(item.datapoint[1].toFixed(1) + '(W)');
                    } else {
                        tooltipPlaceholder.text(item.datapoint[1].toFixed(1) + '%');
                    }
                    var yToMax = item.datapoint[1] / item.series.yaxis.max;
                    var topOffset = 65.0;
                    var color = 'white';
                    if (self.zoneBarTextType == 'watt') {
                        topOffset = 85.0;
                        color = 'black';
                    } else {
                        if (yToMax < 0.1) {
                            topOffset = 25.0;
                            color = 'black';
                        }
                    }
                    var offset = zonesPlaceholder.offset();
                    var cssOptions = { top: item.pageY - offset.top + topOffset, left: item.pageX - offset.left + 12.5, color: color };
                    if (self.zoneBarTextType == 'watt') {
                        cssOptions["backgroundColor"] = "white";
                    } else {
                        cssOptions["backgroundColor"] = "transparent";
                    }
                    tooltipPlaceholder
                        .css(cssOptions)
                        .fadeIn(200);
                } else {
                    tooltipPlaceholder.stop(true, true).hide();
                }

            });
        }

    }

    self.BuildHrZonesMarkings = function (tool, minX, maxX) {
        var markings = [];
        var maxHr = 220;
        // var minAvHr = 20;
        // var maxAvHr = maxHr;
        if (self.ShowHrZones() && self.ServerModel.ZoneMarkings && self.ServerModel.ZoneMarkings.length) {
            for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
                var serie = self.ServerModel.Series[i_serie];
                if (serie.MaxHr) {
                    maxHr = serie.MaxHr;
                    // maxAvHr = maxHr * 1.1;
                }
            }
            for (var i = 0; i < self.ServerModel.ZoneMarkings.length; i++) {
                var zoneMarkings = self.ServerModel.ZoneMarkings[i];
                if (zoneMarkings.ZoneTypeId == HR_zoneTypeId) {
                    var from = Math.round(maxHr * zoneMarkings.yaxis.from / 100.0);
                    var to = Math.round(maxHr * zoneMarkings.yaxis.to / 100.0);
                    var fromNrm = tool.N * tool.valueToNrm(from) - 0.5;
                    var toNrm = tool.N * tool.valueToNrm(to) - 0.5;
                    if (toNrm > -1.5 && fromNrm < tool.N + 1) {
                        markings.push({ xaxis: { from: fromNrm, to: toNrm }, color: zoneMarkings.color });
                    }
                }
            }
        }
        if (markings.length > 0) {
            markings[0].xaxis.from = Math.min(markings[0].xaxis.from, minX);
            markings[markings.length - 1].xaxis.to = Math.max(markings[markings.length - 1].xaxis.to, maxX);
        }
        return markings;
    };
    self.BuildPowerZonesMarkings = function (tool, minX, maxX) {
        var markings = [];
        var ftp = 0;
        if (self.ShowPowerZones() && self.ServerModel.ZoneMarkings && self.ServerModel.ZoneMarkings.length) {
            for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
                var serie = self.ServerModel.Series[i_serie];
                if (serie.FTP) {
                    ftp = serie.FTP;
                    // maxAvHr = maxHr * 1.1;
                }
            }
            if (ftp > 0) {
                for (var i = 0; i < self.ServerModel.ZoneMarkings.length; i++) {
                    var zoneMarkings = self.ServerModel.ZoneMarkings[i];
                    if (zoneMarkings.ZoneTypeId == POWER_zoneTypeId) {
                        var from = Math.round(ftp * zoneMarkings.yaxis.from / 100.0);
                        var to = Math.round(ftp * zoneMarkings.yaxis.to / 100.0);
                        var fromNrm = tool.N * tool.valueToNrm(from) - 0.5;
                        var toNrm = tool.N * tool.valueToNrm(to) - 0.5;
                        if (toNrm > -1.5 && fromNrm < tool.N + 1) {
                            markings.push({ xaxis: { from: fromNrm, to: toNrm }, color: zoneMarkings.color });
                        }
                    }
                }
            }
        }
        if (markings.length > 0) {
            markings[0].xaxis.from = Math.min(markings[0].xaxis.from, minX);
            markings[markings.length - 1].xaxis.to = Math.max(markings[markings.length - 1].xaxis.to, maxX);
        }
        return markings;
    };

    self.DrawLabels = function (plot, canvascontext) {

        if (self.zoneBarTextType == 'watt') {
            return;
        }

        var plotOffset = plot.getPlotOffset();

        var ctx = canvascontext;
        ctx.save();
        ctx.translate(plotOffset.left, plotOffset.top);

        var labelIndex = 0;

        var data = plot.getData();
        for (var n = 0; n < data.length; n++) {
            var series = data[n];

            var ps = series.datapoints.pointsize;
            var ax = [series.xaxis, series.yaxis];
            var points = series.datapoints.points;
            for (var i = 0; i < points.length; i += ps) {
                //data coordinates
                var x = points[i],
                    y = points[i + 1];
                if (y > 1e-3) {
                    var xPixels = ax[0].p2c(x);
                    var yPixels = ax[1].p2c(y);
                    ctx.fillStyle = 'black';
                    ctx.font = "10px aller_lightregular";
                    ctx.textAlign = "center";
                    if (self.zoneBarTextType == 'watt') {
                        ctx.fillText(y.toFixed(1), xPixels, yPixels - 10);
                    } else {
                        ctx.fillText(self.formatTime(self.duration * (y / 100.0)), xPixels, yPixels - 10);
                    }
                } else {
                    var xPixels = ax[0].p2c(x);
                    var yPixels = ax[1].p2c(y);
                    ctx.fillStyle = 'black';
                    ctx.font = "10px aller_lightregular";
                    ctx.textAlign = "center";
                    if (self.zoneBarTextType == 'watt') {
                        ctx.fillText('0.0', xPixels, yPixels - 10);
                    } /*else {
                        ctx.fillText('00:00:00', xPixels, yPixels - 10);
                    }*/
                }
                if (self.zoneBarNames && labelIndex < self.zoneBarNames.length) {
                    var zoneName = self.zoneBarNames[labelIndex];
                    var parts = zoneName.split(" ");
                    ctx.rotate(-Math.PI / 2);
                    ctx.font = "12px aller_lightregular";
                    /*ctx.textAlign = "center";
                    var offset = 10 + parts.length*10;
                    for (var p = 0, len = parts.length; p < len; p++) {
                        ctx.fillText(parts[p], xPixels, yPixels - offset);
                        offset += 10;
                    }*/

                    // x ujemny  -> w dol
                    // x dodatni -> w gore
                    // y dodatni -> w prawo
                    // y ujemny  -> w lewo
                    var offset = 0.0;
                    if (y / series.yaxis.max <= 0.5) {
                        ctx.textAlign = "left";
                        offset = 40.0;
                        ctx.fillStyle = 'black';
                    } else {
                        ctx.textAlign = "right";
                        offset = -40.0;
                        ctx.fillStyle = 'white';
                    }
                    ctx.fillText(zoneName, offset - yPixels, xPixels);
                    ctx.rotate(Math.PI / 2);
                }
                labelIndex++;
            }
        }
        ctx.restore();
    };

    //#region zoom/mini
    self.ClearChbSelections = function () {
        $(".showSelectOnChart .smalChb").each(function (idx, elem) {
            var mod = ko.dataFor(elem);
            mod.SelectedOnChart(false);
        });
    }
    self.ResetZoom = function () {
        self.rangeFrom = null;
        self.rangeTo = null;
        if (self.ranges) {
            self.ranges.xaxis.from = 0;
            self.ranges.xaxis.to = self.MaxX();
            var yAxes = self.Chart.getYAxes();
            $.each(yAxes, function (_, axis) {
                var opts = axis.options;
                var n = axis.n == 1 ? '' : axis.n;
                n = 'y' + n + 'axis';
                var yRange = self.ranges[n];
                if (yRange) {
                    yRange.from = opts.orgMin;
                    yRange.to = opts.orgMax;
                }
            });

            self.Chart.setSelection(self.ranges);
        }

        self.ClearChbSelections();

        self.Chart.clearSelection();
        self.MiniChart.clearSelection();
        self.DefferedDraw();
        self.ShowZoomReset(false);
    };

    //#region mini controls
    self.mouseUpHandler = null;
    self.savedhandlers = {};

    self.anchorMoveInfo = { draggingLeft: false, draggingRight: false, draggingMiddle: false, startPoint: null, prevPoint: null, dragElem: null, moveStart: false, leftAnchorValue: null, rightAnchorValue: null };
    self.anchorMouseDown = function (e, type) {
        if (e.which != 1)  // only accept left-click
            return;

        // cancel out any text selections
        document.body.focus();

        // prevent text selection and drag in old-school browsers
        if (document.onselectstart !== undefined && self.savedhandlers.onselectstart == null) {
            self.savedhandlers.onselectstart = document.onselectstart;
            document.onselectstart = function () { return false; };
        }
        if (document.ondrag !== undefined && self.savedhandlers.ondrag == null) {
            self.savedhandlers.ondrag = document.ondrag;
            document.ondrag = function () { return false; };
        }

        if (type == 'left') {
            self.anchorMoveInfo.draggingLeft = true;
            self.anchorMoveInfo.dragElem = self.miniPlaceholder.find('.left.anchor');
        } else if (type == 'right') {
            self.anchorMoveInfo.draggingRight = true;
            self.anchorMoveInfo.dragElem = self.miniPlaceholder.find('.right.anchor');
        } else if (type == 'middle') {
            self.anchorMoveInfo.draggingMiddle = true;
            self.anchorMoveInfo.dragElem = self.miniPlaceholder.find('.middle.anchor');
        }
        self.anchorMoveInfo.moveStart = false;
        self.anchorMoveInfo.startPoint = e.pageX;
        self.anchorMoveInfo.prevPoint = e.pageX;
        //self.selection.first.x = e.pageX;

        self.anchorMoveInfo.leftAnchorValue = self.miniPlaceholder.find('.left.anchor.value');
        self.anchorMoveInfo.rightAnchorValue = self.miniPlaceholder.find('.right.anchor.value');

        // this is a bit silly, but we have to use a closure to be
        // able to whack the same handler again
        self.mouseUpHandler = function (e) { self.anchorMouseUp(e); };
        $(document).one("mouseup", self.mouseUpHandler);

        if (e && e.stopPropagation) {
            e.stopPropagation();
        } else {
            e = window.event;
            e.cancelBubble = true;
        }

        return false;
    }

    self.leftAnchorMouseDown = function (e) {
        return self.anchorMouseDown(e, 'left');
    }
    self.rightAnchorMouseDown = function (e) {
        return self.anchorMouseDown(e, 'right');
    }
    self.middleAnchorMouseDown = function (e) {
        return self.anchorMouseDown(e, 'middle');
    }

    self.anchorMoveUpdateTimeOut = null;
    self.coreAnchorMoveUpdate = function (e, onMini) {
        if (self.anchorMoveUpdateTimeOut) clearTimeout(self.anchorMoveUpdateTimeOut);
        //update 
        if (self.anchorMoveInfo.moveStart) {
            var totalDiff = e.pageX - self.anchorMoveInfo.startPoint;
            var o = self.MiniChart.pointOffset({ x: self.MaxX(), y: 0 });
            var multip = self.MaxX() / o.left;
            var shift = totalDiff * multip;

            if (shift != 0) {
                var ranges = jQuery.extend(true, {}, self.ranges);

                if (self.anchorMoveInfo.draggingLeft) {
                    ranges.xaxis.from = ranges.xaxis.from + shift;
                }
                if (self.anchorMoveInfo.draggingRight) {
                    ranges.xaxis.to = ranges.xaxis.to + shift;
                }
                if (self.anchorMoveInfo.draggingMiddle) {
                    ranges.xaxis.from = ranges.xaxis.from + shift;
                    ranges.xaxis.to = ranges.xaxis.to + shift;
                }

                if (ranges.xaxis.from < 0) ranges.xaxis.from = 0;
                if (ranges.xaxis.to > self.MaxX()) ranges.xaxis.to = self.MaxX();

                if (onMini) {
                    self.MiniChart.setSelection(ranges, true);

                    //#region value tooltips
                    self.anchorMoveInfo.leftAnchorValue.show();
                    self.anchorMoveInfo.rightAnchorValue.show();

                    self.anchorMoveInfo.leftAnchorValue.html(self.X_time() ? MsToString(ranges.xaxis.from) : MathRoundTo(ranges.xaxis.from, 2));
                    self.anchorMoveInfo.rightAnchorValue.html(self.X_time() ? MsToString(ranges.xaxis.to) : MathRoundTo(ranges.xaxis.to, 2));

                    var lx = self.MiniChart.pointOffset({ x: ranges.xaxis.from, y: 0 }).left - 10;
                    var rx = self.MiniChart.pointOffset({ x: ranges.xaxis.to, y: 0 }).left - 10;
                    self.anchorMoveInfo.leftAnchorValue.css('left', lx + 'px');
                    self.anchorMoveInfo.rightAnchorValue.css('left', rx + 'px');

                    if (rx - lx < 30 && rx - lx > -30) {
                        self.miniPlaceholder.find('.middle.anchor').hide();
                    } else {
                        self.miniPlaceholder.find('.middle.anchor').show();
                    }

                    //#endregion
                } else {
                    self.Chart.setSelection(ranges);
                    self.anchorMoveInfo.leftAnchorValue.hide();
                    self.anchorMoveInfo.rightAnchorValue.hide();
                }
                self.ClearChbSelections();
            }
        }
    }
    self.getValidRangeValue = function (value) {
        if (value < 0) value = 0;
        if (value > self.MaxX()) value = self.MaxX();
        return value;
    }
    self.anchorMouseMove = function (e) {
        if (self.anchorMoveInfo.draggingLeft || self.anchorMoveInfo.draggingRight || self.anchorMoveInfo.draggingMiddle) {
            self.anchorMoveInfo.moveStart = true;
            if (self.anchorMoveInfo.dragElem) {
                var currX = e.pageX;
                var diff = e.pageX - self.anchorMoveInfo.prevPoint;
                self.anchorMoveInfo.prevPoint = e.pageX;
                self.anchorMoveInfo.dragElem.css('left', (self.anchorMoveInfo.dragElem.position().left + diff) + 'px');
            }

            if (self.anchorMoveUpdateTimeOut) clearTimeout(self.anchorMoveUpdateTimeOut);
            self.anchorMoveUpdateTimeOut = setTimeout(function () {
                self.coreAnchorMoveUpdate(e, true);
            }, 10);
        }
    }
    self.anchorMouseUp = function (e) {
        self.mouseUpHandler = null;

        // revert drag stuff for old-school browsers
        if (document.onselectstart !== undefined)
            document.onselectstart = self.savedhandlers.onselectstart;
        if (document.ondrag !== undefined)
            document.ondrag = self.savedhandlers.ondrag;

        self.coreAnchorMoveUpdate(e);

        self.anchorMoveInfo.draggingLeft = false;
        self.anchorMoveInfo.draggingRight = false;
        self.anchorMoveInfo.draggingMiddle = false;

        return false;
    }
    self.anchorStepMouseUp = function (e, type) {
        if (!self.anchorMoveInfo.moveStart) {
            var shift = self.MaxX() / 20;

            if (type === 'onlyLeft') {
                self.ranges.xaxis.from = self.getValidRangeValue(self.ranges.xaxis.from - shift);
            } else if (type === 'onlyRight') {
                self.ranges.xaxis.to = self.getValidRangeValue(self.ranges.xaxis.to + shift);
            } else if (type === 'left') {
                self.ranges.xaxis.from = self.getValidRangeValue(self.ranges.xaxis.from - shift);
                self.ranges.xaxis.to = self.getValidRangeValue(self.ranges.xaxis.to - shift);
            } else if (type === 'right') {
                self.ranges.xaxis.from = self.getValidRangeValue(self.ranges.xaxis.from + shift);
                self.ranges.xaxis.to = self.getValidRangeValue(self.ranges.xaxis.to + shift);
            }

            self.Chart.setSelection(self.ranges);
        }
    }
    self.anchorLeftStepMouseUp = function (e) {
        self.anchorStepMouseUp(e, 'onlyLeft');
    }
    self.anchorRightStepMouseUp = function (e) {
        self.anchorStepMouseUp(e, 'onlyRight');
    }
    self.anchorMLeftStepMouseUp = function (e) {
        self.anchorStepMouseUp(e, 'left');
    }
    self.anchorMRightStepMouseUp = function (e) {
        self.anchorStepMouseUp(e, 'right');
    }

    self.selectXRange = function (timeStart, timeEnd, distStart, distEnd) {
        if (!self.MiniChart) return;

        self.rangeFrom = self.X_time() ? timeStart * 1000 : distStart;
        self.rangeTo = self.X_time() ? timeEnd * 1000 : distEnd;

        var ranges = jQuery.extend(true, {
            xaxis: { from: 0, to: 0 }
        }, self.ranges);

        ranges.xaxis.from = self.rangeFrom;
        ranges.xaxis.to = self.rangeTo;
        /*
            var yAxes = self.Chart.getYAxes();
            $.each(yAxes, function (_, axis) {
                var opts = axis.options;
                var n = axis.n == 1 ? '' : axis.n;
                n = 'y' + n + 'axis';
                var yRange = ranges[n];
                if (yRange) {
                    yRange.from = opts.orgMin;
                    yRange.to = opts.orgMax;
                }
            });*/

        //self.Chart.setSelection(self.ranges);
        self.MiniChart.setSelection(ranges, false);


        //self.Chart.clearSelection();
        //self.MiniChart.clearSelection();
        self.DefferedDraw();
        self.ShowZoomReset(true);
    }
    self.selectExeXRange = function (exeId) {
        if (!exeId) return;
        var trainingExeBlocks = self.ServerModel.TrainingExcerciseBlocks;
        var planExeBlocks = self.ServerModel.TrainingPlanExcerciseBlocks;

        if (!trainingExeBlocks && !planExeBlocks && !trainingExeBlocks.length && !planExeBlocks.length) return;
        var allBlocs = [];
        allBlocs = trainingExeBlocks.concat(planExeBlocks);

        var selectedExes = _.where(allBlocs, { ExerciseId: exeId });

        var minTime = _.min(selectedExes, function (exe) { return exe.FlotData.t1; }).FlotData.t1;
        var minDist = _.min(selectedExes, function (exe) { return exe.FlotData.d1; }).FlotData.d1;
        var maxTime = _.max(selectedExes, function (exe) { return exe.FlotData.t2; }).FlotData.t2;
        var maxDist = _.max(selectedExes, function (exe) { return exe.FlotData.d2; }).FlotData.d2;

        self.selectXRange(minTime, maxTime, minDist, maxDist)
    }

    //#endregion
    self.copyWidthToMini = function () {
        if (self.placeholder && self.miniPlaceholder) {
            self.miniPlaceholder.width(self.placeholder.width());
        }
    }

    self.mainPlotselected = function (event, ranges) {

        var sholudReload = false;
        if (ranges) {
            if (!self.ranges) {
                sholudReload = true;
            } else {
                var chang = ((ranges.xaxis.to - ranges.xaxis.from) / (self.rangeTo - self.rangeFrom));
                sholudReload = chang < 0.7 || chang > 2;
            }
        }

        self.rangeFrom = ranges.xaxis.from;
        self.rangeTo = ranges.xaxis.to;
        self.ranges = ranges;
        if (self.rangeFrom != 0 || self.rangeTo < self.MaxX()) {
            self.ShowZoomReset(true);
        }

        // do the zooming
        $.each(self.Chart.getXAxes(), function (_, axis) {
            var opts = axis.options;
            opts.min = ranges.xaxis.from;
            opts.max = ranges.xaxis.to;
        });

        var yAxes = self.Chart.getYAxes();
        $.each(yAxes, function (_, axis) {
            var opts = axis.options;
            if (typeof (opts.orgMin) == 'undefined' || opts.orgMin == null) opts.orgMin = opts.min;
            if (typeof (opts.orgMax) == 'undefined' || opts.orgMax == null) opts.orgMax = opts.max;
            if (self.ShowZoomReset()) {
                var n = axis.n == 1 ? '' : axis.n;
                n = 'y' + n + 'axis';
                var yRange = ranges[n];
                if (yRange) {
                    opts.min = yRange.from;
                    opts.max = yRange.to;
                }
                /*
                //#region y min max
                if (!opts.recalculatedHr) {
                    var chartTypeIndex = opts.chartTypeIndex;
                    var selfDataType = self.DataTypes[chartTypeIndex];
                    
                    var min = null;
                    var max = null;
                    var x, y, point;
                    var xTime = self.X_time();
                    if (selfDataType) {
                        var chartTypeAvgIndex = selfDataType.typeAvg;
                        for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
                            var dps = self.ServerModel.DataPoints[i_dps].Points;
                            for (var i_p = 0, i_len = dps.length; i_p < i_len; i_p++) {
                                point = dps[i_p];
                                x = point[xTime ? chartType_Time : chartType_Distance];
                                if (x != null && x >= ranges.xaxis.from && x <= ranges.xaxis.to) {
                                    y = point[chartTypeIndex];
                                    if (y >= opts.orgMin && y <= opts.orgMax) {
                                        if (min == null || min > y) min = y;
                                        if (max == null || max < y) max = y;
                                    }
                                    y = point[chartTypeAvgIndex];
                                    if (y >= opts.orgMin && y <= opts.orgMax) {
                                        if (min == null || min > y) min = y;
                                        if (max == null || max < y) max = y;
                                    }
                                }
                            }
                        }
                    }
                    if (min !== null) {
                       // min = 0.9 * min;
                        opts.min = (opts.orgMin >= min) ? opts.orgMin : min;
                    }
                    if (max !== null) {
                       // max = 1.1 * max;
                        opts.max =(opts.orgMax <= max)?opts.orgMax:max;
                    }
                    if (chartTypeIndex == chartType_hr) {
                        var hrOpts = yAxes[0].options;
                        hrOpts.min = opts.min*100/self.maxHr;
                        hrOpts.max = opts.max * 100 / self.maxHr;
                    }
                    
                }
                //#endregion
                    */
            } else {
                opts.min = opts.orgMin;
                opts.max = opts.orgMax;
            }
        });

        self.Chart.setupGrid();
        self.Chart.draw();
        self.Chart.clearSelection();

        // don't fire event on the overview to prevent eternal loop
        self.MiniChart.setSelection(ranges, true);

        //#region mini controls
        var leftAnchorPoint = self.MiniChart.pointOffset({ x: self.rangeFrom !== null ? self.rangeFrom : 0, y: 0 }).left;
        var rightAnchorPoint = self.MiniChart.pointOffset({ x: self.rangeTo !== null ? self.rangeTo : self.MaxX(), y: 0 }).left;
        var middleAnchorPoint = (leftAnchorPoint + rightAnchorPoint) / 2;
        var showMiddle = (rightAnchorPoint - leftAnchorPoint) > 30;

        var leftAnchor = self.miniPlaceholder.find('.left.anchor');
        if (!leftAnchor.length) {
            self.miniPlaceholder.append('<span class="left anchor"><span class="icon left"></span></span>');
            leftAnchor = self.miniPlaceholder.find('.left.anchor');
            leftAnchor.off("mousedown").on("mousedown", self.leftAnchorMouseDown);
            leftAnchor.find('.icon.left').off("mouseup").on("mouseup", self.anchorLeftStepMouseUp);
        }
        leftAnchor.css('left', leftAnchorPoint + 'px');

        var rightAnchor = self.miniPlaceholder.find('.right.anchor');
        if (!rightAnchor.length) {
            self.miniPlaceholder.append('<span class="right anchor"><span class="icon right"></span></span>');
            rightAnchor = self.miniPlaceholder.find('.right.anchor');
            rightAnchor.off("mousedown").on("mousedown", self.rightAnchorMouseDown);
            rightAnchor.find('.icon.right').off("mouseup").on("mouseup", self.anchorRightStepMouseUp);
        }
        rightAnchor.css('left', rightAnchorPoint + 'px');

        var middleAnchor = self.miniPlaceholder.find('.middle.anchor');
        if (showMiddle) {
            if (!middleAnchor.length) {
                self.miniPlaceholder.append('<span class="middle anchor"><span class="icon middle"></span><span class="icon left"></span><span class="icon right"></span></span>');
                middleAnchor = self.miniPlaceholder.find('.middle.anchor');
                middleAnchor.off("mousedown").on("mousedown", self.middleAnchorMouseDown);
                middleAnchor.find('.icon.left').off("mouseup").on("mouseup", self.anchorMLeftStepMouseUp);
                middleAnchor.find('.icon.right').off("mouseup").on("mouseup", self.anchorMRightStepMouseUp);
            }
            middleAnchor.show();
            middleAnchor.css('left', middleAnchorPoint + 'px');
        } else {
            if (!middleAnchor.length) {
                middleAnchor.hide();
            }
        }

        var leftAnchorValue = self.miniPlaceholder.find('.left.anchor.value');
        if (!leftAnchorValue.length) {
            self.miniPlaceholder.append('<span class="left anchor value"></span>');
            leftAnchorValue = self.miniPlaceholder.find('.left.anchor.value');
        }
        leftAnchorValue.css('left', leftAnchorPoint + 'px');

        var rightAnchorValue = self.miniPlaceholder.find('.right.anchor.value');
        if (!rightAnchorValue.length) {
            self.miniPlaceholder.append('<span class="right anchor value"></span>');
            rightAnchorValue = self.miniPlaceholder.find('.right.anchor.value');
        }
        rightAnchorValue.css('left', rightAnchorPoint + 'px');

        //#endregion

        self.SetIcons();

        self.recalculateTimeWindowStats(self.rangeFrom, self.rangeTo);

        if (sholudReload) {
            self.DefferedDraw();
        };
    }

    self.SetMiniChart = function () {
        if (self.showMini && !self.MiniIsValid) {

            self.copyWidthToMini();
            setTimeout(self.copyWidthToMini, 1000);

            var miniOptions = {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 1
                    },
                    shadowSize: 0,
                    downsample: { threshold: self.baseDownsampleTreshold() === 'auto' ? downsampleTreshold : self.baseDownsampleTreshold() },
                    curvedLines: self.ChartOptions.series.curvedLines
                },
                xaxis: self.X_time() ? getTimeXaxis(self.ServerModel.MaxXTime) : getDistanceXaxis(self.ServerModel.MaxXDistance),
                yaxes: [],
                selection: { mode: "x", minSize: 1 },
                legend: {
                    show: false
                },
                grid: {
                    backgroundColor: '#ffffff',
                    borderWidth: 1,
                    borderColor: '#888',
                    markings: [],
                    hoverable: false
                },
            };

            miniOptions.xaxis.ticks = [];
            for (var i_axe = 0, len_axe = self.ChartOptions.yaxes.length; i_axe < len_axe; i_axe++) {
                var axe = self.ChartOptions.yaxes[i_axe];
                axe.orgMin = axe.min;
                axe.orgMax = axe.max;
                miniOptions.yaxes.push({
                    ticks: [],
                    min: axe.min,
                    max: axe.max,
                    autoscaleMargin: 0.1,
                    show: axe.show,
                    position: axe.position,
                    labelWidth: axe.labelWidth,
                    tickLength: 0,
                    font: { size: 10, color: 'black', family: 'aller_lightregular' }
                });
            }

            self.MiniChart = $.plot(self.miniPlaceholder, self.ChartData, miniOptions);


            if (self.ranges) {
                self.Chart.setSelection(self.ranges);
                self.MiniChart.setSelection(self.ranges, true);
                if (self.rangeFrom != 0 || self.rangeTo < self.MaxX()) {
                    self.ShowZoomReset(true);
                }
            }

            self.miniPlaceholder.off("mousemove").on("mousemove", self.anchorMouseMove);



            // now connect the two
            self.placeholder.off("plotselected").on("plotselected", self.mainPlotselected);

            self.miniPlaceholder.off("plotselected").on("plotselected", function (event, ranges) {
                var sholudReload = false;
                if (ranges) {
                    if (!self.ranges) {
                        sholudReload = true;
                    } else {
                        var chang = ((ranges.xaxis.to - ranges.xaxis.from) / (self.rangeTo - self.rangeFrom));
                        sholudReload = chang < 0.7 || chang > 2;
                    }
                }

                self.rangeFrom = ranges.xaxis.from;
                self.rangeTo = ranges.xaxis.to;
                self.ranges = ranges;
                self.Chart.setSelection(ranges);
                self.MiniChartSelecting = true;
                if (self.rangeFrom != 0 || self.rangeTo < self.MaxX()) self.ShowZoomReset(true);

                if (sholudReload) {
                    self.DefferedDraw();
                };
            });

            self.MiniIsValid = true;
        }

        if (self.showMini && self.MiniIsValid) {
            if (self.ranges && self.Chart && self.MiniChart) {
                self.Chart.setSelection(self.ranges);
                self.Chart.clearSelection();
                self.MiniChart.setSelection(self.ranges, true);
                if (self.rangeFrom != 0 || self.rangeTo < self.MaxX()) {
                    self.ShowZoomReset(true);
                } else {
                    self.MiniChart.clearSelection();
                }
            }
        }
    };


    self.StoreChartFragment = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Chart/StoreChartFragment",
            data: {
                trainingId: self.id,
                start:Math.round( self.rangeFrom/1000),
                end: Math.round(self.rangeTo / 1000)
            }
        })
       .done(function (result) {

       });
    }
    //#endregion

    //#region chart elements
    self.BuildHrZones = function () {
        if (!self.ShowHrZones()) return;
        if (!self.ServerModel.ZoneMarkings || !self.ServerModel.ZoneMarkings.length) return;
        var hrZones = _.filter(self.ServerModel.ZoneMarkings, function (z) { return z.ZoneTypeId == HR_zoneTypeId; });
        if (!hrZones || !hrZones.length) return;

        var halfHeight = self.ShowPowerZones();
        var width = self.placeholder.width();
        var nrOfMarks = width / 10;
        var zoneWidth = (self.MaxX()) / nrOfMarks;

        var _zones = _(hrZones);

        var xTime = self.X_time();

        var maxHr = 220;
        var minAvHr = 20;
        var maxAvHr = maxHr;

        for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
            var serie = self.ServerModel.Series[i_serie];
            if (serie.MaxHr) {
                maxHr = serie.MaxHr;
                maxAvHr = maxHr * 1.1;
            }
        }


        var zoneFind = function (z) {
            if (xTime) {
                return (typeof (z.xaxis) == 'undefined' || !z.xaxis || (z.xaxis.fromTime <= x0 && z.xaxis.toTime >= x0)) && z.yaxis.from <= y && z.yaxis.to >= y;
            } else {
                return (typeof (z.xaxis) == 'undefined' || !z.xaxis || (z.xaxis.fromDist <= x0 && z.xaxis.toDist >= x0)) && z.yaxis.from <= y && z.yaxis.to >= y;
            }
        };

        for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
            var dps = self.ServerModel.DataPoints[i_dps];

            var x0 = null;
            var ySum = 0;
            var prevX = null;

            var last_i = dps.Points.length - 1;
            for (var i_dp = 0, len_dp = dps.Points.length; i_dp < len_dp; i_dp++) {
                var point = dps.Points[i_dp];
                var x = xTime ? point[chartType_Time] : point[chartType_Distance];
                var value = point[chartType_hr];
                if (x != null && value >= minAvHr && value <= maxAvHr) {
                    if (x0 == null) {
                        if (prevX == null) {
                            prevX = x;
                        }
                        x0 = prevX;
                    }

                    if (prevX != null) {
                        ySum += (x - prevX) * value;
                    }
                    if ((x - x0) > zoneWidth || i_dp == last_i) {
                        var y = ySum / (x - x0) * 100 / maxHr;

                        var zone = _zones.find(zoneFind);

                        if (zone) {
                            var mark = { xaxis: { from: x0, to: x }, color: zone.color };
                            if (halfHeight) {
                                mark.yaxis = { from: 0, to: 50 };
                            }
                            self.ChartOptions.grid.markings.push(mark);
                        }

                        x0 = null;
                        ySum = 0;
                    }

                    prevX = x;
                }
            }
        }
    };

    self.BuildPowerZones = function () {
        if (!self.ShowPowerZones()) return;
        if (!self.ServerModel.ZoneMarkings || !self.ServerModel.ZoneMarkings.length) return;
        var powerZones = _.filter(self.ServerModel.ZoneMarkings, function (z) { return z.ZoneTypeId == POWER_zoneTypeId; });
        if (!powerZones || !powerZones.length) return;

        var halfHeight = self.ShowHrZones();
        var width = self.placeholder.width();
        var nrOfMarks = width / 10;
        var zoneWidth = (self.MaxX()) / nrOfMarks;

        var _zones = _(powerZones);

        var xTime = self.X_time();

        var ftp = 0;
        //var minAvHr = 20;
        //var maxAvHr = maxHr;

        for (var i_serie = 0, len_serie = self.ServerModel.Series.length; i_serie < len_serie; i_serie++) {
            var serie = self.ServerModel.Series[i_serie];
            if (serie.FTP) {
                ftp = serie.FTP;
                break;
                //maxAvHr = maxHr * 1.1;
            }
        }


        var zoneFind = function (z) {
            if (xTime) {
                return (typeof (z.xaxis) == 'undefined' || !z.xaxis || (z.xaxis.fromTime <= x0 && z.xaxis.toTime >= x0)) && z.yaxis.from <= y && z.yaxis.to >= y;
            } else {
                return (typeof (z.xaxis) == 'undefined' || !z.xaxis || (z.xaxis.fromDist <= x0 && z.xaxis.toDist >= x0)) && z.yaxis.from <= y && z.yaxis.to >= y;
            }
        };

        for (var i_dps = 0, len_dps = self.ServerModel.DataPoints.length; i_dps < len_dps; i_dps++) {
            var dps = self.ServerModel.DataPoints[i_dps];

            var x0 = null;
            var ySum = 0;
            var prevX = null;

            var last_i = dps.Points.length - 1;
            for (var i_dp = 0, len_dp = dps.Points.length; i_dp < len_dp; i_dp++) {
                var point = dps.Points[i_dp];
                var x = xTime ? point[chartType_Time] : point[chartType_Distance];
                var value = point[chartType_power];
                if (x != null && value) {
                    if (x0 == null) {
                        if (prevX == null) {
                            prevX = x;
                        }
                        x0 = prevX;
                    }

                    if (prevX != null) {
                        ySum += (x - prevX) * value;
                    }
                    if ((x - x0) > zoneWidth || i_dp == last_i) {
                        var y = ySum / (x - x0) * 100 / ftp;

                        var zone = _zones.find(zoneFind);

                        if (zone) {
                            var mark = { xaxis: { from: x0, to: x }, color: zone.color };
                            if (halfHeight) {
                                mark.yaxis = { from: 50, to: 100 };
                            }
                            self.ChartOptions.grid.markings.push(mark);
                        }

                        x0 = null;
                        ySum = 0;
                    }

                    prevX = x;
                }
            }
        }
    };

    self.AddActivityIcons = function () {
        self.placeholder.find('span.disc_30').remove();
        var icons = self.ServerModel.Icons;

        if (icons.length > 1) {

            var minX = self.rangeFrom ? self.rangeFrom : 0;
            var maxX = self.rangeTo ? self.rangeTo : self.MaxX();

            for (var i = 0, len = icons.length; i < len; i++) {
                var x = self.X_time() ? icons[i].Time : icons[i].Distance;
                if (x >= minX && x <= maxX) {
                    var o = self.Chart.pointOffset({ x: x, y: 0 });
                    self.placeholder.append("<span class='" + icons[i].Icon + " disc_30' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
                }
            }
        }
    };

    self.SetLaps = function () {
        self.placeholder.find('div.chartLap').remove();
        setLaps({
            laps: self.ServerModel.Laps,
            maxX: self.rangeTo ? self.rangeTo : self.MaxX(),
            minX: self.rangeFrom ? self.rangeFrom : 0,
        },
                self.Chart,
                self.placeholder,
                !self.X_time(),
                self.RefreshMapCurrentPositionCallback
          );
    };

    self.SetIcons = function () {
        self.SetLaps();
        self.AddActivityIcons();
    };

    self.AddXButtons = function () {
        var o = self.Chart.pointOffset({ x: 0, y: 0 });
        var content = '<div class="xLabelsSwich" style="left: ' + (o.left) + 'px">' +
                    '<span class="hover czas2_24" data-bind="css: { selected: X_time }, click: function () { X_time(true); }" title="' + translations.Time + '"></span>';
        if (self.CanBeDistance) {
            content += '<span class="hover dystans2_22" data-bind="css: { selected: X_distance }, click: function () { X_distance(true); }" title="' + translations.Distance + '"></span>';
        }
        content += '</div>';

        self.placeholder.append(content);
        var elem = self.placeholder.find('.xLabelsSwich')[0];
        ko.cleanNode(elem);
        ko.applyBindings(self, elem);
    }
    //#endregion

    //#region hoover/tooltip
    self.TooltipDataIndex = null;
    self.TooltipTimeOut = null;
    self.HideHoverTooltip = function () {
        $("#tooltip").remove();
        if (self.RefreshMapCurrentPositionCallback) {
            self.RefreshMapCurrentPositionCallback(null);
        }
        if (self.TooltipTimeOut) clearTimeout(self.TooltipTimeOut);
    }
    self.BindHover = function () {
        var previousPoint = null;
        self.placeholder.off("mouseout").on("mouseout", self.HideHoverTooltip);
        self.placeholder.off("plothover").on("plothover", function (event, pos, item) {

            var xTime = self.X_time();
            var pointX = pos.x;
            var timePoint = xTime ? pointX : -1;
            var distancePoint = xTime ? -1 : pointX;
            var maxX = xTime ? self.ServerModel.MaxXTime : self.ServerModel.MaxXDistance;

            if (pointX < 0 || pointX > maxX) {
                self.HideHoverTooltip();
                return;
            }

            var tid = 0;
            if (item) {
                var cid = item.series.serieId;
                if (cid) tid = cid.substr(cid.lastIndexOf('_') + 1);
            }

            var datas = [];
            for (var i_dt = 0; i_dt < self.DataTypesLength; i_dt++) {
                var dt = self.DataTypes[i_dt];
                if (dt.Available()) {
                    var data = null;
                    data = { val: null, index: dt.type, avg: false };
                    datas.push(data);

                    avgData = { val: null, index: dt.typeAvg, avg: true };
                    if (data) data.avgData = avgData;
                    datas.push(avgData);
                }
            }
            var _datas = _(datas);
            var distanceDataAvailable = self.DataTypes[chartType_speed].Available(); //distance


            var dataPointsIdx = 0;
            while ((timePoint < 0 || (distancePoint < 0 && distanceDataAvailable)) && dataPointsIdx < self.ServerModel.DataPoints.length) {
                var pointSerie = self.ServerModel.DataPoints[dataPointsIdx++]
                if (!pointSerie) continue;
                var points = pointSerie.Points;

                var index = 0
                while ((timePoint < 0 || (distancePoint < 0 && distanceDataAvailable)) && index < points.length) {
                    var point = points[index];
                    var indexX = xTime ? point[chartType_Time] : point[chartType_Distance];
                    if (indexX != null && indexX >= pointX) {
                        if (timePoint < 0) timePoint = point[chartType_Time];
                        if (distanceDataAvailable && distancePoint < 0) distancePoint = point[chartType_Distance];
                    }
                    index++;
                }
            }


            if (distancePoint >= 0 && self.RefreshMapCurrentPositionCallback) {
                self.RefreshMapCurrentPositionCallback(distancePoint);
            }

            $("#tooltip").remove();
            if (self.TooltipTimeOut) clearTimeout(self.TooltipTimeOut);
            self.TooltipTimeOut = setTimeout(function () {

                var dpoints, dpointsLength;

                var series = self.Chart.getData();
                for (var i = 0; i < series.length; ++i) { //dla kazdej lini na wykresie
                    var serie = series[i];
                    if (typeof (serie.chartTypeIndex) != 'undefined') {
                        var dt = _datas.findWhere({ index: serie.chartTypeIndex });
                        if (dt) {
                            var dti = 1;
                            //lec po danych i przypisz y z punktu ktory jako pierwszy ma >= x
                            dpoints = serie.datapoints.points;
                            dpointsLength = dpoints.length;
                            var step = (serie.chartTypeIndex == 2 || serie.chartTypeIndex == 11) ? 3 : 2;
                            while (dt.val === null && dti < dpointsLength) {
                                if (dpoints[dti - 1] >= pointX) { //dpoints to jednowymiarowa tablica z wartosciami: x,y,x,y,....
                                    dt.val = dpoints[dti];
                                }
                                dti += step;
                            }
                        }
                    }
                }



                //#region build tooltip
                var maxLeft = self.Chart.pointOffset({ x: self.MaxX(), y: 0 }).left;
                var sideTresh = maxLeft - 300;

                var o = self.Chart.pointOffset({ x: pointX, y: 0 });

                var lapHtml = "<div class='multiChartTooltip ";
                lapHtml += (o.left < sideTresh) ? 'right' : 'left';
                lapHtml += "'><div class='info'>";

                var insertRowFnc = function (iconFile, value, avgValue, unit) {
                    if (value || avgValue) {
                        if (value && _.isNumber(value)) value = Math.round(value * 10) / 10;
                        if (value === null) value = '';
                        var content = dotToColon(value);
                        if (avgValue) {
                            if (_.isNumber(avgValue)) avgValue = Math.round(avgValue * 10) / 10;
                            content += '/' + dotToColon(avgValue);
                        }
                        if (unit) {
                            content += ' ' + unit;
                        }

                        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/' + iconFile + '.png" /></div><div class="value">' + content + '</div></div>';
                    }
                }
                var insertRowFncWrap = function (iconFile, index, unit) {
                    var data = _datas.findWhere({ index: index });
                    if (data) {
                        insertRowFnc(iconFile, data.val, data.avgData.val, unit);
                    }
                }

                if (timePoint >= 0) {
                    insertRowFnc('cz_rozpocz_32', MsToString(timePoint));
                }
                if (distancePoint >= 0) {
                    insertRowFnc('dystans_32', distancePoint, null, 'km');
                }

                var data = _datas.findWhere({ index: chartType_hr });
                if (data && (data.val || data.avgData.val)) {
                    var serieInfo = _(self.ServerModel.Series).find(function (elem) { return (!tid || elem.TrainingId == tid) && elem.MaxHr > 0 });
                    if (serieInfo) {
                        insertRowFnc('serce_przerywane_32', data.val > 0 ? Math.round(data.val * serieInfo.MaxHr / 100) : '', Math.round(data.avgData.val * serieInfo.MaxHr / 100), 'bpm');
                        insertRowFnc('serce_przerywane_32', data.val > 0 ? Math.round(data.val) : '', Math.round(data.avgData.val), '%HRmax');
                    }
                }

                var data = _datas.findWhere({ index: chartType_speed });
                if (data) {
                    if (self.paceParams && self.paceParams.unit) {
                        insertRowFnc('tempo_30',
                            calculatePaceString(data.val, self.paceParams.unit, self.paceParams.factor, false, false),
                            calculatePaceString(data.avgData.val, self.paceParams.unit, self.paceParams.factor, false, false),
                            self.paceParams.unit);
                    } else {
                        insertRowFnc('predkosc_30', data.val, data.avgData.val, 'km/h');
                    }
                }
                insertRowFncWrap('kadencja_30', chartType_cadence, 'rpm');

                var ftp = self.FTP();
                if (ftp) {
                    var data = _datas.findWhere({ index: chartType_power });
                    if (data && (data.val || data.avgData.val)) {
                        insertRowFnc('moc_16', data.val > 0 ? Math.round(data.val) : '', Math.round(data.avgData.val), 'W');
                        insertRowFnc('moc_16', data.val > 0 ? Math.round(100.0 * data.val / ftp) : '', Math.round(100.0 * data.avgData.val / ftp), '%FTP');
                    }
                } else {
                    insertRowFncWrap('moc_16', chartType_power, 'W');
                }


                insertRowFncWrap('wysokosc_25', chartType_altitude, 'm');

                //insertRowFncWrap('wysokosc_25', chartType_altitude, 'm');


                lapHtml += "</div></div>";
                //#endregion

                $('<div id="tooltip">' + lapHtml + '</div>').css({
                    position: 'absolute',
                    display: 'none',
                    top: pos.pageY - 10,
                    left: pos.pageX + 5,
                    'z-index': 9999
                }).appendTo("body").fadeIn(200);

            }, 75);


            /*
        else {
            $("#tooltip").remove();
            if (self.RefreshMapCurrentPositionCallback) {
                self.RefreshMapCurrentPositionCallback(null);
            }
            if (self.TooltipTimeOut) clearTimeout(self.TooltipTimeOut);
            self.TooltipDataIndex = null;
        }*/
        });
    };
    //#endregion

    //#region measuring

    self.measurePlotselected = function (event, ranges) {
        var yAxes = self.Chart.getYAxes();
        var ranges2 = self.Chart.getSelection(true);
        self.recalculateTimeWindowStats(ranges.xaxis.from, ranges.xaxis.to, ranges2);
    }

    self.MeasureMode.subscribe(function (newValue) {
        self.Chart.clearSelection(true);
        if (self.MeasureMode() == 'none') {
            self.placeholder.off("plotselected").on("plotselected", self.mainPlotselected);
            self.ChartOptions.selection = { mode: "xy", minSize: 1, shape: "round" };
            self.Chart.getOptions().selection = { mode: "xy", minSize: 1, shape: "round" };//shape: "round" or "miter" or "bevel"
        } else {
            var shape = "lineXY";
            if (self.MeasureMode() == 'horizontal') shape = "lineX";
            if (self.MeasureMode() == 'vertical') shape = "lineY";
            self.placeholder.off("plotselected").on("plotselected", self.measurePlotselected);
            self.Chart.getOptions().selection = { mode: "xy", minSize: 0, shape: shape, color: 'rgb(35, 148, 211)' };
        }
    });


    //#endregion



    self.Show = function () {
        self.GetModel(self.DrawChart);
    }

    self.RefreshMapCurrentPositionCallback = null;
};;/* Javascript plotting library for jQuery, version 0.8.1.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

*/// first an inline dependency, jquery.colorhelpers.js, we inline it here
// for convenience
/* Plugin for jQuery for working with colors.
 *
 * Version 1.1.
 *
 * Inspiration from jQuery color animation plugin by John Resig.
 *
 * Released under the MIT license by Ole Laursen, October 2009.
 *
 * Examples:
 *
 *   $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
 *   var c = $.color.extract($("#mydiv"), 'background-color');
 *   console.log(c.r, c.g, c.b, c.a);
 *   $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
 *
 * Note that .scale() and .add() return the same modified object
 * instead of making a new one.
 *
 * V. 1.1: Fix error handling so e.g. parsing an empty string does
 * produce a color rather than just crashing.
 */(function(e){e.color={},e.color.make=function(t,n,r,i){var s={};return s.r=t||0,s.g=n||0,s.b=r||0,s.a=i!=null?i:1,s.add=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]+=t;return s.normalize()},s.scale=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]*=t;return s.normalize()},s.toString=function(){return s.a>=1?"rgb("+[s.r,s.g,s.b].join(",")+")":"rgba("+[s.r,s.g,s.b,s.a].join(",")+")"},s.normalize=function(){function e(e,t,n){return t<e?e:t>n?n:t}return s.r=e(0,parseInt(s.r),255),s.g=e(0,parseInt(s.g),255),s.b=e(0,parseInt(s.b),255),s.a=e(0,s.a,1),s},s.clone=function(){return e.color.make(s.r,s.b,s.g,s.a)},s.normalize()},e.color.extract=function(t,n){var r;do{r=t.css(n).toLowerCase();if(r!=""&&r!="transparent")break;t=t.parent()}while(!e.nodeName(t.get(0),"body"));return r=="rgba(0, 0, 0, 0)"&&(r="transparent"),e.color.parse(r)},e.color.parse=function(n){var r,i=e.color.make;if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(n))return i(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(n))return i(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));var s=e.trim(n).toLowerCase();return s=="transparent"?i(255,255,255,0):(r=t[s]||[0,0,0],i(r[0],r[1],r[2]))};var t={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery),function(e){function n(t,n){var r=n.children("."+t)[0];if(r==null){r=document.createElement("canvas"),r.className=t,e(r).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(n);if(!r.getContext){if(!window.G_vmlCanvasManager)throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");r=window.G_vmlCanvasManager.initElement(r)}}this.element=r;var i=this.context=r.getContext("2d"),s=window.devicePixelRatio||1,o=i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1;this.pixelRatio=s/o,this.resize(n.width(),n.height()),this.textContainer=null,this.text={},this._textCache={}}function r(t,r,s,o){function E(e,t){t=[w].concat(t);for(var n=0;n<e.length;++n)e[n].apply(this,t)}function S(){var t={Canvas:n};for(var r=0;r<o.length;++r){var i=o[r];i.init(w,t),i.options&&e.extend(!0,a,i.options)}}function x(n){e.extend(!0,a,n),n&&n.colors&&(a.colors=n.colors),a.xaxis.color==null&&(a.xaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.yaxis.color==null&&(a.yaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.xaxis.tickColor==null&&(a.xaxis.tickColor=a.grid.tickColor||a.xaxis.color),a.yaxis.tickColor==null&&(a.yaxis.tickColor=a.grid.tickColor||a.yaxis.color),a.grid.borderColor==null&&(a.grid.borderColor=a.grid.color),a.grid.tickColor==null&&(a.grid.tickColor=e.color.parse(a.grid.color).scale("a",.22).toString());var r,i,s,o={style:t.css("font-style"),size:Math.round(.8*(+t.css("font-size").replace("px","")||13)),variant:t.css("font-variant"),weight:t.css("font-weight"),family:t.css("font-family")};o.lineHeight=o.size*1.15,s=a.xaxes.length||1;for(r=0;r<s;++r)i=a.xaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.xaxis,i),a.xaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));s=a.yaxes.length||1;for(r=0;r<s;++r)i=a.yaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.yaxis,i),a.yaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));a.xaxis.noTicks&&a.xaxis.ticks==null&&(a.xaxis.ticks=a.xaxis.noTicks),a.yaxis.noTicks&&a.yaxis.ticks==null&&(a.yaxis.ticks=a.yaxis.noTicks),a.x2axis&&(a.xaxes[1]=e.extend(!0,{},a.xaxis,a.x2axis),a.xaxes[1].position="top"),a.y2axis&&(a.yaxes[1]=e.extend(!0,{},a.yaxis,a.y2axis),a.yaxes[1].position="right"),a.grid.coloredAreas&&(a.grid.markings=a.grid.coloredAreas),a.grid.coloredAreasColor&&(a.grid.markingsColor=a.grid.coloredAreasColor),a.lines&&e.extend(!0,a.series.lines,a.lines),a.points&&e.extend(!0,a.series.points,a.points),a.bars&&e.extend(!0,a.series.bars,a.bars),a.shadowSize!=null&&(a.series.shadowSize=a.shadowSize),a.highlightColor!=null&&(a.series.highlightColor=a.highlightColor);for(r=0;r<a.xaxes.length;++r)O(d,r+1).options=a.xaxes[r];for(r=0;r<a.yaxes.length;++r)O(v,r+1).options=a.yaxes[r];for(var u in b)a.hooks[u]&&a.hooks[u].length&&(b[u]=b[u].concat(a.hooks[u]));E(b.processOptions,[a])}function T(e){u=N(e),M(),_()}function N(t){var n=[];for(var r=0;r<t.length;++r){var i=e.extend(!0,{},a.series);t[r].data!=null?(i.data=t[r].data,delete t[r].data,e.extend(!0,i,t[r]),t[r].data=i.data):i.data=t[r],n.push(i)}return n}function C(e,t){var n=e[t+"axis"];return typeof n=="object"&&(n=n.n),typeof n!="number"&&(n=1),n}function k(){return e.grep(d.concat(v),function(e){return e})}function L(e){var t={},n,r;for(n=0;n<d.length;++n)r=d[n],r&&r.used&&(t["x"+r.n]=r.c2p(e.left));for(n=0;n<v.length;++n)r=v[n],r&&r.used&&(t["y"+r.n]=r.c2p(e.top));return t.x1!==undefined&&(t.x=t.x1),t.y1!==undefined&&(t.y=t.y1),t}function A(e){var t={},n,r,i;for(n=0;n<d.length;++n){r=d[n];if(r&&r.used){i="x"+r.n,e[i]==null&&r.n==1&&(i="x");if(e[i]!=null){t.left=r.p2c(e[i]);break}}}for(n=0;n<v.length;++n){r=v[n];if(r&&r.used){i="y"+r.n,e[i]==null&&r.n==1&&(i="y");if(e[i]!=null){t.top=r.p2c(e[i]);break}}}return t}function O(t,n){return t[n-1]||(t[n-1]={n:n,direction:t==d?"x":"y",options:e.extend(!0,{},t==d?a.xaxis:a.yaxis)}),t[n-1]}function M(){var t=u.length,n=-1,r;for(r=0;r<u.length;++r){var i=u[r].color;i!=null&&(t--,typeof i=="number"&&i>n&&(n=i))}t<=n&&(t=n+1);var s,o=[],f=a.colors,l=f.length,c=0;for(r=0;r<t;r++)s=e.color.parse(f[r%l]||"#666"),r%l==0&&r&&(c>=0?c<.5?c=-c-.2:c=0:c=-c),o[r]=s.scale("rgb",1+c);var h=0,p;for(r=0;r<u.length;++r){p=u[r],p.color==null?(p.color=o[h].toString(),++h):typeof p.color=="number"&&(p.color=o[p.color].toString());if(p.lines.show==null){var m,g=!0;for(m in p)if(p[m]&&p[m].show){g=!1;break}g&&(p.lines.show=!0)}p.lines.zero==null&&(p.lines.zero=!!p.lines.fill),p.xaxis=O(d,C(p,"x")),p.yaxis=O(v,C(p,"y"))}}function _(){function x(e,t,n){t<e.datamin&&t!=-r&&(e.datamin=t),n>e.datamax&&n!=r&&(e.datamax=n)}var t=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY,r=Number.MAX_VALUE,i,s,o,a,f,l,c,h,p,d,v,m,g,y,w,S;e.each(k(),function(e,r){r.datamin=t,r.datamax=n,r.used=!1});for(i=0;i<u.length;++i)l=u[i],l.datapoints={points:[]},E(b.processRawData,[l,l.data,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],w=l.data,S=l.datapoints.format;if(!S){S=[],S.push({x:!0,number:!0,required:!0}),S.push({y:!0,number:!0,required:!0});if(l.bars.show||l.lines.show&&l.lines.fill){var T=!!(l.bars.show&&l.bars.zero||l.lines.show&&l.lines.zero);S.push({y:!0,number:!0,required:!1,defaultValue:0,autoscale:T}),l.bars.horizontal&&(delete S[S.length-1].y,S[S.length-1].x=!0)}l.datapoints.format=S}if(l.datapoints.pointsize!=null)continue;l.datapoints.pointsize=S.length,h=l.datapoints.pointsize,c=l.datapoints.points;var N=l.lines.show&&l.lines.steps;l.xaxis.used=l.yaxis.used=!0;for(s=o=0;s<w.length;++s,o+=h){y=w[s];var C=y==null;if(!C)for(a=0;a<h;++a)m=y[a],g=S[a],g&&(g.number&&m!=null&&(m=+m,isNaN(m)?m=null:m==Infinity?m=r:m==-Infinity&&(m=-r)),m==null&&(g.required&&(C=!0),g.defaultValue!=null&&(m=g.defaultValue))),c[o+a]=m;if(C)for(a=0;a<h;++a)m=c[o+a],m!=null&&(g=S[a],g.autoscale&&(g.x&&x(l.xaxis,m,m),g.y&&x(l.yaxis,m,m))),c[o+a]=null;else if(N&&o>0&&c[o-h]!=null&&c[o-h]!=c[o]&&c[o-h+1]!=c[o+1]){for(a=0;a<h;++a)c[o+h+a]=c[o+a];c[o+1]=c[o-h+1],o+=h}}}for(i=0;i<u.length;++i)l=u[i],E(b.processDatapoints,[l,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],c=l.datapoints.points,h=l.datapoints.pointsize,S=l.datapoints.format;var L=t,A=t,O=n,M=n;for(s=0;s<c.length;s+=h){if(c[s]==null)continue;for(a=0;a<h;++a){m=c[s+a],g=S[a];if(!g||g.autoscale===!1||m==r||m==-r)continue;g.x&&(m<L&&(L=m),m>O&&(O=m)),g.y&&(m<A&&(A=m),m>M&&(M=m))}}if(l.bars.show){var _;switch(l.bars.align){case"left":_=0;break;case"right":_=-l.bars.barWidth;break;case"center":_=-l.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+l.bars.align)}l.bars.horizontal?(A+=_,M+=_+l.bars.barWidth):(L+=_,O+=_+l.bars.barWidth)}x(l.xaxis,L,O),x(l.yaxis,A,M)}e.each(k(),function(e,r){r.datamin==t&&(r.datamin=null),r.datamax==n&&(r.datamax=null)})}function D(){t.css("padding",0).children(":not(.flot-base,.flot-overlay)").remove(),t.css("position")=="static"&&t.css("position","relative"),f=new n("flot-base",t),l=new n("flot-overlay",t),h=f.context,p=l.context,c=e(l.element).unbind();var r=t.data("plot");r&&(r.shutdown(),l.clear()),t.data("plot",w)}function P(){a.grid.hoverable&&(c.mousemove(at),c.bind("mouseleave",ft)),a.grid.clickable&&c.click(lt),E(b.bindEvents,[c])}function H(){ot&&clearTimeout(ot),c.unbind("mousemove",at),c.unbind("mouseleave",ft),c.unbind("click",lt),E(b.shutdown,[c])}function B(e){function t(e){return e}var n,r,i=e.options.transform||t,s=e.options.inverseTransform;e.direction=="x"?(n=e.scale=g/Math.abs(i(e.max)-i(e.min)),r=Math.min(i(e.max),i(e.min))):(n=e.scale=y/Math.abs(i(e.max)-i(e.min)),n=-n,r=Math.max(i(e.max),i(e.min))),i==t?e.p2c=function(e){return(e-r)*n}:e.p2c=function(e){return(i(e)-r)*n},s?e.c2p=function(e){return s(r+e/n)}:e.c2p=function(e){return r+e/n}}function j(e){var t=e.options,n=e.ticks||[],r=t.labelWidth||0,i=t.labelHeight||0,s=r||e.direction=="x"?Math.floor(f.width/(n.length||1)):null;legacyStyles=e.direction+"Axis "+e.direction+e.n+"Axis",layer="flot-"+e.direction+"-axis flot-"+e.direction+e.n+"-axis "+legacyStyles,font=t.font||"flot-tick-label tickLabel";for(var o=0;o<n.length;++o){var u=n[o];if(!u.label)continue;var a=f.getTextInfo(layer,u.label,font,null,s);r=Math.max(r,a.width),i=Math.max(i,a.height)}e.labelWidth=t.labelWidth||r,e.labelHeight=t.labelHeight||i}function F(t){var n=t.labelWidth,r=t.labelHeight,i=t.options.position,s=t.options.tickLength,o=a.grid.axisMargin,u=a.grid.labelMargin,l=t.direction=="x"?d:v,c,h,p=e.grep(l,function(e){return e&&e.options.position==i&&e.reserveSpace});e.inArray(t,p)==p.length-1&&(o=0);if(s==null){var g=e.grep(l,function(e){return e&&e.reserveSpace});h=e.inArray(t,g)==0,h?s="full":s=5}isNaN(+s)||(u+=+s),t.direction=="x"?(r+=u,i=="bottom"?(m.bottom+=r+o,t.box={top:f.height-m.bottom,height:r}):(t.box={top:m.top+o,height:r},m.top+=r+o)):(n+=u,i=="left"?(t.box={left:m.left+o,width:n},m.left+=n+o):(m.right+=n+o,t.box={left:f.width-m.right,width:n})),t.position=i,t.tickLength=s,t.box.padding=u,t.innermost=h}function I(e){e.direction=="x"?(e.box.left=m.left-e.labelWidth/2,e.box.width=f.width-m.left-m.right+e.labelWidth):(e.box.top=m.top-e.labelHeight/2,e.box.height=f.height-m.bottom-m.top+e.labelHeight)}function q(){var t=a.grid.minBorderMargin,n={x:0,y:0},r,i;if(t==null){t=0;for(r=0;r<u.length;++r)t=Math.max(t,2*(u[r].points.radius+u[r].points.lineWidth/2))}n.x=n.y=Math.ceil(t),e.each(k(),function(e,t){var r=t.direction;t.reserveSpace&&(n[r]=Math.ceil(Math.max(n[r],(r=="x"?t.labelWidth:t.labelHeight)/2)))}),m.left=Math.max(n.x,m.left),m.right=Math.max(n.x,m.right),m.top=Math.max(n.y,m.top),m.bottom=Math.max(n.y,m.bottom)}function R(){var t,n=k(),r=a.grid.show;for(var i in m){var s=a.grid.margin||0;m[i]=typeof s=="number"?s:s[i]||0}E(b.processOffset,[m]);for(var i in m)typeof a.grid.borderWidth=="object"?m[i]+=r?a.grid.borderWidth[i]:0:m[i]+=r?a.grid.borderWidth:0;e.each(n,function(e,t){t.show=t.options.show,t.show==null&&(t.show=t.used),t.reserveSpace=t.show||t.options.reserveSpace,U(t)});if(r){var o=e.grep(n,function(e){return e.reserveSpace});e.each(o,function(e,t){z(t),W(t),X(t,t.ticks),j(t)});for(t=o.length-1;t>=0;--t)F(o[t]);q(),e.each(o,function(e,t){I(t)})}g=f.width-m.left-m.right,y=f.height-m.bottom-m.top,e.each(n,function(e,t){B(t)}),r&&G(),it()}function U(e){var t=e.options,n=+(t.min!=null?t.min:e.datamin),r=+(t.max!=null?t.max:e.datamax),i=r-n;if(i==0){var s=r==0?1:.01;t.min==null&&(n-=s);if(t.max==null||t.min!=null)r+=s}else{var o=t.autoscaleMargin;o!=null&&(t.min==null&&(n-=i*o,n<0&&e.datamin!=null&&e.datamin>=0&&(n=0)),t.max==null&&(r+=i*o,r>0&&e.datamax!=null&&e.datamax<=0&&(r=0)))}e.min=n,e.max=r}function z(t){var n=t.options,r;typeof n.ticks=="number"&&n.ticks>0?r=n.ticks:r=.3*Math.sqrt(t.direction=="x"?f.width:f.height);var s=(t.max-t.min)/r,o=-Math.floor(Math.log(s)/Math.LN10),u=n.tickDecimals;u!=null&&o>u&&(o=u);var a=Math.pow(10,-o),l=s/a,c;l<1.5?c=1:l<3?(c=2,l>2.25&&(u==null||o+1<=u)&&(c=2.5,++o)):l<7.5?c=5:c=10,c*=a,n.minTickSize!=null&&c<n.minTickSize&&(c=n.minTickSize),t.delta=s,t.tickDecimals=Math.max(0,u!=null?u:o),t.tickSize=n.tickSize||c;if(n.mode=="time"&&!t.tickGenerator)throw new Error("Time mode requires the flot.time plugin.");t.tickGenerator||(t.tickGenerator=function(e){var t=[],n=i(e.min,e.tickSize),r=0,s=Number.NaN,o;do o=s,s=n+r*e.tickSize,t.push(s),++r;while(s<e.max&&s!=o);return t},t.tickFormatter=function(e,t){var n=t.tickDecimals?Math.pow(10,t.tickDecimals):1,r=""+Math.round(e*n)/n;if(t.tickDecimals!=null){var i=r.indexOf("."),s=i==-1?0:r.length-i-1;if(s<t.tickDecimals)return(s?r:r+".")+(""+n).substr(1,t.tickDecimals-s)}return r}),e.isFunction(n.tickFormatter)&&(t.tickFormatter=function(e,t){return""+n.tickFormatter(e,t)});if(n.alignTicksWithAxis!=null){var h=(t.direction=="x"?d:v)[n.alignTicksWithAxis-1];if(h&&h.used&&h!=t){var p=t.tickGenerator(t);p.length>0&&(n.min==null&&(t.min=Math.min(t.min,p[0])),n.max==null&&p.length>1&&(t.max=Math.max(t.max,p[p.length-1]))),t.tickGenerator=function(e){var t=[],n,r;for(r=0;r<h.ticks.length;++r)n=(h.ticks[r].v-h.min)/(h.max-h.min),n=e.min+n*(e.max-e.min),t.push(n);return t};if(!t.mode&&n.tickDecimals==null){var m=Math.max(0,-Math.floor(Math.log(t.delta)/Math.LN10)+1),g=t.tickGenerator(t);g.length>1&&/\..*0$/.test((g[1]-g[0]).toFixed(m))||(t.tickDecimals=m)}}}}function W(t){var n=t.options.ticks,r=[];n==null||typeof n=="number"&&n>0?r=t.tickGenerator(t):n&&(e.isFunction(n)?r=n(t):r=n);var i,s;t.ticks=[];for(i=0;i<r.length;++i){var o=null,u=r[i];typeof u=="object"?(s=+u[0],u.length>1&&(o=u[1])):s=+u,o==null&&(o=t.tickFormatter(s,t)),isNaN(s)||t.ticks.push({v:s,label:o})}}function X(e,t){e.options.autoscaleMargin&&t.length>0&&(e.options.min==null&&(e.min=Math.min(e.min,t[0].v)),e.options.max==null&&t.length>1&&(e.max=Math.max(e.max,t[t.length-1].v)))}function V(){f.clear(),E(b.drawBackground,[h]);var e=a.grid;e.show&&e.backgroundColor&&K(),e.show&&!e.aboveData&&Q();for(var t=0;t<u.length;++t)E(b.drawSeries,[h,u[t]]),Y(u[t]);E(b.draw,[h]),e.show&&e.aboveData&&Q(),f.render(),ht()}function J(e,t){var n,r,i,s,o=k();for(var u=0;u<o.length;++u){n=o[u];if(n.direction==t){s=t+n.n+"axis",!e[s]&&n.n==1&&(s=t+"axis");if(e[s]){r=e[s].from,i=e[s].to;break}}}e[s]||(n=t=="x"?d[0]:v[0],r=e[t+"1"],i=e[t+"2"]);if(r!=null&&i!=null&&r>i){var a=r;r=i,i=a}return{from:r,to:i,axis:n}}function K(){h.save(),h.translate(m.left,m.top),h.fillStyle=bt(a.grid.backgroundColor,y,0,"rgba(255, 255, 255, 0)"),h.fillRect(0,0,g,y),h.restore()}function Q(){var t,n,r,i;h.save(),h.translate(m.left,m.top);var s=a.grid.markings;if(s){e.isFunction(s)&&(n=w.getAxes(),n.xmin=n.xaxis.min,n.xmax=n.xaxis.max,n.ymin=n.yaxis.min,n.ymax=n.yaxis.max,s=s(n));for(t=0;t<s.length;++t){var o=s[t],u=J(o,"x"),f=J(o,"y");u.from==null&&(u.from=u.axis.min),u.to==null&&(u.to=u.axis.max),f.from==null&&(f.from=f.axis.min),f.to==null&&(f.to=f.axis.max);if(u.to<u.axis.min||u.from>u.axis.max||f.to<f.axis.min||f.from>f.axis.max)continue;u.from=Math.max(u.from,u.axis.min),u.to=Math.min(u.to,u.axis.max),f.from=Math.max(f.from,f.axis.min),f.to=Math.min(f.to,f.axis.max);if(u.from==u.to&&f.from==f.to)continue;u.from=u.axis.p2c(u.from),u.to=u.axis.p2c(u.to),f.from=f.axis.p2c(f.from),f.to=f.axis.p2c(f.to),u.from==u.to||f.from==f.to?(h.beginPath(),h.strokeStyle=o.color||a.grid.markingsColor,h.lineWidth=o.lineWidth||a.grid.markingsLineWidth,h.moveTo(u.from,f.from),h.lineTo(u.to,f.to),h.stroke()):(h.fillStyle=o.color||a.grid.markingsColor,h.fillRect(u.from,f.to,u.to-u.from,f.from-f.to))}}n=k(),r=a.grid.borderWidth;for(var l=0;l<n.length;++l){var c=n[l],p=c.box,d=c.tickLength,v,b,E,S;if(!c.show||c.ticks.length==0)continue;h.lineWidth=1,c.direction=="x"?(v=0,d=="full"?b=c.position=="top"?0:y:b=p.top-m.top+(c.position=="top"?p.height:0)):(b=0,d=="full"?v=c.position=="left"?0:g:v=p.left-m.left+(c.position=="left"?p.width:0)),c.innermost||(h.strokeStyle=c.options.color,h.beginPath(),E=S=0,c.direction=="x"?E=g+1:S=y+1,h.lineWidth==1&&(c.direction=="x"?b=Math.floor(b)+.5:v=Math.floor(v)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S),h.stroke()),h.strokeStyle=c.options.tickColor,h.beginPath();for(t=0;t<c.ticks.length;++t){var x=c.ticks[t].v;E=S=0;if(isNaN(x)||x<c.min||x>c.max||d=="full"&&(typeof r=="object"&&r[c.position]>0||r>0)&&(x==c.min||x==c.max))continue;c.direction=="x"?(v=c.p2c(x),S=d=="full"?-y:d,c.position=="top"&&(S=-S)):(b=c.p2c(x),E=d=="full"?-g:d,c.position=="left"&&(E=-E)),h.lineWidth==1&&(c.direction=="x"?v=Math.floor(v)+.5:b=Math.floor(b)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S)}h.stroke()}r&&(i=a.grid.borderColor,typeof r=="object"||typeof i=="object"?(typeof r!="object"&&(r={top:r,right:r,bottom:r,left:r}),typeof i!="object"&&(i={top:i,right:i,bottom:i,left:i}),r.top>0&&(h.strokeStyle=i.top,h.lineWidth=r.top,h.beginPath(),h.moveTo(0-r.left,0-r.top/2),h.lineTo(g,0-r.top/2),h.stroke()),r.right>0&&(h.strokeStyle=i.right,h.lineWidth=r.right,h.beginPath(),h.moveTo(g+r.right/2,0-r.top),h.lineTo(g+r.right/2,y),h.stroke()),r.bottom>0&&(h.strokeStyle=i.bottom,h.lineWidth=r.bottom,h.beginPath(),h.moveTo(g+r.right,y+r.bottom/2),h.lineTo(0,y+r.bottom/2),h.stroke()),r.left>0&&(h.strokeStyle=i.left,h.lineWidth=r.left,h.beginPath(),h.moveTo(0-r.left/2,y+r.bottom),h.lineTo(0-r.left/2,0),h.stroke())):(h.lineWidth=r,h.strokeStyle=a.grid.borderColor,h.strokeRect(-r/2,-r/2,g+r,y+r))),h.restore()}function G(){e.each(k(),function(e,t){if(!t.show||t.ticks.length==0)return;var n=t.box,r=t.direction+"Axis "+t.direction+t.n+"Axis",i="flot-"+t.direction+"-axis flot-"+t.direction+t.n+"-axis "+r,s=t.options.font||"flot-tick-label tickLabel",o,u,a,l,c;f.removeText(i);for(var h=0;h<t.ticks.length;++h){o=t.ticks[h];if(!o.label||o.v<t.min||o.v>t.max)continue;t.direction=="x"?(l="center",u=m.left+t.p2c(o.v),t.position=="bottom"?a=n.top+n.padding:(a=n.top+n.height-n.padding,c="bottom")):(c="middle",a=m.top+t.p2c(o.v),t.position=="left"?(u=n.left+n.width-n.padding,l="right"):u=n.left+n.padding),f.addText(i,u,a,o.label,s,null,null,l,c)}})}function Y(e){e.lines.show&&Z(e),e.bars.show&&nt(e),e.points.show&&et(e)}function Z(e){function t(e,t,n,r,i){var s=e.points,o=e.pointsize,u=null,a=null;h.beginPath();for(var f=o;f<s.length;f+=o){var l=s[f-o],c=s[f-o+1],p=s[f],d=s[f+1];if(l==null||p==null)continue;if(c<=d&&c<i.min){if(d<i.min)continue;l=(i.min-c)/(d-c)*(p-l)+l,c=i.min}else if(d<=c&&d<i.min){if(c<i.min)continue;p=(i.min-c)/(d-c)*(p-l)+l,d=i.min}if(c>=d&&c>i.max){if(d>i.max)continue;l=(i.max-c)/(d-c)*(p-l)+l,c=i.max}else if(d>=c&&d>i.max){if(c>i.max)continue;p=(i.max-c)/(d-c)*(p-l)+l,d=i.max}if(l<=p&&l<r.min){if(p<r.min)continue;c=(r.min-l)/(p-l)*(d-c)+c,l=r.min}else if(p<=l&&p<r.min){if(l<r.min)continue;d=(r.min-l)/(p-l)*(d-c)+c,p=r.min}if(l>=p&&l>r.max){if(p>r.max)continue;c=(r.max-l)/(p-l)*(d-c)+c,l=r.max}else if(p>=l&&p>r.max){if(l>r.max)continue;d=(r.max-l)/(p-l)*(d-c)+c,p=r.max}(l!=u||c!=a)&&h.moveTo(r.p2c(l)+t,i.p2c(c)+n),u=p,a=d,h.lineTo(r.p2c(p)+t,i.p2c(d)+n)}h.stroke()}function n(e,t,n){var r=e.points,i=e.pointsize,s=Math.min(Math.max(0,n.min),n.max),o=0,u,a=!1,f=1,l=0,c=0;for(;;){if(i>0&&o>r.length+i)break;o+=i;var p=r[o-i],d=r[o-i+f],v=r[o],m=r[o+f];if(a){if(i>0&&p!=null&&v==null){c=o,i=-i,f=2;continue}if(i<0&&o==l+i){h.fill(),a=!1,i=-i,f=1,o=l=c+i;continue}}if(p==null||v==null)continue;if(p<=v&&p<t.min){if(v<t.min)continue;d=(t.min-p)/(v-p)*(m-d)+d,p=t.min}else if(v<=p&&v<t.min){if(p<t.min)continue;m=(t.min-p)/(v-p)*(m-d)+d,v=t.min}if(p>=v&&p>t.max){if(v>t.max)continue;d=(t.max-p)/(v-p)*(m-d)+d,p=t.max}else if(v>=p&&v>t.max){if(p>t.max)continue;m=(t.max-p)/(v-p)*(m-d)+d,v=t.max}a||(h.beginPath(),h.moveTo(t.p2c(p),n.p2c(s)),a=!0);if(d>=n.max&&m>=n.max){h.lineTo(t.p2c(p),n.p2c(n.max)),h.lineTo(t.p2c(v),n.p2c(n.max));continue}if(d<=n.min&&m<=n.min){h.lineTo(t.p2c(p),n.p2c(n.min)),h.lineTo(t.p2c(v),n.p2c(n.min));continue}var g=p,y=v;d<=m&&d<n.min&&m>=n.min?(p=(n.min-d)/(m-d)*(v-p)+p,d=n.min):m<=d&&m<n.min&&d>=n.min&&(v=(n.min-d)/(m-d)*(v-p)+p,m=n.min),d>=m&&d>n.max&&m<=n.max?(p=(n.max-d)/(m-d)*(v-p)+p,d=n.max):m>=d&&m>n.max&&d<=n.max&&(v=(n.max-d)/(m-d)*(v-p)+p,m=n.max),p!=g&&h.lineTo(t.p2c(g),n.p2c(d)),h.lineTo(t.p2c(p),n.p2c(d)),h.lineTo(t.p2c(v),n.p2c(m)),v!=y&&(h.lineTo(t.p2c(v),n.p2c(m)),h.lineTo(t.p2c(y),n.p2c(m)))}}h.save(),h.translate(m.left,m.top),h.lineJoin="round";var r=e.lines.lineWidth,i=e.shadowSize;if(r>0&&i>0){h.lineWidth=i,h.strokeStyle="rgba(0,0,0,0.1)";var s=Math.PI/18;t(e.datapoints,Math.sin(s)*(r/2+i/2),Math.cos(s)*(r/2+i/2),e.xaxis,e.yaxis),h.lineWidth=i/2,t(e.datapoints,Math.sin(s)*(r/2+i/4),Math.cos(s)*(r/2+i/4),e.xaxis,e.yaxis)}h.lineWidth=r,h.strokeStyle=e.color;var o=rt(e.lines,e.color,0,y);o&&(h.fillStyle=o,n(e.datapoints,e.xaxis,e.yaxis)),r>0&&t(e.datapoints,0,0,e.xaxis,e.yaxis),h.restore()}function et(e){function t(e,t,n,r,i,s,o,u){var a=e.points,f=e.pointsize;for(var l=0;l<a.length;l+=f){var c=a[l],p=a[l+1];if(c==null||c<s.min||c>s.max||p<o.min||p>o.max)continue;h.beginPath(),c=s.p2c(c),p=o.p2c(p)+r,u=="circle"?h.arc(c,p,t,0,i?Math.PI:Math.PI*2,!1):u(h,c,p,t,i),h.closePath(),n&&(h.fillStyle=n,h.fill()),h.stroke()}}h.save(),h.translate(m.left,m.top);var n=e.points.lineWidth,r=e.shadowSize,i=e.points.radius,s=e.points.symbol;n==0&&(n=1e-4);if(n>0&&r>0){var o=r/2;h.lineWidth=o,h.strokeStyle="rgba(0,0,0,0.1)",t(e.datapoints,i,null,o+o/2,!0,e.xaxis,e.yaxis,s),h.strokeStyle="rgba(0,0,0,0.2)",t(e.datapoints,i,null,o/2,!0,e.xaxis,e.yaxis,s)}h.lineWidth=n,h.strokeStyle=e.color,t(e.datapoints,i,rt(e.points,e.color),0,!1,e.xaxis,e.yaxis,s),h.restore()}function tt(e,t,n,r,i,s,o,u,a,f,l,c){var h,p,d,v,m,g,y,b,w;l?(b=g=y=!0,m=!1,h=n,p=e,v=t+r,d=t+i,p<h&&(w=p,p=h,h=w,m=!0,g=!1)):(m=g=y=!0,b=!1,h=e+r,p=e+i,d=n,v=t,v<d&&(w=v,v=d,d=w,b=!0,y=!1));if(p<u.min||h>u.max||v<a.min||d>a.max)return;h<u.min&&(h=u.min,m=!1),p>u.max&&(p=u.max,g=!1),d<a.min&&(d=a.min,b=!1),v>a.max&&(v=a.max,y=!1),h=u.p2c(h),d=a.p2c(d),p=u.p2c(p),v=a.p2c(v),o&&(f.beginPath(),f.moveTo(h,d),f.lineTo(h,v),f.lineTo(p,v),f.lineTo(p,d),f.fillStyle=o(d,v),f.fill()),c>0&&(m||g||y||b)&&(f.beginPath(),f.moveTo(h,d+s),m?f.lineTo(h,v+s):f.moveTo(h,v+s),y?f.lineTo(p,v+s):f.moveTo(p,v+s),g?f.lineTo(p,d+s):f.moveTo(p,d+s),b?f.lineTo(h,d+s):f.moveTo(h,d+s),f.stroke())}function nt(e){function t(t,n,r,i,s,o,u){var a=t.points,f=t.pointsize;for(var l=0;l<a.length;l+=f){if(a[l]==null)continue;tt(a[l],a[l+1],a[l+2],n,r,i,s,o,u,h,e.bars.horizontal,e.bars.lineWidth)}}h.save(),h.translate(m.left,m.top),h.lineWidth=e.bars.lineWidth,h.strokeStyle=e.color;var n;switch(e.bars.align){case"left":n=0;break;case"right":n=-e.bars.barWidth;break;case"center":n=-e.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+e.bars.align)}var r=e.bars.fill?function(t,n){return rt(e.bars,e.color,t,n)}:null;t(e.datapoints,n,n+e.bars.barWidth,0,r,e.xaxis,e.yaxis),h.restore()}function rt(t,n,r,i){var s=t.fill;if(!s)return null;if(t.fillColor)return bt(t.fillColor,r,i,n);var o=e.color.parse(n);return o.a=typeof s=="number"?s:.4,o.normalize(),o.toString()}function it(){t.find(".legend").remove();if(!a.legend.show)return;var n=[],r=[],i=!1,s=a.legend.labelFormatter,o,f;for(var l=0;l<u.length;++l)o=u[l],o.label&&(f=s?s(o.label,o):o.label,f&&r.push({label:f,color:o.color}));if(a.legend.sorted)if(e.isFunction(a.legend.sorted))r.sort(a.legend.sorted);else if(a.legend.sorted=="reverse")r.reverse();else{var c=a.legend.sorted!="descending";r.sort(function(e,t){return e.label==t.label?0:e.label<t.label!=c?1:-1})}for(var l=0;l<r.length;++l){var h=r[l];l%a.legend.noColumns==0&&(i&&n.push("</tr>"),n.push("<tr>"),i=!0),n.push('<td class="legendColorBox"><div style="border:1px solid '+a.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+h.color+';overflow:hidden"></div></div></td>'+'<td class="legendLabel">'+h.label+"</td>")}i&&n.push("</tr>");if(n.length==0)return;var p='<table style="font-size:smaller;color:'+a.grid.color+'">'+n.join("")+"</table>";if(a.legend.container!=null)e(a.legend.container).html(p);else{var d="",v=a.legend.position,g=a.legend.margin;g[0]==null&&(g=[g,g]),v.charAt(0)=="n"?d+="top:"+(g[1]+m.top)+"px;":v.charAt(0)=="s"&&(d+="bottom:"+(g[1]+m.bottom)+"px;"),v.charAt(1)=="e"?d+="right:"+(g[0]+m.right)+"px;":v.charAt(1)=="w"&&(d+="left:"+(g[0]+m.left)+"px;");var y=e('<div class="legend">'+p.replace('style="','style="position:absolute;'+d+";")+"</div>").appendTo(t);if(a.legend.backgroundOpacity!=0){var b=a.legend.backgroundColor;b==null&&(b=a.grid.backgroundColor,b&&typeof b=="string"?b=e.color.parse(b):b=e.color.extract(y,"background-color"),b.a=1,b=b.toString());var w=y.children();e('<div style="position:absolute;width:'+w.width()+"px;height:"+w.height()+"px;"+d+"background-color:"+b+';"> </div>').prependTo(y).css("opacity",a.legend.backgroundOpacity)}}}function ut(e,t,n){var r=a.grid.mouseActiveRadius,i=r*r+1,s=null,o=!1,f,l,c;for(f=u.length-1;f>=0;--f){if(!n(u[f]))continue;var h=u[f],p=h.xaxis,d=h.yaxis,v=h.datapoints.points,m=p.c2p(e),g=d.c2p(t),y=r/p.scale,b=r/d.scale;c=h.datapoints.pointsize,p.options.inverseTransform&&(y=Number.MAX_VALUE),d.options.inverseTransform&&(b=Number.MAX_VALUE);if(h.lines.show||h.points.show)for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1];if(w==null)continue;if(w-m>y||w-m<-y||E-g>b||E-g<-b)continue;var S=Math.abs(p.p2c(w)-e),x=Math.abs(d.p2c(E)-t),T=S*S+x*x;T<i&&(i=T,s=[f,l/c])}if(h.bars.show&&!s){var N=h.bars.align=="left"?0:-h.bars.barWidth/2,C=N+h.bars.barWidth;for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1],k=v[l+2];if(w==null)continue;if(u[f].bars.horizontal?m<=Math.max(k,w)&&m>=Math.min(k,w)&&g>=E+N&&g<=E+C:m>=w+N&&m<=w+C&&g>=Math.min(k,E)&&g<=Math.max(k,E))s=[f,l/c]}}}return s?(f=s[0],l=s[1],c=u[f].datapoints.pointsize,{datapoint:u[f].datapoints.points.slice(l*c,(l+1)*c),dataIndex:l,series:u[f],seriesIndex:f}):null}function at(e){a.grid.hoverable&&ct("plothover",e,function(e){return e["hoverable"]!=0})}function ft(e){a.grid.hoverable&&ct("plothover",e,function(e){return!1})}function lt(e){ct("plotclick",e,function(e){return e["clickable"]!=0})}function ct(e,n,r){var i=c.offset(),s=n.pageX-i.left-m.left,o=n.pageY-i.top-m.top,u=L({left:s,top:o});u.pageX=n.pageX,u.pageY=n.pageY;var f=ut(s,o,r);f&&(f.pageX=parseInt(f.series.xaxis.p2c(f.datapoint[0])+i.left+m.left,10),f.pageY=parseInt(f.series.yaxis.p2c(f.datapoint[1])+i.top+m.top,10));if(a.grid.autoHighlight){for(var l=0;l<st.length;++l){var h=st[l];h.auto==e&&(!f||h.series!=f.series||h.point[0]!=f.datapoint[0]||h.point[1]!=f.datapoint[1])&&vt(h.series,h.point)}f&&dt(f.series,f.datapoint,e)}t.trigger(e,[u,f])}function ht(){var e=a.interaction.redrawOverlayInterval;if(e==-1){pt();return}ot||(ot=setTimeout(pt,e))}function pt(){ot=null,p.save(),l.clear(),p.translate(m.left,m.top);var e,t;for(e=0;e<st.length;++e)t=st[e],t.series.bars.show?yt(t.series,t.point):gt(t.series,t.point);p.restore(),E(b.drawOverlay,[p])}function dt(e,t,n){typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var r=e.datapoints.pointsize;t=e.datapoints.points.slice(r*t,r*(t+1))}var i=mt(e,t);i==-1?(st.push({series:e,point:t,auto:n}),ht()):n||(st[i].auto=!1)}function vt(e,t){if(e==null&&t==null){st=[],ht();return}typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var n=e.datapoints.pointsize;t=e.datapoints.points.slice(n*t,n*(t+1))}var r=mt(e,t);r!=-1&&(st.splice(r,1),ht())}function mt(e,t){for(var n=0;n<st.length;++n){var r=st[n];if(r.series==e&&r.point[0]==t[0]&&r.point[1]==t[1])return n}return-1}function gt(t,n){var r=n[0],i=n[1],s=t.xaxis,o=t.yaxis,u=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString();if(r<s.min||r>s.max||i<o.min||i>o.max)return;var a=t.points.radius+t.points.lineWidth/2;p.lineWidth=a,p.strokeStyle=u;var f=1.5*a;r=s.p2c(r),i=o.p2c(i),p.beginPath(),t.points.symbol=="circle"?p.arc(r,i,f,0,2*Math.PI,!1):t.points.symbol(p,r,i,f,!1),p.closePath(),p.stroke()}function yt(t,n){var r=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString(),i=r,s=t.bars.align=="left"?0:-t.bars.barWidth/2;p.lineWidth=t.bars.lineWidth,p.strokeStyle=r,tt(n[0],n[1],n[2]||0,s,s+t.bars.barWidth,0,function(){return i},t.xaxis,t.yaxis,p,t.bars.horizontal,t.bars.lineWidth)}function bt(t,n,r,i){if(typeof t=="string")return t;var s=h.createLinearGradient(0,r,0,n);for(var o=0,u=t.colors.length;o<u;++o){var a=t.colors[o];if(typeof a!="string"){var f=e.color.parse(i);a.brightness!=null&&(f=f.scale("rgb",a.brightness)),a.opacity!=null&&(f.a*=a.opacity),a=f.toString()}s.addColorStop(o/(u-1),a)}return s}var u=[],a={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:!0,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:!1,radius:3,lineWidth:2,fill:!0,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:!1,fillColor:null,steps:!1},bars:{show:!1,lineWidth:2,barWidth:1,fill:!0,fillColor:null,align:"left",horizontal:!1,zero:!0},shadowSize:3,highlightColor:null},grid:{show:!0,aboveData:!1,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:!1,hoverable:!1,autoHighlight:!0,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1e3/60},hooks:{}},f=null,l=null,c=null,h=null,p=null,d=[],v=[],m={left:0,right:0,top:0,bottom
:0},g=0,y=0,b={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},w=this;w.setData=T,w.setupGrid=R,w.draw=V,w.getPlaceholder=function(){return t},w.getCanvas=function(){return f.element},w.getPlotOffset=function(){return m},w.width=function(){return g},w.height=function(){return y},w.offset=function(){var e=c.offset();return e.left+=m.left,e.top+=m.top,e},w.getData=function(){return u},w.getAxes=function(){var t={},n;return e.each(d.concat(v),function(e,n){n&&(t[n.direction+(n.n!=1?n.n:"")+"axis"]=n)}),t},w.getXAxes=function(){return d},w.getYAxes=function(){return v},w.c2p=L,w.p2c=A,w.getOptions=function(){return a},w.highlight=dt,w.unhighlight=vt,w.triggerRedrawOverlay=ht,w.pointOffset=function(e){return{left:parseInt(d[C(e,"x")-1].p2c(+e.x)+m.left,10),top:parseInt(v[C(e,"y")-1].p2c(+e.y)+m.top,10)}},w.shutdown=H,w.resize=function(){var e=t.width(),n=t.height();f.resize(e,n),l.resize(e,n)},w.hooks=b,S(w),x(s),D(),T(r),R(),V(),P();var st=[],ot=null}function i(e,t){return t*Math.floor(e/t)}var t=Object.prototype.hasOwnProperty;n.prototype.resize=function(e,t){if(e<=0||t<=0)throw new Error("Invalid dimensions for plot, width = "+e+", height = "+t);var n=this.element,r=this.context,i=this.pixelRatio;this.width!=e&&(n.width=e*i,n.style.width=e+"px",this.width=e),this.height!=t&&(n.height=t*i,n.style.height=t+"px",this.height=t),r.restore(),r.save(),r.scale(i,i)},n.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)},n.prototype.render=function(){var e=this._textCache;for(var n in e)if(t.call(e,n)){var r=this.getTextLayer(n),i=e[n];r.hide();for(var s in i)if(t.call(i,s)){var o=i[s];for(var u in o)if(t.call(o,u)){var a=o[u].positions;for(var f=0,l;l=a[f];f++)l.active?l.rendered||(r.append(l.element),l.rendered=!0):(a.splice(f--,1),l.rendered&&l.element.detach());a.length==0&&delete o[u]}}r.show()}},n.prototype.getTextLayer=function(t){var n=this.text[t];return n==null&&(this.textContainer==null&&(this.textContainer=e("<div class='flot-text'></div>").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)),n=this.text[t]=e("<div></div>").addClass(t).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)),n},n.prototype.getTextInfo=function(t,n,r,i,s){var o,u,a,f;n=""+n,typeof r=="object"?o=r.style+" "+r.variant+" "+r.weight+" "+r.size+"px/"+r.lineHeight+"px "+r.family:o=r,u=this._textCache[t],u==null&&(u=this._textCache[t]={}),a=u[o],a==null&&(a=u[o]={}),f=a[n];if(f==null){var l=e("<div></div>").html(n).css({position:"absolute","max-width":s,top:-9999}).appendTo(this.getTextLayer(t));typeof r=="object"?l.css({font:o,color:r.color}):typeof r=="string"&&l.addClass(r),f=a[n]={width:l.outerWidth(!0),height:l.outerHeight(!0),element:l,positions:[]},l.detach()}return f},n.prototype.addText=function(e,t,n,r,i,s,o,u,a){var f=this.getTextInfo(e,r,i,s,o),l=f.positions;u=="center"?t-=f.width/2:u=="right"&&(t-=f.width),a=="middle"?n-=f.height/2:a=="bottom"&&(n-=f.height);for(var c=0,h;h=l[c];c++)if(h.x==t&&h.y==n){h.active=!0;return}h={active:!0,rendered:!1,element:l.length?f.element.clone():f.element,x:t,y:n},l.push(h),h.element.css({top:Math.round(n),left:Math.round(t),"text-align":u})},n.prototype.removeText=function(e,n,r,i,s,o){if(i==null){var u=this._textCache[e];if(u!=null)for(var a in u)if(t.call(u,a)){var f=u[a];for(var l in f)if(t.call(f,l)){var c=f[l].positions;for(var h=0,p;p=c[h];h++)p.active=!1}}}else{var c=this.getTextInfo(e,i,s,o).positions;for(var h=0,p;p=c[h];h++)p.x==n&&p.y==r&&(p.active=!1)}},e.plot=function(t,n,i){var s=new r(e(t),n,i,e.plot.plugins);return s},e.plot.version="0.8.1",e.plot.plugins=[],e.fn.plot=function(t,n){return this.each(function(){e.plot(this,t,n)})}}(jQuery);;/* Flot plugin for selecting regions of a plot.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

The plugin supports these options:

selection: {
	mode: null or "x" or "y" or "xy",
	color: color,
	shape: "round" or "miter" or "bevel",
	minSize: number of pixels
}

Selection support is enabled by setting the mode to one of "x", "y" or "xy".
In "x" mode, the user will only be able to specify the x range, similarly for
"y" mode. For "xy", the selection becomes a rectangle where both ranges can be
specified. "color" is color of the selection (if you need to change the color
later on, you can get to it with plot.getOptions().selection.color). "shape"
is the shape of the corners of the selection.

"minSize" is the minimum size a selection can be in pixels. This value can
be customized to determine the smallest size a selection can be and still
have the selection rectangle be displayed. When customizing this value, the
fact that it refers to pixels, not axis units must be taken into account.
Thus, for example, if there is a bar graph in time mode with BarWidth set to 1
minute, setting "minSize" to 1 will not make the minimum selection size 1
minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent
"plotunselected" events from being fired when the user clicks the mouse without
dragging.

When selection support is enabled, a "plotselected" event will be emitted on
the DOM element you passed into the plot function. The event handler gets a
parameter with the ranges selected on the axes, like this:

	placeholder.bind( "plotselected", function( event, ranges ) {
		alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
		// similar for yaxis - with multiple axes, the extra ones are in
		// x2axis, x3axis, ...
	});

The "plotselected" event is only fired when the user has finished making the
selection. A "plotselecting" event is fired during the process with the same
parameters as the "plotselected" event, in case you want to know what's
happening while it's happening,

A "plotunselected" event with no arguments is emitted when the user clicks the
mouse to remove the selection. As stated above, setting "minSize" to 0 will
destroy this behavior.

The plugin allso adds the following methods to the plot object:

- setSelection( ranges, preventEvent )

  Set the selection rectangle. The passed in ranges is on the same form as
  returned in the "plotselected" event. If the selection mode is "x", you
  should put in either an xaxis range, if the mode is "y" you need to put in
  an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
  this:

	setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });

  setSelection will trigger the "plotselected" event when called. If you don't
  want that to happen, e.g. if you're inside a "plotselected" handler, pass
  true as the second parameter. If you are using multiple axes, you can
  specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of
  xaxis, the plugin picks the first one it sees.

- clearSelection( preventEvent )

  Clear the selection rectangle. Pass in true to avoid getting a
  "plotunselected" event.

- getSelection()

  Returns the current selection in the same format as the "plotselected"
  event. If there's currently no selection, the function returns null.

*/

(function ($) {
    function init(plot) {
        var selection = {
                first: { x: -1, y: -1}, second: { x: -1, y: -1},
                show: false,
                active: false
            };

        // FIXME: The drag handling implemented here should be
        // abstracted out, there's some similar code from a library in
        // the navigation plugin, this should be massaged a bit to fit
        // the Flot cases here better and reused. Doing this would
        // make this plugin much slimmer.
        var savedhandlers = {};

        var mouseUpHandler = null;
        
        function onMouseMove(e) {
            if (selection.active) {
                updateSelection(e);
                
                plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
            }
        }

        function onMouseDown(e) {
            if (e.which != 1)  // only accept left-click
                return;
            
            // cancel out any text selections
            document.body.focus();

            // prevent text selection and drag in old-school browsers
            if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
                savedhandlers.onselectstart = document.onselectstart;
                document.onselectstart = function () { return false; };
            }
            if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
                savedhandlers.ondrag = document.ondrag;
                document.ondrag = function () { return false; };
            }

            setSelectionPos(selection.first, e);

            selection.active = true;

            // this is a bit silly, but we have to use a closure to be
            // able to whack the same handler again
            mouseUpHandler = function (e) { onMouseUp(e); };
            
            $(document).one("mouseup", mouseUpHandler);
        }

        function onMouseUp(e) {
            mouseUpHandler = null;
            
            // revert drag stuff for old-school browsers
            if (document.onselectstart !== undefined)
                document.onselectstart = savedhandlers.onselectstart;
            if (document.ondrag !== undefined)
                document.ondrag = savedhandlers.ondrag;

            // no more dragging
            selection.active = false;
            updateSelection(e);

            if (selectionIsSane())
                triggerSelectedEvent();
            else {
                // this counts as a clear
                plot.getPlaceholder().trigger("plotunselected", [ ]);
                plot.getPlaceholder().trigger("plotselecting", [ null ]);
            }

            return false;
        }

        function getSelection(notRect) {
            if (!selectionIsSane())
                return null;
            
            if (!selection.show) return null;

            var r = {}, c1 = selection.first, c2 = selection.second;

            $.each(plot.getAxes(), function (name, axis) {
                if (axis.used) {
                    var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); 
                    if (!notRect) {
                        r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
                    } else {
                        r[name] = { from: p1, to: p2 };
                    }
                }
            });
            return r;
        }

        function triggerSelectedEvent() {
            var r = getSelection();

            plot.getPlaceholder().trigger("plotselected", [ r ]);

            // backwards-compat stuff, to be removed in future
            if (r.xaxis && r.yaxis)
                plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
        }

        function clamp(min, value, max) {
            return value < min ? min: (value > max ? max: value);
        }

        function setSelectionPos(pos, e) {
            var o = plot.getOptions();
            var offset = plot.getPlaceholder().offset();
            var plotOffset = plot.getPlotOffset();
            pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
            pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());

            if (o.selection.mode == "y")
                pos.x = pos == selection.first ? 0 : plot.width();

            if (o.selection.mode == "x")
                pos.y = pos == selection.first ? 0 : plot.height();
        }

        function updateSelection(pos) {
            if (pos.pageX == null)
                return;

            setSelectionPos(selection.second, pos);
            if (selectionIsSane()) {
                selection.show = true;
                plot.triggerRedrawOverlay();
            }
            else
                clearSelection(true);
        }

        function clearSelection(preventEvent) {
            if (selection.show) {
                selection.show = false;
                plot.triggerRedrawOverlay();
                if (!preventEvent)
                    plot.getPlaceholder().trigger("plotunselected", [ ]);
            }
        }

        // function taken from markings support in Flot
        function extractRange(ranges, coord) {
            var axis, from, to, key, axes = plot.getAxes();

            for (var k in axes) {
                axis = axes[k];
                if (axis.direction == coord) {
                    key = coord + axis.n + "axis";
                    if (!ranges[key] && axis.n == 1)
                        key = coord + "axis"; // support x1axis as xaxis
                    if (ranges[key]) {
                        from = ranges[key].from;
                        to = ranges[key].to;
                        break;
                    }
                }
            }

            // backwards-compat stuff - to be removed in future
            if (!ranges[key]) {
                axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
                from = ranges[coord + "1"];
                to = ranges[coord + "2"];
            }

            // auto-reverse as an added bonus
            if (from != null && to != null && from > to) {
                var tmp = from;
                from = to;
                to = tmp;
            }
            
            return { from: from, to: to, axis: axis };
        }
        
        function setSelection(ranges, preventEvent) {
            var axis, range, o = plot.getOptions();

            if (o.selection.mode == "y") {
                selection.first.x = 0;
                selection.second.x = plot.width();
            }
            else {
                range = extractRange(ranges, "x");

                selection.first.x = range.axis.p2c(range.from);
                selection.second.x = range.axis.p2c(range.to);
            }

            if (o.selection.mode == "x") {
                selection.first.y = 0;
                selection.second.y = plot.height();
            }
            else {
                range = extractRange(ranges, "y");

                selection.first.y = range.axis.p2c(range.from);
                selection.second.y = range.axis.p2c(range.to);
            }

            selection.show = true;
            plot.triggerRedrawOverlay();
            if (!preventEvent && selectionIsSane())
                triggerSelectedEvent();
        }

        function selectionIsSane() {
            var minSize = plot.getOptions().selection.minSize;
            return Math.abs(selection.second.x - selection.first.x) >= minSize &&
                Math.abs(selection.second.y - selection.first.y) >= minSize;
        }

        plot.clearSelection = clearSelection;
        plot.setSelection = setSelection;
        plot.getSelection = getSelection;
        plot.updateSelection = updateSelection;
        plot.setSelectionPos = setSelectionPos;

        plot.hooks.bindEvents.push(function(plot, eventHolder) {
            var o = plot.getOptions();
            if (o.selection.mode != null) {
                eventHolder.mousemove(onMouseMove);
                eventHolder.mousedown(onMouseDown);
            }
        });


        plot.hooks.drawOverlay.push(function (plot, ctx) {
            // draw selection
            if (selection.show && selectionIsSane()) {
                var plotOffset = plot.getPlotOffset();
                var o = plot.getOptions();

                ctx.save();
                ctx.translate(plotOffset.left, plotOffset.top);

                var c = $.color.parse(o.selection.color);

                ctx.strokeStyle = c.scale('a', 0.8).toString();
                ctx.lineWidth = 1;
                ctx.lineJoin = o.selection.shape;
                ctx.fillStyle = c.scale('a', 0.4).toString();

                

                if (o.selection.shape == 'lineX') {
                    ctx.strokeStyle = c;
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(selection.first.x, selection.first.y);
                    ctx.lineTo(selection.second.x, selection.first.y);
                    ctx.stroke();

                } else if (o.selection.shape == 'lineY') {

                    ctx.strokeStyle = c;
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(selection.first.x, selection.first.y);
                    ctx.lineTo(selection.first.x, selection.second.y);
                    ctx.stroke();

                } else if (o.selection.shape == 'lineXY') {

                    ctx.strokeStyle = c;
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(selection.first.x, selection.first.y);
                    ctx.lineTo(selection.second.x, selection.second.y);
                    ctx.stroke();

                } else {
                    var x = Math.min(selection.first.x, selection.second.x) + 0.5,
                    y = Math.min(selection.first.y, selection.second.y) + 0.5,
                    w = Math.abs(selection.second.x - selection.first.x) - 1,
                    h = Math.abs(selection.second.y - selection.first.y) - 1;

                    ctx.fillRect(x, y, w, h);
                    ctx.strokeRect(x, y, w, h);
                }
                ctx.restore();
            }
        });
        
        plot.hooks.shutdown.push(function (plot, eventHolder) {
            eventHolder.unbind("mousemove", onMouseMove);
            eventHolder.unbind("mousedown", onMouseDown);
            
            if (mouseUpHandler)
                $(document).unbind("mouseup", mouseUpHandler);
        });

    }

    $.plot.plugins.push({
        init: init,
        options: {
            selection: {
                mode: null, // one of null, "x", "y" or "xy"
                color: "#e8cfac",
                shape: "round", // one of "round", "miter", or "bevel"
                minSize: 5 // minimum number of pixels
            }
        },
        name: 'selection',
        version: '1.1'
    });
})(jQuery);
;/* Flot plugin for automatically redrawing plots as the placeholder resizes.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

It works by listening for changes on the placeholder div (through the jQuery
resize event plugin) - if the size changes, it will redraw the plot.

There are no options. If you need to disable the plugin for some plots, you
can just fix the size of their placeholders.

*//* Inline dependency:
 * jQuery resize event - v1.1 - 3/14/2010
 * http://benalman.com/projects/jquery-resize-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */(function(e,t,n){function c(){s=t[o](function(){r.each(function(){var t=e(this),n=t.width(),r=t.height(),i=e.data(this,a);(n!==i.w||r!==i.h)&&t.trigger(u,[i.w=n,i.h=r])}),c()},i[f])}var r=e([]),i=e.resize=e.extend(e.resize,{}),s,o="setTimeout",u="resize",a=u+"-special-event",f="delay",l="throttleWindow";i[f]=250,i[l]=!0,e.event.special[u]={setup:function(){if(!i[l]&&this[o])return!1;var t=e(this);r=r.add(t),e.data(this,a,{w:t.width(),h:t.height()}),r.length===1&&c()},teardown:function(){if(!i[l]&&this[o])return!1;var t=e(this);r=r.not(t),t.removeData(a),r.length||clearTimeout(s)},add:function(t){function s(t,i,s){var o=e(this),u=e.data(this,a);u.w=i!==n?i:o.width(),u.h=s!==n?s:o.height(),r.apply(this,arguments)}if(!i[l]&&this[o])return!1;var r;if(e.isFunction(t))return r=t,s;r=t.handler,t.handler=s}}})(jQuery,this),function(e){function n(e){function t(){var t=e.getPlaceholder();if(t.width()==0||t.height()==0)return;e.resize(),e.setupGrid(),e.draw()}function n(e,n){e.getPlaceholder().resize(t)}function r(e,n){e.getPlaceholder().unbind("resize",t)}e.hooks.bindEvents.push(n),e.hooks.shutdown.push(r)}var t={};e.plot.plugins.push({init:n,options:t,name:"resize",version:"1.0"})}(jQuery);;/* Flot plugin for stacking data sets rather than overlyaing them.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

The plugin assumes the data is sorted on x (or y if stacking horizontally).
For line charts, it is assumed that if a line has an undefined gap (from a
null point), then the line above it should have the same gap - insert zeros
instead of "null" if you want another behaviour. This also holds for the start
and end of the chart. Note that stacking a mix of positive and negative values
in most instances doesn't make sense (so it looks weird).

Two or more series are stacked when their "stack" attribute is set to the same
key (which can be any number or string or just "true"). To specify the default
stack, you can set the stack option like this:

	series: {
		stack: null/false, true, or a key (number/string)
	}

You can also specify it for a single series, like this:

	$.plot( $("#placeholder"), [{
		data: [ ... ],
		stack: true
	}])

The stacking order is determined by the order of the data series in the array
(later series end up on top of the previous).

Internally, the plugin modifies the datapoints in each series, adding an
offset to the y value. For line series, extra data points are inserted through
interpolation. If there's a second y value, it's also adjusted (e.g for bar
charts or filled areas).

*/(function(e){function n(e){function t(e,t){var n=null;for(var r=0;r<t.length;++r){if(e==t[r])break;t[r].stack==e.stack&&(n=t[r])}return n}function n(e,n,r){if(n.stack==null||n.stack===!1)return;var i=t(n,e.getData());if(!i)return;var s=r.pointsize,o=r.points,u=i.datapoints.pointsize,a=i.datapoints.points,f=[],l,c,h,p,d,v,m=n.lines.show,g=n.bars.horizontal,y=s>2&&(g?r.format[2].x:r.format[2].y),b=m&&n.lines.steps,w=!0,E=g?1:0,S=g?0:1,x=0,T=0,N,C;for(;;){if(x>=o.length)break;N=f.length;if(o[x]==null){for(C=0;C<s;++C)f.push(o[x+C]);x+=s}else if(T>=a.length){if(!m)for(C=0;C<s;++C)f.push(o[x+C]);x+=s}else if(a[T]==null){for(C=0;C<s;++C)f.push(null);w=!0,T+=u}else{l=o[x+E],c=o[x+S],p=a[T+E],d=a[T+S],v=0;if(l==p){for(C=0;C<s;++C)f.push(o[x+C]);f[N+S]+=d,v=d,x+=s,T+=u}else if(l>p){if(m&&x>0&&o[x-s]!=null){h=c+(o[x-s+S]-c)*(p-l)/(o[x-s+E]-l),f.push(p),f.push(h+d);for(C=2;C<s;++C)f.push(o[x+C]);v=d}T+=u}else{if(w&&m){x+=s;continue}for(C=0;C<s;++C)f.push(o[x+C]);m&&T>0&&a[T-u]!=null&&(v=d+(a[T-u+S]-d)*(l-p)/(a[T-u+E]-p)),f[N+S]+=v,x+=s}w=!1,N!=f.length&&y&&(f[N+2]+=v)}if(b&&N!=f.length&&N>0&&f[N]!=null&&f[N]!=f[N-s]&&f[N+1]!=f[N-s+1]){for(C=0;C<s;++C)f[N+s+C]=f[N+C];f[N+1]=f[N-s+1]}}r.points=f}e.hooks.processDatapoints.push(n)}var t={series:{stack:null}};e.plot.plugins.push({init:n,options:t,name:"stack",version:"1.2"})})(jQuery);;/* Pretty handling of time axes.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

Set axis.mode to "time" to enable. See the section "Time series data" in
API.txt for details.

*/(function(e){function n(e,t){return t*Math.floor(e/t)}function r(e,t,n,r){if(typeof e.strftime=="function")return e.strftime(t);var i=function(e,t){return e=""+e,t=""+(t==null?"0":t),e.length==1?t+e:e},s=[],o=!1,u=e.getHours(),a=u<12;n==null&&(n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),r==null&&(r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);var f;u>12?f=u-12:u==0?f=12:f=u;for(var l=0;l<t.length;++l){var c=t.charAt(l);if(o){switch(c){case"a":c=""+r[e.getDay()];break;case"b":c=""+n[e.getMonth()];break;case"d":c=i(e.getDate());break;case"e":c=i(e.getDate()," ");break;case"h":case"H":c=i(u);break;case"I":c=i(f);break;case"l":c=i(f," ");break;case"m":c=i(e.getMonth()+1);break;case"M":c=i(e.getMinutes());break;case"q":c=""+(Math.floor(e.getMonth()/3)+1);break;case"S":c=i(e.getSeconds());break;case"y":c=i(e.getFullYear()%100);break;case"Y":c=""+e.getFullYear();break;case"p":c=a?"am":"pm";break;case"P":c=a?"AM":"PM";break;case"w":c=""+e.getDay()}s.push(c),o=!1}else c=="%"?o=!0:s.push(c)}return s.join("")}function i(e){function t(e,t,n,r){e[t]=function(){return n[r].apply(n,arguments)}}var n={date:e};e.strftime!=undefined&&t(n,"strftime",e,"strftime"),t(n,"getTime",e,"getTime"),t(n,"setTime",e,"setTime");var r=["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds"];for(var i=0;i<r.length;i++)t(n,"get"+r[i],e,"getUTC"+r[i]),t(n,"set"+r[i],e,"setUTC"+r[i]);return n}function s(e,t){if(t.timezone=="browser")return new Date(e);if(!t.timezone||t.timezone=="utc")return i(new Date(e));if(typeof timezoneJS!="undefined"&&typeof timezoneJS.Date!="undefined"){var n=new timezoneJS.Date;return n.setTimezone(t.timezone),n.setTime(e),n}return i(new Date(e))}function l(t){t.hooks.processOptions.push(function(t,i){e.each(t.getAxes(),function(e,t){var i=t.options;i.mode=="time"&&(t.tickGenerator=function(e){var t=[],r=s(e.min,i),u=0,l=i.tickSize&&i.tickSize[1]==="quarter"||i.minTickSize&&i.minTickSize[1]==="quarter"?f:a;i.minTickSize!=null&&(typeof i.tickSize=="number"?u=i.tickSize:u=i.minTickSize[0]*o[i.minTickSize[1]]);for(var c=0;c<l.length-1;++c)if(e.delta<(l[c][0]*o[l[c][1]]+l[c+1][0]*o[l[c+1][1]])/2&&l[c][0]*o[l[c][1]]>=u)break;var h=l[c][0],p=l[c][1];if(p=="year"){if(i.minTickSize!=null&&i.minTickSize[1]=="year")h=Math.floor(i.minTickSize[0]);else{var d=Math.pow(10,Math.floor(Math.log(e.delta/o.year)/Math.LN10)),v=e.delta/o.year/d;v<1.5?h=1:v<3?h=2:v<7.5?h=5:h=10,h*=d}h<1&&(h=1)}e.tickSize=i.tickSize||[h,p];var m=e.tickSize[0];p=e.tickSize[1];var g=m*o[p];p=="second"?r.setSeconds(n(r.getSeconds(),m)):p=="minute"?r.setMinutes(n(r.getMinutes(),m)):p=="hour"?r.setHours(n(r.getHours(),m)):p=="month"?r.setMonth(n(r.getMonth(),m)):p=="quarter"?r.setMonth(3*n(r.getMonth()/3,m)):p=="year"&&r.setFullYear(n(r.getFullYear(),m)),r.setMilliseconds(0),g>=o.minute&&r.setSeconds(0),g>=o.hour&&r.setMinutes(0),g>=o.day&&r.setHours(0),g>=o.day*4&&r.setDate(1),g>=o.month*2&&r.setMonth(n(r.getMonth(),3)),g>=o.quarter*2&&r.setMonth(n(r.getMonth(),6)),g>=o.year&&r.setMonth(0);var y=0,b=Number.NaN,w;do{w=b,b=r.getTime(),t.push(b);if(p=="month"||p=="quarter")if(m<1){r.setDate(1);var E=r.getTime();r.setMonth(r.getMonth()+(p=="quarter"?3:1));var S=r.getTime();r.setTime(b+y*o.hour+(S-E)*m),y=r.getHours(),r.setHours(0)}else r.setMonth(r.getMonth()+m*(p=="quarter"?3:1));else p=="year"?r.setFullYear(r.getFullYear()+m):r.setTime(b+g)}while(b<e.max&&b!=w);return t},t.tickFormatter=function(e,t){var n=s(e,t.options);if(i.timeformat!=null)return r(n,i.timeformat,i.monthNames,i.dayNames);var u=t.options.tickSize&&t.options.tickSize[1]=="quarter"||t.options.minTickSize&&t.options.minTickSize[1]=="quarter",a=t.tickSize[0]*o[t.tickSize[1]],f=t.max-t.min,l=i.twelveHourClock?" %p":"",c=i.twelveHourClock?"%I":"%H",h;a<o.minute?h=c+":%M:%S"+l:a<o.day?f<2*o.day?h=c+":%M"+l:h="%b %d "+c+":%M"+l:a<o.month?h="%b %d":u&&a<o.quarter||!u&&a<o.year?f<o.year?h="%b":h="%b %Y":u&&a<o.year?f<o.year?h="Q%q":h="Q%q %Y":h="%Y";var p=r(n,h,i.monthNames,i.dayNames);return p})})})}var t={xaxis:{timezone:null,timeformat:null,twelveHourClock:!1,monthNames:null}},o={second:1e3,minute:6e4,hour:36e5,day:864e5,month:2592e6,quarter:7776e6,year:525949.2*60*1e3},u=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[.25,"month"],[.5,"month"],[1,"month"],[2,"month"]],a=u.concat([[3,"month"],[6,"month"],[1,"year"]]),f=u.concat([[1,"quarter"],[2,"quarter"],[1,"year"]]);e.plot.plugins.push({init:l,options:t,name:"time",version:"1.0"}),e.plot.formatDate=r})(jQuery);;/*
 * The MIT License

Copyright (c) 2013 by Sveinn Steinarsson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

(function ($) {
    "use strict";

    var floor = Math.floor,
        abs = Math.abs;

    function largestTriangleThreeBuckets(data, threshold) {

        var data_length = data.length;
        if (threshold >= data_length || threshold === 0) {
            return data; // Nothing to do
        }

        var sampled = [],
            sampled_index = 0;

        // Bucket size. Leave room for start and end data points
        var every = (data_length - 2) / (threshold - 2);

        var maxWidth = 5.0 * (data[data_length - 1][0] * 1 - data[0][0] * 1) / data_length;

        var a = 0,  // Initially a is the first point in the triangle
            max_area_point,
            max_area,
            area,
            next_a,
                data_range_offs;

        sampled[ sampled_index++ ] = data[ a ]; // Always add the first point

        for (var i = 0; i < threshold - 2; i++) {

            // Calculate point average for next bucket (containing c)
            var avg_x = 0,
                avg_y = 0,
                avg_range_start  = floor( ( i + 1 ) * every ) + 1,
                avg_range_end    = floor( ( i + 2 ) * every ) + 1;
            avg_range_end = avg_range_end < data_length ? avg_range_end : data_length;

            var avg_range_length = avg_range_end - avg_range_start;

            for ( ; avg_range_start<avg_range_end; avg_range_start++ ) {
              avg_x += data[ avg_range_start ][ 0 ] * 1; // * 1 enforces Number (value may be Date)
              avg_y += data[ avg_range_start ][ 1 ] * 1;
            }
            avg_x /= avg_range_length;
            avg_y /= avg_range_length;

            // Get the range for this bucket
            var range_offs = floor( (i + 0) * every ) + 1,
                range_to   = floor( (i + 1) * every ) + 1;

            // Point a
            var da = data[a];
            var point_a_x = da?da[ 0 ] * 1:0, // enforce Number (value may be Date)
                point_a_y = da?da[ 1 ] * 1:0;

            max_area = area = -1;

            for (; range_offs < range_to; range_offs++) {
                data_range_offs = data[range_offs];
                if (data_range_offs) {
                    if (!data_range_offs[1]) {
                        sampled[sampled_index++] = data_range_offs;
                    } else {
                        // Calculate triangle area over three buckets
                        area = abs((point_a_x - avg_x) * (data_range_offs[1] - point_a_y) -
                                    (point_a_x - data_range_offs[0]) * (avg_y - point_a_y)
                                  ) * 0.5;
                        if (area > max_area) {
                            max_area = area;
                            max_area_point = data_range_offs;
                            next_a = range_offs; // Next a is this b
                        }
                    }
                }
            }

            sampled[ sampled_index++ ] = max_area_point; // Pick this point from the bucket
            a = next_a; // This a is the next a (chosen b)
        }

        sampled[ sampled_index++ ] = data[ data_length - 1 ]; // Always add last

        return sampled;
    }


    function processRawData ( plot, series ) {
        series.data = largestTriangleThreeBuckets( series.data, series.downsample.threshold );
    }


    var options = {
        series: {
            downsample: {
                threshold: 1000 // 0 disables downsampling for this series.
            }
        }
    };

    function init(plot) {
        plot.hooks.processRawData.push(processRawData);
    }

    $.plot.plugins.push({
        init: init,
        options: options,
        name: "downsample",
        version: "1.0"
    });

})(jQuery);
;/* Flot plugin for showing crosshairs when the mouse hovers over the plot.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

The plugin supports these options:

	crosshair: {
		mode: null or "x" or "y" or "xy"
		color: color
		lineWidth: number
	}

Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical
crosshair that lets you trace the values on the x axis, "y" enables a
horizontal crosshair and "xy" enables them both. "color" is the color of the
crosshair (default is "rgba(170, 0, 0, 0.80)"), "lineWidth" is the width of
the drawn lines (default is 1).

The plugin also adds four public methods:

  - setCrosshair( pos )

    Set the position of the crosshair. Note that this is cleared if the user
    moves the mouse. "pos" is in coordinates of the plot and should be on the
    form { x: xpos, y: ypos } (you can use x2/x3/... if you're using multiple
    axes), which is coincidentally the same format as what you get from a
    "plothover" event. If "pos" is null, the crosshair is cleared.

  - clearCrosshair()

    Clear the crosshair.

  - lockCrosshair(pos)

    Cause the crosshair to lock to the current location, no longer updating if
    the user moves the mouse. Optionally supply a position (passed on to
    setCrosshair()) to move it to.

    Example usage:

	var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } };
	$("#graph").bind( "plothover", function ( evt, position, item ) {
		if ( item ) {
			// Lock the crosshair to the data point being hovered
			myFlot.lockCrosshair({
				x: item.datapoint[ 0 ],
				y: item.datapoint[ 1 ]
			});
		} else {
			// Return normal crosshair operation
			myFlot.unlockCrosshair();
		}
	});

  - unlockCrosshair()

    Free the crosshair to move again after locking it.
*/(function(e){function n(e){function n(n){if(t.locked)return;t.x!=-1&&(t.x=-1,e.triggerRedrawOverlay())}function r(n){if(t.locked)return;if(e.getSelection&&e.getSelection()){t.x=-1;return}var r=e.offset();t.x=Math.max(0,Math.min(n.pageX-r.left,e.width())),t.y=Math.max(0,Math.min(n.pageY-r.top,e.height())),e.triggerRedrawOverlay()}var t={x:-1,y:-1,locked:!1};e.setCrosshair=function(r){if(!r)t.x=-1;else{var i=e.p2c(r);t.x=Math.max(0,Math.min(i.left,e.width())),t.y=Math.max(0,Math.min(i.top,e.height()))}e.triggerRedrawOverlay()},e.clearCrosshair=e.setCrosshair,e.lockCrosshair=function(r){r&&e.setCrosshair(r),t.locked=!0},e.unlockCrosshair=function(){t.locked=!1},e.hooks.bindEvents.push(function(e,t){if(!e.getOptions().crosshair.mode)return;t.mouseout(n),t.mousemove(r)}),e.hooks.drawOverlay.push(function(e,n){var r=e.getOptions().crosshair;if(!r.mode)return;var i=e.getPlotOffset();n.save(),n.translate(i.left,i.top);if(t.x!=-1){var s=e.getOptions().crosshair.lineWidth%2===0?0:.5;n.strokeStyle=r.color,n.lineWidth=r.lineWidth,n.lineJoin="round",n.beginPath();if(r.mode.indexOf("x")!=-1){var o=Math.round(t.x)+s;n.moveTo(o,0),n.lineTo(o,e.height())}if(r.mode.indexOf("y")!=-1){var u=Math.round(t.y)+s;n.moveTo(0,u),n.lineTo(e.width(),u)}n.stroke()}n.restore()}),e.hooks.shutdown.push(function(e,t){t.unbind("mouseout",n),t.unbind("mousemove",r)})}var t={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};e.plot.plugins.push({init:n,options:t,name:"crosshair",version:"1.0"})})(jQuery);;/* The MIT License

 Copyright (c) 2011 by Michael Zinsmaier and nergal.dev
 Copyright (c) 2012 by Thomas Ritou

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */

	/*

	 ____________________________________________________

	 what it is:
	 ____________________________________________________

	 curvedLines is a plugin for flot, that tries to display lines in a smoother way.
	 The plugin is based on nergal.dev's work https://code.google.com/p/flot/issues/detail?id=226
	 and further extended with a mode that forces the min/max points of the curves to be on the
	 points. Both modes are achieved through adding of more data points
	 => 1) with large data sets you may get trouble
	 => 2) if you want to display the points too, you have to plot them as 2nd data series over the lines
	 
	 && 3) consecutive x data points are not allowed to have the same value

	 This is version 0.5 of curvedLines so it will probably not work in every case. However
	 the basic form of use descirbed next works (:

	 Feel free to further improve the code

	 ____________________________________________________

	 how to use it:
	 ____________________________________________________

	 var d1 = [[5,5],[7,3],[9,12]];

	 var options = { series: { curvedLines: {  active: true }}};

	 $.plot($("#placeholder"), [{data = d1, lines: { show: true}, curvedLines: {apply: true}}], options);

	 _____________________________________________________

	 options:
	 _____________________________________________________

	 active:           bool true => plugin can be used
	 apply:            bool true => series will be drawn as curved line
	 fit:              bool true => forces the max,mins of the curve to be on the datapoints
	 curvePointFactor  int  defines how many "virtual" points are used per "real" data point to
	 						emulate the curvedLines (points total = real points * curvePointFactor)
	 fitPointDist:     int  defines the x axis distance of the additional two points that are used
	 						to enforce the min max condition. 
	 						
	 + line options (since v0.5 curved lines use flots line implementation for drawing
	   => line options like fill, show ... are supported out of the box)

	 */

	/*
	 *  v0.1   initial commit
	 *  v0.15  negative values should work now (outcommented a negative -> 0 hook hope it does no harm)
	 *  v0.2   added fill option (thanks to monemihir) and multi axis support (thanks to soewono effendi)
	 *  v0.3   improved saddle handling and added basic handling of Dates
	 *  v0.4   rewritten fill option (thomas ritou) mostly from original flot code (now fill between points rather than to graph bottom), corrected fill Opacity bug
	 *  v0.5   rewritten instead of implementing a own draw function CurvedLines is now based on the processDatapoints flot hook (credits go to thomas ritou).
	 * 		   This change breakes existing code however CurvedLines are now just many tiny straight lines to flot and therefore all flot lines options (like gradient fill,
	 * 	       shadow) are now supported out of the box
	 *  v0.6   flot 0.8 compatibility and some bug fixes
	 */

	(function($) {

		var options = {
			series : {
				curvedLines : {
					active : false,
					apply: false,
					fit : false,
					curvePointFactor : 20,
					fitPointDist : undefined
				}
			}
		};

		function init(plot) {

			plot.hooks.processOptions.push(processOptions);

			//if the plugin is active register processDatapoints method
			function processOptions(plot, options) {
				if (options.series.curvedLines.active) {
					plot.hooks.processDatapoints.unshift(processDatapoints);
				}
			}

			//only if the plugin is active
			function processDatapoints(plot, series, datapoints) {
				var nrPoints = datapoints.points.length / datapoints.pointsize;
				var EPSILON = 0.5; //pretty large epsilon but save

				if (series.curvedLines.apply == true && series.originSeries === undefined && nrPoints > (1 + EPSILON)) {
					if (series.lines.fill) {

						var pointsTop = calculateCurvePoints(datapoints, series.curvedLines, 1)
						,pointsBottom = calculateCurvePoints(datapoints, series.curvedLines, 2); //flot makes sure for us that we've got a second y point if fill is true !

						//Merge top and bottom curve
						datapoints.pointsize = 3;
						datapoints.points = [];
						var j = 0;
						var k = 0;
						var i = 0;
						var ps = 2;
						while (i < pointsTop.length || j < pointsBottom.length) {
							if (pointsTop[i] == pointsBottom[j]) {
								datapoints.points[k] = pointsTop[i];
								datapoints.points[k + 1] = pointsTop[i + 1];
								datapoints.points[k + 2] = pointsBottom[j + 1];
								j += ps;
								i += ps;

							} else if (pointsTop[i] < pointsBottom[j]) {
								datapoints.points[k] = pointsTop[i];
								datapoints.points[k + 1] = pointsTop[i + 1];
								datapoints.points[k + 2] = k > 0 ? datapoints.points[k-1] : null;
								i += ps;
							} else {
								datapoints.points[k] = pointsBottom[j];
								datapoints.points[k + 1] = k > 1 ? datapoints.points[k-2] : null;
								datapoints.points[k + 2] = pointsBottom[j + 1];
								j += ps;
							}
							k += 3;
						}
					} else if (series.lines.lineWidth > 0) {
						datapoints.points = calculateCurvePoints(datapoints, series.curvedLines, 1);
						datapoints.pointsize = 2;
					}
				}
			}

		//no real idea whats going on here code mainly from https://code.google.com/p/flot/issues/detail?id=226
		//if fit option is selected additional datapoints get inserted before the curve calculations in nergal.dev s code.
			function calculateCurvePoints(datapoints, curvedLinesOptions, yPos) {

				var points = datapoints.points, ps = datapoints.pointsize;
				var num = curvedLinesOptions.curvePointFactor * (points.length / ps);

				var xdata = new Array;
				var ydata = new Array;
				
				var curX = -1;
				var curY = -1;
				var j = 0;

				if (curvedLinesOptions.fit) {
					//insert a point before and after the "real" data point to force the line
					//to have a max,min at the data point.
					
					var fpDist;
					if(typeof curvedLinesOptions.fitPointDist == 'undefined') {
						//estimate it
						var minX = points[0];
						var maxX = points[points.length-ps];			
						fpDist = (maxX - minX) / (500 * 100); //x range / (estimated pixel length of placeholder * factor)
					} else {
						//use user defined value
						fpDist = curvedLinesOptions.fitPointDist;
					}
					if (fpDist == 0) fpDist = 1;

					for (var i = 0; i < points.length; i += ps) {

						var frontX;
						var backX;
						curX = i;
						curY = i + yPos;

						//add point X s
						frontX = points[curX] - fpDist;
						backX = points[curX] + fpDist;
						
						var factor = 2;
						while (frontX == points[curX] || backX == points[curX]) {
							//inside the ulp
							frontX = points[curX] - (fpDist * factor);
							backX = points[curX] + (fpDist * factor);
							factor++;
						}												
						
						//add curve points
						xdata[j] = frontX;
						ydata[j] = points[curY];
						j++;

						xdata[j] = points[curX];
						ydata[j] = points[curY];
						j++;

						xdata[j] = backX;
						ydata[j] = points[curY];
						j++;
					}
				} else {
					//just use the datapoints
					for (var i = 0; i < points.length; i += ps) {
						curX = i;
						curY = i + yPos;

						xdata[j] = points[curX];
						ydata[j] = points[curY];
						j++;
					}
				}

				var n = xdata.length;

				var y2 = new Array();
				var delta = new Array();
				y2[0] = 0;
				y2[n - 1] = 0;
				delta[0] = 0;

				for (var i = 1; i < n - 1; ++i) {
					var d = (xdata[i + 1] - xdata[i - 1]);
					if (d == 0) {
						//point before current point and after current point need some space in between
						return [];
					}

					var s = (xdata[i] - xdata[i - 1]) / d;
					var p = s * y2[i - 1] + 2;
					y2[i] = (s - 1) / p;
					delta[i] = (ydata[i + 1] - ydata[i]) / (xdata[i + 1] - xdata[i]) - (ydata[i] - ydata[i - 1]) / (xdata[i] - xdata[i - 1]);
					delta[i] = (6 * delta[i] / (xdata[i + 1] - xdata[i - 1]) - s * delta[i - 1]) / p;
				}

				for (var j = n - 2; j >= 0; --j) {
					y2[j] = y2[j] * y2[j + 1] + delta[j];
				}

				//   xmax  - xmin  / #points
				var step = (xdata[n - 1] - xdata[0]) / (num - 1);

				var xnew = new Array;
				var ynew = new Array;
				var result = new Array;

				xnew[0] = xdata[0];
				ynew[0] = ydata[0];

				result.push(xnew[0]);
				result.push(ynew[0]);

				for ( j = 1; j < num; ++j) {
					//new x point (sampling point for the created curve)
					xnew[j] = xnew[0] + j * step;

					var max = n - 1;
					var min = 0;

					while (max - min > 1) {
						var k = Math.round((max + min) / 2);
						if (xdata[k] > xnew[j]) {
							max = k;
						} else {
							min = k;
						}
					}

					//found point one to the left and one to the right of generated new point
					var h = (xdata[max] - xdata[min]);

					if (h == 0) {
						//similar to above two points from original x data need some space between them
						return [];
					}

					var a = (xdata[max] - xnew[j]) / h;
					var b = (xnew[j] - xdata[min]) / h;

					ynew[j] = a * ydata[min] + b * ydata[max] + ((a * a * a - a) * y2[min] + (b * b * b - b) * y2[max]) * (h * h) / 6;
					
					result.push(xnew[j]);
					result.push(ynew[j]);
				}

				return result;
			}

		}//end init

		$.plot.plugins.push({
			init : init,
			options : options,
			name : 'curvedLines',
			version : '0.5'
		});

	})(jQuery);

;!function (t, n) { "object" == typeof exports && "undefined" != typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define(n) : t.Macy = n() }(this, function () { "use strict"; function t(t, n) { var e = void 0; return function () { e && clearTimeout(e), e = setTimeout(t, n) } } function n(t, n) { for (var e = t.length, o = e, r = []; e--;) r.push(n(t[o - e - 1])); return r } function e(t, n) { A(t, n, arguments.length > 2 && void 0 !== arguments[2] && arguments[2]) } function o(t) { for (var n = t.options, e = t.responsiveOptions, o = t.keys, r = t.docWidth, i = void 0, s = 0; s < o.length; s++) { var a = parseInt(o[s], 10); r >= a && (i = n.breakAt[a], O(i, e)) } return e } function r(t) { for (var n = t.options, e = t.responsiveOptions, o = t.keys, r = t.docWidth, i = void 0, s = o.length - 1; s >= 0; s--) { var a = parseInt(o[s], 10); r <= a && (i = n.breakAt[a], O(i, e)) } return e } function i(t) { var n = document.body.clientWidth, e = { columns: t.columns }; L(t.margin) ? e.margin = { x: t.margin.x, y: t.margin.y } : e.margin = { x: t.margin, y: t.margin }; var i = Object.keys(t.breakAt); return t.mobileFirst ? o({ options: t, responsiveOptions: e, keys: i, docWidth: n }) : r({ options: t, responsiveOptions: e, keys: i, docWidth: n }) } function s(t) { return i(t).columns } function a(t) { return i(t).margin } function c(t) { var n = !(arguments.length > 1 && void 0 !== arguments[1]) || arguments[1], e = s(t), o = a(t).x, r = 100 / e; return n ? 1 === e ? "100%" : (o = (e - 1) * o / e, "calc(" + r + "% - " + o + "px)") : r } function u(t, n) { var e = s(t.options), o = 0, r = void 0, i = void 0; return 1 === ++n ? 0 : (i = a(t.options).x, r = (i - (e - 1) * i / e) * (n - 1), o += c(t.options, !1) * (n - 1), "calc(" + o + "% + " + r + "px)") } function l(t) { var n = 0, e = t.container; m(t.rows, function (t) { n = t > n ? t : n }), e.style.height = n + "px" } function p(t, n) { var e = arguments.length > 2 && void 0 !== arguments[2] && arguments[2], o = !(arguments.length > 3 && void 0 !== arguments[3]) || arguments[3], r = s(t.options), i = a(t.options).y; C(t, r, e), m(n, function (n) { var e = 0, r = parseInt(n.offsetHeight, 10); isNaN(r) || (t.rows.forEach(function (n, o) { n < t.rows[e] && (e = o) }), n.style.position = "absolute", n.style.top = t.rows[e] + "px", n.style.left = "" + t.cols[e], t.rows[e] += isNaN(r) ? 0 : r + i, o && (n.dataset.macyComplete = 1)) }), o && (t.tmpRows = null), l(t) } function h(t, n) { var e = arguments.length > 2 && void 0 !== arguments[2] && arguments[2], o = !(arguments.length > 3 && void 0 !== arguments[3]) || arguments[3], r = s(t.options), i = a(t.options).y; C(t, r, e), m(n, function (n) { t.lastcol === r && (t.lastcol = 0); var e = M(n, "height"); e = parseInt(n.offsetHeight, 10), isNaN(e) || (n.style.position = "absolute", n.style.top = t.rows[t.lastcol] + "px", n.style.left = "" + t.cols[t.lastcol], t.rows[t.lastcol] += isNaN(e) ? 0 : e + i, t.lastcol += 1, o && (n.dataset.macyComplete = 1)) }), o && (t.tmpRows = null), l(t) } var f = function t(n, e) { if (!(this instanceof t)) return new t(n, e); if (n = n.replace(/^\s*/, "").replace(/\s*$/, ""), e) return this.byCss(n, e); for (var o in this.selectors) if (e = o.split("/"), new RegExp(e[1], e[2]).test(n)) return this.selectors[o](n); return this.byCss(n) }; f.prototype.byCss = function (t, n) { return (n || document).querySelectorAll(t) }, f.prototype.selectors = {}, f.prototype.selectors[/^\.[\w\-]+$/] = function (t) { return document.getElementsByClassName(t.substring(1)) }, f.prototype.selectors[/^\w+$/] = function (t) { return document.getElementsByTagName(t) }, f.prototype.selectors[/^\#[\w\-]+$/] = function (t) { return document.getElementById(t.substring(1)) }; var m = function (t, n) { for (var e = t.length, o = e; e--;) n(t[o - e - 1]) }, v = function () { var t = arguments.length > 0 && void 0 !== arguments[0] && arguments[0]; this.running = !1, this.events = [], this.add(t) }; v.prototype.run = function () { if (!this.running && this.events.length > 0) { var t = this.events.shift(); this.running = !0, t(), this.running = !1, this.run() } }, v.prototype.add = function () { var t = this, n = arguments.length > 0 && void 0 !== arguments[0] && arguments[0]; return !!n && (Array.isArray(n) ? m(n, function (n) { return t.add(n) }) : (this.events.push(n), void this.run())) }, v.prototype.clear = function () { this.events = [] }; var d = function (t) { var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; return this.instance = t, this.data = n, this }, g = function () { var t = arguments.length > 0 && void 0 !== arguments[0] && arguments[0]; this.events = {}, this.instance = t }; g.prototype.on = function () { var t = arguments.length > 0 && void 0 !== arguments[0] && arguments[0], n = arguments.length > 1 && void 0 !== arguments[1] && arguments[1]; return !(!t || !n) && (Array.isArray(this.events[t]) || (this.events[t] = []), this.events[t].push(n)) }, g.prototype.emit = function () { var t = arguments.length > 0 && void 0 !== arguments[0] && arguments[0], n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; if (!t || !Array.isArray(this.events[t])) return !1; var e = new d(this.instance, n); m(this.events[t], function (t) { return t(e) }) }; var y = function (t) { return !("naturalHeight" in t && t.naturalHeight + t.naturalWidth === 0) || t.width + t.height !== 0 }, E = function (t, n) { var e = arguments.length > 2 && void 0 !== arguments[2] && arguments[2]; return new Promise(function (t, e) { if (n.complete) return y(n) ? t(n) : e(n); n.addEventListener("load", function () { return y(n) ? t(n) : e(n) }), n.addEventListener("error", function () { return e(n) }) }).then(function (n) { e && t.emit(t.constants.EVENT_IMAGE_LOAD, { img: n }) }).catch(function (n) { return t.emit(t.constants.EVENT_IMAGE_ERROR, { img: n }) }) }, w = function (t, e) { var o = arguments.length > 2 && void 0 !== arguments[2] && arguments[2]; return n(e, function (n) { return E(t, n, o) }) }, A = function (t, n) { var e = arguments.length > 2 && void 0 !== arguments[2] && arguments[2]; return Promise.all(w(t, n, e)).then(function () { t.emit(t.constants.EVENT_IMAGE_COMPLETE) }) }, I = function (n) { return t(function () { n.emit(n.constants.EVENT_RESIZE), n.queue.add(function () { return n.recalculate(!0, !0) }) }, 100) }, N = function (t) { if (t.container = f(t.options.container), t.container instanceof f || !t.container) return !!t.options.debug && console.error("Error: Container not found"); delete t.options.container, t.container.length && (t.container = t.container[0]), t.container.style.position = "relative" }, T = function (t) { t.queue = new v, t.events = new g(t), t.rows = [], t.resizer = I(t) }, b = function (t) { var n = f("img", t.container); window.addEventListener("resize", t.resizer), t.on(t.constants.EVENT_IMAGE_LOAD, function () { return t.recalculate(!1, !1) }), t.on(t.constants.EVENT_IMAGE_COMPLETE, function () { return t.recalculate(!0, !0) }), t.options.useOwnImageLoader || e(t, n, !t.options.waitForImages), t.emit(t.constants.EVENT_INITIALIZED) }, _ = function (t) { N(t), T(t), b(t) }, L = function (t) { return t === Object(t) && "[object Array]" !== Object.prototype.toString.call(t) }, O = function (t, n) { L(t) || (n.columns = t), L(t) && t.columns && (n.columns = t.columns), L(t) && t.margin && !L(t.margin) && (n.margin = { x: t.margin, y: t.margin }), L(t) && t.margin && L(t.margin) && t.margin.x && (n.margin.x = t.margin.x), L(t) && t.margin && L(t.margin) && t.margin.y && (n.margin.y = t.margin.y) }, M = function (t, n) { return window.getComputedStyle(t, null).getPropertyValue(n) }, C = function (t, n) { var e = arguments.length > 2 && void 0 !== arguments[2] && arguments[2]; if (t.lastcol || (t.lastcol = 0), t.rows.length < 1 && (e = !0), e) { t.rows = [], t.cols = [], t.lastcol = 0; for (var o = n - 1; o >= 0; o--) t.rows[o] = 0, t.cols[o] = u(t, o) } else if (t.tmpRows) { t.rows = []; for (var o = n - 1; o >= 0; o--) t.rows[o] = t.tmpRows[o] } else { t.tmpRows = []; for (var o = n - 1; o >= 0; o--) t.tmpRows[o] = t.rows[o] } }, V = function (t) { var n = arguments.length > 1 && void 0 !== arguments[1] && arguments[1], e = !(arguments.length > 2 && void 0 !== arguments[2]) || arguments[2], o = n ? t.container.children : f(':scope > *:not([data-macy-complete="1"])', t.container), r = c(t.options); return m(o, function (t) { n && (t.dataset.macyComplete = 0), t.style.width = r }), t.options.trueOrder ? (h(t, o, n, e), t.emit(t.constants.EVENT_RECALCULATED)) : (p(t, o, n, e), t.emit(t.constants.EVENT_RECALCULATED)) }, R = Object.assign || function (t) { for (var n = 1; n < arguments.length; n++) { var e = arguments[n]; for (var o in e) Object.prototype.hasOwnProperty.call(e, o) && (t[o] = e[o]) } return t }, x = { columns: 4, margin: 2, trueOrder: !1, waitForImages: !1, useImageLoader: !0, breakAt: {}, useOwnImageLoader: !1, onInit: !1 }; !function () { try { document.createElement("a").querySelector(":scope *") } catch (t) { !function () { function t(t) { return function (e) { if (e && n.test(e)) { var o = this.getAttribute("id"); o || (this.id = "q" + Math.floor(9e6 * Math.random()) + 1e6), arguments[0] = e.replace(n, "#" + this.id); var r = t.apply(this, arguments); return null === o ? this.removeAttribute("id") : o || (this.id = o), r } return t.apply(this, arguments) } } var n = /:scope\b/gi, e = t(Element.prototype.querySelector); Element.prototype.querySelector = function (t) { return e.apply(this, arguments) }; var o = t(Element.prototype.querySelectorAll); Element.prototype.querySelectorAll = function (t) { return o.apply(this, arguments) } }() } }(); var q = function t() { var n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : x; if (!(this instanceof t)) return new t(n); this.options = {}, R(this.options, x, n), _(this) }; return q.init = function (t) { return console.warn("Depreciated: Macy.init will be removed in v3.0.0 opt to use Macy directly like so Macy({ /*options here*/ }) "), new q(t) }, q.prototype.recalculateOnImageLoad = function () { var t = arguments.length > 0 && void 0 !== arguments[0] && arguments[0]; return e(this, f("img", this.container), !t) }, q.prototype.runOnImageLoad = function (t) { var n = arguments.length > 1 && void 0 !== arguments[1] && arguments[1], o = f("img", this.container); return this.on(this.constants.EVENT_IMAGE_COMPLETE, t), n && this.on(this.constants.EVENT_IMAGE_LOAD, t), e(this, o, n) }, q.prototype.recalculate = function () { var t = this, n = arguments.length > 0 && void 0 !== arguments[0] && arguments[0], e = !(arguments.length > 1 && void 0 !== arguments[1]) || arguments[1]; return e && this.queue.clear(), this.queue.add(function () { return V(t, n, e) }) }, q.prototype.remove = function () { window.removeEventListener("resize", this.resizer), m(this.container.children, function (t) { t.removeAttribute("data-macy-complete"), t.removeAttribute("style") }), this.container.removeAttribute("style") }, q.prototype.reInit = function () { this.recalculate(!0, !0), this.emit(this.constants.EVENT_INITIALIZED), window.addEventListener("resize", this.resizer), this.container.style.position = "relative" }, q.prototype.on = function (t, n) { this.events.on(t, n) }, q.prototype.emit = function (t, n) { this.events.emit(t, n) }, q.constants = { EVENT_INITIALIZED: "macy.initialized", EVENT_RECALCULATED: "macy.recalculated", EVENT_IMAGE_LOAD: "macy.image.load", EVENT_IMAGE_ERROR: "macy.image.error", EVENT_IMAGE_COMPLETE: "macy.images.complete", EVENT_RESIZE: "macy.resize" }, q.prototype.constants = q.constants, q });
;/*! jssocials - v1.5.0 - 2017-04-30
* http://js-socials.com
* Copyright (c) 2017 Artem Tabalin; Licensed MIT */
(function(window, $, undefined) {

    var JSSOCIALS = "JSSocials",
        JSSOCIALS_DATA_KEY = JSSOCIALS;

    var getOrApply = function(value, context) {
        if($.isFunction(value)) {
            return value.apply(context, $.makeArray(arguments).slice(2));
        }
        return value;
    };

    var IMG_SRC_REGEX = /(\.(jpeg|png|gif|bmp|svg)$|^data:image\/(jpeg|png|gif|bmp|svg\+xml);base64)/i;
    var URL_PARAMS_REGEX = /(&?[a-zA-Z0-9]+=)?\{([a-zA-Z0-9]+)\}/g;

    var MEASURES = {
        "G": 1000000000,
        "M": 1000000,
        "K": 1000
    };

    var shares = {};

    function Socials(element, config) {
        var $element = $(element);

        $element.data(JSSOCIALS_DATA_KEY, this);

        this._$element = $element;

        this.shares = [];

        this._init(config);
        this._render();
    }

    Socials.prototype = {
        url: "",
        text: "",
        shareIn: "blank",

        showLabel: function(screenWidth) {
            return (this.showCount === false) ?
                (screenWidth > this.smallScreenWidth) :
                (screenWidth >= this.largeScreenWidth);
        },

        showCount: function(screenWidth) {
            return (screenWidth <= this.smallScreenWidth) ? "inside" : true;
        },

        smallScreenWidth: 640,
        largeScreenWidth: 1024,

        resizeTimeout: 200,

        elementClass: "jssocials",
        sharesClass: "jssocials-shares",
        shareClass: "jssocials-share",
        shareButtonClass: "jssocials-share-button",
        shareLinkClass: "jssocials-share-link",
        shareLogoClass: "jssocials-share-logo",
        shareLabelClass: "jssocials-share-label",
        shareLinkCountClass: "jssocials-share-link-count",
        shareCountBoxClass: "jssocials-share-count-box",
        shareCountClass: "jssocials-share-count",
        shareZeroCountClass: "jssocials-share-no-count",

        _init: function(config) {
            this._initDefaults();
            $.extend(this, config);
            this._initShares();
            this._attachWindowResizeCallback();
        },

        _initDefaults: function() {
            this.url = window.location.href;
            this.text = $.trim($("meta[name=description]").attr("content") || $("title").text());
        },

        _initShares: function() {
            this.shares = $.map(this.shares, $.proxy(function(shareConfig) {
                if(typeof shareConfig === "string") {
                    shareConfig = { share: shareConfig };
                }

                var share = (shareConfig.share && shares[shareConfig.share]);

                if(!share && !shareConfig.renderer) {
                    throw Error("Share '" + shareConfig.share + "' is not found");
                }

                return $.extend({ url: this.url, text: this.text }, share, shareConfig);
            }, this));
        },

        _attachWindowResizeCallback: function() {
            $(window).on("resize", $.proxy(this._windowResizeHandler, this));
        },

        _detachWindowResizeCallback: function() {
            $(window).off("resize", this._windowResizeHandler);
        },

        _windowResizeHandler: function() {
            if($.isFunction(this.showLabel) || $.isFunction(this.showCount)) {
                window.clearTimeout(this._resizeTimer);
                this._resizeTimer = setTimeout($.proxy(this.refresh, this), this.resizeTimeout);
            }
        },

        _render: function() {
            this._clear();

            this._defineOptionsByScreen();

            this._$element.addClass(this.elementClass);

            this._$shares = $("<div>").addClass(this.sharesClass)
                .appendTo(this._$element);

            this._renderShares();
        },

        _defineOptionsByScreen: function() {
            this._screenWidth = $(window).width();
            this._showLabel = getOrApply(this.showLabel, this, this._screenWidth);
            this._showCount = getOrApply(this.showCount, this, this._screenWidth);
        },

        _renderShares: function() {
            $.each(this.shares, $.proxy(function(_, share) {
                this._renderShare(share);
            }, this));
        },

        _renderShare: function(share) {
            var $share;

            if($.isFunction(share.renderer)) {
                $share = $(share.renderer());
            } else {
                $share = this._createShare(share);
            }

            $share.addClass(this.shareClass)
                .addClass(share.share ? "jssocials-share-" + share.share : "")
                .addClass(share.css)
                .appendTo(this._$shares);
        },

        _createShare: function(share) {
            var $result = $("<div>");
            var $shareLink = this._createShareLink(share).appendTo($result);

            if(this._showCount) {
                var isInsideCount = (this._showCount === "inside");
                var $countContainer = isInsideCount ? $shareLink : $("<div>").addClass(this.shareCountBoxClass).appendTo($result);
                $countContainer.addClass(isInsideCount ? this.shareLinkCountClass : this.shareCountBoxClass);
                this._renderShareCount(share, $countContainer);
            }

            return $result;
        },

        _createShareLink: function(share) {
            var shareStrategy = this._getShareStrategy(share);

            var $result = shareStrategy.call(share, {
                shareUrl: this._getShareUrl(share)
            });

            $result.addClass(this.shareLinkClass)
                .append(this._createShareLogo(share));

            if(this._showLabel) {
                $result.append(this._createShareLabel(share));
            }

            $.each(this.on || {}, function(event, handler) {
                if($.isFunction(handler)) {
                    $result.on(event, $.proxy(handler, share));
                }
            });

            return $result;
        },

        _getShareStrategy: function(share) {
            var result = shareStrategies[share.shareIn || this.shareIn];

            if(!result)
                throw Error("Share strategy '" + this.shareIn + "' not found");

            return result;
        },

        _getShareUrl: function(share) {
            var shareUrl = getOrApply(share.shareUrl, share);
            return this._formatShareUrl(shareUrl, share);
        },

        _createShareLogo: function(share) {
            var logo = share.logo;

            var $result = IMG_SRC_REGEX.test(logo) ?
                $("<img>").attr("src", share.logo) :
                $("<i>").addClass(logo);

            $result.addClass(this.shareLogoClass);

            return $result;
        },

        _createShareLabel: function(share) {
            return $("<span>").addClass(this.shareLabelClass)
                .text(share.label);
        },

        _renderShareCount: function(share, $container) {
            var $count = $("<span>").addClass(this.shareCountClass);

            $container.addClass(this.shareZeroCountClass)
                .append($count);

            this._loadCount(share).done($.proxy(function(count) {
                if(count) {
                    $container.removeClass(this.shareZeroCountClass);
                    $count.text(count);
                }
            }, this));
        },

        _loadCount: function(share) {
            var deferred = $.Deferred();
            var countUrl = this._getCountUrl(share);

            if(!countUrl) {
                return deferred.resolve(0).promise();
            }

            var handleSuccess = $.proxy(function(response) {
                deferred.resolve(this._getCountValue(response, share));
            }, this);

            $.getJSON(countUrl).done(handleSuccess)
                .fail(function() {
                    $.get(countUrl).done(handleSuccess)
                        .fail(function() {
                            deferred.resolve(0);
                        });
                });

            return deferred.promise();
        },

        _getCountUrl: function(share) {
            var countUrl = getOrApply(share.countUrl, share);
            return this._formatShareUrl(countUrl, share);
        },

        _getCountValue: function(response, share) {
            var count = ($.isFunction(share.getCount) ? share.getCount(response) : response) || 0;
            return (typeof count === "string") ? count : this._formatNumber(count);
        },

        _formatNumber: function(number) {
            $.each(MEASURES, function(letter, value) {
                if(number >= value) {
                    number = parseFloat((number / value).toFixed(2)) + letter;
                    return false;
                }
            });

            return number;
        },

        _formatShareUrl: function(url, share) {
            return url.replace(URL_PARAMS_REGEX, function(match, key, field) {
                var value = share[field] || "";
                return value ? (key || "") + window.encodeURIComponent(value) : "";
            });
        },

        _clear: function() {
            window.clearTimeout(this._resizeTimer);
            this._$element.empty();
        },

        _passOptionToShares: function(key, value) {
            var shares = this.shares;

            $.each(["url", "text"], function(_, optionName) {
                if(optionName !== key)
                    return;

                $.each(shares, function(_, share) {
                    share[key] = value;
                });
            });
        },

        _normalizeShare: function(share) {
            if($.isNumeric(share)) {
                return this.shares[share];
            }

            if(typeof share === "string") {
                return $.grep(this.shares, function(s) {
                    return s.share === share;
                })[0];
            }

            return share;
        },

        refresh: function() {
            this._render();
        },

        destroy: function() {
            this._clear();
            this._detachWindowResizeCallback();

            this._$element
                .removeClass(this.elementClass)
                .removeData(JSSOCIALS_DATA_KEY);
        },

        option: function(key, value) {
            if(arguments.length === 1) {
                return this[key];
            }

            this[key] = value;

            this._passOptionToShares(key, value);

            this.refresh();
        },

        shareOption: function(share, key, value) {
            share = this._normalizeShare(share);

            if(arguments.length === 2) {
                return share[key];
            }

            share[key] = value;
            this.refresh();
        }
    };


    $.fn.jsSocials = function(config) {
        var args = $.makeArray(arguments),
            methodArgs = args.slice(1),
            result = this;

        this.each(function() {
            var $element = $(this),
                instance = $element.data(JSSOCIALS_DATA_KEY),
                methodResult;

            if(instance) {
                if(typeof config === "string") {
                    methodResult = instance[config].apply(instance, methodArgs);
                    if(methodResult !== undefined && methodResult !== instance) {
                        result = methodResult;
                        return false;
                    }
                } else {
                    instance._detachWindowResizeCallback();
                    instance._init(config);
                    instance._render();
                }
            } else {
                new Socials($element, config);
            }
        });

        return result;
    };

    var setDefaults = function(config) {
        var component;

        if($.isPlainObject(config)) {
            component = Socials.prototype;
        } else {
            component = shares[config];
            config = arguments[1] || {};
        }

        $.extend(component, config);
    };

    var shareStrategies = {
        popup: function(args) {
            return $("<a>").attr("href", "#")
                .on("click", function() {
                    window.open(args.shareUrl, null, "width=600, height=400, location=0, menubar=0, resizeable=0, scrollbars=0, status=0, titlebar=0, toolbar=0");
                    return false;
                });
        },

        blank: function(args) {
            return $("<a>").attr({ target: "_blank", href: args.shareUrl });
        },

        self: function(args) {
            return $("<a>").attr({ target: "_self", href: args.shareUrl });
        }
    };

    window.jsSocials = {
        Socials: Socials,
        shares: shares,
        shareStrategies: shareStrategies,
        setDefaults: setDefaults
    };

}(window, jQuery));


(function(window, $, jsSocials, undefined) {

    $.extend(jsSocials.shares, {

        email: {
            label: "E-mail",
            logo: "fa fa-at",
            shareUrl: "mailto:{to}?subject={text}&body={url}",
            countUrl: "",
            shareIn: "self"
        },

        twitter: {
            label: "Tweeter",
            logo: "fa fa-twitter",
            shareUrl: "https://twitter.com/share?url={url}&text={text}&via={via}&hashtags={hashtags}",
            countUrl: ""
        },

        facebook: {
            label: "Facebook",
            logo: "fa fa-facebook",
            shareUrl: "https://facebook.com/sharer/sharer.php?u={url}",
            countUrl: "https://graph.facebook.com/?id={url}",
            getCount: function(data) {
                return data.share && data.share.share_count || 0;
            }
        },

        vkontakte: {
            label: "Like",
            logo: "fa fa-vk",
            shareUrl: "https://vk.com/share.php?url={url}&title={title}&description={text}",
            countUrl: "https://vk.com/share.php?act=count&index=1&url={url}",
            getCount: function(data) {
                return parseInt(data.slice(15, -2).split(', ')[1]);
            }
        },

        googleplus: {
            label: "Google+",
            logo: "fa fa-google",
            shareUrl: "https://plus.google.com/share?url={url}",
            countUrl: ""
        },

        linkedin: {
            label: "LinkedIn",
            logo: "fa fa-linkedin",
            shareUrl: "https://www.linkedin.com/shareArticle?mini=true&url={url}&summary={text}",
            countUrl: "https://www.linkedin.com/countserv/count/share?format=jsonp&url={url}&callback=?",
            getCount: function(data) {
                return data.count;
            }
        },

        pinterest: {
            label: "Pin it",
            logo: "fa fa-pinterest",
            shareUrl: "https://pinterest.com/pin/create/bookmarklet/?media={media}&url={url}&description={text}",
            countUrl: "https://api.pinterest.com/v1/urls/count.json?&url={url}&callback=?",
            getCount: function(data) {
                return data.count;
            }
        },

        stumbleupon: {
            label: "Share",
            logo: "fa fa-stumbleupon",
            shareUrl: "http://www.stumbleupon.com/submit?url={url}&title={title}",
            countUrl:  "https://cors-anywhere.herokuapp.com/https://www.stumbleupon.com/services/1.01/badge.getinfo?url={url}",
            getCount: function(data) {
                return data.result && data.result.views;
            }
        },

        telegram: {
            label: "Telegram",
            logo: "fa fa-telegram",
            shareUrl: "tg://msg?text={url} {text}",
            countUrl: "",
            shareIn: "self"
        },

        whatsapp: {
            label: "WhatsApp",
            logo: "fa fa-whatsapp",
            shareUrl: "whatsapp://send?text={url} {text}",
            countUrl: "",
            shareIn: "self"
        },

        line: {
            label: "LINE",
            logo: "fa fa-comment",
            shareUrl: "http://line.me/R/msg/text/?{text} {url}",
            countUrl: ""
        },

        viber: {
            label: "Viber",
            logo: "fa fa-volume-control-phone",
            shareUrl: "viber://forward?text={url} {text}",
            countUrl: "",
            shareIn: "self"
        },

        pocket: {
            label: "Pocket",
            logo: "fa fa-get-pocket",
            shareUrl: "https://getpocket.com/save?url={url}&title={title}",
            countUrl: ""
        },

        messenger: {
            label: "Messenger",
            logo: "fa fa-commenting",
            shareUrl: "fb-messenger://share?link={url}",
            countUrl: "",
            shareIn: "self"
        },
        rss: {
            label: "RSS",
            logo: "fa fa-rss",
            shareUrl: "/feeds/",
            countUrl: "",
            shareIn: "blank"
        }

    });

}(window, jQuery, window.jsSocials));

;/*!
 * Cropper.js v1.3.5
 * https://github.com/fengyuanchen/cropperjs
 *
 * Copyright (c) 2015-2018 Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2018-04-15T06:20:44.574Z
 */
!function (t, i) { "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : t.Cropper = i() }(this, function () { "use strict"; var n = "undefined" != typeof window, h = n ? window : {}, d = "cropper", k = "all", T = "crop", W = "move", E = "zoom", H = "e", N = "w", L = "s", z = "n", O = "ne", Y = "nw", X = "se", R = "sw", r = d + "-crop", t = d + "-disabled", S = d + "-hidden", l = d + "-hide", o = d + "-modal", p = d + "-move", m = d + "Action", g = d + "Preview", s = "crop", c = "move", u = "none", a = "crop", f = "cropend", v = "cropmove", w = "cropstart", b = "dblclick", x = h.PointerEvent ? "pointerdown" : "touchstart mousedown", y = h.PointerEvent ? "pointermove" : "touchmove mousemove", M = h.PointerEvent ? "pointerup pointercancel" : "touchend touchcancel mouseup", C = "ready", D = "resize", B = "wheel mousewheel DOMMouseScroll", A = "zoom", I = /^(?:e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/, U = /^data:/, j = /^data:image\/jpeg;base64,/, P = /^(?:img|canvas)$/i, q = { viewMode: 0, dragMode: s, aspectRatio: NaN, data: null, preview: "", responsive: !0, restore: !0, checkCrossOrigin: !0, checkOrientation: !0, modal: !0, guides: !0, center: !0, highlight: !0, background: !0, autoCrop: !0, autoCropArea: .8, movable: !0, rotatable: !0, scalable: !0, zoomable: !0, zoomOnTouch: !0, zoomOnWheel: !0, wheelZoomRatio: .1, cropBoxMovable: !0, cropBoxResizable: !0, toggleDragModeOnDblclick: !0, minCanvasWidth: 0, minCanvasHeight: 0, minCropBoxWidth: 0, minCropBoxHeight: 0, minContainerWidth: 200, minContainerHeight: 100, ready: null, cropstart: null, cropmove: null, cropend: null, crop: null, zoom: null }, i = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t }, $ = function (t, i) { if (!(t instanceof i)) throw new TypeError("Cannot call a class as a function") }, Q = function () { function a(t, i) { for (var e = 0; e < i.length; e++) { var a = i[e]; a.enumerable = a.enumerable || !1, a.configurable = !0, "value" in a && (a.writable = !0), Object.defineProperty(t, a.key, a) } } return function (t, i, e) { return i && a(t.prototype, i), e && a(t, e), t } }(), bt = function (t) { if (Array.isArray(t)) { for (var i = 0, e = Array(t.length) ; i < t.length; i++) e[i] = t[i]; return e } return Array.from(t) }, e = Number.isNaN || h.isNaN; function Z(t) { return "number" == typeof t && !e(t) } function F(t) { return void 0 === t } function K(t) { return "object" === (void 0 === t ? "undefined" : i(t)) && null !== t } var V = Object.prototype.hasOwnProperty; function G(t) { if (!K(t)) return !1; try { var i = t.constructor, e = i.prototype; return i && e && V.call(e, "isPrototypeOf") } catch (t) { return !1 } } function J(t) { return "function" == typeof t } function _(i, e) { if (i && J(e)) if (Array.isArray(i) || Z(i.length)) { var t = i.length, a = void 0; for (a = 0; a < t && !1 !== e.call(i, i[a], a, i) ; a += 1); } else K(i) && Object.keys(i).forEach(function (t) { e.call(i, i[t], t, i) }); return i } var tt = Object.assign || function (e) { for (var t = arguments.length, i = Array(1 < t ? t - 1 : 0), a = 1; a < t; a++) i[a - 1] = arguments[a]; return K(e) && 0 < i.length && i.forEach(function (i) { K(i) && Object.keys(i).forEach(function (t) { e[t] = i[t] }) }), e }, it = /\.\d*(?:0|9){12}\d*$/i; function xt(t) { var i = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : 1e11; return it.test(t) ? Math.round(t * i) / i : t } var et = /^(?:width|height|left|top|marginLeft|marginTop)$/; function at(t, i) { var e = t.style; _(i, function (t, i) { et.test(i) && Z(t) && (t += "px"), e[i] = t }) } function nt(t, i) { if (i) if (Z(t.length)) _(t, function (t) { nt(t, i) }); else if (t.classList) t.classList.add(i); else { var e = t.className.trim(); e ? e.indexOf(i) < 0 && (t.className = e + " " + i) : t.className = i } } function ot(t, i) { i && (Z(t.length) ? _(t, function (t) { ot(t, i) }) : t.classList ? t.classList.remove(i) : 0 <= t.className.indexOf(i) && (t.className = t.className.replace(i, ""))) } function ht(t, i, e) { i && (Z(t.length) ? _(t, function (t) { ht(t, i, e) }) : e ? nt(t, i) : ot(t, i)) } var rt = /([a-z\d])([A-Z])/g; function st(t) { return t.replace(rt, "$1-$2").toLowerCase() } function ct(t, i) { return K(t[i]) ? t[i] : t.dataset ? t.dataset[i] : t.getAttribute("data-" + st(i)) } function dt(t, i, e) { K(e) ? t[i] = e : t.dataset ? t.dataset[i] = e : t.setAttribute("data-" + st(i), e) } function lt(i, e) { if (K(i[e])) try { delete i[e] } catch (t) { i[e] = void 0 } else if (i.dataset) try { delete i.dataset[e] } catch (t) { i.dataset[e] = void 0 } else i.removeAttribute("data-" + st(e)) } var pt = /\s\s*/, mt = function () { var t = !1; if (n) { var i = !1, e = function () { }, a = Object.defineProperty({}, "once", { get: function () { return t = !0, i }, set: function (t) { i = t } }); h.addEventListener("test", e, a), h.removeEventListener("test", e, a) } return t }(); function gt(e, t, a) { var n = 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : {}, o = a; t.trim().split(pt).forEach(function (t) { if (!mt) { var i = e.listeners; i && i[t] && i[t][a] && (o = i[t][a], delete i[t][a], 0 === Object.keys(i[t]).length && delete i[t], 0 === Object.keys(i).length && delete e.listeners) } e.removeEventListener(t, o, n) }) } function ut(o, t, h) { var r = 3 < arguments.length && void 0 !== arguments[3] ? arguments[3] : {}, s = h; t.trim().split(pt).forEach(function (a) { if (r.once && !mt) { var t = o.listeners, n = void 0 === t ? {} : t; s = function () { for (var t = arguments.length, i = Array(t), e = 0; e < t; e++) i[e] = arguments[e]; delete n[a][h], o.removeEventListener(a, s, r), h.apply(o, i) }, n[a] || (n[a] = {}), n[a][h] && o.removeEventListener(a, n[a][h], r), n[a][h] = s, o.listeners = n } o.addEventListener(a, s, r) }) } function ft(t, i, e) { var a = void 0; return J(Event) && J(CustomEvent) ? a = new CustomEvent(i, { detail: e, bubbles: !0, cancelable: !0 }) : (a = document.createEvent("CustomEvent")).initCustomEvent(i, !0, !0, e), t.dispatchEvent(a) } function vt(t) { var i = t.getBoundingClientRect(); return { left: i.left + (window.pageXOffset - document.documentElement.clientLeft), top: i.top + (window.pageYOffset - document.documentElement.clientTop) } } var wt = h.location, yt = /^(https?:)\/\/([^:/?#]+):?(\d*)/i; function Mt(t) { var i = t.match(yt); return i && (i[1] !== wt.protocol || i[2] !== wt.hostname || i[3] !== wt.port) } function Ct(t) { var i = "timestamp=" + (new Date).getTime(); return t + (-1 === t.indexOf("?") ? "?" : "&") + i } function Dt(t) { var i = t.rotate, e = t.scaleX, a = t.scaleY, n = t.translateX, o = t.translateY, h = []; Z(n) && 0 !== n && h.push("translateX(" + n + "px)"), Z(o) && 0 !== o && h.push("translateY(" + o + "px)"), Z(i) && 0 !== i && h.push("rotate(" + i + "deg)"), Z(e) && 1 !== e && h.push("scaleX(" + e + ")"), Z(a) && 1 !== a && h.push("scaleY(" + a + ")"); var r = h.length ? h.join(" ") : "none"; return { WebkitTransform: r, msTransform: r, transform: r } } function Bt(t, i) { var e = t.pageX, a = t.pageY, n = { endX: e, endY: a }; return i ? n : tt({ startX: e, startY: a }, n) } var kt = Number.isFinite || h.isFinite; function Tt(t) { var i = t.aspectRatio, e = t.height, a = t.width, n = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : "contain", o = function (t) { return kt(t) && 0 < t }; if (o(a) && o(e)) { var h = e * i; "contain" === n && a < h || "cover" === n && h < a ? e = a / i : a = e * i } else o(a) ? e = a / i : o(e) && (a = e * i); return { width: a, height: e } } var Wt = String.fromCharCode; var Et = /^data:.*,/; function Ht(t) { var i = new DataView(t), e = void 0, a = void 0, n = void 0, o = void 0; if (255 === i.getUint8(0) && 216 === i.getUint8(1)) for (var h = i.byteLength, r = 2; r < h;) { if (255 === i.getUint8(r) && 225 === i.getUint8(r + 1)) { n = r; break } r += 1 } if (n) { var s = n + 10; if ("Exif" === function (t, i, e) { var a = "", n = void 0; for (e += i, n = i; n < e; n += 1) a += Wt(t.getUint8(n)); return a }(i, n + 4, 4)) { var c = i.getUint16(s); if (((a = 18761 === c) || 19789 === c) && 42 === i.getUint16(s + 2, a)) { var d = i.getUint32(s + 4, a); 8 <= d && (o = s + d) } } } if (o) { var l = i.getUint16(o, a), p = void 0, m = void 0; for (m = 0; m < l; m += 1) if (p = o + 12 * m + 2, 274 === i.getUint16(p, a)) { p += 8, e = i.getUint16(p, a), i.setUint16(p, 1, a); break } } return e } var Nt = { render: function () { this.initContainer(), this.initCanvas(), this.initCropBox(), this.renderCanvas(), this.cropped && this.renderCropBox() }, initContainer: function () { var t = this.element, i = this.options, e = this.container, a = this.cropper; nt(a, S), ot(t, S); var n = { width: Math.max(e.offsetWidth, Number(i.minContainerWidth) || 200), height: Math.max(e.offsetHeight, Number(i.minContainerHeight) || 100) }; at(a, { width: (this.containerData = n).width, height: n.height }), nt(t, S), ot(a, S) }, initCanvas: function () { var t = this.containerData, i = this.imageData, e = this.options.viewMode, a = Math.abs(i.rotate) % 180 == 90, n = a ? i.naturalHeight : i.naturalWidth, o = a ? i.naturalWidth : i.naturalHeight, h = n / o, r = t.width, s = t.height; t.height * h > t.width ? 3 === e ? r = t.height * h : s = t.width / h : 3 === e ? s = t.width / h : r = t.height * h; var c = { aspectRatio: h, naturalWidth: n, naturalHeight: o, width: r, height: s }; c.left = (t.width - r) / 2, c.top = (t.height - s) / 2, c.oldLeft = c.left, c.oldTop = c.top, this.canvasData = c, this.limited = 1 === e || 2 === e, this.limitCanvas(!0, !0), this.initialImageData = tt({}, i), this.initialCanvasData = tt({}, c) }, limitCanvas: function (t, i) { var e = this.options, a = this.containerData, n = this.canvasData, o = this.cropBoxData, h = e.viewMode, r = n.aspectRatio, s = this.cropped && o; if (t) { var c = Number(e.minCanvasWidth) || 0, d = Number(e.minCanvasHeight) || 0; 1 < h ? (c = Math.max(c, a.width), d = Math.max(d, a.height), 3 === h && (c < d * r ? c = d * r : d = c / r)) : 0 < h && (c ? c = Math.max(c, s ? o.width : 0) : d ? d = Math.max(d, s ? o.height : 0) : s && ((c = o.width) < (d = o.height) * r ? c = d * r : d = c / r)); var l = Tt({ aspectRatio: r, width: c, height: d }); c = l.width, d = l.height, n.minWidth = c, n.minHeight = d, n.maxWidth = 1 / 0, n.maxHeight = 1 / 0 } if (i) if (h) { var p = a.width - n.width, m = a.height - n.height; n.minLeft = Math.min(0, p), n.minTop = Math.min(0, m), n.maxLeft = Math.max(0, p), n.maxTop = Math.max(0, m), s && this.limited && (n.minLeft = Math.min(o.left, o.left + (o.width - n.width)), n.minTop = Math.min(o.top, o.top + (o.height - n.height)), n.maxLeft = o.left, n.maxTop = o.top, 2 === h && (n.width >= a.width && (n.minLeft = Math.min(0, p), n.maxLeft = Math.max(0, p)), n.height >= a.height && (n.minTop = Math.min(0, m), n.maxTop = Math.max(0, m)))) } else n.minLeft = -n.width, n.minTop = -n.height, n.maxLeft = a.width, n.maxTop = a.height }, renderCanvas: function (t, i) { var e = this.canvasData, a = this.imageData; if (i) { var n = function (t) { var i = t.width, e = t.height, a = t.degree; if (90 == (a = Math.abs(a) % 180)) return { width: e, height: i }; var n = a % 90 * Math.PI / 180, o = Math.sin(n), h = Math.cos(n), r = i * h + e * o, s = i * o + e * h; return 90 < a ? { width: s, height: r } : { width: r, height: s } }({ width: a.naturalWidth * Math.abs(a.scaleX || 1), height: a.naturalHeight * Math.abs(a.scaleY || 1), degree: a.rotate || 0 }), o = n.width, h = n.height, r = e.width * (o / e.naturalWidth), s = e.height * (h / e.naturalHeight); e.left -= (r - e.width) / 2, e.top -= (s - e.height) / 2, e.width = r, e.height = s, e.aspectRatio = o / h, e.naturalWidth = o, e.naturalHeight = h, this.limitCanvas(!0, !1) } (e.width > e.maxWidth || e.width < e.minWidth) && (e.left = e.oldLeft), (e.height > e.maxHeight || e.height < e.minHeight) && (e.top = e.oldTop), e.width = Math.min(Math.max(e.width, e.minWidth), e.maxWidth), e.height = Math.min(Math.max(e.height, e.minHeight), e.maxHeight), this.limitCanvas(!1, !0), e.left = Math.min(Math.max(e.left, e.minLeft), e.maxLeft), e.top = Math.min(Math.max(e.top, e.minTop), e.maxTop), e.oldLeft = e.left, e.oldTop = e.top, at(this.canvas, tt({ width: e.width, height: e.height }, Dt({ translateX: e.left, translateY: e.top }))), this.renderImage(t), this.cropped && this.limited && this.limitCropBox(!0, !0) }, renderImage: function (t) { var i = this.canvasData, e = this.imageData, a = e.naturalWidth * (i.width / i.naturalWidth), n = e.naturalHeight * (i.height / i.naturalHeight); tt(e, { width: a, height: n, left: (i.width - a) / 2, top: (i.height - n) / 2 }), at(this.image, tt({ width: e.width, height: e.height }, Dt(tt({ translateX: e.left, translateY: e.top }, e)))), t && this.output() }, initCropBox: function () { var t = this.options, i = this.canvasData, e = t.aspectRatio, a = Number(t.autoCropArea) || .8, n = { width: i.width, height: i.height }; e && (i.height * e > i.width ? n.height = n.width / e : n.width = n.height * e), this.cropBoxData = n, this.limitCropBox(!0, !0), n.width = Math.min(Math.max(n.width, n.minWidth), n.maxWidth), n.height = Math.min(Math.max(n.height, n.minHeight), n.maxHeight), n.width = Math.max(n.minWidth, n.width * a), n.height = Math.max(n.minHeight, n.height * a), n.left = i.left + (i.width - n.width) / 2, n.top = i.top + (i.height - n.height) / 2, n.oldLeft = n.left, n.oldTop = n.top, this.initialCropBoxData = tt({}, n) }, limitCropBox: function (t, i) { var e = this.options, a = this.containerData, n = this.canvasData, o = this.cropBoxData, h = this.limited, r = e.aspectRatio; if (t) { var s = Number(e.minCropBoxWidth) || 0, c = Number(e.minCropBoxHeight) || 0, d = Math.min(a.width, h ? n.width : a.width), l = Math.min(a.height, h ? n.height : a.height); s = Math.min(s, a.width), c = Math.min(c, a.height), r && (s && c ? s < c * r ? c = s / r : s = c * r : s ? c = s / r : c && (s = c * r), d < l * r ? l = d / r : d = l * r), o.minWidth = Math.min(s, d), o.minHeight = Math.min(c, l), o.maxWidth = d, o.maxHeight = l } i && (h ? (o.minLeft = Math.max(0, n.left), o.minTop = Math.max(0, n.top), o.maxLeft = Math.min(a.width, n.left + n.width) - o.width, o.maxTop = Math.min(a.height, n.top + n.height) - o.height) : (o.minLeft = 0, o.minTop = 0, o.maxLeft = a.width - o.width, o.maxTop = a.height - o.height)) }, renderCropBox: function () { var t = this.options, i = this.containerData, e = this.cropBoxData; (e.width > e.maxWidth || e.width < e.minWidth) && (e.left = e.oldLeft), (e.height > e.maxHeight || e.height < e.minHeight) && (e.top = e.oldTop), e.width = Math.min(Math.max(e.width, e.minWidth), e.maxWidth), e.height = Math.min(Math.max(e.height, e.minHeight), e.maxHeight), this.limitCropBox(!1, !0), e.left = Math.min(Math.max(e.left, e.minLeft), e.maxLeft), e.top = Math.min(Math.max(e.top, e.minTop), e.maxTop), e.oldLeft = e.left, e.oldTop = e.top, t.movable && t.cropBoxMovable && dt(this.face, m, e.width >= i.width && e.height >= i.height ? W : k), at(this.cropBox, tt({ width: e.width, height: e.height }, Dt({ translateX: e.left, translateY: e.top }))), this.cropped && this.limited && this.limitCanvas(!0, !0), this.disabled || this.output() }, output: function () { this.preview(), ft(this.element, a, this.getData()) } }, Lt = { initPreview: function () { var e = this.crossOrigin, t = this.options.preview, a = e ? this.crossOriginUrl : this.url, i = document.createElement("img"); if (e && (i.crossOrigin = e), i.src = a, this.viewBox.appendChild(i), this.viewBoxImage = i, t) { var n = t; "string" == typeof t ? n = this.element.ownerDocument.querySelectorAll(t) : t.querySelector && (n = [t]), _(this.previews = n, function (t) { var i = document.createElement("img"); dt(t, g, { width: t.offsetWidth, height: t.offsetHeight, html: t.innerHTML }), e && (i.crossOrigin = e), i.src = a, i.style.cssText = 'display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;"', t.innerHTML = "", t.appendChild(i) }) } }, resetPreview: function () { _(this.previews, function (t) { var i = ct(t, g); at(t, { width: i.width, height: i.height }), t.innerHTML = i.html, lt(t, g) }) }, preview: function () { var r = this.imageData, t = this.canvasData, i = this.cropBoxData, s = i.width, c = i.height, d = r.width, l = r.height, p = i.left - t.left - r.left, m = i.top - t.top - r.top; this.cropped && !this.disabled && (at(this.viewBoxImage, tt({ width: d, height: l }, Dt(tt({ translateX: -p, translateY: -m }, r)))), _(this.previews, function (t) { var i = ct(t, g), e = i.width, a = i.height, n = e, o = a, h = 1; s && (o = c * (h = e / s)), c && a < o && (n = s * (h = a / c), o = a), at(t, { width: n, height: o }), at(t.getElementsByTagName("img")[0], tt({ width: d * h, height: l * h }, Dt(tt({ translateX: -p * h, translateY: -m * h }, r)))) })) } }, zt = { bind: function () { var t = this.element, i = this.options, e = this.cropper; J(i.cropstart) && ut(t, w, i.cropstart), J(i.cropmove) && ut(t, v, i.cropmove), J(i.cropend) && ut(t, f, i.cropend), J(i.crop) && ut(t, a, i.crop), J(i.zoom) && ut(t, A, i.zoom), ut(e, x, this.onCropStart = this.cropStart.bind(this)), i.zoomable && i.zoomOnWheel && ut(e, B, this.onWheel = this.wheel.bind(this)), i.toggleDragModeOnDblclick && ut(e, b, this.onDblclick = this.dblclick.bind(this)), ut(t.ownerDocument, y, this.onCropMove = this.cropMove.bind(this)), ut(t.ownerDocument, M, this.onCropEnd = this.cropEnd.bind(this)), i.responsive && ut(window, D, this.onResize = this.resize.bind(this)) }, unbind: function () { var t = this.element, i = this.options, e = this.cropper; J(i.cropstart) && gt(t, w, i.cropstart), J(i.cropmove) && gt(t, v, i.cropmove), J(i.cropend) && gt(t, f, i.cropend), J(i.crop) && gt(t, a, i.crop), J(i.zoom) && gt(t, A, i.zoom), gt(e, x, this.onCropStart), i.zoomable && i.zoomOnWheel && gt(e, B, this.onWheel), i.toggleDragModeOnDblclick && gt(e, b, this.onDblclick), gt(t.ownerDocument, y, this.onCropMove), gt(t.ownerDocument, M, this.onCropEnd), i.responsive && gt(window, D, this.onResize) } }, Ot = { resize: function () { var t = this.options, i = this.container, e = this.containerData, a = Number(t.minContainerWidth) || 200, n = Number(t.minContainerHeight) || 100; if (!(this.disabled || e.width <= a || e.height <= n)) { var o = i.offsetWidth / e.width; if (1 !== o || i.offsetHeight !== e.height) { var h = void 0, r = void 0; t.restore && (h = this.getCanvasData(), r = this.getCropBoxData()), this.render(), t.restore && (this.setCanvasData(_(h, function (t, i) { h[i] = t * o })), this.setCropBoxData(_(r, function (t, i) { r[i] = t * o }))) } } }, dblclick: function () { var t, i; this.disabled || this.options.dragMode === u || this.setDragMode((t = this.dragBox, i = r, (t.classList ? t.classList.contains(i) : -1 < t.className.indexOf(i)) ? c : s)) }, wheel: function (t) { var i = this, e = Number(this.options.wheelZoomRatio) || .1, a = 1; this.disabled || (t.preventDefault(), this.wheeling || (this.wheeling = !0, setTimeout(function () { i.wheeling = !1 }, 50), t.deltaY ? a = 0 < t.deltaY ? 1 : -1 : t.wheelDelta ? a = -t.wheelDelta / 120 : t.detail && (a = 0 < t.detail ? 1 : -1), this.zoom(-a * e, t))) }, cropStart: function (t) { if (!this.disabled) { var i = this.options, e = this.pointers, a = void 0; t.changedTouches ? _(t.changedTouches, function (t) { e[t.identifier] = Bt(t) }) : e[t.pointerId || 0] = Bt(t), a = 1 < Object.keys(e).length && i.zoomable && i.zoomOnTouch ? E : ct(t.target, m), I.test(a) && !1 !== ft(this.element, w, { originalEvent: t, action: a }) && (t.preventDefault(), this.action = a, this.cropping = !1, a === T && (this.cropping = !0, nt(this.dragBox, o))) } }, cropMove: function (t) { var i = this.action; if (!this.disabled && i) { var e = this.pointers; t.preventDefault(), !1 !== ft(this.element, v, { originalEvent: t, action: i }) && (t.changedTouches ? _(t.changedTouches, function (t) { tt(e[t.identifier], Bt(t, !0)) }) : tt(e[t.pointerId || 0], Bt(t, !0)), this.change(t)) } }, cropEnd: function (t) { if (!this.disabled) { var i = this.action, e = this.pointers; t.changedTouches ? _(t.changedTouches, function (t) { delete e[t.identifier] }) : delete e[t.pointerId || 0], i && (t.preventDefault(), Object.keys(e).length || (this.action = ""), this.cropping && (this.cropping = !1, ht(this.dragBox, o, this.cropped && this.options.modal)), ft(this.element, f, { originalEvent: t, action: i })) } } }, Yt = { change: function (t) { var i = this.options, e = this.canvasData, a = this.containerData, n = this.cropBoxData, o = this.pointers, h = this.action, r = i.aspectRatio, s = n.left, c = n.top, d = n.width, l = n.height, p = s + d, m = c + l, g = 0, u = 0, f = a.width, v = a.height, w = !0, b = void 0; !r && t.shiftKey && (r = d && l ? d / l : 1), this.limited && (g = n.minLeft, u = n.minTop, f = g + Math.min(a.width, e.width, e.left + e.width), v = u + Math.min(a.height, e.height, e.top + e.height)); var x, y, M, C = o[Object.keys(o)[0]], D = { x: C.endX - C.startX, y: C.endY - C.startY }, B = function (t) { switch (t) { case H: p + D.x > f && (D.x = f - p); break; case N: s + D.x < g && (D.x = g - s); break; case z: c + D.y < u && (D.y = u - c); break; case L: m + D.y > v && (D.y = v - m) } }; switch (h) { case k: s += D.x, c += D.y; break; case H: if (0 <= D.x && (f <= p || r && (c <= u || v <= m))) { w = !1; break } B(H), d += D.x, r && (l = d / r, c -= D.x / r / 2), d < 0 && (h = N, d = 0); break; case z: if (D.y <= 0 && (c <= u || r && (s <= g || f <= p))) { w = !1; break } B(z), l -= D.y, c += D.y, r && (d = l * r, s += D.y * r / 2), l < 0 && (h = L, l = 0); break; case N: if (D.x <= 0 && (s <= g || r && (c <= u || v <= m))) { w = !1; break } B(N), d -= D.x, s += D.x, r && (l = d / r, c += D.x / r / 2), d < 0 && (h = H, d = 0); break; case L: if (0 <= D.y && (v <= m || r && (s <= g || f <= p))) { w = !1; break } B(L), l += D.y, r && (d = l * r, s -= D.y * r / 2), l < 0 && (h = z, l = 0); break; case O: if (r) { if (D.y <= 0 && (c <= u || f <= p)) { w = !1; break } B(z), l -= D.y, c += D.y, d = l * r } else B(z), B(H), 0 <= D.x ? p < f ? d += D.x : D.y <= 0 && c <= u && (w = !1) : d += D.x, D.y <= 0 ? u < c && (l -= D.y, c += D.y) : (l -= D.y, c += D.y); d < 0 && l < 0 ? (h = R, d = l = 0) : d < 0 ? (h = Y, d = 0) : l < 0 && (h = X, l = 0); break; case Y: if (r) { if (D.y <= 0 && (c <= u || s <= g)) { w = !1; break } B(z), l -= D.y, c += D.y, d = l * r, s += D.y * r } else B(z), B(N), D.x <= 0 ? g < s ? (d -= D.x, s += D.x) : D.y <= 0 && c <= u && (w = !1) : (d -= D.x, s += D.x), D.y <= 0 ? u < c && (l -= D.y, c += D.y) : (l -= D.y, c += D.y); d < 0 && l < 0 ? (h = X, d = l = 0) : d < 0 ? (h = O, d = 0) : l < 0 && (h = R, l = 0); break; case R: if (r) { if (D.x <= 0 && (s <= g || v <= m)) { w = !1; break } B(N), d -= D.x, s += D.x, l = d / r } else B(L), B(N), D.x <= 0 ? g < s ? (d -= D.x, s += D.x) : 0 <= D.y && v <= m && (w = !1) : (d -= D.x, s += D.x), 0 <= D.y ? m < v && (l += D.y) : l += D.y; d < 0 && l < 0 ? (h = O, d = l = 0) : d < 0 ? (h = X, d = 0) : l < 0 && (h = Y, l = 0); break; case X: if (r) { if (0 <= D.x && (f <= p || v <= m)) { w = !1; break } B(H), l = (d += D.x) / r } else B(L), B(H), 0 <= D.x ? p < f ? d += D.x : 0 <= D.y && v <= m && (w = !1) : d += D.x, 0 <= D.y ? m < v && (l += D.y) : l += D.y; d < 0 && l < 0 ? (h = Y, d = l = 0) : d < 0 ? (h = R, d = 0) : l < 0 && (h = O, l = 0); break; case W: this.move(D.x, D.y), w = !1; break; case E: this.zoom((y = tt({}, x = o), M = [], _(x, function (r, t) { delete y[t], _(y, function (t) { var i = Math.abs(r.startX - t.startX), e = Math.abs(r.startY - t.startY), a = Math.abs(r.endX - t.endX), n = Math.abs(r.endY - t.endY), o = Math.sqrt(i * i + e * e), h = (Math.sqrt(a * a + n * n) - o) / o; M.push(h) }) }), M.sort(function (t, i) { return Math.abs(t) < Math.abs(i) }), M[0]), t), w = !1; break; case T: if (!D.x || !D.y) { w = !1; break } b = vt(this.cropper), s = C.startX - b.left, c = C.startY - b.top, d = n.minWidth, l = n.minHeight, 0 < D.x ? h = 0 < D.y ? X : O : D.x < 0 && (s -= d, h = 0 < D.y ? R : Y), D.y < 0 && (c -= l), this.cropped || (ot(this.cropBox, S), this.cropped = !0, this.limited && this.limitCropBox(!0, !0)) } w && (n.width = d, n.height = l, n.left = s, n.top = c, this.action = h, this.renderCropBox()), _(o, function (t) { t.startX = t.endX, t.startY = t.endY }) } }, Xt = { crop: function () { return !this.ready || this.cropped || this.disabled || (this.cropped = !0, this.limitCropBox(!0, !0), this.options.modal && nt(this.dragBox, o), ot(this.cropBox, S), this.setCropBoxData(this.initialCropBoxData)), this }, reset: function () { return this.ready && !this.disabled && (this.imageData = tt({}, this.initialImageData), this.canvasData = tt({}, this.initialCanvasData), this.cropBoxData = tt({}, this.initialCropBoxData), this.renderCanvas(), this.cropped && this.renderCropBox()), this }, clear: function () { return this.cropped && !this.disabled && (tt(this.cropBoxData, { left: 0, top: 0, width: 0, height: 0 }), this.cropped = !1, this.renderCropBox(), this.limitCanvas(!0, !0), this.renderCanvas(), ot(this.dragBox, o), nt(this.cropBox, S)), this }, replace: function (i) { var t = 1 < arguments.length && void 0 !== arguments[1] && arguments[1]; return !this.disabled && i && (this.isImg && (this.element.src = i), t ? (this.url = i, this.image.src = i, this.ready && (this.viewBoxImage.src = i, _(this.previews, function (t) { t.getElementsByTagName("img")[0].src = i }))) : (this.isImg && (this.replaced = !0), this.options.data = null, this.uncreate(), this.load(i))), this }, enable: function () { return this.ready && this.disabled && (this.disabled = !1, ot(this.cropper, t)), this }, disable: function () { return this.ready && !this.disabled && (this.disabled = !0, nt(this.cropper, t)), this }, destroy: function () { var t = this.element; return ct(t, d) && (this.isImg && this.replaced && (t.src = this.originalUrl), this.uncreate(), lt(t, d)), this }, move: function (t) { var i = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, e = this.canvasData, a = e.left, n = e.top; return this.moveTo(F(t) ? t : a + Number(t), F(i) ? i : n + Number(i)) }, moveTo: function (t) { var i = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, e = this.canvasData, a = !1; return t = Number(t), i = Number(i), this.ready && !this.disabled && this.options.movable && (Z(t) && (e.left = t, a = !0), Z(i) && (e.top = i, a = !0), a && this.renderCanvas(!0)), this }, zoom: function (t, i) { var e = this.canvasData; return t = (t = Number(t)) < 0 ? 1 / (1 - t) : 1 + t, this.zoomTo(e.width * t / e.naturalWidth, null, i) }, zoomTo: function (t, i, e) { var a, n, o, h = this.options, r = this.canvasData, s = r.width, c = r.height, d = r.naturalWidth, l = r.naturalHeight; if (0 <= (t = Number(t)) && this.ready && !this.disabled && h.zoomable) { var p = d * t, m = l * t; if (!1 === ft(this.element, A, { originalEvent: e, oldRatio: s / d, ratio: p / d })) return this; if (e) { var g = this.pointers, u = vt(this.cropper), f = g && Object.keys(g).length ? (o = n = a = 0, _(g, function (t) { var i = t.startX, e = t.startY; a += i, n += e, o += 1 }), { pageX: a /= o, pageY: n /= o }) : { pageX: e.pageX, pageY: e.pageY }; r.left -= (p - s) * ((f.pageX - u.left - r.left) / s), r.top -= (m - c) * ((f.pageY - u.top - r.top) / c) } else G(i) && Z(i.x) && Z(i.y) ? (r.left -= (p - s) * ((i.x - r.left) / s), r.top -= (m - c) * ((i.y - r.top) / c)) : (r.left -= (p - s) / 2, r.top -= (m - c) / 2); r.width = p, r.height = m, this.renderCanvas(!0) } return this }, rotate: function (t) { return this.rotateTo((this.imageData.rotate || 0) + Number(t)) }, rotateTo: function (t) { return Z(t = Number(t)) && this.ready && !this.disabled && this.options.rotatable && (this.imageData.rotate = t % 360, this.renderCanvas(!0, !0)), this }, scaleX: function (t) { var i = this.imageData.scaleY; return this.scale(t, Z(i) ? i : 1) }, scaleY: function (t) { var i = this.imageData.scaleX; return this.scale(Z(i) ? i : 1, t) }, scale: function (t) { var i = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, e = this.imageData, a = !1; return t = Number(t), i = Number(i), this.ready && !this.disabled && this.options.scalable && (Z(t) && (e.scaleX = t, a = !0), Z(i) && (e.scaleY = i, a = !0), a && this.renderCanvas(!0, !0)), this }, getData: function () { var e = 0 < arguments.length && void 0 !== arguments[0] && arguments[0], t = this.options, i = this.imageData, a = this.canvasData, n = this.cropBoxData, o = void 0; if (this.ready && this.cropped) { o = { x: n.left - a.left, y: n.top - a.top, width: n.width, height: n.height }; var h = i.width / i.naturalWidth; _(o, function (t, i) { t /= h, o[i] = e ? Math.round(t) : t }) } else o = { x: 0, y: 0, width: 0, height: 0 }; return t.rotatable && (o.rotate = i.rotate || 0), t.scalable && (o.scaleX = i.scaleX || 1, o.scaleY = i.scaleY || 1), o }, setData: function (t) { var i = this.options, e = this.imageData, a = this.canvasData, n = {}; if (this.ready && !this.disabled && G(t)) { var o = !1; i.rotatable && Z(t.rotate) && t.rotate !== e.rotate && (e.rotate = t.rotate, o = !0), i.scalable && (Z(t.scaleX) && t.scaleX !== e.scaleX && (e.scaleX = t.scaleX, o = !0), Z(t.scaleY) && t.scaleY !== e.scaleY && (e.scaleY = t.scaleY, o = !0)), o && this.renderCanvas(!0, !0); var h = e.width / e.naturalWidth; Z(t.x) && (n.left = t.x * h + a.left), Z(t.y) && (n.top = t.y * h + a.top), Z(t.width) && (n.width = t.width * h), Z(t.height) && (n.height = t.height * h), this.setCropBoxData(n) } return this }, getContainerData: function () { return this.ready ? tt({}, this.containerData) : {} }, getImageData: function () { return this.sized ? tt({}, this.imageData) : {} }, getCanvasData: function () { var i = this.canvasData, e = {}; return this.ready && _(["left", "top", "width", "height", "naturalWidth", "naturalHeight"], function (t) { e[t] = i[t] }), e }, setCanvasData: function (t) { var i = this.canvasData, e = i.aspectRatio; return this.ready && !this.disabled && G(t) && (Z(t.left) && (i.left = t.left), Z(t.top) && (i.top = t.top), Z(t.width) ? (i.width = t.width, i.height = t.width / e) : Z(t.height) && (i.height = t.height, i.width = t.height * e), this.renderCanvas(!0)), this }, getCropBoxData: function () { var t = this.cropBoxData, i = void 0; return this.ready && this.cropped && (i = { left: t.left, top: t.top, width: t.width, height: t.height }), i || {} }, setCropBoxData: function (t) { var i = this.cropBoxData, e = this.options.aspectRatio, a = void 0, n = void 0; return this.ready && this.cropped && !this.disabled && G(t) && (Z(t.left) && (i.left = t.left), Z(t.top) && (i.top = t.top), Z(t.width) && t.width !== i.width && (a = !0, i.width = t.width), Z(t.height) && t.height !== i.height && (n = !0, i.height = t.height), e && (a ? i.height = i.width / e : n && (i.width = i.height * e)), this.renderCropBox()), this }, getCroppedCanvas: function () { var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; if (!this.ready || !window.HTMLCanvasElement) return null; var i, e, a, n, o, h, r, s, c, d, l, p, m, g, u, f, v, w, b, x, y, M, C, D, B, k, T, W, E, H, N, L, z, O, Y, X, R, S, A, I, U, j = this.canvasData, P = (i = this.image, e = this.imageData, a = j, n = t, o = e.aspectRatio, h = e.naturalWidth, r = e.naturalHeight, s = e.rotate, c = void 0 === s ? 0 : s, d = e.scaleX, l = void 0 === d ? 1 : d, p = e.scaleY, m = void 0 === p ? 1 : p, g = a.aspectRatio, u = a.naturalWidth, f = a.naturalHeight, v = n.fillColor, w = void 0 === v ? "transparent" : v, b = n.imageSmoothingEnabled, x = void 0 === b || b, y = n.imageSmoothingQuality, M = void 0 === y ? "low" : y, C = n.maxWidth, D = void 0 === C ? 1 / 0 : C, B = n.maxHeight, k = void 0 === B ? 1 / 0 : B, T = n.minWidth, W = void 0 === T ? 0 : T, E = n.minHeight, H = void 0 === E ? 0 : E, N = document.createElement("canvas"), L = N.getContext("2d"), z = Tt({ aspectRatio: g, width: D, height: k }), O = Tt({ aspectRatio: g, width: W, height: H }, "cover"), Y = Math.min(z.width, Math.max(O.width, u)), X = Math.min(z.height, Math.max(O.height, f)), R = Tt({ aspectRatio: o, width: D, height: k }), S = Tt({ aspectRatio: o, width: W, height: H }, "cover"), A = Math.min(R.width, Math.max(S.width, h)), I = Math.min(R.height, Math.max(S.height, r)), U = [-A / 2, -I / 2, A, I], N.width = xt(Y), N.height = xt(X), L.fillStyle = w, L.fillRect(0, 0, Y, X), L.save(), L.translate(Y / 2, X / 2), L.rotate(c * Math.PI / 180), L.scale(l, m), L.imageSmoothingEnabled = x, L.imageSmoothingQuality = M, L.drawImage.apply(L, [i].concat(bt(U.map(function (t) { return Math.floor(xt(t)) })))), L.restore(), N); if (!this.cropped) return P; var q = this.getData(), $ = q.x, Q = q.y, Z = q.width, F = q.height, K = P.width / Math.floor(j.naturalWidth); 1 !== K && ($ *= K, Q *= K, Z *= K, F *= K); var V = Z / F, G = Tt({ aspectRatio: V, width: t.maxWidth || 1 / 0, height: t.maxHeight || 1 / 0 }), J = Tt({ aspectRatio: V, width: t.minWidth || 0, height: t.minHeight || 0 }, "cover"), _ = Tt({ aspectRatio: V, width: t.width || (1 !== K ? P.width : Z), height: t.height || (1 !== K ? P.height : F) }), tt = _.width, it = _.height; tt = Math.min(G.width, Math.max(J.width, tt)), it = Math.min(G.height, Math.max(J.height, it)); var et = document.createElement("canvas"), at = et.getContext("2d"); et.width = xt(tt), et.height = xt(it), at.fillStyle = t.fillColor || "transparent", at.fillRect(0, 0, tt, it); var nt = t.imageSmoothingEnabled, ot = void 0 === nt || nt, ht = t.imageSmoothingQuality; at.imageSmoothingEnabled = ot, ht && (at.imageSmoothingQuality = ht); var rt = P.width, st = P.height, ct = $, dt = Q, lt = void 0, pt = void 0, mt = void 0, gt = void 0, ut = void 0, ft = void 0; ct <= -Z || rt < ct ? ut = mt = lt = ct = 0 : ct <= 0 ? (mt = -ct, ct = 0, ut = lt = Math.min(rt, Z + ct)) : ct <= rt && (mt = 0, ut = lt = Math.min(Z, rt - ct)), lt <= 0 || dt <= -F || st < dt ? ft = gt = pt = dt = 0 : dt <= 0 ? (gt = -dt, dt = 0, ft = pt = Math.min(st, F + dt)) : dt <= st && (gt = 0, ft = pt = Math.min(F, st - dt)); var vt = [ct, dt, lt, pt]; if (0 < ut && 0 < ft) { var wt = tt / Z; vt.push(mt * wt, gt * wt, ut * wt, ft * wt) } return at.drawImage.apply(at, [P].concat(bt(vt.map(function (t) { return Math.floor(xt(t)) })))), et }, setAspectRatio: function (t) { var i = this.options; return this.disabled || F(t) || (i.aspectRatio = Math.max(0, t) || NaN, this.ready && (this.initCropBox(), this.cropped && this.renderCropBox())), this }, setDragMode: function (t) { var i = this.options, e = this.dragBox, a = this.face; if (this.ready && !this.disabled) { var n = t === s, o = i.movable && t === c; t = n || o ? t : u, i.dragMode = t, dt(e, m, t), ht(e, r, n), ht(e, p, o), i.cropBoxMovable || (dt(a, m, t), ht(a, r, n), ht(a, p, o)) } return this } }, Rt = h.Cropper, St = function () { function e(t) { var i = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}; if ($(this, e), !t || !P.test(t.tagName)) throw new Error("The first argument is required and must be an <img> or <canvas> element."); this.element = t, this.options = tt({}, q, G(i) && i), this.cropped = !1, this.disabled = !1, this.pointers = {}, this.ready = !1, this.reloading = !1, this.replaced = !1, this.sized = !1, this.sizing = !1, this.init() } return Q(e, [{ key: "init", value: function () { var t = this.element, i = t.tagName.toLowerCase(), e = void 0; if (!ct(t, d)) { if (dt(t, d, this), "img" === i) { if (this.isImg = !0, e = t.getAttribute("src") || "", !(this.originalUrl = e)) return; e = t.src } else "canvas" === i && window.HTMLCanvasElement && (e = t.toDataURL()); this.load(e) } } }, { key: "load", value: function (t) { var i = this; if (t) { this.url = t, this.imageData = {}; var e = this.element, a = this.options; if (a.checkOrientation && window.ArrayBuffer) if (U.test(t)) j.test(t) ? this.read((n = t.replace(Et, ""), o = atob(n), h = new ArrayBuffer(o.length), _(r = new Uint8Array(h), function (t, i) { r[i] = o.charCodeAt(i) }), h)) : this.clone(); else { var n, o, h, r, s = new XMLHttpRequest; this.reloading = !0, this.xhr = s; var c = function () { i.reloading = !1, i.xhr = null }; s.ontimeout = c, s.onabort = c, s.onerror = function () { c(), i.clone() }, s.onload = function () { c(), i.read(s.response) }, a.checkCrossOrigin && Mt(t) && e.crossOrigin && (t = Ct(t)), s.open("get", t), s.responseType = "arraybuffer", s.withCredentials = "use-credentials" === e.crossOrigin, s.send() } else this.clone() } } }, { key: "read", value: function (t) { var i, e, a, n = this.options, o = this.imageData, h = Ht(t), r = 0, s = 1, c = 1; if (1 < h) { this.url = (i = "image/jpeg", e = new Uint8Array(t), a = "", _(e, function (t) { a += Wt(t) }), "data:" + i + ";base64," + btoa(a)); var d = function (t) { var i = 0, e = 1, a = 1; switch (t) { case 2: e = -1; break; case 3: i = -180; break; case 4: a = -1; break; case 5: i = 90, a = -1; break; case 6: i = 90; break; case 7: i = 90, e = -1; break; case 8: i = -90 } return { rotate: i, scaleX: e, scaleY: a } }(h); r = d.rotate, s = d.scaleX, c = d.scaleY } n.rotatable && (o.rotate = r), n.scalable && (o.scaleX = s, o.scaleY = c), this.clone() } }, { key: "clone", value: function () { var t = this.element, i = this.url, e = void 0, a = void 0; this.options.checkCrossOrigin && Mt(i) && ((e = t.crossOrigin) ? a = i : (e = "anonymous", a = Ct(i))), this.crossOrigin = e, this.crossOriginUrl = a; var n = document.createElement("img"); e && (n.crossOrigin = e), n.src = a || i, (this.image = n).onload = this.start.bind(this), n.onerror = this.stop.bind(this), nt(n, l), t.parentNode.insertBefore(n, t.nextSibling) } }, { key: "start", value: function () { var e = this, t = this.isImg ? this.element : this.image; t.onload = null, t.onerror = null, this.sizing = !0; var i = h.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(h.navigator.userAgent), a = function (t, i) { tt(e.imageData, { naturalWidth: t, naturalHeight: i, aspectRatio: t / i }), e.sizing = !1, e.sized = !0, e.build() }; if (!t.naturalWidth || i) { var n = document.createElement("img"), o = document.body || document.documentElement; (this.sizingImage = n).onload = function () { a(n.width, n.height), i || o.removeChild(n) }, n.src = t.src, i || (n.style.cssText = "left:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;opacity:0;position:absolute;top:0;z-index:-1;", o.appendChild(n)) } else a(t.naturalWidth, t.naturalHeight) } }, { key: "stop", value: function () { var t = this.image; t.onload = null, t.onerror = null, t.parentNode.removeChild(t), this.image = null } }, { key: "build", value: function () { if (this.sized && !this.ready) { var t = this.element, i = this.options, e = this.image, a = t.parentNode, n = document.createElement("div"); n.innerHTML = '<div class="cropper-container" touch-action="none"><div class="cropper-wrap-box"><div class="cropper-canvas"></div></div><div class="cropper-drag-box"></div><div class="cropper-crop-box"><span class="cropper-view-box"></span><span class="cropper-dashed dashed-h"></span><span class="cropper-dashed dashed-v"></span><span class="cropper-center"></span><span class="cropper-face"></span><span class="cropper-line line-e" data-cropper-action="e"></span><span class="cropper-line line-n" data-cropper-action="n"></span><span class="cropper-line line-w" data-cropper-action="w"></span><span class="cropper-line line-s" data-cropper-action="s"></span><span class="cropper-point point-e" data-cropper-action="e"></span><span class="cropper-point point-n" data-cropper-action="n"></span><span class="cropper-point point-w" data-cropper-action="w"></span><span class="cropper-point point-s" data-cropper-action="s"></span><span class="cropper-point point-ne" data-cropper-action="ne"></span><span class="cropper-point point-nw" data-cropper-action="nw"></span><span class="cropper-point point-sw" data-cropper-action="sw"></span><span class="cropper-point point-se" data-cropper-action="se"></span></div></div>'; var o = n.querySelector("." + d + "-container"), h = o.querySelector("." + d + "-canvas"), r = o.querySelector("." + d + "-drag-box"), s = o.querySelector("." + d + "-crop-box"), c = s.querySelector("." + d + "-face"); this.container = a, this.cropper = o, this.canvas = h, this.dragBox = r, this.cropBox = s, this.viewBox = o.querySelector("." + d + "-view-box"), this.face = c, h.appendChild(e), nt(t, S), a.insertBefore(o, t.nextSibling), this.isImg || ot(e, l), this.initPreview(), this.bind(), i.aspectRatio = Math.max(0, i.aspectRatio) || NaN, i.viewMode = Math.max(0, Math.min(3, Math.round(i.viewMode))) || 0, nt(s, S), i.guides || nt(s.getElementsByClassName(d + "-dashed"), S), i.center || nt(s.getElementsByClassName(d + "-center"), S), i.background && nt(o, d + "-bg"), i.highlight || nt(c, "cropper-invisible"), i.cropBoxMovable && (nt(c, p), dt(c, m, k)), i.cropBoxResizable || (nt(s.getElementsByClassName(d + "-line"), S), nt(s.getElementsByClassName(d + "-point"), S)), this.render(), this.ready = !0, this.setDragMode(i.dragMode), i.autoCrop && this.crop(), this.setData(i.data), J(i.ready) && ut(t, C, i.ready, { once: !0 }), ft(t, C) } } }, { key: "unbuild", value: function () { this.ready && (this.ready = !1, this.unbind(), this.resetPreview(), this.cropper.parentNode.removeChild(this.cropper), ot(this.element, S)) } }, { key: "uncreate", value: function () { this.ready ? (this.unbuild(), this.ready = !1, this.cropped = !1) : this.sizing ? (this.sizingImage.onload = null, this.sizing = !1, this.sized = !1) : this.reloading ? this.xhr.abort() : this.image && this.stop() } }], [{ key: "noConflict", value: function () { return window.Cropper = Rt, e } }, { key: "setDefaults", value: function (t) { tt(q, G(t) && t) } }]), e }(); return tt(St.prototype, Nt, Lt, zt, Ot, Yt, Xt), St });
;/*!
 * jQuery Cropper v1.0.0
 * https://github.com/fengyuanchen/jquery-cropper
 *
 * Copyright (c) 2018 Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2018-04-01T06:20:13.168Z
 */

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery'), require('cropperjs')) :
  typeof define === 'function' && define.amd ? define(['jquery', 'cropperjs'], factory) :
  (factory(global.jQuery,global.Cropper));
}(this, (function ($,Cropper) { 'use strict';

  $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
  Cropper = Cropper && Cropper.hasOwnProperty('default') ? Cropper['default'] : Cropper;

  if ($.fn) {
    var AnotherCropper = $.fn.cropper;
    var NAMESPACE = 'cropper';

    $.fn.cropper = function jQueryCropper(option) {
      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      var result = void 0;

      this.each(function (i, element) {
        var $element = $(element);
        var isDestroy = option === 'destroy';
        var cropper = $element.data(NAMESPACE);

        if (!cropper) {
          if (isDestroy) {
            return;
          }

          var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);

          cropper = new Cropper(element, options);
          $element.data(NAMESPACE, cropper);
        }

        if (typeof option === 'string') {
          var fn = cropper[option];

          if ($.isFunction(fn)) {
            result = fn.apply(cropper, args);

            if (result === cropper) {
              result = undefined;
            }

            if (isDestroy) {
              $element.removeData(NAMESPACE);
            }
          }
        }
      });

      return result !== undefined ? result : this;
    };

    $.fn.cropper.Constructor = Cropper;
    $.fn.cropper.setDefaults = Cropper.setDefaults;
    $.fn.cropper.noConflict = function noConflict() {
      $.fn.cropper = AnotherCropper;
      return this;
    };
  }

})));
;var UserId = null;
var browserName;
var browserVersion;
var ErrorMessageType = 'Error';
var DebugMessageType = 'Debug';
var InfoMessageType = 'Info';
var FatalMessageType = 'Fatal';
var WarnMessageType = 'Warn';
var CurrentUser = new UserSimpleModel();
var PrivateModeStatus = '';
var notHandled = true;

var psApp = psApp || {};
function extendNs(ns_string, ns) {
    if (!ns) ns = psApp;
    var parts = ns_string.split('.'),
        parent = ns,
        pl, i;

    if (parts[0] == "psApp") {
        parts = parts.slice(1);
    }

    pl = parts.length;
    for (i = 0; i < pl; i++) {
        //create a property if it doesnt exist
        if (typeof parent[parts[i]] == 'undefined') {
            parent[parts[i]] = {};
        }

        parent = parent[parts[i]];
    }

    return parent;
}


Function.prototype.inheritsFrom = function (parentClassOrObject) {
    if (parentClassOrObject.constructor == Function) {
        //Normal Inheritance 
        this.prototype = new parentClassOrObject;
        this.prototype.constructor = this;
        this.prototype.parent = parentClassOrObject.prototype;
    }
    else {
        //Pure Virtual Inheritance 
        this.prototype = parentClassOrObject;
        this.prototype.constructor = this;
        this.prototype.parent = parentClassOrObject;
    }
    return this;
}

BaseKoModelObject = function (parent) {
    var self = this;
    self.Parent = parent;
    self.BaseObj = null;
}
BaseKoModelObject.prototype = {
    init: function () {
    },
    CancelChanges: function () {
        var self = this;
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    },
    initByModel: function (serverModel) {
    },
    getClearData: function () {
        return null;
    }
}

if (jQuery.fn.dataTableExt) {
    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
        "title-string-pre": function (a) {
            if (!a) return a;
            return a.match(/title="(.*?)"/)[1].toLowerCase();
        },

        "title-string-asc": function (a, b) {
            return ((a < b) ? -1 : ((a > b) ? 1 : 0));
        },

        "title-string-desc": function (a, b) {
            return ((a < b) ? 1 : ((a > b) ? -1 : 0));
        }
    });

    ///zeby dzialao sortowanie z polskimi znakami
    jQuery.fn.dataTableExt.oSort['string-asc'] = function (a, b) {
        var x = a.toLowerCase();
        var y = b.toLowerCase();
        return x.localeCompare(y);
    };

    jQuery.fn.dataTableExt.oSort['string-desc'] = function (a, b) {
        var x = a.toLowerCase();
        var y = b.toLowerCase();
        return y.localeCompare(x);
    };
}

function setBrowserInfo() {
    var _browser = {};
    var uagent = navigator.userAgent.toLowerCase();

    _browser.opera = /mozilla/.test(uagent) && /applewebkit/.test(uagent) && /chrome/.test(uagent) && /safari/.test(uagent) && /opr/.test(uagent);
    _browser.safari = /applewebkit/.test(uagent) && /safari/.test(uagent) && !/chrome/.test(uagent);
    _browser.firefox = /mozilla/.test(uagent) && /firefox/.test(uagent);
    _browser.chrome = /webkit/.test(uagent) && /chrome/.test(uagent);
    _browser.msie = /msie/.test(uagent);
    _browser.version = '';

    for (x in _browser) {
        if (_browser[x]) {
            if (x !== "opera") {
                _browser.version = uagent.match(new RegExp("(" + x + ")( |/)([0-9]+)"))[3];
                browserName = x;
                browserVersion = _browser.version;
            }
            else {
                _browser.version = uagent.match(new RegExp("(opr)( |/)([0-9]+)"))[3];
                browserName = x;
                browserVersion = _browser.version;
            }
            break;
        }
    }

    //ShowEmptyMsg(browserName + ', ' + browserVersion, translations.Info);        
}


function koArrayCopyComplex(source, target, doNotClear, callback) {
    if (!doNotClear) {
        target.removeAll();
    }
    if (source) {
        var elem;
        for (var i = 0, len = source.length; i < len; i++) {
            elem = callback(source[i]);
            if (typeof (elem) != 'undefined') {
                target.push(elem);
            }
        }
    }
}
function koArrayCopy(source, target, doNotClear, observable) {
    koArrayCopyComplex(source, target, doNotClear, function (elem) { return observable ? ko.observable(elem) : elem; });
}

function GetDataFromKoArray(koArray, getDataFunc) {
    var _result = [];
    var _result_Iter = koArray();
    var e;
    for (var i = 0, len = _result_Iter.length; i < len; i++) {
        e = _result_Iter[i];
        if (getDataFunc) {
            e = getDataFunc(e);
        } else {
            if (typeof (e.getClearData) != 'undefined') {
                e = e.getClearData();
            }
        }
        if (typeof (e) != 'undefined') {
            _result.push(e);
        }
    }
    return _result
}



ko.validation.rules.pattern.message = 'Invalid.';
ko.validation.configure({
    registerExtenders: true,
    messagesOnModified: true,
    insertMessages: true,
    parseInputAttributes: true,
    messageTemplate: null
});


var dataTables;
$(document).ready(function () {

    $('input[id=fileUpload]').change(function () {
        var val = $(this).val();
        val = val.replace("C:\\fakepath", "...");
        $('#photoCover').val(val);
    });

    initDataTable();

    initDatapicker();

    initTimePicker();
    /* $("select").select2({
         width: resolve,
         multiple: false,
 
     });*/

    setBrowserInfo();

    setFooterInfoModalEvents();

    if (browserName == 'firefox') {
        loadCss("all_firefox_only");
    }

    detectPrivateMode(
        function (is_private) {
            PrivateModeStatus = typeof is_private === 'undefined' ? 'cannot detect' : is_private ? 'private' : 'not private';
        }
    );
});

function setFooterInfoModalEvents() {
    $('#footerInfoModal').on('hidden', function (event) {
        $('.modal-header .tripleStripeParent', event.target).css('display', 'none');
    })
}

function initDatapicker(container) {
    $('.datapicker', container).each(function () {
        var elem = $(this);
        var past = elem.data('onlypast');
        var feature = elem.data('onlyfuture');

        var nowTemp = new Date();
        var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0);

        if (past) {
            elem.datepicker({
                weekStart: 1,
                onRender: function (date) {
                    return date.valueOf() > now.valueOf() ? 'disabled' : '';
                }
            });
        } else if (feature) {
            elem.datepicker({
                weekStart: 1,
                onRender: function (date) {
                    return date.valueOf() < now.valueOf() ? 'disabled' : '';
                }
            });
        } else {
            elem.datepicker({ weekStart: 1 });
        }
    });
}

function initTimePicker(container) {
    var tps = $('.timePicker', container);
    tps.timepicker({
        minuteStep: 5,
        showMeridian: false,
        defaultTime: false
    });
    /*
    tps.click(function () {
        $(this).timepicker('showWidget');
    });
    */
}

var dataTableLanguageOptions = {
    "sEmptyTable": translations.sEmptyTable,
    "sInfo": translations.sInfo,
    "sInfoEmpty": translations.sInfoEmpty,
    "sInfoFiltered": translations.sInfoFiltered,
    "sLengthMenu": translations.sLengthMenu,
    "sLoadingRecords": translations.sLoadingRecords,
    "sZeroRecords": translations.sZeroRecords,
    "sSearch": translations.sSearch,
    "oPaginate": {
        "sFirst": translations.sFirst,
        "sLast": translations.sLast,
        "sNext": translations.sNext,
        "sPrevious": translations.sPrevious
    }
};

function initDataTable(parent) {
    if (jQuery.fn.dataTableExt) {
        dataTables = $('table.dataTable', parent).dataTable({
            "oLanguage": dataTableLanguageOptions
        });

        $('table#eventsTable', parent).dataTable({
            "oLanguage": dataTableLanguageOptions,
            "aaSorting": [[0, "desc"]],
            "aoColumnDefs": [
              { "asSorting": ["desc", "asc"], "aTargets": [0] }
            ]
        });



    }
}




(function ($) {
    $.QueryString = (function (a) {
        if (a == "") return {};
        var b = {};
        for (var i = 0; i < a.length; ++i) {
            var p = a[i].split('=');
            if (p.length != 2) continue;
            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
        }
        return b;
    })(window.location.search.substr(1).split('&'))
})(jQuery);




////********************************
////Cookies*************************
////********************************

$(document).ready(function () {

    var cookie = $.cookie ? $.cookie("hidden") : null;
    var hidden = cookie ? cookie.split("|").getUnique() : [];
    var cookieExpires = 365; // cookie expires in 365 days

    /*
    // Remember the message was hidden
    $.each(hidden, function () {
        var pid = this;
        $('#' + pid).hide();
    })
    */

    var elems = $('#cookieClose');
    elems.each(function (index, value) {
        var pid = $(value).attr("id");
        if (pid) {
            if (jQuery.inArray(pid, hidden) < 0) {
                $('#' + pid).show();
            }
        }
    });

    // Add Click functionality
    elems.click(function () {
        $('#cookie-msg').hide();
        updateCookie($('#cookie-msg'));
    });

    // Update the Cookie
    function updateCookie(el) {
        var indx = el.attr('id');
        var tmp = hidden.getUnique();
        if (el.is(':hidden')) {
            // add item to hidden list
            tmp.push(indx);
        } else {
            // remove element id from the list
            tmp.splice(tmp.indexOf(indx), 1);
        }
        hidden = tmp.getUnique();
        $.cookie("hidden", hidden.join('|'), { path: '/', expires: cookieExpires });
    }

    if (hidden.indexOf('cookie-msg') == -1) {
        $('#cookie-msg').slideDown();
    };

});
// Return a unique array.
Array.prototype.getUnique = function () {
    var o = new Object();
    var i, e;
    for (i = 0; e = this[i]; i++) { o[e] = 1 };
    var a = new Array();
    for (e in o) { a.push(e) };
    return a;
}

// Fix indexOf in IE
if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (obj, start) {
        for (var i = (start || 0), j = this.length; i < j; i++) {
            if (this[i] == obj) { return i; }
        }
        return -1;
    }
}

function ShowSavedAlert(container) {
    $(".savePanel .savedAertContainer .alert", container).show();
    setTimeout(function () { $(".savePanel .alert").fadeOut(850); }, 3000);

    $(".saveInfoCloudContainer", container).fadeIn(850);
    setTimeout(function () { $(".saveInfoCloudContainer").fadeOut(850); }, 3000);
}
function ShowDeletedAlert(container) {
    $(".saveInfoCloudContainer.deleted", container).fadeIn(850);
    setTimeout(function () { $(".saveInfoCloudContainer.deleted").fadeOut(850); }, 3000);
}

function dateToString(d, withTime) {
    var curr_date = d.getDate();
    var curr_month = d.getMonth() + 1; //Months are zero based
    var curr_year = d.getFullYear();
    var curr_hours = d.getHours();
    var curr_minutes = d.getMinutes();

    if (curr_month < 10) curr_month = '0' + curr_month;
    if (curr_date < 10) curr_date = '0' + curr_date;
    if (curr_hours < 10) curr_hours = '0' + curr_hours;
    if (curr_minutes < 10) curr_minutes = '0' + curr_minutes;

    var result = curr_year + "-" + curr_month + "-" + curr_date;

    if (withTime) return result += ' ' + curr_hours + ':' + curr_minutes;
    else
        return result;
}

function dateToLongHtml(d) {
    if (d) {
        var curr_day = d.substr(8, 2);
        var curr_month = d.substr(5, 2);
        var curr_year = d.substr(0, 4);

        var date = new Date(curr_year, parseInt(curr_month) - 1, curr_day);
        var weekDay = translations['day_' + date.getDay()];

        return '<span class="longDate"><span class="weekDay">' + weekDay + '</span><span class="day">' + curr_day + '</span><span class="month">' + translations["month_" + curr_month] + '</span><span class="year">' + curr_year + '</span></span>';
    } else {
        return d;
    }
}
function datesToLongHtml(d, e) {
    if (!e) return dateToLongHtml(d);
    if (d && e) {
        var curr_day = d.substr(8, 2);
        var curr_month = d.substr(5, 2);
        var curr_year = d.substr(0, 4);

        var end_day = e.substr(8, 2);
        var end_month = e.substr(5, 2);
        var end_year = e.substr(0, 4);

        if (curr_day == end_day && curr_month == end_month && curr_year == end_year) return dateToLongHtml(d);

        var result = '<span class="longDate">';
        result += '<span class="day from">' + curr_day + '</span><span class="month from">' + translations["month_" + curr_month] + '</span><span class="year from">' + curr_year + '</span>';
        result += '<span>-</span>';
        result += '<span class="day to">' + end_day + '</span><span class="month to">' + translations["month_" + end_month] + '</span><span class="year to">' + end_year + '</span>';
        result += '</span>';

        return result;
    } else {
        return d;
    }
}

function getDayFromDate(d) {
    if (d) {
        var curr_day = d.substr(8, 2);

        return curr_day;
    } else {
        return d;
    }
}


function getMonthFromDate(d) {
    if (d) {
        var curr_month = d.substr(5, 2);

        return translations["month_" + curr_month];
    } else {
        return d;
    }
}

function getYearFromDate(d) {
    if (d) {
        var curr_year = d.substr(0, 4);

        return curr_year;
    } else {
        return d;
    }
}

function setClub(clubId, Selected, SelectedUpMenu, SelectedId, fastReload, location) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/SetCurrentClub",
        data: { clubId: clubId, Selected: Selected, SelectedUpMenu: SelectedUpMenu, SelectedId: SelectedId }
    })
    .done(function (result) {
        if (!fastReload) {
            $('#mainManuContainer').html(result);
        }
    });
    setTimeout(function () {
        if (location) {
            window.location = ContextPath + location;
        }
        else {
            if (window.location.href.indexOf("ClubPage") >= 0) {
                window.location = ContextPath + 'Home/Home';
            } else {
                window.location.reload(true);
            }
        }
    }, 100);
}

function ReloadClubs() {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/ReloadClubs"
    })
    .done(function (result) {
        $('#mainManuContainer').html(result);
        userBarModel.clearCache();
        window.location.reload(true);
    });
}

function openTimeControl(elem) {
    $('.bootstrap-timepicker', $(elem).parent().parent()).timepicker('showWidget');
}

var pleaseWait = null;
function setWait() {
    var container = $('#tabContainer');
    container.html('<img src="' + ContextPath + 'Content/images/ajax-loader.gif" alt="Proszę czekać" title="Proszę czekać" />');
}


Number.prototype.formatNumber = function (c, d, t) {
    var n = this,
        c = isNaN(c = Math.abs(c)) ? 2 : c,
        d = d == undefined ? "," : d,
        t = t == undefined ? " " : t,
        s = n < 0 ? "-" : "",
        i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
        j = (j = i.length) > 3 ? j % 3 : 0;
    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

function formatedTargetValue(value) {
    var n = new Number(value);
    var x = n.formatNumber(0, 3, ' ' /*c, d, t*/);
    return x;
}
function formatedTargetValueWithUnit(value, targetValueType, unit) {
    if (targetValueType == 'int') {
        return formatedTargetValue(value) + ' ' + unit;
    } else {
        return value + ' ' + unit;
    }
}


//metka do nawigacji
$(document).ready(function () {
    initTrainingNaviBelt();
});

function initTrainingNaviBelt(selector) {
    if (!selector) selector = '#trainingNaviBelt';
    var trainingNaviBelt = $(selector);
    if (!trainingNaviBelt.data("initiated")) {
        trainingNaviBelt.data("initiated", true);
        if (trainingNaviBelt.length > 0) {

            var modalContainer = trainingNaviBelt.closest('.modal-scrollable');
            var scrolContainr = modalContainer.length ? modalContainer : $(window);

            var top = modalContainer.length ? 10 : trainingNaviBelt.offset().top - parseFloat(trainingNaviBelt.css('marginTop').replace(/auto/, 0));
            var left = trainingNaviBelt.offset().left;



            scrolContainr.scroll(function (event) {
                // what the y position of the scroll is
                var y = $(this).scrollTop();

                var fixed = trainingNaviBelt.data("fixed");

                // whether that's below the form
                if (y >= top) {
                    if (!fixed) {
                        left = trainingNaviBelt.offset().left;
                        trainingNaviBelt.data("fixed", true);
                        // if so, ad the fixed class
                        trainingNaviBelt.addClass('fixed');
                        trainingNaviBelt.css('left', left + 'px');
                        trainingNaviBelt.css('right', 'auto');
                    }
                } else {
                    // otherwise remove it
                    if (fixed) {
                        trainingNaviBelt.data("fixed", false);
                        trainingNaviBelt.removeClass('fixed');
                        trainingNaviBelt.css('right', '0px');
                        trainingNaviBelt.css('left', 'auto');
                    }
                }
            });
        }
    }
}

/*
function Stat(container, labelClass, labelValue, iconClass, icon, iconSize, valueClass, value) {
    if (value != null && value != "0")
    {                 
        var statControlJSIcon = $(document.createElement('img'));
        statControlJSIcon.attr('src', ContextPath + "Content/images/" + icon + "_" + iconSize + ".png");
        statControlJSIcon.addClass(iconClass);
        container.append(statControlJSIcon);
        
        var statControlJSLabel = $(document.createElement('div'));
        statControlJSLabel.addClass(labelClass);
        statControlJSLabel.value(labelValue);

        var statControlJSValue = $(document.createElement('div'));
        statControlJSValue.addClass(valueClass);
        statControlJSValue.value(value);

        var statControlJS = $(document.createElement('div'));
        statControlJS.addClass('statControlJS');
        
        statControlJS.append(statControlJSLabel);
        statControlJS.append(statControlJSIcon);                
        statControlJS.append(statControlJSValue);                
    }        
}*/

function showSlide(container, carousel, slideNr) {
    $('.carousel-indicators > li', container).each(function (index) {
        $($('.carousel-indicators > li', container)[index]).removeClass('active');
    });

    $($('.carousel-indicators > li', container)[slideNr]).addClass('active');

    $(carousel).carousel({
        interval: false
    });
    $(carousel).carousel(slideNr);
}

function initTiny(container) {
    setTimeout(function () {
        $('.tinymce', container).each(
             function () {
                 if (this.renderTinymce != undefined) this.renderTinymce();
             });
    }, 300);
}
function initTinyImediate(container) {
    $('.tinymce', container).each(
        function () {
            if (this.renderTinymce != undefined) this.renderTinymce();
        });
}
function resetTiny(container) {
    $('.tinymce', container).each(function () {
        if (this.remove != undefined) {
            this.remove();
        }
    });
}

function loadStatControl() {

    $.each($('.statControl .bar'), function (index, value) {
        var val = $(value);
        val.animate({
            width: val.data("percent") + '%'
        }, 1500);
    });
}

function loadStatControlForElem(elem) {

    $.each($('.statControl .bar', elem), function (index, value) {
        var val = $(value);
        val.animate({
            width: val.data("percent") + '%'
        }, 1500);
    });
}

(function ($) {

    $.fn.fitText = function (options) {

        var settings = $.extend({
            'minFontSize': Number.NEGATIVE_INFINITY,
            'maxFontSize': Number.POSITIVE_INFINITY,
            'padding': Number.POSITIVE_INFINITY
        }, options);

        return this.each(function () {

            var $this = $(this);

            var resizer = function () {

                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext("2d");
                var elemWidth = $this.width();
                if (elemWidth == 0 && settings.elemWidth) elemWidth = settings.elemWidth;
                var textWidth = 0;
                var minFontSizeF = parseFloat(settings.minFontSize);
                var maxFontSizeF = parseFloat(settings.maxFontSize);
                var fontSize = minFontSizeF;
                do {
                    ctx.font = fontSize + "px " + $this.css('font-family');
                    textWidth = ctx.measureText($this.text()).width;

                    fontSize++;
                }
                while (fontSize < maxFontSizeF &&
                        textWidth < elemWidth - settings.padding
                        );

                $this.css('font-size', fontSize + "px");
            };

            resizer();
        });
    };

})(jQuery);

ko.bindingHandlers.adjustFont = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var options = valueAccessor() || {};
        for (var val in options) {
            options[val] = ko.unwrap(options[val]);
        }
        if (options.deffered) {
            setTimeout(function () {
                $(element).fitText({ minFontSize: options.minFontSize, maxFontSize: options.maxFontSize, padding: options.padding, elemWidth: options.elemWidth });
            }, 100);
        } else {
            $(element).fitText({ minFontSize: options.minFontSize, maxFontSize: options.maxFontSize, padding: options.padding, elemWidth: options.elemWidth });
        }
    }
};

function removeKoItem(array, item) {
    for (var i = 0; i < array().length; i++) {
        if (array()[i]() == item) {
            array.splice(i, 1);
            break;
        }
    }
}

function removeKoNoFunctionItem(array, item) {
    for (var i = 0; i < array().length; i++) {
        if (array()[i] == item) {
            array.splice(i, 1);
            break;
        }
    }
}

function tabContainerAccordionToggleClick(container) {
    var dzyndzolek = $('.dzyndzolek', container)

    if (dzyndzolek.hasClass('collapsed')) {
        dzyndzolek.removeClass('collapsed')
    }
    else {
        dzyndzolek.addClass('collapsed')
    }
}


function SocialServiceModel(parent, id, name, link) {
    var self = this;

    self.Parent = parent;

    self.Id = ko.observable(id);
    self.Name = ko.observable(name);
    self.Link = ko.observable(link);
}

function DisciplineModel(parent) {
    var self = this;

    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Name = ko.observable('');
    self.Icon = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
            self.Icon(serverModel.Icon);
        }
    }
}

function StatsNaviBarModel() {
    var self = this;

    self.StartDate = ko.observable('');
    self.DataPickerViewMode = ko.observable(0);
    self.LengthType = ko.observable(0);
    self.DisciplineId = ko.observable(0);
    self.CurrentDate = ko.observable('');
    self.NextClickParam = ko.observable('');
    self.PreviousClickParam = ko.observable('');
    self.NextParam = ko.observable('');
    self.PreviousParam = ko.observable('');
    self.DisciplineParam = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.StartDate(serverModel.StartDate);
            self.DataPickerViewMode(serverModel.DataPickerViewMode);
            self.LengthType(serverModel.LengthType);
            self.DisciplineId(serverModel.DisciplineId);
            self.CurrentDate(serverModel.CurrentDate);
            self.NextClickParam(serverModel.NextClickParam);
            self.NextParam(serverModel.NextParam);
            self.PreviousParam(serverModel.PreviousParam);
            self.DisciplineParam(serverModel.DisciplineParam);
        }
    }
}

function EventActivity() {
    var self = this;

    self.TrainingSubTypeId = ko.observable(0);
    self.Count = ko.observable(0);
    self.TypeIcon = ko.observable('');
    self.Percent = ko.observable(0);
    self.BacgroundColorRed = ko.observable(0);
    self.BacgroundColorGreen = ko.observable(0);
    self.BacgroundColorBlue = ko.observable(0);
    self.BacgroundColor = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.TrainingSubTypeId(serverModel.TrainingSubTypeId);
            self.Count(serverModel.Count);
            self.TypeIcon(serverModel.TypeIcon);
            self.Percent(serverModel.Percent);
            self.BacgroundColorRed(serverModel.BacgroundColorRed);
            self.BacgroundColorGreen(serverModel.BacgroundColorGreen);
            self.BacgroundColorBlue(serverModel.BacgroundColorBlue);
            self.BacgroundColor(serverModel.BacgroundColor);
        }
    }
}

function ShowEmptyMsg(content, label) {
    var modal = $('#emptyMsgModal');
    modal.find('.askMsg').html(content);
    modal.find('#emptyMsgModalLabel').html(label);
    modal.modal("show");
    return modal;
}

function returnToPositionAfterDzynClick(container, expanderName) {
    if (!container) return;
    var scrollTopPosition = container.find('.scrollTopPosition');
    var dzyndzolekCollapsed = container.find(expanderName + '.collapsed');
    var top = window.pageYOffset || document.documentElement.scrollTop
    var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

    if (dzyndzolekCollapsed.length == 0) {
        if (is_firefox) {
            $.scrollTo(scrollTopPosition[0].innerText, 0, { duration: 1000, easing: 'swing' });
        }
        else {
            $.scrollTo(scrollTopPosition[0].innerHTML, 0, { duration: 1000, easing: 'swing' });
        }
    } else {
        scrollTopPosition[0].innerText = top;
    }
}

//porównywanie obiektów
Object.equals = function (x, y, allowExtendedY) {
    if (x === y) return true;
    // if both x and y are null or undefined and exactly the same

    if (!(x instanceof Object) || !(y instanceof Object)) return false;
    // if they are not strictly equal, they both need to be Objects

    if (x.constructor !== y.constructor) return false;
    // they must have the exact same prototype chain, the closest we can do is
    // test there constructor.

    for (var p in x) {
        if (!x.hasOwnProperty(p)) continue;
        // other properties were tested using x.constructor === y.constructor

        if (!y.hasOwnProperty(p)) return false;
        // allows to compare x[ p ] and y[ p ] when set to undefined

        if (x[p] === y[p]) continue;
        // if they have the same strict value or identity then they are equal

        if (typeof (x[p]) !== "object") return false;
        // Numbers, Strings, Functions, Booleans must be strictly equal

        if (!Object.equals(x[p], y[p], allowExtendedY)) return false;
        // Objects and Arrays must be tested recursively
    }

    if (!allowExtendedY) {
        for (p in y) {
            if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) return false;
            // allows x[ p ] to be set to undefined
        }
    }
    return true;
}

function markElemAsLeftRight(container, elemClassName) {
    var index = 1;
    $(elemClassName, container).each(function () {
        var $this = $(this);

        if (index % 2 == 0) {
            if (!$this.hasClass('right')) $this.addClass('right');
        }
        else {
            if (!$this.hasClass('left')) $this.addClass('left');
        }

        index++;
    });
}

function removeMarkElemAsLeftRight(container, elemClassName) {
    $(elemClassName, container).each(function () {
        var $this = $(this);

        if (!$this.hasClass('right')) $this.removeClass('right');
        if (!$this.hasClass('left')) $this.removeClass('left');
    });
}

function GetScreenCordinatesToFirstRelative(obj) {
    var p = {};
    p.x = obj.offsetLeft;
    p.y = obj.offsetTop;
    while (obj.offsetParent) {
        if (obj == document.getElementsByTagName("body")[0] ||
            $(obj.offsetParent).css("position") == "relative") {
            break;
        }
        else {
            p.x = p.x + obj.offsetParent.offsetLeft;
            p.y = p.y + obj.offsetParent.offsetTop;

            obj = obj.offsetParent;
        }
    }
    return p;
}

function GetScreenCordinatesToFirstObjName(obj, parentClass) {
    var p = {};
    p.x = obj.offsetLeft;
    p.y = obj.offsetTop;
    while (obj.offsetParent) {
        if (obj == document.getElementsByTagName("body")[0] ||
            $(obj.offsetParent).hasClass(parentClass)) {
            break;
        }
        else {
            p.x = p.x + obj.offsetParent.offsetLeft;
            p.y = p.y + obj.offsetParent.offsetTop;

            obj = obj.offsetParent;
        }
    }
    return p;
}

function GetParentElementByName(obj, parentName, exactName) {
    if (!obj) return null;

    var p = null;

    while (obj) {
        if (obj == document.getElementsByTagName("body")[0]) {
            break;
        }
        if ((exactName && ((obj.className == parentName) || (obj.id == parentName))) ||
            (!exactName && (obj.className.indexOf(parentName) != -1 || obj.id.indexOf(parentName) != -1))) {

            p = obj;

            break;
        }
        else {
            obj = obj.parentElement;
        }
    }
    return p;
}

function userAvatarMouseOver(elem, scrollParent) {
    var p = GetScreenCordinatesToFirstRelative(elem[0]);
    var avatarContainerHeight = elem.height();
    var personalDataContainer = $(".personalDataContainerRight.displayNone", elem);
    var scrolltop = scrollParent.scrollTop();
    var top = (p.y + (avatarContainerHeight / 2)) - (personalDataContainer.height() / 2);
    top = top - scrolltop;
    $(".personalDataContainerRight.displayNone", elem).css("top", top + 'px');
    var left = p.x + 50;
    $(".personalDataContainerRight.displayNone", elem).css("left", left + 'px');
}

function userAvatarMouseOverByEvent(container, elem, event) {
    var p = event;
    var avatarContainerHeight = elem.height();
    var personalDataContainer = $(".personalDataContainerRight.displayNone", elem);
    //var scrolltop = scrollParent.scrollTop();
    var top = (p.y + (avatarContainerHeight / 2)) - (personalDataContainer.height() / 2) + window.scrollY;
    $(".personalDataContainerRight.displayNone", elem).css("top", top + 'px');
    var left = container.offset().left - 20; //p.x;
    $(".personalDataContainerRight.displayNone", elem).css("left", left + 'px');

    var clone = $(".personalDataContainerRight.displayNone", elem).clone();

    if ($('body > .personalDataContainerRight').length) {
        $('body > .personalDataContainerRight').remove();
    }

    $('body').append(clone);
    $('body > .personalDataContainerRight.avatar').removeClass('displayNone');
}

function userAvatarMouseOut() {
    $('body > .personalDataContainerRight.avatar').remove();
}

function logMsgOnServer(msg, type) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Base/LogMsgOnServer",
        data: { message: msg, type: type, browserName: browserName, browserVersion: browserVersion }
    })
	 .done(function (result) {
	 });
}

function logMsg(msg, type, prefix, logOnServer) {
    if (msg) {
        var msg = prefix ? prefix + ": " + msg : msg;
        console.log(msg);

        if (logOnServer) {
            logMsgOnServer(msg, type);
        }
    }
}

function logResponseError(response, type, prefix, logOnServer) {
    if (response) {
        logMsg(response.error, type, prefix, logOnServer);

        if (response.error) {
            logMsg(response.error.message, type, prefix, logOnServer);

            var errors = response.error.errors;

            if (errors && $.isArray(errors)) {
                for (var j = 0, jlen = errors.length; j < jlen; j++) {
                    logMsg(errors[j].message, type, prefix, logOnServer);
                }
            }
        }
    }
}

function generalUploadInit(elemId, fileIdBinding) {
    var progresElem = $(elemId + ' .progress .bar');
    $(elemId).fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            if (data.result[0].url) fileIdBinding(data.result[0].url);
            progresElem.css(
                'width',
                0 + '%'
            );
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            progresElem.css(
                'width',
                progress + '%'
            );
        }
    });
}


function LocationModel(parent, changedCalllBack) {
    var self = this;
    self.Parent = parent;
    self.ChangedCalllBack = changedCalllBack;

    self.Id = ko.observable(0);
    self.Name = ko.observable('');
    self.Latitude = ko.observable(null);
    self.Longitude = ko.observable(null);
    self.WindowTitle = '';
    self.LocationLabelTitle = '';

    self.init = function () {
        self.Id(0);
        self.Name('');
        self.Latitude(null);
        self.Longitude(null);
    };
    /*
    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }
    */

    self.initByModel = function (serverModel) {
        if (serverModel) {
            // self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
            self.Latitude(serverModel.Latitude);
            self.Longitude(serverModel.Longitude);
        } else {
            self.init();
        }
    };

    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            Name: self.Name(),
            Latitude: self.Latitude(),
            Longitude: self.Longitude()
        }
        return data;
    };

    self.setValues = function (text, long, lat) {
        self.Longitude(long);
        self.Latitude(lat);
        self.Name(text);

        if (self.ChangedCalllBack) {
            self.ChangedCalllBack(self);
        }
    }

    self.OpenControl = function (model, event, initText) {
        localisationControl.OpenCtrl(self, initText);
    }
}

function ClubSimpleModel(parent) {
    var self = this;
    self.Parent = parent;
    self.ServerModel = null;

    self.Id = ko.observable(0);
    self.ClubLogoId = ko.observable(0);
    self.ClubName = ko.observable('');

    self.DisplayClubLogo = ko.computed(function () { return self.ClubLogoId() != 0; });
    self.ClubLogoUrl = ko.computed(function () { return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.ClubLogoId(); });

    self.init = function () {
        self.Id(0);
        self.ClubLogoId(0);
        self.ClubName('');
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.ClubLogoId(serverModel.ClubLogoId);
            self.ClubName(serverModel.ClubName);

            self.ServerModel = serverModel;
        }
    };

    self.serialize = function () {
        var data = {
            Id: self.Id(),
            ClubLogoId: self.ClubLogoId(),
            ClubName: self.ClubName()
        };
        var strData = JSON.stringify(data);

        return strData;
    }

    self.deserialize = function (clubModelTmp) {
        var data = JSON.parse(clubModelTmp);

        self.Id(data.Id);
        self.ClubLogoId(data.ClubLogoId);
        self.ClubName(data.ClubName);
    }

    self.setClub = function () {
        $('#selectCommunityModal').modal('hide');
        userBarModel.setCurrentClub(self.serialize());
        setClub(self.Id(), '', '', 0, true);
    }
}

function FacebookAccountModel() {
    var self = this;

    self.FacebookId = '';
    self.BusinessToken = '';
    self.AppId = '';

    self.initByModel = function (serverModel) {
        self.FacebookId = serverModel.FacebookId;
        self.BusinessToken = serverModel.BusinessToken;
        self.AppId = serverModel.AppId;
    }
}

function UserSimpleModel() {
    var self = this;

    self.UserId = ko.observable(0);
    self.FirstName = ko.observable('');
    self.LastName = ko.observable('');
    self.MaleSex = ko.observable('');
    self.Nick = ko.observable('');
    self.UserName = ko.observable('');
    self.BirthDate = ko.observable('');
    self.AvatarFileId = ko.observable(0);
    self.CommunitySearchVisibilty = ko.observable('');
    self.ComShowTrainings = ko.observable(false);
    self.ComTileVisible = ko.observable(false);
    self.Name = ko.observable('');
    self.NameOrNick = ko.observable('');
    self.AdditionalClass = ko.observable('');
    self.ProfileText = ko.observable('');
    self.FacebookAccounts = [];
    self.IsAvatarSmall = ko.observable(false);

    self.LocationModel = ko.observable(new LocationModel(self));
    self.PrimaryDisciplineId = ko.observable(0);

    self.CommunityTrainingsCount = ko.observable(0);
    self.CommunityTargetsCount = ko.observable(0);
    self.CommunityCompetitionsCount = ko.observable(0);
    self.CommunityFriendsCount = ko.observable(0);
    self.AvailableClubsCount = ko.observable(0);
    self.CommunityIsFriend = ko.observable(false);

    self.IsAvatar = ko.computed(function () {
        return self.AvatarFileId() > 0;
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.UserId(serverModel.UserId);
            self.FirstName(serverModel.FirstName);
            self.LastName(serverModel.LastName);
            self.MaleSex(serverModel.MaleSex);
            self.Nick(serverModel.Nick);
            self.UserName(serverModel.UserName);
            self.BirthDate(serverModel.BirthDate);
            self.AvatarFileId(serverModel.AvatarFileId);
            self.CommunitySearchVisibilty(serverModel.CommunitySearchVisibilty);
            self.ComShowTrainings(serverModel.ComShowTrainings);
            self.ComTileVisible(serverModel.ComTileVisible);
            self.Name(serverModel.Name);
            self.NameOrNick(serverModel.NameOrNick);
            self.ProfileText(serverModel.ProfileText);
            self.AdditionalClass(serverModel.AdditionalClass);

            self.LocationModel().initByModel(serverModel.LocationModel);
            self.PrimaryDisciplineId(serverModel.PrimaryDisciplineId);

            self.CommunityTrainingsCount(serverModel.CommunityTrainingsCount);
            self.CommunityTargetsCount(serverModel.CommunityTargetsCount);
            self.CommunityCompetitionsCount(serverModel.CommunityCompetitionsCount);
            self.CommunityFriendsCount(serverModel.CommunityFriendsCount);
            self.AvailableClubsCount(serverModel.AvailableClubsCount);
            self.CommunityIsFriend(serverModel.CommunityIsFriend);
            self.IsAvatarSmall(serverModel.IsAvatarSmall);

            self.FacebookAccounts = [];
            for (var i = 0, len = serverModel.FacebookAccounts.length; i < len; i++) {
                var elem = new FacebookAccountModel();
                elem.initByModel(serverModel.FacebookAccounts[i]);
                self.FacebookAccounts.push(elem);
            }
        }
    };

    self.init = function (serverModel) {
        self.UserId(0);
        self.FirstName('');
        self.LastName('');
        self.MaleSex('');
        self.Nick('');
        self.UserName('');
        self.BirthDate('');
        self.AvatarFileId(0);
        self.CommunitySearchVisibilty('');
        self.ComShowTrainings(false);
        self.ComTileVisible(false);
        self.Name('');
        self.NameOrNick('');
        self.AdditionalClass('');
        self.LocationModel().init();
        self.PrimaryDisciplineId(0);
        self.FacebookAccounts = [];
        self.IsAvatarSmall(false);
    };
}

UserInfoModel = function (serverModel) {
    var self = $.extend(this, serverModel);

    self.AvatarUrl = ContextPath + 'Image/GetImage?id=' + self.AvatarId;

    _.bindAll(self, 'AvatarHover');
}
UserInfoModel.prototype.AvatarHover = function (callback, AvatarUrl) {
    var self = this;
    var content = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
        + (AvatarUrl || self.AvatarUrl)
        + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
    if (callback) callback(content);
}

function getCurrentUser(doneCalback, doneCalbackParams) {
    cachedAjaxCall({
        type: "POST",
        url: ContextPath + "Account/GetSimpleUserModel"
    }, function (serverModel) {
        if (serverModel && serverModel.UserName) {
            CurrentUser.initByModel(serverModel);
        } else {
            clearCachedAjaxCall({
                type: "POST",
                url: ContextPath + "Account/GetSimpleUserModel"
            });
        }
        if (doneCalback) doneCalback(CurrentUser, doneCalbackParams);
    }, 3600000 //1h
    );

    return CurrentUser;
}

function getCurrentUserCacheKey() {
    var callParams = {
        type: "POST",
        url: ContextPath + "Account/GetSimpleUserModel"
    }

    var key = JSON.stringify(callParams);

    return key;
}

//#region cachAjax

function cachedAjaxCall(callParams, doneCalback, ttl, force) {
    var key = JSON.stringify(callParams);
    var value = $.jStorage.get(key);

    if (!value || force) {
        if (!ttl) { ttl = 600000 };
        $.ajax(callParams)
            .done(function (result) {
                $.jStorage.set(key, result, { TTL: ttl });
                doneCalback(result);
            });
    } else {
        doneCalback(value);
    }
}

function clearCachedAjaxCall(callParams) {
    var key = JSON.stringify(callParams);
    $.jStorage.deleteKey(key);
}

function addToCache(key, value, ttl) {
    if (!ttl) { ttl = 600000 };
    $.jStorage.set(key, value, { TTL: ttl });
}

function deleteFromCache(key) {
    $.jStorage.deleteKey(key);
}

function clearCache() {
    $.jStorage.flush();
}

function getCacheKeyFromCallParams(callParams) {
    var key = JSON.stringify(callParams);

    return key;
}

function getCacheKeys(prefix) {
    var keys = [];

    for (i = 0; i <= $.jStorage.index().length - 1; i++) {
        var key = $.jStorage.index()[i];

        if (prefix) {
            if (key.indexOf(prefix) > -1) {
                keys.push(key);
            }
        }
        else {
            keys.push(key);
        }
    }

    return keys;
}

function deleteFromCacheWithPrefix(prefix) {
    var keys = getCacheKeys(prefix);

    for (var i = 0, len = keys.length; i < len; i++) {
        var keyWithParams = keys[i].substring(prefix.length);
        deleteFromCache(keyWithParams);
        deleteFromCache(keys[i]);
    }
}

//#endregion

var notUnloading = true;
$(window).on('beforeunload', function (e) {
    notUnloading = false;
});
$(document).ajaxError(function (event, jqxhr, settings, thrownError) {
    if (notUnloading && notHandled) {
        $('body').css('cursor', 'default');
        if (jqxhr.status == 0) return; //abort
        if (jqxhr.status == 403) {
            ShowEmptyMsg(jqxhr.responseText, translations.NoRightsErrorLabel);
        } else if (jqxhr.status == 401) {
            location = ContextPath + "Account/LogOn?ReturnUrl=" + encodeURI(location.pathname + location.search);
            //location.reload();
        } else {
            ShowEmptyMsg(translations.ErrorMsg, translations.Error);
        }
    }

    notHandled = true;
});

function addTimestamToParams(params, timestampName) {
    if ($.jStorage.get("clearTrainingsTypesCache", false)) {
        if (timestampName) {
            params[timestampName] = new Date();
        } else {
            params.timestamp = new Date();
        }
    }
    return params;
}

function getSearchParameters() {
    var prmstr = window.location.search.substr(1);
    return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}

function transformToAssocArray(prmstr) {
    var params = {};
    var prmarr = prmstr.split("&");
    for (var i = 0; i < prmarr.length; i++) {
        var tmparr = prmarr[i].split("=");
        params[tmparr[0]] = tmparr[1];
    }
    return params;
}

function getSelectorFromClassList(classList) {
    var result = '';
    for (var i = 0; i < classList.length; i++) {
        result = result + '.' + classList[i];
    }
    return result;
}

//#region pace calculator

function isMinPaceUnit(unit) {
    return unit.indexOf("min") == 0 || unit.indexOf("/") == 0;
}

function getMaxPace(unit) {
    return isMinPaceUnit(unit) ? 200 : 2000;
}

function formatPace(pace, unit, invertFactor, showUnit, showStandingTxt) {

    var dispUnit = showUnit ? ('&nbsp;' + unit) : '';

    if (isMinPaceUnit(unit)) {
        if (pace < 200) { //200 min/km
            var nr = Math.floor(pace);
            var secs = ((pace - nr) * 60).toFixed();
            if (secs == 60) {
                nr++;
                secs = 0;
            }
            return nr.toString() + ':' + ("0" + secs.toString()).slice(-2) + dispUnit;
        } else {
            if (showStandingTxt) return translations.Standing;
            return '';
        }
    } else {
        if (pace < 2000) { //2000 s/100m
            return pace.toFixed().toString() + dispUnit;
        } else {
            if (showStandingTxt) return translations.Standing;
            return '';
        }
    }
}
function calculatePaceString(value, unit, invertFactor, showUnit, showStandingTxt) {
    if (!value) {
        if (showStandingTxt) {
            return translations.Standing;
        } else {
            return '';
        }
    }

    if (value && isNaN(value)) {
        value = parseFloat(value.replace(',', '.'));
    }

    var pace = invertFactor / value;

    return formatPace(pace, unit, invertFactor, showUnit, showStandingTxt);
}


function convertTimeToPaceNumber(time) {
    var timeSeparator = ":";
    var value = 0;
    if (time.indexOf(timeSeparator) != -1) {
        var timeArray = time.split(timeSeparator);
        value = (parseFloat(timeArray[0]) * 60 + parseFloat(timeArray[1])) / 60;
    } else {
        value = parseFloat(time.replace(',', '.'));
    }
    return value;
};
function calculateSpeedFromPace(value, paceUnit, invertFactor) {

    if (!value) return 0;

    value = convertTimeToPaceNumber(value);

    if (value <= 0) return 0;

    var speed = invertFactor / value;
    return speed;
}
/*
function calculatePaceFromSpeed(speed) {
    if (speed) {
        var val = parseFloat(speed.replace(',', '.'));
        var pace = val > 0 ? 60 / val : 0;
        return pace;
    }
    else {
        return 0;
    }
}

function calculateSwimPaceFromPace(pace) {
    var pace = convertTimeToPaceNumber(pace.replace(',', '.'));
    var swimPace = (pace * 60 / 10).toFixed(2);
    return swimPace;
}

function calculateTimeFromPace(value) {
    if (value != 0) {
        var nr = Math.floor(value);
        return nr.toString() + ':' + ("0" + ((value - nr) * 60).toFixed().toString()).slice(-2);
    } else {
        return "0:00";
    }
}


function calculatePaceFromSwimPace(swimPace) {
    var n = parseFloat(swimPace.replace(',', '.'));
    var pace = n > 0 ? n * 10 / 60 : 0
    return pace;
}

function convertNumberToTime(value) {
    if (value != 0) {
        var nr = Math.floor(value);
        return nr.toString() + ':' + ("0" + ((value - nr) * 60).toFixed().toString()).slice(-2);
    } else {
        return "0:00";
    }
}*/

//#endregion

//#region login/register/club join

function loginFormShowEmailCloud() {
    $('body.logonPage div.loginForm .emailCloudContainer').css('display', 'block');
}

function loginFormHideEmailCloud() {
    $('body.logonPage div.loginForm .emailCloudContainer').css('display', 'none');
}


function checkClubAccessCode(clubId, callBack) {
    var accessCode = $('#ClubAccessCode').val();
    $.ajax({
        type: "POST",
        url: ContextPath + "Club/CheckClubAccessCode",
        data: { clubId: clubId, accessCode: accessCode }
    })
    .done(function (result) {
        if (callBack) callBack(result);
    });
}

var acceptsForExternals = null;
function loginFormAccptsBtnClick() {
    if (acceptsForExternals) {
        acceptsForExternals();
    } else {
        loginFormRegisterBtn();
    }
}

var acceptsWasShown = false;
function loginFormRegisterBtn(provider, page) {
    var clubRequireCode = $('#ClubRequireCode').val() == 'True';
    var ClubId = $('#ClubId').val();

    if (ClubId && clubRequireCode) {
        checkClubAccessCode(ClubId, function (result) {
            loginFormRegisterBtnCore(result, ClubId, provider, page);
        })
    } else {
        loginFormRegisterBtnCore(true, ClubId, provider, page);
    }
}
function loginFormRegisterBtnCore(isCodeValid, ClubId, provider, page) {
    var FromExternalApp = $('#FromExternalApp').val() == 'True';
    var acceptRulesChecked = $('#AcceptRules').is(':checked');
    var acceptRulesClubChecked = $('#AcceptClubTerms').is(':checked');
    var acceptDataProcess = $('#AcceptDataProcess').is(':checked');
    var clubRequireUserDataAcces = $('#ClubRequireUserDataAcces').val() == 'True';
    var clubCanAccessData = $('#ClubCanAccessData').is(':checked');
    var acceptSpam = $('#AcceptSpam').is(':checked');

    var PeriodId = $('#PeriodId').val();
    var TargetId = $('#TargetId').val();

    var isFromSocialBtn = provider;

    JsRequestLog('script.js', 'loginFormRegisterBtnCore',
            ' FromExternalApp:' + FromExternalApp
            + ' isCodeValid:' + isCodeValid
            + ' acceptRulesChecked:' + acceptRulesChecked
            + ' acceptRulesClubChecked:' + acceptRulesClubChecked
            + ' acceptDataProcess:' + acceptDataProcess
            + ' clubRequireUserDataAcces:' + clubRequireUserDataAcces
            + ' clubCanAccessData:' + clubCanAccessData
            + ' acceptSpam:' + acceptSpam
            + ' ClubId:' + ClubId
            + ' PeriodId:' + PeriodId
            + ' TargetId:' + TargetId
            + ' isFromSocialBtn:' + isFromSocialBtn
            );

    var canAccept = true;
    if (!FromExternalApp || isFromSocialBtn) {
        if (!isCodeValid) {
            canAccept = false;
        } else {
            if (!acceptRulesChecked) {
                if (acceptsWasShown) $('.forExternalAcceptRules').css('display', 'block');
                canAccept = false;
            }
            else
                $('.forExternalAcceptRules').css('display', 'none');

         /*   if (!acceptDataProcess) {
                if (acceptsWasShown) $('.forExternalAcceptDataProcess').css('display', 'block');
                canAccept = false;
            }
            else
                $('.forExternalAcceptDataProcess').css('display', 'none');*/

            if (ClubId && ClubId != '0') {
                if (!acceptRulesClubChecked) {
                    if (acceptsWasShown) $('.forExternalAcceptRulesClub').css('display', 'block');
                    canAccept = false;
                }
                else
                    $('.forExternalAcceptRulesClub').css('display', 'none');

                if (clubRequireUserDataAcces && !clubCanAccessData) {
                    if (acceptsWasShown) $('.forExternalAcceptDataProcessClub').css('display', 'block');
                    canAccept = false;
                }
                else
                    $('.forExternalAcceptDataProcessClub').css('display', 'none');
            }
        }
    }

    if (canAccept) {
        if (isFromSocialBtn) {
            var accessCode = $('#ClubAccessCode').val();
            var options = (acceptSpam ? '1;' : '0;') + (clubCanAccessData ? '1;' : '0;') + ClubId + ';' + PeriodId + ';' + TargetId + ';' + encodeURIComponent(accessCode);
            window.location.href = "/ExternalLogin/Login?provider=" + provider + "&page=" + page + "&options=" + options;
        } else {
            $('#acceptsModal').hide();

            $(".logonPage form").submit();
        }
    } else {
        if (!isCodeValid) {
            $('#ClubAccessCodeValidMsg').show();
        } else {
            $('#ClubAccessCodeValidMsg').hide();
            var modal = $('#acceptsModal');
            if (isFromSocialBtn) {
                acceptsForExternals = function () {
                    loginFormRegisterBtn(provider, page);
                };
            }
            acceptsWasShown = true;
            modal.show();
        }
    }
}



function LogOnModel() {
    var self = this;

    self.modal = null;
    self.loginCallback = null;

    self.Open = function (loginCallback, clubId) {
        if (!clubId) clubId = 0;
        $('body').css('cursor', 'wait');
        self.loginCallback = loginCallback;
        self.modal = $('#loginModal');
        if (!self.modal.length) {
            $.ajax({
                type: "GET",
                url: ContextPath + "Account/GetLogOnModal",
                data: { clubId: clubId }
            })
            .done(function (result) {
                $('body').append(result);
                self.modal = $('#loginModal');
                self.ShowModal();
            });
        } else {
            self.ShowModal();
        }
    }
    self.ShowModal = function () {
        self.modal.modal("show");
        $('body').css('cursor', 'default');
    }

    self.AfterLogin = function () {
        if (self.loginCallback) self.loginCallback();
        self.Close();
    }

    self.Close = function () {
        self.modal.modal("hide");
        $('body').css('cursor', 'default');
    }
}
var logOnModel = null;
function openLogOnPanel(loginCallback, clubId) {
    if (!logOnModel) {
        logOnModel = new LogOnModel();
        //GeneralModel.addProperty("LogOnModel", logOnModel);
    }
    setTimeout(function () { logOnModel.Open(loginCallback, clubId); }, 50);
}


function SimpleClubJoinModel() {
    var self = this;

    self.modal = null;
    self.callback = null;

    self.Open = function (callback, clubId) {
        $('body').css('cursor', 'wait');
        self.callback = callback;

        clubsListModel.LoadSingleClub(clubId, function () {
            var clubs = clubsListModel.MyClubs();
            if (clubs.length) {
                var club = clubs[0];
                club.AfterJoinCallback = callback;
                $('#joinModalAccessCodeContainer').html('');
                loadAndBindTemplate({
                    simpleContainer: 'joinModalAccessCodeContainer',
                    existingElem: '#joinModalAccessCode',
                    ajaxPath: 'Club/GetJoinAccessCodeModal',
                    bindData: club,
                    callback: function () {
                        self.modal = $('#joinModalAccessCode');
                        self.ShowModal();
                    }
                });
            }
        });
    }
    self.ShowModal = function () {
        self.modal.modal("show");
        $('body').css('cursor', 'default');
    }

    self.AfterJoin = function () {
        if (self.callback) self.callback();
        self.Close();
    }

    self.Close = function () {
        self.modal.modal("hide");
        $('body').css('cursor', 'default');
    }
}
var simpleClubJoinModel = null;
function openSimpleClubJoinPanel(callback, clubId) {
    if (!simpleClubJoinModel) {
        simpleClubJoinModel = new SimpleClubJoinModel();
        //GeneralModel.addProperty("SimpleClubJoinModel", simpleClubJoinModel);
    }
    setTimeout(function () { simpleClubJoinModel.Open(callback, clubId); }, 50);
}

//#endregion

//#region topSlide

var sourceTopSlidePos = null;
function showTopSlide(fixed) {
    var topSlider = $('#topSlider');
    topSlider.show();

    if (fixed && $(window).height() > topSlider.height()) {
        topSlider.addClass('fixed');
        sourceTopSlidePos = 0;
        var h = topSlider.height() - 120;
        if (h < 0) h = 0;
        $('section#main').css('margin-top', h + 'px');
    } else {
        topSlider.removeClass('fixed');
        sourceTopSlidePos = $(window).scrollTop();
        $(window).scrollTop(0);
    }
}
function hideTopSlide() {
    $('#topSlider').hide();
    if (sourceTopSlidePos) $(window).scrollTop(sourceTopSlidePos);
    $('section#main').css('margin-top', 0 + 'px');
}
function getTopSlide() {
    return $('#topSlider');
}
function setTopSlideContent(content, greenBg) {
    var topSlider = $('#topSlider');
    topSlider.html(content);
}
function topSlideapplayBinding() {

    GeneralModel.CheckIfApply($('#topSlider').children('div')[0]);

}

//#endregion

function ParamsVisibility(visibilityConf) {
    var self = this;

    self.CadnceGroup = true;
    self.SpeedGroup = true;
    self.RouteGroup = true;
    self.WeatherGroup = true;
    self.SweamSpeed = false;

    self.ShowPace = false;

    self.PowerGroup = true;
    self.AltitudeGroup = true;
    self.LapGroup = true;

    self.PaceParams = null;

    self.OnlyDistance = false;

    self.UseHrZones = true;

    //self.HideLoadInExcer = false;

    self.UseInRecords = false;

    if (visibilityConf) {
        visibilityConf = JSON.parse(visibilityConf);
        if (typeof (visibilityConf.CadnceGroup) != "undefined") self.CadnceGroup = visibilityConf.CadnceGroup;
        if (typeof (visibilityConf.SpeedGroup) != "undefined") self.SpeedGroup = visibilityConf.SpeedGroup;
        if (typeof (visibilityConf.RouteGroup) != "undefined") self.RouteGroup = visibilityConf.RouteGroup;
        if (typeof (visibilityConf.WeatherGroup) != "undefined") self.WeatherGroup = visibilityConf.WeatherGroup;
        if (typeof (visibilityConf.SweamSpeed) != "undefined") self.SweamSpeed = visibilityConf.SweamSpeed;
        if (typeof (visibilityConf.PowerGroup) != "undefined") self.PowerGroup = visibilityConf.PowerGroup;
        if (typeof (visibilityConf.LapGroup) != "undefined") self.LapGroup = visibilityConf.LapGroup;
        if (typeof (visibilityConf.AltitudeGroup) != "undefined") self.AltitudeGroup = visibilityConf.AltitudeGroup;
        if (typeof (visibilityConf.ShowPace) != "undefined") self.ShowPace = visibilityConf.ShowPace;
        if (typeof (visibilityConf.PaceParams) != "undefined") self.PaceParams = visibilityConf.PaceParams;
        if (typeof (visibilityConf.OnlyDistance) != "undefined") self.OnlyDistance = visibilityConf.OnlyDistance;
        //if (typeof (visibilityConf.HideLoadInExcer) != "undefined") self.HideLoadInExcer = visibilityConf.HideLoadInExcer;
        if (typeof (visibilityConf.UseInRecords) != "undefined") self.UseInRecords = visibilityConf.UseInRecords;
        if (typeof (visibilityConf.UseHrZones) != "undefined") self.UseHrZones = visibilityConf.UseHrZones;
    }
}
function GetParamsVisibility(trainingType, typeManager) {
    if (!trainingType) return new ParamsVisibility();

    if (!typeManager) typeManager = CommonTrainingTypesLibControl;

    if (_.isNumber(trainingType) && typeManager) {
        trainingType = typeManager.GetTypeById(trainingType);
    }

    if (trainingType) {
        if (trainingType.GetParsedParamsVisibility) {
            return trainingType.GetParsedParamsVisibility();
        }
    }

}

function loadSerialisedJson(idsMap) {
    //idsMap: [['periodPlansTilesContainerModel', 'periodPlansTilesContainer']]
    _(idsMap).each(function (idsPair) {
        loadSingleSerialisedJson(idsPair[0], idsPair[1], idsPair[2])
    });
}
function loadSingleSerialisedJson(modelId, containerId, modelProcess) {
    var strMdl = $('#' + modelId).val();
    if (strMdl) {
        var model = JSON.parse(strMdl);
        if (model) {
            if (modelProcess) {
                model = modelProcess(model);
            }
            var container = $('#' + containerId);
            if (container.length) {
                ko.applyBindings(model, container[0]);
            }
            return model;
        }
    }
    return null;
}

function loadCss(fileName) {
    var css = $("<link>", {
        "rel": "stylesheet",
        "type": "text/css",
        "href": "/Content/" + fileName + ".css"
    })[0];

    css.onload = function () {
        console.log("CSS IN IFRAME LOADED");
    };

    document
      .getElementsByTagName("head")[0]
      .appendChild(css);
}

function SimpleCoachModel(parent, serverModel) {
    var self = this;
    self.Parent = parent;

    self.IdObs = ko.observable(0);

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id = serverModel.Id;
            self.PhotoId = serverModel.PhotoId;
            self.FirstName = serverModel.FirstName ? serverModel.FirstName : '';
            self.LastName = serverModel.LastName ? serverModel.LastName : '';
            self.Nick = serverModel.Nick;

            self.DisplayName = self.FirstName + ' ' + self.LastName;

            self.ImageUrl = ContextPath + "Image/GetImage?id=" + self.PhotoId;

            self.IdObs(serverModel.Id);
        } else {
            self.Id = 0;
            self.PhotoId = 0;
            self.FirstName = '';
            self.LastName = '';
            self.Nick = '';
            self.DisplayName = '';
            self.ImageUrl = '';
            self.IdObs(0);
        }
    }
    self.initByModel(serverModel);
}

function getObjectClass(obj) {
    if (typeof obj != "object" || obj === null) return false;
    else return /(\w+)\(/.exec(obj.constructor.toString())[1];
}

Array.prototype.insert = function (index, item) {
    this.splice(index, 0, item);
};

function FanModelForPublication(parent) {
    var self = this;
    self.Parent = parent;

    self.TotalResuts = ko.observable(0);

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.TotalResuts(serverModel.TotalResuts);
        }
    };
}

function ModalAskModel() {
    var self = this;

    self.parent = null;

    self.onAction = null;

    self.Title = ko.observable("");
    self.Message = ko.observable("");
    self.DeleteLabel = ko.observable("");
    self.AskMsgClass = ko.observable("");
    self.HideCancel = ko.observable(false);

    self.DeleteClick = function () {
        if (self.onAction) {
            self.onAction();
            $('#GenericAskModal').modal("hide");
        }
    }

    self.Show = function (title, msg, onAction, btnLabel, askMsgClass, parent, hideCancel) {
        self.Title(title);
        self.Message(msg);
        self.onAction = onAction;
        self.DeleteLabel(btnLabel);
        self.AskMsgClass(askMsgClass);
        self.parent = parent;
        self.HideCancel(hideCancel);
        $('#GenericAskModal').modal("show");
    }       
}
var modalAskModel = new ModalAskModel();
GeneralModel.addProperty("ModalAskModel", modalAskModel);

function JsRequestLog(controller, action, queryString) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/JsRequestLog",
        data: { controller: controller, action: action, queryString: queryString }
    });
}

function addZero(i) {
    if (i < 10) {
        i = "0" + i;
    }
    return i;
}

function dotToColon(s) {
    return ('' + s).replace('.', ',');
}

function getFuncVal(param) {
    var val = param;
    if (val && _.isFunction(val)) {
        val = val();
    }
    return val;
}

function fullScreenPhotoId(photoId) {
    fullScreenPhoto(ContextPath + "Image/GetImage?id=" + photoId);
}
function fullScreenPhoto(photoUrl) {
    var container = $('#fullScreenPhotoImg');
    if (!container.length) {
        $.ajax({
            type: "GET",
            url: ContextPath + "Home/FullScreenPhoto"
        })
         .done(function (result) {
             if (result) {
                 $('body').append(result);
                 container = $('#fullScreenPhotoImg');
                 setFullScreenPhoto(container, photoUrl);
             }
         });
    } else {
        setFullScreenPhoto(container, photoUrl);
    }
}
function setFullScreenPhoto(container, photoUrl) {
    container.attr('src', '');
    container.attr('src', photoUrl);
    var modal = container.parents('.modal').modal('show');
    $('img#fullScreenPhotoImg').css('max-height', ($(window).height() - 165));
}


var medSortFunc = function (a, b) { return a - b; }
var getMedianFunc = function (arr) {
    var values = arr.slice(0).sort(medSortFunc);

    var half = Math.ceil(values.length / 2);

    if (values.length % 2)
        return values[half];
    else
        return (values[half - 1] + values[half]) / 2.0;
}
function MathRoundTo(val, to) {
    if (!val) return val;
    if (isNaN(val)) return val;
    var toMultip = Math.pow(10, to);
    return dotToColon(Math.round(val * toMultip) / toMultip);
}
function MsToString(ms, hideSec) {
    var d = new Date(ms);
    var h = addZero(d.getUTCHours() + 24 * (d.getUTCDate() - 1));
    var m = addZero(d.getUTCMinutes());
    var time = h + ":" + m;
    if (!hideSec) {
        var s = addZero(d.getUTCSeconds());
        time = time + ":" + s;
    }
    return time;
}
function MsToMinutesString(ms) {
    var d = new Date(ms);
    var h = addZero(d.getUTCHours() + 24 * (d.getUTCDate() - 1));
    var m = h * 60 + addZero(d.getUTCMinutes());
    var s = addZero(d.getUTCSeconds());
    var time = m + ":" + s;

    return time;
}
function stringToSeconds(str) {
    var seconds = 0;
    if (str) {
        var a = str.split(':');
        seconds = (+a[0]) * 60 * 60 + (+a[1]) * 60;
        if (a.length > 2) seconds += (+a[2]);
    }
    return seconds;
};
ko.bindingHandlers.secondsToString = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();
        var hideSecs = allBindings.hideSec;

        var str = MsToString(valueUnwrapped * 1000, hideSecs);
        $(element).html(str);
    }
};
ko.bindingHandlers.secondsToShortDesc = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();
        var hideSecs = allBindings.hideSec;

        $(element).html(secondsToShortDesc(valueUnwrapped, hideSecs));
    }
};
secondsToShortDesc = function (value, hideSecs) {
    var d = new Date(value * 1000);
    return dateToShortDesc(d, hideSecs);
}
dateToShortDesc = function (d, hideSecs, noSpaces) {

    var h = d.getUTCHours() + 24 * (d.getUTCDate() - 1);
    var m = d.getUTCMinutes();
    var s = d.getUTCSeconds();

    var time = '';
    if (h > 0) {
        time += '' + h;
    }
    if (m > 0) {
        if (h > 0) {
            time += ":" + addZero(m);
        } else {
            time += '' + m;
        }
    }
    if (s > 0 && !hideSecs) {
        if (h > 0) {
            if (m > 0) {
                time += ":" + addZero(s);
            } else {
                time += ":00:" + addZero(s);
            }
        } else {
            if (m > 0) {
                time += ":" + addZero(s);
            } else {
                time += "" + addZero(s);
            }
        }
    }
    if (h > 0) {
        time += noSpaces ? '' : ' ' + translations.godz;
    } else if (m > 0) {
        time += noSpaces ? '' : ' ' + translations.mins;
    } else if (s > 0 && !hideSecs) {
        time += noSpaces ? '' : ' ' + translations.secs;
    }

    return time
};
secondsToShortDescFromat = function (value, hideSecs) {
    var d = new Date(value * 1000);
    return dateToShortDescFromat(d, hideSecs);
}
dateToShortDescFromat = function (d, hideSecs, noSpaces) { //1h20m20s

    var h = d.getUTCHours() + 24 * (d.getUTCDate() - 1);
    var m = d.getUTCMinutes();
    var s = d.getUTCSeconds();

    var time = '';
    if (h > 0) {
        time += '' + h + translations.h;
    }
    if (m > 0) {
        time += '' + m + translations.min;
    }
    if (s > 0 && !hideSecs) {
        if (h > 0 && m == 0) {
            time += "0" + translations.min + s;
        } else {
            time += "" + s;
        }
        time += translations.s;
    }

    return time
};


longSecondsToDesc = function (value, space) {

    var y = Math.floor(value / (3600 * 24 * 30 * 12));
    value = value - y * 3600 * 24 * 30 * 12;

    var M = Math.floor(value / (3600 * 24 * 30 ));
    value = value - M * 3600 * 24 * 30 ;

    var d = Math.floor(value / (3600 * 24 ));
    value = value - d * 3600 * 24;

    var h = Math.floor(value / (3600 ));
    value = value - h * 3600;

    var m = Math.floor(value / 60);
    value = value - m * 60;

    var s = value ;

    var time = '';
    if (y > 0) {
        time += '' + y + (space?' ':'') + translations.long_y;
    } if (M > 0) {
        time += ' ' + M + (space ? ' ' : '') + translations.long_M;
    } if (d > 0) {
        time += ' ' + d + (space ? ' ' : '') + translations.long_d;
    }
    if (h > 0) {
        time += ' ' + h + (space ? ' ' : '') + translations.long_h;
    }
 /*   if (m > 0) {
        time += ' ' + m + (space?' ':'') + translations.long_min;
    }
    if (s > 0 ) {
        time += ' ' + s + (space?' ':'') + translations.long_s;
    }*/

    return time
};
ko.bindingHandlers.longSecondsToDesc = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();

        $(element).html(longSecondsToDesc(valueUnwrapped, true));
    }
};



ko.bindingHandlers.timeSpanToString = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();
        var hideSecs = allBindings.hideSec;

        var str = '';
        if (valueUnwrapped) {
            if (_.isNumber(valueUnwrapped.TotalMilliseconds)) {
                str = MsToString(valueUnwrapped.TotalMilliseconds, hideSecs);
            }
            else {
                str = valueUnwrapped;
            };
        }
        $(element).html(str);
    }
};

function SToShortString(ms) {
    var h = Math.floor(ms / 3600);
    var m = Math.floor((ms - h * 3600) / 60);
    var time = h + ":" + addZero(m);
    return time;
}
function HToShortString(val) {
    var h = Math.floor(val);
    var mins = Math.round((val - h) * 60);
    return h + ':' + addZero(mins);
}


/*
{
    container: '',
    existingElem: '',
    callback: '',
    ajaxPath: '',
    ajaxData: null,
    bindData:null
}
*/
function loadAndBindTemplate(params) {
    if (!params || (!params.container && !params.simpleContainer) || !params.ajaxPath) return;

    var container;
    if (params.container) {
        container = $(params.container);
    } else if (params.simpleContainer) {
        container = $('#' + params.simpleContainer);
        if (!container.length) {
            $('body').append($('<div id="' + params.simpleContainer + '"></div>'));
        }
        container = $('#' + params.simpleContainer);
    }

    if (!container.length) return;

    if (params.existingElem) {
        var exi = container.find(params.existingElem);
        if (exi.length > 0) {
            if (params.bindData && params.reBind) {
                ko.cleanNode(container[0]);
                ko.applyBindings(params.bindData, container[0]);
            }
            if (params.callback) params.callback();
            return;
        }
    }

    $.ajax({
        type: "POST",
        url: ContextPath + params.ajaxPath,
        data: params.ajaxData
    })
    .done(function (result) {
        ko.cleanNode(container[0]);
        container.html(result);

        if (params.bindData) {
            ko.applyBindings(params.bindData, container[0]);
        } else {
            ko.applyBindings(GeneralModel, container[0]);
        }

        if (params.callback) params.callback();
    });
}

function stopPropagation(evt) {
    if (evt) {
        if (typeof evt.stopPropagation != "undefined") {
            evt.stopPropagation();
        } else {
            evt.cancelBubble = true;
        }
    }
}

DefferedHover = function (options) {
    var self = this;

    self.options = {
        showTimeout: 700,
        hideTmeout: 350,
        showConfition: null
    };
    $.extend(self.options, options);

    self.Visible = ko.observable(false);
    self.showAddTimeOut = null;
    self.hideAddTimeOut = null;

    _.bindAll(self, 'Show', 'Hide');
}
DefferedHover.prototype = {
    Show: function (data, event) {
        var self = this;
        if (!self.options || !self.options.showConfition || self.options.showConfition()) {
            clearTimeout(self.hideAddTimeOut);
            if (!self.Visible()) {
                self.showAddTimeOut = setTimeout(function () { self.Visible(true); }, self.options.showTimeout);
            }
        }
    },
    Hide: function (data, event) {
        var self = this;
        clearTimeout(self.showAddTimeOut);
        if (self.Visible()) {
            self.hideAddTimeOut = setTimeout(function () { self.Visible(false); }, self.options.hideTmeout);
        }
    },
    HideImediate: function () {
        var self = this;
        clearTimeout(self.showAddTimeOut);
        if (self.Visible()) {
            self.Visible(false);
        }
    }
}

PiorityBusy = function () {
    var self = this;

    self.counter = 0;

    self.Increase = function () {
        self.counter++;
        canLoadCommunityWall = false;
    }
    self.Decrease = function () {
        self.counter--;
        if (self.counter <= 0) {
            self.counter = 0;
            canLoadCommunityWall = true;
        }
    }
}
var piorityBusy = new PiorityBusy();

ko.bindingHandlers.brHtml = {
    update: function (element, valueAccessor, allBindings) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        if (valueUnwrapped) valueUnwrapped = valueUnwrapped.replace(/\n/g, '<br />').replace(/\t/g, '&emsp;');

        $(element).html(valueUnwrapped);
    }
};


ko.bindingHandlers.sex = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();
        var short = allBindings.short;

        if (valueUnwrapped) {
            valueUnwrapped = valueUnwrapped.toLowerCase();
            if (valueUnwrapped == 'k' || valueUnwrapped == 'female' || valueUnwrapped == 'f' || valueUnwrapped == 'kobieta') {
                valueUnwrapped = short ? translations.FemaleShort : translations.Female;
            } else if (valueUnwrapped == 'm' || valueUnwrapped == 'male' || valueUnwrapped == 'mezczyzna' || valueUnwrapped == 'mężczyzna') {
                valueUnwrapped = short ? translations.MaleShort : translations.Male;
            }
        }

        $(element).html(valueUnwrapped);
    }
};

(function (d, s, id) {
    if (typeof fbId !== 'undefined' && fbId) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) return;
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/pl_PL/sdk.js#xfbml=1&version=v2.12&appId=" + fbId;
        fjs.parentNode.insertBefore(js, fjs);
    }
}(document, 'script', 'facebook-jssdk'));

function nextTutorial(currentStatus) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/GetNextTutorial",
        data: { currentStatus: currentStatus }
    })
    .done(function (result) {
        var tutorialContainer = $('#tutorialContainer');
        tutorialContainer.html(result);
    });
}
function prevTutorial(currentStatus) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/GetPrevTutorial",
        data: { currentStatus: currentStatus }
    })
    .done(function (result) {
        var tutorialContainer = $('#tutorialContainer');
        tutorialContainer.html(result);
    });
}
function passInitTutorial() {
    $.ajax({
        type: "POST",
        url: ContextPath + "Home/PassInitTutorial"
    })
    .done(function (result) {
        var tutorialContainer = $('#tutorialContainer');
        tutorialContainer.html(result);
    });
}

function setlang(event, lang) {
    if (typeof communityExplorerModel !== 'undefined' && communityExplorerModel) {
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTrainings');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTrainingInvitations');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyCompetitions');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTargets');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyLiveStreams');
        deleteFromCache('actualScrollPosition_GetOnlyTrainings');
        deleteFromCache('actualScrollPosition_GetOnlyTrainingInvitations');
        deleteFromCache('actualScrollPosition_GetOnlyCompetitions');
        deleteFromCache('actualScrollPosition_GetOnlyTargets');
        deleteFromCache('actualScrollPosition_GetOnlyLiveStreams');
    }

    var url = ContextPath + 'Home/SetLang?lang=' + lang;

    if (event.ctrlKey) {
        window.open(url, '_blank');
    }
    else {
        window.location = url;
    }
}

function mobailMenuIconClick(mobailMenuIcon) {
    var elem = $(mobailMenuIcon).closest('.mobileMenuContainer');
    elem.addClass('hover');

    var lis = elem.find('li');
    lis.off("click").on("click", function (e) {
        lis.removeClass('hover');
        $(e.target).closest('li').addClass('hover');
    });

    var moveFnc = function (e) {
        if (!$(e.target).closest('.mobileMenuContainer').length) {
            $('body').off("click");
            $('body').off("mousemove");
            elem.removeClass('hover');
            lis.removeClass('hover');
        }
    };

    $('body').off("mousemove").on("mousemove", moveFnc);
    $('body').off("click").on("click", moveFnc);

    stopPropagation(this.event);
    return false;
}

function ChangesHistoryModel() {
    var self = this;

    self.Changes = ko.observableArray([]);

    self.initByModel = function (serverModel) {
        if (serverModel) {
            if (serverModel.Changes) {
                for (var i = 0, len = serverModel.Changes.length; i < len; i++) {
                    var changesModel = new ChangesModel();
                    changesModel.initByModel(serverModel.Changes[i]);
                    self.Changes.push(changesModel);
                }
            }
        }
    }
}

function ChangesModel() {
    var self = this;

    self.Field = ko.observable('');
    self.NewValue = ko.observable('');
    self.NewValueDate = ko.observable('');
    self.ChangeDate = ko.observable('');

    self.NewValueDisplay = ko.computed(function () {
        var result = '';

        if (self.NewValue()) result = self.NewValue();
        else if (self.NewValueDate()) result = self.NewValueDate();

        if (translations[result]) return translations[result];
        else
            return result;
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Field(serverModel.Field);
            self.NewValue(serverModel.NewValue);
            if (serverModel.NewValueDate) {
                self.NewValueDate(dateToString(new Date(parseInt(serverModel.NewValueDate.substr(6)))));
            }
            self.ChangeDate(dateToString(new Date(parseInt(serverModel.ChangeDate.substr(6))), true));
        }
    }
}
if (!IsAdmin && typeof $.connection !== 'undefined' && $.connection) {

    var notificationHub = $.connection.notificationHub;

    $(function () {
        $.connection.hub.start()
            .then(function () {
                return notificationHub.server.activate();
            })
            .done(function (response) {
                //                 if (response === 'Activated') {
                //                 } else {
                //                 }
            })
            .fail(function (reason) {
                console.log("SignalR connection failed: " + reason);
            });

        $.extend(notificationHub.client, {
            sendMessage: function (notificationId, message) {
                notificationHub.server.notificationReceived(notificationId);

                ShowEmptyMsg(message, translations["Message"]);
            }
        });
    });
}

function retry(isDone, next) {
    var current_trial = 0, max_retry = 50, interval = 10, is_timeout = false;
    var id = window.setInterval(
        function () {
            if (isDone()) {
                window.clearInterval(id);
                next(is_timeout);
            }
            if (current_trial++ > max_retry) {
                window.clearInterval(id);
                is_timeout = true;
                next(is_timeout);
            }
        },
        10
    );
}

function isIE10OrLater(user_agent) {
    var ua = user_agent.toLowerCase();
    if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
        return false;
    }
    var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
    if (match && parseInt(match[1], 10) >= 10) {
        return true;
    }
    return false;
}

function detectPrivateMode(callback) {
    var is_private;

    if (window.webkitRequestFileSystem) {
        window.webkitRequestFileSystem(
            window.TEMPORARY, 1,
            function () {
                is_private = false;
            },
            function (e) {
                console.log(e);
                is_private = true;
            }
        );
    } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
        var db;
        try {
            db = window.indexedDB.open('test');
        } catch (e) {
            is_private = true;
        }

        if (typeof is_private === 'undefined') {
            retry(
                function isDone() {
                    return db.readyState === 'done' ? true : false;
                },
                function next(is_timeout) {
                    if (!is_timeout) {
                        is_private = db.result ? false : true;
                    }
                }
            );
        }
    } else if (isIE10OrLater(window.navigator.userAgent)) {
        is_private = false;
        try {
            if (!window.indexedDB) {
                is_private = true;
            }
        } catch (e) {
            is_private = true;
        }
    } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
        try {
            window.localStorage.setItem('test', 1);
        } catch (e) {
            is_private = true;
        }

        if (typeof is_private === 'undefined') {
            is_private = false;
            window.localStorage.removeItem('test');
        }
    }

    retry(
        function isDone() {
            return typeof is_private !== 'undefined' ? true : false;
        },
        function next(is_timeout) {
            callback(is_private);
        }
    );
}


function copyTextToClipboard(text) {
    var textArea = document.createElement("textarea");

    //
    // *** This styling is an extra step which is likely not required. ***
    //
    // Why is it here? To ensure:
    // 1. the element is able to have focus and selection.
    // 2. if element was to flash render it has minimal visual impact.
    // 3. less flakyness with selection and copying which **might** occur if
    //    the textarea element is not visible.
    //
    // The likelihood is the element won't even render, not even a flash,
    // so some of these are just precautions. However in IE the element
    // is visible whilst the popup box asking the user for permission for
    // the web page to copy to the clipboard.
    //

    // Place in top-left corner of screen regardless of scroll position.
    textArea.style.position = 'fixed';
    textArea.style.top = 0;
    textArea.style.left = 0;

    // Ensure it has a small width and height. Setting to 1px / 1em
    // doesn't work as this gives a negative w/h on some browsers.
    textArea.style.width = '2em';
    textArea.style.height = '2em';

    // We don't need padding, reducing the size if it does flash render.
    textArea.style.padding = 0;

    // Clean up any borders.
    textArea.style.border = 'none';
    textArea.style.outline = 'none';
    textArea.style.boxShadow = 'none';

    // Avoid flash of white box if rendered for any reason.
    textArea.style.background = 'transparent';


    textArea.value = text;

    document.body.appendChild(textArea);

    textArea.select();

    try {
        var successful = document.execCommand('copy');
        var msg = successful ? 'successful' : 'unsuccessful';
        console.log('Copying text command was ' + msg);
    } catch (err) {
        console.log('Oops, unable to copy');
    }

    document.body.removeChild(textArea);
}

function getClipboardText(callback) {

    var modal = ShowEmptyMsg(translations.PressCtrlV, InfoMessageType);

    var textArea = modal[0];
    textArea.contentEditable = true;

    $(textArea).on('paste', function (event) {
        var e = event.originalEvent;
        modal.modal("hide");
        // Stop data actually being pasted into div
        e.stopPropagation();
        e.preventDefault();

        // Get pasted data via clipboard API
        clipboardData = e.clipboardData || window.clipboardData;
        pastedData = clipboardData.getData('Text');

        textArea.contentEditable = false;
        if (pastedData) callback(pastedData);


        $(textArea).off('paste');
    });

}

(function ($) {

    $.fn.dynamicForm = function (options) {

        if (options && options.ActionName) {

            url = ContextPath + options.ActionName;

            var inputs = '';

            if (options.Parameters) {

                for (var property in options.Parameters) {
                    if (options.Parameters.hasOwnProperty(property)) {
                        var value = options.Parameters[property];
                        inputs = inputs + '<input type="hidden" name="' + property + '" value="' + value + '"/>';
                    }
                }
            }
            $('<form action="' + url + '" method="POST">' + inputs + '<input type="submit" value="Submit"></form>').appendTo('body').submit().remove();
        }

        return this;

    };

}(jQuery));


(function ($) {

    $.fn.collapsible = function (options) {

        var table = this;

        if (!table.hasClass("collapsible-handler")) {
            table.addClass("collapsible-handler");
            // Add event listener for opening and closing details
            table.find('tbody').on('click', 'td.details-control', function () {
                var td = $(this);
                var tr = td.closest('tr');
                if (tr.hasClass('shown')) {
                    tr.removeClass('shown');
                    td.attr('title', translations.Expand);
                    var node = tr.next('tr');
                    while (node.hasClass('details')) {
                        node.addClass('hide');
                        node = node.next('tr');
                    }
                } else {
                    tr.addClass('shown');
                    td.attr('title', translations.Collapse);
                    var node = tr.next('tr');
                    while (node.hasClass('details')) {
                        node.removeClass('hide');
                        node = node.next('tr');
                    }
                }
            });
            table.find('thead a.show_up').click(function () {
                table.find('tbody td.details-control').attr('title', translations.Collapse);
                table.find('tbody tr').removeClass('hide');
                table.find('tbody tr:not(.details)').addClass('shown');
            });
            table.find('thead a.show_down').click(function () {
                table.find('tbody td.details-control').attr('title', translations.Expand);
                table.find('tbody tr.details').addClass('hide');
                table.find('tbody tr').removeClass('shown');
            });
        }
        return this;

    };

}(jQuery));

ko.bindingHandlers.googleRecaptcha_old = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

        var responseCheck = function (gRecaptchaResponse) {
            var value = valueAccessor();
            value(gRecaptchaResponse);
        };

        var id = $(element).attr('id');
        if (id) {
            var intializeGUI = function () {
                grecaptcha.render(element, {
                    'sitekey': '6LepgUIUAAAAACRVN2lVca91Fwe_eC-ZuG-cCJ1Z',
                    'callback': responseCheck
                });
            };

            if (grecaptcha) {
                intializeGUI();
            } else {
                window.setTimeout(intializeGUI, 3000);
            }
        }

    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        // NONE
    }
};

ko.bindingHandlers.googleRecaptcha = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

        var responseCheck = function (gRecaptchaResponse) {
            var value = valueAccessor();
            value(gRecaptchaResponse);
        };

        function loadReCaptcha() {
            if (typeof grecaptcha !== "undefined") {
                var $target = $('<div />').appendTo($(element));
                grecaptcha.render($target[0], {
                    'sitekey': '6LepgUIUAAAAACRVN2lVca91Fwe_eC-ZuG-cCJ1Z',
                    'callback': responseCheck
                });
            }
            else {
                setTimeout(function () {
                    loadReCaptcha();
                }, 150);
            }
        }

        loadReCaptcha();
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        // NONE
    }
};

levensteinCompare = function (a, b) {
    a = a + "";
    b = b + "";

    var m = [], i, j, min = Math.min;

    if (!(a && b)) return (b || a).length;

    for (i = 0; i <= b.length; m[i] = [i++]);
    for (j = 0; j <= a.length; m[0][j] = j++);

    for (i = 1; i <= b.length; i++) {
        for (j = 1; j <= a.length; j++) {
            m[i][j] = b.charAt(i - 1) == a.charAt(j - 1)
                ? m[i - 1][j - 1]
                : m[i][j] = min(
                    m[i - 1][j - 1] + 1,
                    min(m[i][j - 1] + 1, m[i - 1][j] + 1))
        }
    }

    return m[b.length][a.length];
};

(function ($) {
    ko.bindingHandlers.hoverCloud = {
        init: function (element, valueAccessor) {//, allBindingsAccessor) {
            var options = ko.utils.unwrapObservable(valueAccessor());
            //allBindings = allBindingsAccessor();

            options = jQuery.extend(true, {
                showTimeout: 700,
                hideTmeout: 350,
                showCondition: null,
                contentCall: null,
                content: null,
                contentSelector: null,
                additionalShow: null,
                additionalHide: null,
                left: true,
                top: true,
                width: 100,
                bindObj: null
            }, options);

            var showTimeOut = null;
            var hideTimeOut = null;
            var contentTimeOut = null;

            var cloudTxt = null;
            var cloudElem = null;

            var buildCloud = function (event) {
                if (cloudElem) cloudElem.remove();
                if (!cloudTxt) {
                    cloudTxt = '';
                    cloudTxt += '<div class="cloudContainer hoverCloud">';
                    cloudTxt += '<div class="hrCloud ' + (options.left ? 'left' : 'right') + ' ' + (options.top ? 'top' : 'down') + '">';
                    if (!options.top) {
                        cloudTxt += '<div class="cloudBottom">';
                        cloudTxt += '<div class="cloudBottomFill"></div>';
                        cloudTxt += '</div>';
                    }
                    cloudTxt += '<div class="cloudContent" style="width:' + options.width + 'px">';
                    cloudTxt += options.content
                    cloudTxt += '</div>';
                    if (options.top) {
                        cloudTxt += '<div class="cloudBottom">';
                        cloudTxt += '<div class="cloudBottomFill"></div>';
                        cloudTxt += '</div>';
                    }
                    cloudTxt += '</div>';
                    cloudTxt += '</div>';
                }

                cloudElem = $(cloudTxt);
                cloudElem.css({ top: event.pageY + 'px' });
                cloudElem.css({ left: event.pageX + 'px' });
                $('body').append(cloudElem);

                if (options.bindObj) ko.applyBindings(options.bindObj, cloudElem[0]);

                cloudElem.on("mouseover", function (event) {
                    //console.log("element over");
                    clearTimeout(options.hideTimeOut);
                });
                cloudElem.on("mouseout", function () {
                    //console.log("element out");
                    if (options.additionalHide) options.additionalHide();
                    clearTimeout(options.showTimeOut);

                    options.hideTimeOut = setTimeout(function () {
                        //console.log("element out timeout");
                        if (cloudElem) cloudElem.remove();
                    }, options.hideTmeout);
                });
            }
            element = $(element);
            element.on("mouseover", function (event) {
                //console.log("row over");
                if (options.additionalShow) options.additionalShow();

                if (!options.showCondition || options.showCondition()) {
                    clearTimeout(options.hideTimeOut);
                    options.showTimeOut = setTimeout(function () {
                        //console.log("row over timeout");
                        if (options.contentSelector) {
                            options.content = $(options.contentSelector).html();
                        }
                        if (options.content) {
                            buildCloud(event);
                        } else if (options.contentCall) {
                            options.contentCall(function (content) { options.content = content; buildCloud(event); });
                        }
                    }, options.showTimeout);
                }
            });
            element.on("mouseout", function () {
                //console.log("row out");
                if (options.additionalHide) options.additionalHide();
                clearTimeout(options.showTimeOut);

                options.hideTimeOut = setTimeout(function () {
                    //console.log("row out timeout");
                    if (cloudElem) cloudElem.remove();
                }, options.hideTmeout);
            });

        },
        update: function (element, valueAccessor) {

        }
    };
})(jQuery);


(function ($) {
    ko.bindingHandlers.hoverContent = {
        init: function (element, valueAccessor) {//, allBindingsAccessor) {
            var options = ko.utils.unwrapObservable(valueAccessor());
            //allBindings = allBindingsAccessor();

            options = jQuery.extend(true, {
                showTimeout: 700,
                hideTmeout: 350,
                showCondition: null,
                contentCall: null,
                content: null,
                additionalShow: null,
                additionalHide: null,
                bindObj: null
            }, options);

            var showTimeOut = null;
            var hideTimeOut = null;
            var contentTimeOut = null;

            var cloudTxt = null;
            var cloudElem = null;

            var buildCloud = function (event) {
                if (cloudElem) cloudElem.remove();
                if (!cloudTxt) {
                    cloudTxt = '';
                    cloudTxt += '<div class="hoverContentContainer">';

                    cloudTxt += options.content

                    cloudTxt += '</div>';
                }

                cloudElem = $(cloudTxt);
                cloudElem.css({ top: event.pageY + 'px' });
                cloudElem.css({ left: event.pageX + 'px' });
                $('body').append(cloudElem);

                if (options.bindObj) ko.applyBindings(options.bindObj, cloudElem[0]);

                cloudElem.on("mouseover", function (event) {
                    console.log("element over");
                    clearTimeout(options.hideTimeOut);
                });
                cloudElem.on("mouseout", function () {
                    console.log("element out");
                    if (options.additionalHide) options.additionalHide();
                    clearTimeout(options.showTimeOut);

                    options.hideTimeOut = setTimeout(function () {
                        console.log("element out timeout");
                        if (cloudElem) cloudElem.remove();
                    }, options.hideTmeout);
                });
            }
            element = $(element);
            element.on("mouseover", function (event) {
                console.log("content over");
                if (options.additionalShow) options.additionalShow();

                if (!options.showCondition || options.showCondition()) {
                    clearTimeout(options.hideTimeOut);
                    options.showTimeOut = setTimeout(function () {
                        console.log("row over timeout");
                        if (options.content) {
                            buildCloud(event);
                        } else if (options.contentCall) {
                            options.contentCall(function (content) { options.content = content; buildCloud(event); });
                        }
                    }, options.showTimeout);
                }
            });
            element.on("mouseout", function () {
                console.log("content out");
                if (options.additionalHide) options.additionalHide();
                clearTimeout(options.showTimeOut);

                options.hideTimeOut = setTimeout(function () {
                    console.log("row out timeout");
                    if (cloudElem) cloudElem.remove();
                }, options.hideTmeout);
            });

        },
        update: function (element, valueAccessor) {

        }
    };
})(jQuery);

(function ($) {
    ko.bindingHandlers.enterkey = {
        init: function (element, valueAccessor, allBindings, viewModel) {
            var callback = valueAccessor();
            $(element).keypress(function (event) {
                var keyCode = (event.which ? event.which : event.keyCode);
                if (keyCode === 13) {
                    callback.call(viewModel);
                    return false;
                }
                return true;
            });
        }
    };
})(jQuery);

(function ($) {
    ko.bindingHandlers.tapClick = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var enable = ko.utils.unwrapObservable(valueAccessor());
            var callback = allBindingsAccessor().click;
            if (callback) {
                $(element).on("touchend", function () {
                    if (enable) {
                        callback(viewModel);
                    }
                });
            }
        }
        , update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { }
    };
})(jQuery);

function selectBigImg(elem, id) {
    var p = $(elem).parents(".parentBigImg");
    p.css('background-image', "url('../Image/GetImage?width=1000&height=600&id=" + id + "')");
    p.find('.tumb').removeClass('active');
    $(elem).addClass('active');
}

ko.bindingHandlers.htmlLazy = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor());


        element.innerHTML = value;

    },
    update: function (element, valueAccessor) {
        var value = ko.unwrap(valueAccessor());

        if (!element.isContentEditable) {
            element.innerHTML = value;
        }
    }
};
ko.bindingHandlers.contentEditable = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor()),
            htmlLazy = allBindingsAccessor().htmlLazy;

        $(element).on("input", function () {
            if (this.isContentEditable && ko.isWriteableObservable(htmlLazy)) {
                htmlLazy(this.innerHTML);
            }
        });
    },
    update: function (element, valueAccessor) {
        var value = ko.unwrap(valueAccessor());

        element.contentEditable = value;

        if (!element.isContentEditable) {
            $(element).trigger("input");
        }
    }
};

function plusOr0(val) {
    return (val < 0) ? 0 : val;
}



function CropperModel() {
    var self = this;

    self.ImageId = ko.observable(0);
    self.ImageIdField = null;

    self.ImageUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetSrcImage?id=" + self.ImageId();
    });

    self.Cropper = null;

    self.Modal = null;

    self.Saving = false;

    self.Save = function () {
        if (!self.Saving) {
            self.Saving = true;
            $('body').css('cursor', 'wait');
            self.ImageIdField(-10);
            $.ajax({
                type: 'POST',
                url: ContextPath + "Image/UpdateImage",
                data: {
                    //croppData: self.Cropper.getData(true),
                    imageDataURL: self.Cropper.getCroppedCanvas().toDataURL(),
                    sourceImageId: self.ImageId()
                }
            })
            .done(function (newImageId) {
                self.ImageId(newImageId);
                self.ImageIdField(newImageId);
                self.Saving = false;

                $('body').css('cursor', 'default');
                self.Modal.modal("hide");
            });
        }
    }

    self.coreShow = function () {
        if (!($("#cropperModal").data('bs.modal') || {}).isShown) {
            self.Modal.modal("show");
        }
        if (!self.Cropper) {
            var img = self.Modal.find('img');
            img.cropper();
            self.Cropper = img.data('cropper');
        } else {
            self.Cropper.replace(self.ImageUrl());
        }
    }

    self.ShowArray = function (array, idx) {
        /*
        self.ImageId(array[idx]());
        self.ImageIdField = array[idx];

        self.commomnShow();
        */
        self.Show(array[idx]);
    }

    self.Show = function (imageId) {
        self.ImageId(imageId());
        self.ImageIdField = imageId;

        self.commomnShow();
    }

    self.commomnShow = function () {       

        if (self.Modal) {
            self.coreShow();
        }
        else {
            loadAndBindTemplate({
                simpleContainer: 'copperModalContainer',
                existingElem: '#cropperModal',
                ajaxPath: 'Image/CropperModal',
                bindData: null,
                callback: function () {
                    self.Modal = $('#cropperModal');
                    self.coreShow();
                }
            });
        }
    }

    self.MoveMode = function () {
        self.Cropper.setDragMode("move");
    }
    self.CropMode = function () {
        self.Cropper.setDragMode("crop");
    }
    self.ZoomIn = function () {
        self.Cropper.zoom(0.1);
    }
    self.ZoomOut = function () {
        self.Cropper.zoom(-0.1);
    }
    self.MoveLeft = function () {
        self.Cropper.move(-10, 0);
    }
    self.MoveRight = function () {
        self.Cropper.move(10, 0);
    }
    self.MoveUp = function () {
        self.Cropper.move(0, -10);
    }
    self.MoveDown = function () {
        self.Cropper.move(0, 10);
    }
    self.RotateRight = function () {
        self.Cropper.rotate(45);
    }
    self.RotateLeft = function () {
        self.Cropper.rotate(-45);
    }
    self.FlippedX = false;
    self.FlipHorizontal = function () {
        self.Cropper.scaleX(self.FlippedX ? 1 : -1);
        self.FlippedX = !self.FlippedX;
    }
    self.FlippedY = false;
    self.FlipVertical = function () {
        self.Cropper.scaleY(self.FlippedY ? 1 : -1);
        self.FlippedY = !self.FlippedY;
    }
    self.Reset = function () {
        self.Cropper.reset();
    }
}
var cropperModel = new CropperModel();
GeneralModel.addProperty("CropperModel", cropperModel);

ko.bindingHandlers.CropperImageId = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //var value = ko.unwrap(valueAccessor());
        element.onclick = function () {
            cropperModel.Show(valueAccessor())
        };
    },
    update: function (element, valueAccessor) {
        //var value = ko.unwrap(valueAccessor());
        element.onclick = function () {
            cropperModel.Show(valueAccessor())
        };
    }
};

ko.bindingHandlers.CropperArrayImageId = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor());
        var idx = allBindingsAccessor().Index();
        element.onclick = function () {
            cropperModel.ShowArray(value, idx)
        };
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor());
        var idx = allBindingsAccessor().Index();
        element.onclick = function () {
            cropperModel.ShowArray(value, idx)
        };
    }
};


// Check whether control button is pressed
var cntrlIsPressed = false;
$(document).keydown(function (event) {
    if (event.which == "17"){
        cntrlIsPressed = true;
        //console.log("ctrl pressed");
    }
});
$(document).keyup(function () {
    cntrlIsPressed = false;
    //console.log("unpressed");
});




;TrainingPointsContainerModel = function (parent) {
    var self = this;

    BaseKoModelObject.call(this, parent);

    self.EndOffset = ko.observable(0);
    self.Id = ko.observable(0);
    self.StartOffset = ko.observable(0);
    self.TrainingId = ko.observable(0);
    self.TrainingPointsIdentifier = ko.observable('');
    self.AltitudePointsActive = ko.observable(false);
    self.AltitudePointsAvailable = ko.observable(false);
    self.CadencePointsActive = ko.observable(false);
    self.CadencePointsAvailable = ko.observable(false);
    self.HrPointsActive = ko.observable(false);
    self.HrPointsAvailable = ko.observable(false);
    self.MapPointsActive = ko.observable(false);
    self.MapPointsAvailable = ko.observable(false);
    self.PowerPointsActive = ko.observable(false);
    self.PowerPointsAvailable = ko.observable(false);
    self.SpeedPointsActive = ko.observable(false);
    self.SpeedPointsAvailable = ko.observable(false);

    self.Duration = ko.pureComputed(self.Duration_compute, this);

    self.Points = ko.observableArray([]);
}

TrainingPointsContainerModel.inheritsFrom(BaseKoModelObject);
TrainingPointsContainerModel.prototype.init = function () {
    var self = this;
    self.EndOffset(0);
    self.Id(0);
    self.StartOffset(0);
    self.TrainingId(0);
    self.TrainingPointsIdentifier('');
    self.AltitudePointsActive(false);
    self.AltitudePointsAvailable(false);
    self.CadencePointsActive(false);
    self.CadencePointsAvailable(false);
    self.HrPointsActive(false);
    self.HrPointsAvailable(false);
    self.MapPointsActive(false);
    self.MapPointsAvailable(false);
    self.PowerPointsActive(false);
    self.PowerPointsAvailable(false);
    self.SpeedPointsActive(false);
    self.SpeedPointsAvailable(false);
    self.Points.removeAll;
}
TrainingPointsContainerModel.prototype.initByModel = function (serverModel) {
    var self = this;
    self.BaseObj = serverModel;

    self.EndOffset(serverModel.EndOffset);
    self.Id(serverModel.Id);
    self.StartOffset(serverModel.StartOffset);
    self.TrainingId(serverModel.TrainingId);
    self.TrainingPointsIdentifier(serverModel.TrainingPointsIdentifier);
    self.AltitudePointsActive(serverModel.AltitudePointsActive);
    self.AltitudePointsAvailable(serverModel.AltitudePointsAvailable);
    self.CadencePointsActive(serverModel.CadencePointsActive);
    self.CadencePointsAvailable(serverModel.CadencePointsAvailable);
    self.HrPointsActive(serverModel.HrPointsActive);
    self.HrPointsAvailable(serverModel.HrPointsAvailable);
    self.MapPointsActive(serverModel.MapPointsActive);
    self.MapPointsAvailable(serverModel.MapPointsAvailable);
    self.PowerPointsActive(serverModel.PowerPointsActive);
    self.PowerPointsAvailable(serverModel.PowerPointsAvailable);
    self.SpeedPointsActive(serverModel.SpeedPointsActive);
    self.SpeedPointsAvailable(serverModel.SpeedPointsAvailable);

    koArrayCopy(serverModel.Points, self.Points);
}
TrainingPointsContainerModel.prototype.getClearData = function () {
    var self = this;
    var data = {
        EndOffset: self.EndOffset(),
        Id: self.Id(),
        StartOffset: self.StartOffset(),
        TrainingId: self.TrainingId(),
        TrainingPointsIdentifier: self.TrainingPointsIdentifier(),
        AltitudePointsActive: self.AltitudePointsActive(),
        AltitudePointsAvailable: self.AltitudePointsAvailable(),
        CadencePointsActive: self.CadencePointsActive(),
        CadencePointsAvailable: self.CadencePointsAvailable(),
        HrPointsActive: self.HrPointsActive(),
        HrPointsAvailable: self.HrPointsAvailable(),
        MapPointsActive: self.MapPointsActive(),
        MapPointsAvailable: self.MapPointsAvailable(),
        PowerPointsActive: self.PowerPointsActive(),
        PowerPointsAvailable: self.PowerPointsAvailable(),
        SpeedPointsActive: self.SpeedPointsActive(),
        SpeedPointsAvailable: self.SpeedPointsAvailable(),
    }
    return data;
}

TrainingPointsContainerModel.prototype.Duration_compute = function () {
    return this.EndOffset() - this.StartOffset();
}
;function IntensivityModel(intensivityParam, translationPrefix, name) {

   var self = this;

   self.IntensivityName = translations[name];

   self.TranslationPrefix = translationPrefix;

   self.CssModifire = name;

    self.Intensivity = intensivityParam ? intensivityParam : ko.observable(0);

    self.IntensToolBarHovered = ko.observable(0);
    self.DisplayIntensivity = ko.computed(function () {
        var hIntens = self.IntensToolBarHovered();
        if (hIntens) {
            return hIntens;
        } else {
            return self.Intensivity();
        }
    });
    self.IntensivityLabel = ko.computed(function () {
        var tp = self.TranslationPrefix;
        return translations[(tp? tp : "Intensivity_") + self.DisplayIntensivity()];
    });

    self.IntensToolBarMouseIn = function (value) {
        self.IntensToolBarHovered(value);
    }
    self.IntensToolBarMouseOut = function () {
        self.IntensToolBarHovered(0);
    }
    self.IntensToolBarMouseClick = function (value) {
        self.Intensivity(value);
    }

}

;
function LocalisationControl() {
    var self = this;

    self.Text = ko.observable("");
    self.Longitude = ko.observable("");
    self.Latitude = ko.observable("");
    self.WindowTitle = ko.observable(translations["SetLocalisation"]);
    self.LocationLabelTitle = ko.observable(translations["Location"]);

    self.ShowLocationNotFoundMsg = ko.observable(false);

    self.modelToSet = null;
    self.Modal = null;
    self.Map = null;
    self.Geocoder = null;
    self.Marker = null;
    self.UpdateAddressTimeout = null;

    //self.useCustomName = false;
    //self.initMode = false;

    self.AddMarker = function (pos, lat, long) {

        if (self.Marker) self.Marker.setMap(null);

        if (!pos) {
            pos = new google.maps.LatLng(lat, long);
        }

        self.Marker = new google.maps.Marker({
            map: self.Map,
            position: pos,
            draggable: true
        });

        google.maps.event.addListener(self.Marker, 'dragend', self.UpdateMarkerPosition);
    }

    self.RemoveMarker = function () {
        if (self.Marker) self.Marker.setMap(null);
    }

    self.UpdateMarkerPosition = function (marcador) {
        var latLng = marcador ? marcador.latLng : self.Marker.getPosition();

        self.Latitude(latLng.lat());
        self.Longitude(latLng.lng());

        if (self.UpdateAddressTimeout) clearTimeout(self.UpdateAddressTimeout);
        self.UpdateAddressTimeout = setTimeout(self.GetMarkerAddress, 700);
    }

    self.GetMarkerAddress = function () {
        var latlng = new google.maps.LatLng(self.Latitude(), self.Longitude());
        self.Geocoder.geocode({ 'latLng': latlng }, function (results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                if (results.length) {
                    var setted = false;
                    for (var i = 0, len = results.length; i < len && !setted; i++) {
                        var res = results[i];
                        if (res.types.length) {
                            for (var j = 0, len2 = res.types.length; j < len2 && !setted; j++) {
                                if (res.types[j] == 'political') {
                                    self.Text(results[i].formatted_address);
                                    setted = true;
                                }
                            }
                        }
                    }
                    if (!setted) {
                        self.Text(results[0].formatted_address);
                    }
                }
            }
        });
    }

    self.Find = function () {
        var address = self.Text();
        if (address) {
            //self.useCustomName = true;
            self.Geocoder.geocode({ 'address': address }, function (results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    self.Map.setCenter(results[0].geometry.location);

                    self.Latitude(results[0].geometry.location.lat());
                    self.Longitude(results[0].geometry.location.lng());

                    self.AddMarker(results[0].geometry.location);

                    self.ShowLocationNotFoundMsg(false);
                } else {
                    self.ShowLocationNotFoundMsg(true);
                }
            });
        }
    }
    self.FindOnEnter = function (data, event) {
        if (event.keyCode == 13) {
            setTimeout(self.Find, 100);
        } else {
            return true;
        }
    }
	
	self.SetModel = function (locModel) {
		self.Text(locModel.Name());
        self.Longitude(locModel.Longitude());
        self.Latitude(locModel.Latitude());

        if (locModel.WindowTitle) {
            self.WindowTitle(locModel.WindowTitle);
        }
        else {
            self.WindowTitle(translations["SetLocalisation"]);
        }

        if (locModel.LocationLabelTitle) {
            self.LocationLabelTitle(locModel.LocationLabelTitle);
        }
        else {
            self.LocationLabelTitle(translations["Location"]);
        }

        self.modelToSet = locModel;
	};

	self.OpenCtrl = function (locModel, initText) {
	    self.SetModel(locModel);
	    if (initText) {
	        self.Text(initText);
	    }
        if (!self.Modal) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Home/GetLocalisationModal"
            })
            .done(function (result) {
                var container = $('#localisationModalContainer');
                container.html(result);
                ko.applyBindings(GeneralModel, container[0]);
                self.Modal = $('#localisationModal');
                self.Modal.on('shown', function () {
                    self.initMap();
                    setTimeout(function () { 
                        self.Modal.find("input[type='text']").focus();
                    }, 100);
                });
                self.showCtrl();
            });
        } else {
            self.showCtrl();
        }
    }

	self.showCtrl = function () {
        self.Modal.modal('show');
    }

    self.initMap = function (container) {
		if (!container) {
			container = 'localisationModalMap';
		}		
        if (!self.Map) {
            google.maps.visualRefresh = true;

            self.Geocoder = new google.maps.Geocoder();
            var mapOptions = {
                zoom: 6,
                center: new google.maps.LatLng(51.9189046, 19.1343786)
            };

            self.Map = new google.maps.Map(document.getElementById(container), mapOptions);

            google.maps.event.addListener(self.Map, 'click', function (event) {
                self.AddMarker(event.latLng);
                self.UpdateMarkerPosition();
            }
            );
        }

        self.ShowLocationNotFoundMsg(false);
        if (self.Longitude() && self.Latitude()) {
            var pos = new google.maps.LatLng(self.Latitude(), self.Longitude());
            self.Map.setCenter(pos);
            self.Map.setZoom(8);

            self.AddMarker(pos);
        } else if (CurrentUser && CurrentUser.LocationModel().Latitude() && CurrentUser.LocationModel().Longitude()) {
            var pos = new google.maps.LatLng(CurrentUser.LocationModel().Latitude(), CurrentUser.LocationModel().Longitude());
            self.Map.setCenter(pos);
            self.Map.setZoom(8);
        } else if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(function (position) {
                var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
                self.Map.setCenter(pos);
                self.Map.setZoom(8);
            });
        }
    }

    self.Set = function () {
        self.modelToSet.setValues(self.Text(), self.Longitude(), self.Latitude());
		if (self.Modal) {
			self.Modal.modal('hide');
		}
    }
}

var localisationControl = new LocalisationControl();
GeneralModel.addProperty("LocalisationControl", localisationControl);;/**
 * @name MarkerClustererPlus for Google Maps V3
 * @version 2.1.2 [May 28, 2014]
 * @author Gary Little
 * @fileoverview
 * The library creates and manages per-zoom-level clusters for large amounts of markers.
 * <p>
 * This is an enhanced V3 implementation of the
 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the
 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/"
 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little.
 * <p>
 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It
 *  adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>,
 *  and <code>calculator</code> properties as well as support for four more events. It also allows
 *  greater control over the styling of the text that appears on the cluster marker. The
 *  documentation has been significantly improved and the overall code has been simplified and
 *  polished. Very large numbers of markers can now be managed without causing Javascript timeout
 *  errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been
 *  deprecated. The new name is <code>click</code>, so please change your application code now.
 */

/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/**
 * @name ClusterIconStyle
 * @class This class represents the object for values in the <code>styles</code> array passed
 *  to the {@link MarkerClusterer} constructor. The element in this array that is used to
 *  style the cluster icon is determined by calling the <code>calculator</code> function.
 *
 * @property {string} url The URL of the cluster icon image file. Required.
 * @property {number} height The display height (in pixels) of the cluster icon. Required.
 * @property {number} width The display width (in pixels) of the cluster icon. Required.
 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to
 *  where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code>
 *  where <code>yoffset</code> increases as you go down from center and <code>xoffset</code>
 *  increases to the right of center. The default is <code>[0, 0]</code>.
 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the
 *  spot on the cluster icon that is to be aligned with the cluster position. The format is
 *  <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and
 *  <code>xoffset</code> increases to the right of the top-left corner of the icon. The default
 *  anchor position is the center of the cluster icon.
 * @property {string} [textColor="black"] The color of the label text shown on the
 *  cluster icon.
 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the
 *  cluster icon.
 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code>
 *  property for the label text shown on the cluster icon.
 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code>
 *  property for the label text shown on the cluster icon.
 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code>
 *  property for the label text shown on the cluster icon.
 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code>
 *  property for the label text shown on the cluster icon.
 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image
 *  within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code>
 *  (the same format as for the CSS <code>background-position</code> property). You must set
 *  this property appropriately when the image defined by <code>url</code> represents a sprite
 *  containing multiple images. Note that the position <i>must</i> be specified in px units.
 */
/**
 * @name ClusterIconInfo
 * @class This class is an object containing general information about a cluster icon. This is
 *  the object that a <code>calculator</code> function returns.
 *
 * @property {string} text The text of the label to be shown on the cluster icon.
 * @property {number} index The index plus 1 of the element in the <code>styles</code>
 *  array to be used to style the cluster icon.
 * @property {string} title The tooltip to display when the mouse moves over the cluster icon.
 *  If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the
 *  value of the <code>title</code> property passed to the MarkerClusterer.
 */
/**
 * A cluster icon.
 *
 * @constructor
 * @extends google.maps.OverlayView
 * @param {Cluster} cluster The cluster with which the icon is to be associated.
 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons
 *  to use for various cluster sizes.
 * @private
 */
function ClusterIcon(cluster, styles) {
    cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);

    this.cluster_ = cluster;
    this.className_ = cluster.getMarkerClusterer().getClusterClass();
    this.styles_ = styles;
    this.center_ = null;
    this.div_ = null;
    this.sums_ = null;
    this.visible_ = false;

    this.setMap(cluster.getMap()); // Note: this causes onAdd to be called
}


/**
 * Adds the icon to the DOM.
 */
ClusterIcon.prototype.onAdd = function () {
    var cClusterIcon = this;
    var cMouseDownInCluster;
    var cDraggingMapByCluster;

    this.div_ = document.createElement("div");
    this.div_.className = this.className_;
    if (this.visible_) {
        this.show();
    }

    this.getPanes().overlayMouseTarget.appendChild(this.div_);

    // Fix for Issue 157
    this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () {
        cDraggingMapByCluster = cMouseDownInCluster;
    });

    google.maps.event.addDomListener(this.div_, "mousedown", function () {
        cMouseDownInCluster = true;
        cDraggingMapByCluster = false;
    });

    google.maps.event.addDomListener(this.div_, "click", function (e) {
        cMouseDownInCluster = false;
        if (!cDraggingMapByCluster) {
            var theBounds;
            var mz;
            var mc = cClusterIcon.cluster_.getMarkerClusterer();
            /**
             * This event is fired when a cluster marker is clicked.
             * @name MarkerClusterer#click
             * @param {Cluster} c The cluster that was clicked.
             * @event
             */
            google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
            google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name

            // The default click handler follows. Disable it by setting
            // the zoomOnClick property to false.
            if (mc.getZoomOnClick()) {
                // Zoom into the cluster.
                mz = mc.getMaxZoom();
                theBounds = cClusterIcon.cluster_.getBounds();
                mc.getMap().fitBounds(theBounds);
                // There is a fix for Issue 170 here:
                setTimeout(function () {
                    mc.getMap().fitBounds(theBounds);
                    // Don't zoom beyond the max zoom level
                    if (mz !== null && (mc.getMap().getZoom() > mz)) {
                        mc.getMap().setZoom(mz + 1);
                    }
                }, 100);
            }

            // Prevent event propagation to the map:
            e.cancelBubble = true;
            if (e.stopPropagation) {
                e.stopPropagation();
            }
        }
    });

    google.maps.event.addDomListener(this.div_, "mouseover", function () {
        var mc = cClusterIcon.cluster_.getMarkerClusterer();
        /**
         * This event is fired when the mouse moves over a cluster marker.
         * @name MarkerClusterer#mouseover
         * @param {Cluster} c The cluster that the mouse moved over.
         * @event
         */
        google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_);
    });

    google.maps.event.addDomListener(this.div_, "mouseout", function () {
        var mc = cClusterIcon.cluster_.getMarkerClusterer();
        /**
         * This event is fired when the mouse moves out of a cluster marker.
         * @name MarkerClusterer#mouseout
         * @param {Cluster} c The cluster that the mouse moved out of.
         * @event
         */
        google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_);
    });
};


/**
 * Removes the icon from the DOM.
 */
ClusterIcon.prototype.onRemove = function () {
    if (this.div_ && this.div_.parentNode) {
        this.hide();
        google.maps.event.removeListener(this.boundsChangedListener_);
        google.maps.event.clearInstanceListeners(this.div_);
        this.div_.parentNode.removeChild(this.div_);
        this.div_ = null;
    }
};


/**
 * Draws the icon.
 */
ClusterIcon.prototype.draw = function () {
    if (this.visible_) {
        var pos = this.getPosFromLatLng_(this.center_);
        this.div_.style.top = pos.y + "px";
        this.div_.style.left = pos.x + "px";
    }
};


/**
 * Hides the icon.
 */
ClusterIcon.prototype.hide = function () {
    if (this.div_) {
        this.div_.style.display = "none";
    }
    this.visible_ = false;
};


/**
 * Positions and shows the icon.
 */
ClusterIcon.prototype.show = function () {
    if (this.div_) {
        var img = "";
        // NOTE: values must be specified in px units
        var bp = this.backgroundPosition_.split(" ");
        var spriteH = parseInt(bp[0].replace(/^\s+|\s+$/g, ""), 10);
        var spriteV = parseInt(bp[1].replace(/^\s+|\s+$/g, ""), 10);
        var pos = this.getPosFromLatLng_(this.center_);
        this.div_.style.cssText = this.createCss(pos);
        img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; ";
        if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) {
            img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " +
                ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);";
        }
        img += "'>";
        this.div_.innerHTML = img + "<div style='" +
            "position: absolute;" +
            "top: " + this.anchorText_[0] + "px;" +
            "left: " + this.anchorText_[1] + "px;" +
            "color: " + this.textColor_ + ";" +
            "font-size: " + this.textSize_ + "px;" +
            "font-family: " + this.fontFamily_ + ";" +
            "font-weight: " + this.fontWeight_ + ";" +
            "font-style: " + this.fontStyle_ + ";" +
            "text-decoration: " + this.textDecoration_ + ";" +
            "text-align: center;" +
            "width: " + this.width_ + "px;" +
            "line-height:" + this.height_ + "px;" +
            "'>" + this.sums_.text + "</div>";
        if (typeof this.sums_.title === "undefined" || this.sums_.title === "") {
            this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
        } else {
            this.div_.title = this.sums_.title;
        }
        this.div_.style.display = "";
    }
    this.visible_ = true;
};


/**
 * Sets the icon styles to the appropriate element in the styles array.
 *
 * @param {ClusterIconInfo} sums The icon label text and styles index.
 */
ClusterIcon.prototype.useStyle = function (sums) {
    this.sums_ = sums;
    var index = Math.max(0, sums.index - 1);
    index = Math.min(this.styles_.length - 1, index);
    var style = this.styles_[index];
    this.url_ = style.url;
    this.height_ = style.height;
    this.width_ = style.width;
    this.anchorText_ = style.anchorText || [0, 0];
    this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)];
    this.textColor_ = style.textColor || "black";
    this.textSize_ = style.textSize || 11;
    this.textDecoration_ = style.textDecoration || "none";
    this.fontWeight_ = style.fontWeight || "bold";
    this.fontStyle_ = style.fontStyle || "normal";
    this.fontFamily_ = style.fontFamily || "Arial,sans-serif";
    this.backgroundPosition_ = style.backgroundPosition || "0 0";
};


/**
 * Sets the position at which to center the icon.
 *
 * @param {google.maps.LatLng} center The latlng to set as the center.
 */
ClusterIcon.prototype.setCenter = function (center) {
    this.center_ = center;
};


/**
 * Creates the cssText style parameter based on the position of the icon.
 *
 * @param {google.maps.Point} pos The position of the icon.
 * @return {string} The CSS style text.
 */
ClusterIcon.prototype.createCss = function (pos) {
    var style = [];
    style.push("cursor: pointer;");
    style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;");
    style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;");
    return style.join("");
};


/**
 * Returns the position at which to place the DIV depending on the latlng.
 *
 * @param {google.maps.LatLng} latlng The position in latlng.
 * @return {google.maps.Point} The position in pixels.
 */
ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {
    var pos = this.getProjection().fromLatLngToDivPixel(latlng);
    pos.x -= this.anchorIcon_[1];
    pos.y -= this.anchorIcon_[0];
    pos.x = parseInt(pos.x, 10);
    pos.y = parseInt(pos.y, 10);
    return pos;
};


/**
 * Creates a single cluster that manages a group of proximate markers.
 *  Used internally, do not call this constructor directly.
 * @constructor
 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this
 *  cluster is associated.
 */
function Cluster(mc) {
    this.markerClusterer_ = mc;
    this.map_ = mc.getMap();
    this.gridSize_ = mc.getGridSize();
    this.minClusterSize_ = mc.getMinimumClusterSize();
    this.averageCenter_ = mc.getAverageCenter();
    this.markers_ = [];
    this.center_ = null;
    this.bounds_ = null;
    this.clusterIcon_ = new ClusterIcon(this, mc.getStyles());
}


/**
 * Returns the number of markers managed by the cluster. You can call this from
 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
 * for the <code>MarkerClusterer</code> object.
 *
 * @return {number} The number of markers in the cluster.
 */
Cluster.prototype.getSize = function () {
    return this.markers_.length;
};


/**
 * Returns the array of markers managed by the cluster. You can call this from
 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
 * for the <code>MarkerClusterer</code> object.
 *
 * @return {Array} The array of markers in the cluster.
 */
Cluster.prototype.getMarkers = function () {
    return this.markers_;
};


/**
 * Returns the center of the cluster. You can call this from
 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
 * for the <code>MarkerClusterer</code> object.
 *
 * @return {google.maps.LatLng} The center of the cluster.
 */
Cluster.prototype.getCenter = function () {
    return this.center_;
};


/**
 * Returns the map with which the cluster is associated.
 *
 * @return {google.maps.Map} The map.
 * @ignore
 */
Cluster.prototype.getMap = function () {
    return this.map_;
};


/**
 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated.
 *
 * @return {MarkerClusterer} The associated marker clusterer.
 * @ignore
 */
Cluster.prototype.getMarkerClusterer = function () {
    return this.markerClusterer_;
};


/**
 * Returns the bounds of the cluster.
 *
 * @return {google.maps.LatLngBounds} the cluster bounds.
 * @ignore
 */
Cluster.prototype.getBounds = function () {
    var i;
    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
    var markers = this.getMarkers();
    for (i = 0; i < markers.length; i++) {
        bounds.extend(markers[i].getPosition());
    }
    return bounds;
};


/**
 * Removes the cluster from the map.
 *
 * @ignore
 */
Cluster.prototype.remove = function () {
    this.clusterIcon_.setMap(null);
    this.markers_ = [];
    delete this.markers_;
};


/**
 * Adds a marker to the cluster.
 *
 * @param {google.maps.Marker} marker The marker to be added.
 * @return {boolean} True if the marker was added.
 * @ignore
 */
Cluster.prototype.addMarker = function (marker) {
    var i;
    var mCount;
    var mz;

    if (this.isMarkerAlreadyAdded_(marker)) {
        return false;
    }

    if (!this.center_) {
        this.center_ = marker.getPosition();
        this.calculateBounds_();
    } else {
        if (this.averageCenter_) {
            var l = this.markers_.length + 1;
            var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
            var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
            this.center_ = new google.maps.LatLng(lat, lng);
            this.calculateBounds_();
        }
    }

    marker.isAdded = true;
    this.markers_.push(marker);

    mCount = this.markers_.length;
    mz = this.markerClusterer_.getMaxZoom();
    if (mz !== null && this.map_.getZoom() > mz) {
        // Zoomed in past max zoom, so show the marker.
        if (marker.getMap() !== this.map_) {
            marker.setMap(this.map_);
        }
    } else if (mCount < this.minClusterSize_) {
        // Min cluster size not reached so show the marker.
        if (marker.getMap() !== this.map_) {
            marker.setMap(this.map_);
        }
    } else if (mCount === this.minClusterSize_) {
        // Hide the markers that were showing.
        for (i = 0; i < mCount; i++) {
            this.markers_[i].setMap(null);
        }
    } else {
        marker.setMap(null);
    }

    this.updateIcon_();
    return true;
};


/**
 * Determines if a marker lies within the cluster's bounds.
 *
 * @param {google.maps.Marker} marker The marker to check.
 * @return {boolean} True if the marker lies in the bounds.
 * @ignore
 */
Cluster.prototype.isMarkerInClusterBounds = function (marker) {
    return this.bounds_.contains(marker.getPosition());
};


/**
 * Calculates the extended bounds of the cluster with the grid.
 */
Cluster.prototype.calculateBounds_ = function () {
    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
    this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
};


/**
 * Updates the cluster icon.
 */
Cluster.prototype.updateIcon_ = function () {
    var mCount = this.markers_.length;
    var mz = this.markerClusterer_.getMaxZoom();

    if (mz !== null && this.map_.getZoom() > mz) {
        this.clusterIcon_.hide();
        return;
    }

    if (mCount < this.minClusterSize_) {
        // Min cluster size not yet reached.
        this.clusterIcon_.hide();
        return;
    }

    var numStyles = this.markerClusterer_.getStyles().length;
    var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
    this.clusterIcon_.setCenter(this.center_);
    this.clusterIcon_.useStyle(sums);
    this.clusterIcon_.show();
};


/**
 * Determines if a marker has already been added to the cluster.
 *
 * @param {google.maps.Marker} marker The marker to check.
 * @return {boolean} True if the marker has already been added.
 */
Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) {
    var i;
    if (this.markers_.indexOf) {
        return this.markers_.indexOf(marker) !== -1;
    } else {
        for (i = 0; i < this.markers_.length; i++) {
            if (marker === this.markers_[i]) {
                return true;
            }
        }
    }
    return false;
};


/**
 * @name MarkerClustererOptions
 * @class This class represents the optional parameter passed to
 *  the {@link MarkerClusterer} constructor.
 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square.
 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or
 *  <code>null</code> if clustering is to be enabled at all zoom levels.
 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is
 *  clicked. You may want to set this to <code>false</code> if you have installed a handler
 *  for the <code>click</code> event and it deals with zooming on its own.
 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be
 *  the average position of all markers in the cluster. If set to <code>false</code>, the
 *  cluster marker is positioned at the location of the first marker added to the cluster.
 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster
 *  before the markers are hidden and a cluster marker appears.
 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You
 *  may want to set this to <code>true</code> to ensure that hidden markers are not included
 *  in the marker count that appears on a cluster marker (this count is the value of the
 *  <code>text</code> property of the result returned by the default <code>calculator</code>).
 *  If set to <code>true</code> and you change the visibility of a marker being clustered, be
 *  sure to also call <code>MarkerClusterer.repaint()</code>.
 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster
 *  marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a
 *  different tooltip for each cluster marker.)
 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine
 *  the text to be displayed on a cluster marker and the index indicating which style to use
 *  for the cluster marker. The input parameters for the function are (1) the array of markers
 *  represented by a cluster marker and (2) the number of cluster icon styles. It returns a
 *  {@link ClusterIconInfo} object. The default <code>calculator</code> returns a
 *  <code>text</code> property which is the number of markers in the cluster and an
 *  <code>index</code> property which is one higher than the lowest integer such that
 *  <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles
 *  array, whichever is less. The <code>styles</code> array element used has an index of
 *  <code>index</code> minus 1. For example, the default <code>calculator</code> returns a
 *  <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code>
 *  for a cluster icon representing 125 markers so the element used in the <code>styles</code>
 *  array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code>
 *  property that contains the text of the tooltip to be used for the cluster marker. If
 *   <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code>
 *   property for the MarkerClusterer.
 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles
 *  for the cluster markers. Use this class to define CSS styles that are not set up by the code
 *  that processes the <code>styles</code> array.
 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles
 *  of the cluster markers to be used. The element to be used to style a given cluster marker
 *  is determined by the function defined by the <code>calculator</code> property.
 *  The default is an array of {@link ClusterIconStyle} elements whose properties are derived
 *  from the values for <code>imagePath</code>, <code>imageExtension</code>, and
 *  <code>imageSizes</code>.
 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that
 * have sizes that are some multiple (typically double) of their actual display size. Icons such
 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays.
 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons.
 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the
 *  number of markers to be processed in a single batch when using a browser other than
 *  Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).
 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is
 *  being used, markers are processed in several batches with a small delay inserted between
 *  each batch in an attempt to avoid Javascript timeout errors. Set this property to the
 *  number of markers to be processed in a single batch; select as high a number as you can
 *  without causing a timeout error in the browser. This number might need to be as low as 100
 *  if 15,000 markers are being managed, for example.
 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH]
 *  The full URL of the root name of the group of image files to use for cluster icons.
 *  The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code>
 *  where n is the image file number (1, 2, etc.).
 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION]
 *  The extension name for the cluster icon image files (e.g., <code>"png"</code> or
 *  <code>"jpg"</code>).
 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES]
 *  An array of numbers containing the widths of the group of
 *  <code>imagePath</code>n.<code>imageExtension</code> image files.
 *  (The images are assumed to be square.)
 */
/**
 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.
 * @constructor
 * @extends google.maps.OverlayView
 * @param {google.maps.Map} map The Google map to attach to.
 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster.
 * @param {MarkerClustererOptions} [opt_options] The optional parameters.
 */
function MarkerClusterer(map, opt_markers, opt_options) {
    // MarkerClusterer implements google.maps.OverlayView interface. We use the
    // extend function to extend MarkerClusterer with google.maps.OverlayView
    // because it might not always be available when the code is defined so we
    // look for it at the last possible moment. If it doesn't exist now then
    // there is no point going ahead :)
    this.extend(MarkerClusterer, google.maps.OverlayView);

    opt_markers = opt_markers || [];
    opt_options = opt_options || {};

    this.markers_ = [];
    this.clusters_ = [];
    this.listeners_ = [];
    this.activeMap_ = null;
    this.ready_ = false;

    this.gridSize_ = opt_options.gridSize || 60;
    this.minClusterSize_ = opt_options.minimumClusterSize || 2;
    this.maxZoom_ = opt_options.maxZoom || null;
    this.styles_ = opt_options.styles || [];
    this.title_ = opt_options.title || "";
    this.zoomOnClick_ = true;
    if (opt_options.zoomOnClick !== undefined) {
        this.zoomOnClick_ = opt_options.zoomOnClick;
    }
    this.averageCenter_ = false;
    if (opt_options.averageCenter !== undefined) {
        this.averageCenter_ = opt_options.averageCenter;
    }
    this.ignoreHidden_ = false;
    if (opt_options.ignoreHidden !== undefined) {
        this.ignoreHidden_ = opt_options.ignoreHidden;
    }
    this.enableRetinaIcons_ = false;
    if (opt_options.enableRetinaIcons !== undefined) {
        this.enableRetinaIcons_ = opt_options.enableRetinaIcons;
    }
    this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH;
    this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;
    this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES;
    this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR;
    this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE;
    this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;
    this.clusterClass_ = opt_options.clusterClass || "cluster";

    if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) {
        // Try to avoid IE timeout when processing a huge number of markers:
        this.batchSize_ = this.batchSizeIE_;
    }

    this.setupStyles_();

    this.addMarkers(opt_markers, true);
    this.setMap(map); // Note: this causes onAdd to be called
}


/**
 * Implementation of the onAdd interface method.
 * @ignore
 */
MarkerClusterer.prototype.onAdd = function () {
    var cMarkerClusterer = this;

    this.activeMap_ = this.getMap();
    this.ready_ = true;

    this.repaint();

    // Add the map event listeners
    this.listeners_ = [
      google.maps.event.addListener(this.getMap(), "zoom_changed", function () {
          cMarkerClusterer.resetViewport_(false);
          // Workaround for this Google bug: when map is at level 0 and "-" of
          // zoom slider is clicked, a "zoom_changed" event is fired even though
          // the map doesn't zoom out any further. In this situation, no "idle"
          // event is triggered so the cluster markers that have been removed
          // do not get redrawn. Same goes for a zoom in at maxZoom.
          if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) {
              google.maps.event.trigger(this, "idle");
          }
      }),
      google.maps.event.addListener(this.getMap(), "idle", function () {
          cMarkerClusterer.redraw_();
      })
    ];
};


/**
 * Implementation of the onRemove interface method.
 * Removes map event listeners and all cluster icons from the DOM.
 * All managed markers are also put back on the map.
 * @ignore
 */
MarkerClusterer.prototype.onRemove = function () {
    var i;

    // Put all the managed markers back on the map:
    for (i = 0; i < this.markers_.length; i++) {
        if (this.markers_[i].getMap() !== this.activeMap_) {
            this.markers_[i].setMap(this.activeMap_);
        }
    }

    // Remove all clusters:
    for (i = 0; i < this.clusters_.length; i++) {
        this.clusters_[i].remove();
    }
    this.clusters_ = [];

    // Remove map event listeners:
    for (i = 0; i < this.listeners_.length; i++) {
        google.maps.event.removeListener(this.listeners_[i]);
    }
    this.listeners_ = [];

    this.activeMap_ = null;
    this.ready_ = false;
};


/**
 * Implementation of the draw interface method.
 * @ignore
 */
MarkerClusterer.prototype.draw = function () { };


/**
 * Sets up the styles object.
 */
MarkerClusterer.prototype.setupStyles_ = function () {
    var i, size;
    if (this.styles_.length > 0) {
        return;
    }

    for (i = 0; i < this.imageSizes_.length; i++) {
        size = this.imageSizes_[i];
        this.styles_.push({
            url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
            height: size,
            width: size
        });
    }
};


/**
 *  Fits the map to the bounds of the markers managed by the clusterer.
 */
MarkerClusterer.prototype.fitMapToMarkers = function () {
    var i;
    var markers = this.getMarkers();
    var bounds = new google.maps.LatLngBounds();
    for (i = 0; i < markers.length; i++) {
        bounds.extend(markers[i].getPosition());
    }

    this.getMap().fitBounds(bounds);
};


/**
 * Returns the value of the <code>gridSize</code> property.
 *
 * @return {number} The grid size.
 */
MarkerClusterer.prototype.getGridSize = function () {
    return this.gridSize_;
};


/**
 * Sets the value of the <code>gridSize</code> property.
 *
 * @param {number} gridSize The grid size.
 */
MarkerClusterer.prototype.setGridSize = function (gridSize) {
    this.gridSize_ = gridSize;
};


/**
 * Returns the value of the <code>minimumClusterSize</code> property.
 *
 * @return {number} The minimum cluster size.
 */
MarkerClusterer.prototype.getMinimumClusterSize = function () {
    return this.minClusterSize_;
};

/**
 * Sets the value of the <code>minimumClusterSize</code> property.
 *
 * @param {number} minimumClusterSize The minimum cluster size.
 */
MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) {
    this.minClusterSize_ = minimumClusterSize;
};


/**
 *  Returns the value of the <code>maxZoom</code> property.
 *
 *  @return {number} The maximum zoom level.
 */
MarkerClusterer.prototype.getMaxZoom = function () {
    return this.maxZoom_;
};


/**
 *  Sets the value of the <code>maxZoom</code> property.
 *
 *  @param {number} maxZoom The maximum zoom level.
 */
MarkerClusterer.prototype.setMaxZoom = function (maxZoom) {
    this.maxZoom_ = maxZoom;
};


/**
 *  Returns the value of the <code>styles</code> property.
 *
 *  @return {Array} The array of styles defining the cluster markers to be used.
 */
MarkerClusterer.prototype.getStyles = function () {
    return this.styles_;
};


/**
 *  Sets the value of the <code>styles</code> property.
 *
 *  @param {Array.<ClusterIconStyle>} styles The array of styles to use.
 */
MarkerClusterer.prototype.setStyles = function (styles) {
    this.styles_ = styles;
};


/**
 * Returns the value of the <code>title</code> property.
 *
 * @return {string} The content of the title text.
 */
MarkerClusterer.prototype.getTitle = function () {
    return this.title_;
};


/**
 *  Sets the value of the <code>title</code> property.
 *
 *  @param {string} title The value of the title property.
 */
MarkerClusterer.prototype.setTitle = function (title) {
    this.title_ = title;
};


/**
 * Returns the value of the <code>zoomOnClick</code> property.
 *
 * @return {boolean} True if zoomOnClick property is set.
 */
MarkerClusterer.prototype.getZoomOnClick = function () {
    return this.zoomOnClick_;
};


/**
 *  Sets the value of the <code>zoomOnClick</code> property.
 *
 *  @param {boolean} zoomOnClick The value of the zoomOnClick property.
 */
MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) {
    this.zoomOnClick_ = zoomOnClick;
};


/**
 * Returns the value of the <code>averageCenter</code> property.
 *
 * @return {boolean} True if averageCenter property is set.
 */
MarkerClusterer.prototype.getAverageCenter = function () {
    return this.averageCenter_;
};


/**
 *  Sets the value of the <code>averageCenter</code> property.
 *
 *  @param {boolean} averageCenter The value of the averageCenter property.
 */
MarkerClusterer.prototype.setAverageCenter = function (averageCenter) {
    this.averageCenter_ = averageCenter;
};


/**
 * Returns the value of the <code>ignoreHidden</code> property.
 *
 * @return {boolean} True if ignoreHidden property is set.
 */
MarkerClusterer.prototype.getIgnoreHidden = function () {
    return this.ignoreHidden_;
};


/**
 *  Sets the value of the <code>ignoreHidden</code> property.
 *
 *  @param {boolean} ignoreHidden The value of the ignoreHidden property.
 */
MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) {
    this.ignoreHidden_ = ignoreHidden;
};


/**
 * Returns the value of the <code>enableRetinaIcons</code> property.
 *
 * @return {boolean} True if enableRetinaIcons property is set.
 */
MarkerClusterer.prototype.getEnableRetinaIcons = function () {
    return this.enableRetinaIcons_;
};


/**
 *  Sets the value of the <code>enableRetinaIcons</code> property.
 *
 *  @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property.
 */
MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) {
    this.enableRetinaIcons_ = enableRetinaIcons;
};


/**
 * Returns the value of the <code>imageExtension</code> property.
 *
 * @return {string} The value of the imageExtension property.
 */
MarkerClusterer.prototype.getImageExtension = function () {
    return this.imageExtension_;
};


/**
 *  Sets the value of the <code>imageExtension</code> property.
 *
 *  @param {string} imageExtension The value of the imageExtension property.
 */
MarkerClusterer.prototype.setImageExtension = function (imageExtension) {
    this.imageExtension_ = imageExtension;
};


/**
 * Returns the value of the <code>imagePath</code> property.
 *
 * @return {string} The value of the imagePath property.
 */
MarkerClusterer.prototype.getImagePath = function () {
    return this.imagePath_;
};


/**
 *  Sets the value of the <code>imagePath</code> property.
 *
 *  @param {string} imagePath The value of the imagePath property.
 */
MarkerClusterer.prototype.setImagePath = function (imagePath) {
    this.imagePath_ = imagePath;
};


/**
 * Returns the value of the <code>imageSizes</code> property.
 *
 * @return {Array} The value of the imageSizes property.
 */
MarkerClusterer.prototype.getImageSizes = function () {
    return this.imageSizes_;
};


/**
 *  Sets the value of the <code>imageSizes</code> property.
 *
 *  @param {Array} imageSizes The value of the imageSizes property.
 */
MarkerClusterer.prototype.setImageSizes = function (imageSizes) {
    this.imageSizes_ = imageSizes;
};


/**
 * Returns the value of the <code>calculator</code> property.
 *
 * @return {function} the value of the calculator property.
 */
MarkerClusterer.prototype.getCalculator = function () {
    return this.calculator_;
};


/**
 * Sets the value of the <code>calculator</code> property.
 *
 * @param {function(Array.<google.maps.Marker>, number)} calculator The value
 *  of the calculator property.
 */
MarkerClusterer.prototype.setCalculator = function (calculator) {
    this.calculator_ = calculator;
};


/**
 * Returns the value of the <code>batchSizeIE</code> property.
 *
 * @return {number} the value of the batchSizeIE property.
 */
MarkerClusterer.prototype.getBatchSizeIE = function () {
    return this.batchSizeIE_;
};


/**
 * Sets the value of the <code>batchSizeIE</code> property.
 *
 *  @param {number} batchSizeIE The value of the batchSizeIE property.
 */
MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) {
    this.batchSizeIE_ = batchSizeIE;
};


/**
 * Returns the value of the <code>clusterClass</code> property.
 *
 * @return {string} the value of the clusterClass property.
 */
MarkerClusterer.prototype.getClusterClass = function () {
    return this.clusterClass_;
};


/**
 * Sets the value of the <code>clusterClass</code> property.
 *
 *  @param {string} clusterClass The value of the clusterClass property.
 */
MarkerClusterer.prototype.setClusterClass = function (clusterClass) {
    this.clusterClass_ = clusterClass;
};


/**
 *  Returns the array of markers managed by the clusterer.
 *
 *  @return {Array} The array of markers managed by the clusterer.
 */
MarkerClusterer.prototype.getMarkers = function () {
    return this.markers_;
};


/**
 *  Returns the number of markers managed by the clusterer.
 *
 *  @return {number} The number of markers.
 */
MarkerClusterer.prototype.getTotalMarkers = function () {
    return this.markers_.length;
};


/**
 * Returns the current array of clusters formed by the clusterer.
 *
 * @return {Array} The array of clusters formed by the clusterer.
 */
MarkerClusterer.prototype.getClusters = function () {
    return this.clusters_;
};


/**
 * Returns the number of clusters formed by the clusterer.
 *
 * @return {number} The number of clusters formed by the clusterer.
 */
MarkerClusterer.prototype.getTotalClusters = function () {
    return this.clusters_.length;
};


/**
 * Adds a marker to the clusterer. The clusters are redrawn unless
 *  <code>opt_nodraw</code> is set to <code>true</code>.
 *
 * @param {google.maps.Marker} marker The marker to add.
 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
 */
MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {
    this.pushMarkerTo_(marker);
    if (!opt_nodraw) {
        this.redraw_();
    }
};


/**
 * Adds an array of markers to the clusterer. The clusters are redrawn unless
 *  <code>opt_nodraw</code> is set to <code>true</code>.
 *
 * @param {Array.<google.maps.Marker>} markers The markers to add.
 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
 */
MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {
    var key;
    for (key in markers) {
        if (markers.hasOwnProperty(key)) {
            this.pushMarkerTo_(markers[key]);
        }
    }
    if (!opt_nodraw) {
        this.redraw_();
    }
};


/**
 * Pushes a marker to the clusterer.
 *
 * @param {google.maps.Marker} marker The marker to add.
 */
MarkerClusterer.prototype.pushMarkerTo_ = function (marker) {
    // If the marker is draggable add a listener so we can update the clusters on the dragend:
    if (marker.getDraggable()) {
        var cMarkerClusterer = this;
        google.maps.event.addListener(marker, "dragend", function () {
            if (cMarkerClusterer.ready_) {
                this.isAdded = false;
                cMarkerClusterer.repaint();
            }
        });
    }
    marker.isAdded = false;
    this.markers_.push(marker);
};


/**
 * Removes a marker from the cluster.  The clusters are redrawn unless
 *  <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the
 *  marker was removed from the clusterer.
 *
 * @param {google.maps.Marker} marker The marker to remove.
 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
 * @return {boolean} True if the marker was removed from the clusterer.
 */
MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {
    var removed = this.removeMarker_(marker);

    if (!opt_nodraw && removed) {
        this.repaint();
    }

    return removed;
};


/**
 * Removes an array of markers from the cluster. The clusters are redrawn unless
 *  <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers
 *  were removed from the clusterer.
 *
 * @param {Array.<google.maps.Marker>} markers The markers to remove.
 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
 * @return {boolean} True if markers were removed from the clusterer.
 */
MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {
    var i, r;
    var removed = false;

    for (i = 0; i < markers.length; i++) {
        r = this.removeMarker_(markers[i]);
        removed = removed || r;
    }

    if (!opt_nodraw && removed) {
        this.repaint();
    }

    return removed;
};


/**
 * Removes a marker and returns true if removed, false if not.
 *
 * @param {google.maps.Marker} marker The marker to remove
 * @return {boolean} Whether the marker was removed or not
 */
MarkerClusterer.prototype.removeMarker_ = function (marker) {
    var i;
    var index = -1;
    if (this.markers_.indexOf) {
        index = this.markers_.indexOf(marker);
    } else {
        for (i = 0; i < this.markers_.length; i++) {
            if (marker === this.markers_[i]) {
                index = i;
                break;
            }
        }
    }

    if (index === -1) {
        // Marker is not in our list of markers, so do nothing:
        return false;
    }

    marker.setMap(null);
    this.markers_.splice(index, 1); // Remove the marker from the list of managed markers
    return true;
};


/**
 * Removes all clusters and markers from the map and also removes all markers
 *  managed by the clusterer.
 */
MarkerClusterer.prototype.clearMarkers = function () {
    this.resetViewport_(true);
    this.markers_ = [];
};


/**
 * Recalculates and redraws all the marker clusters from scratch.
 *  Call this after changing any properties.
 */
MarkerClusterer.prototype.repaint = function () {
    var oldClusters = this.clusters_.slice();
    this.clusters_ = [];
    this.resetViewport_(false);
    this.redraw_();

    // Remove the old clusters.
    // Do it in a timeout to prevent blinking effect.
    setTimeout(function () {
        var i;
        for (i = 0; i < oldClusters.length; i++) {
            oldClusters[i].remove();
        }
    }, 0);
};


/**
 * Returns the current bounds extended by the grid size.
 *
 * @param {google.maps.LatLngBounds} bounds The bounds to extend.
 * @return {google.maps.LatLngBounds} The extended bounds.
 * @ignore
 */
MarkerClusterer.prototype.getExtendedBounds = function (bounds) {
    var projection = this.getProjection();

    // Turn the bounds into latlng.
    var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
        bounds.getNorthEast().lng());
    var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
        bounds.getSouthWest().lng());

    // Convert the points to pixels and the extend out by the grid size.
    var trPix = projection.fromLatLngToDivPixel(tr);
    trPix.x += this.gridSize_;
    trPix.y -= this.gridSize_;

    var blPix = projection.fromLatLngToDivPixel(bl);
    blPix.x -= this.gridSize_;
    blPix.y += this.gridSize_;

    // Convert the pixel points back to LatLng
    var ne = projection.fromDivPixelToLatLng(trPix);
    var sw = projection.fromDivPixelToLatLng(blPix);

    // Extend the bounds to contain the new bounds.
    bounds.extend(ne);
    bounds.extend(sw);

    return bounds;
};


/**
 * Redraws all the clusters.
 */
MarkerClusterer.prototype.redraw_ = function () {
    this.createClusters_(0);
};


/**
 * Removes all clusters from the map. The markers are also removed from the map
 *  if <code>opt_hide</code> is set to <code>true</code>.
 *
 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers
 *  from the map.
 */
MarkerClusterer.prototype.resetViewport_ = function (opt_hide) {
    var i, marker;
    // Remove all the clusters
    for (i = 0; i < this.clusters_.length; i++) {
        this.clusters_[i].remove();
    }
    this.clusters_ = [];

    // Reset the markers to not be added and to be removed from the map.
    for (i = 0; i < this.markers_.length; i++) {
        marker = this.markers_[i];
        marker.isAdded = false;
        if (opt_hide) {
            marker.setMap(null);
        }
    }
};


/**
 * Calculates the distance between two latlng locations in km.
 *
 * @param {google.maps.LatLng} p1 The first lat lng point.
 * @param {google.maps.LatLng} p2 The second lat lng point.
 * @return {number} The distance between the two points in km.
 * @see http://www.movable-type.co.uk/scripts/latlong.html
*/
MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {
    var R = 6371; // Radius of the Earth in km
    var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
    var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
      Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;
    return d;
};


/**
 * Determines if a marker is contained in a bounds.
 *
 * @param {google.maps.Marker} marker The marker to check.
 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
 * @return {boolean} True if the marker is in the bounds.
 */
MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) {
    return bounds.contains(marker.getPosition());
};


/**
 * Adds a marker to a cluster, or creates a new cluster.
 *
 * @param {google.maps.Marker} marker The marker to add.
 */
MarkerClusterer.prototype.addToClosestCluster_ = function (marker) {
    var i, d, cluster, center;
    var distance = 40000; // Some large number
    var clusterToAddTo = null;
    for (i = 0; i < this.clusters_.length; i++) {
        cluster = this.clusters_[i];
        center = cluster.getCenter();
        if (center) {
            d = this.distanceBetweenPoints_(center, marker.getPosition());
            if (d < distance) {
                distance = d;
                clusterToAddTo = cluster;
            }
        }
    }

    if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
        clusterToAddTo.addMarker(marker);
    } else {
        cluster = new Cluster(this);
        cluster.addMarker(marker);
        this.clusters_.push(cluster);
    }
};


/**
 * Creates the clusters. This is done in batches to avoid timeout errors
 *  in some browsers when there is a huge number of markers.
 *
 * @param {number} iFirst The index of the first marker in the batch of
 *  markers to be added to clusters.
 */
MarkerClusterer.prototype.createClusters_ = function (iFirst) {
    var i, marker;
    var mapBounds;
    var cMarkerClusterer = this;
    if (!this.ready_) {
        return;
    }

    // Cancel previous batch processing if we're working on the first batch:
    if (iFirst === 0) {
        /**
         * This event is fired when the <code>MarkerClusterer</code> begins
         *  clustering markers.
         * @name MarkerClusterer#clusteringbegin
         * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
         * @event
         */
        google.maps.event.trigger(this, "clusteringbegin", this);

        if (typeof this.timerRefStatic !== "undefined") {
            clearTimeout(this.timerRefStatic);
            delete this.timerRefStatic;
        }
    }

    // Get our current map view bounds.
    // Create a new bounds object so we don't affect the map.
    //
    // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:
    if (this.getMap().getZoom() > 3) {
        mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),
          this.getMap().getBounds().getNorthEast());
    } else {
        mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625));
    }
    var bounds = this.getExtendedBounds(mapBounds);

    var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);

    for (i = iFirst; i < iLast; i++) {
        marker = this.markers_[i];
        if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
            if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) {
                this.addToClosestCluster_(marker);
            }
        }
    }

    if (iLast < this.markers_.length) {
        this.timerRefStatic = setTimeout(function () {
            cMarkerClusterer.createClusters_(iLast);
        }, 0);
    } else {
        delete this.timerRefStatic;

        /**
         * This event is fired when the <code>MarkerClusterer</code> stops
         *  clustering markers.
         * @name MarkerClusterer#clusteringend
         * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
         * @event
         */
        google.maps.event.trigger(this, "clusteringend", this);
    }
};


/**
 * Extends an object's prototype by another's.
 *
 * @param {Object} obj1 The object to be extended.
 * @param {Object} obj2 The object to extend with.
 * @return {Object} The new extended object.
 * @ignore
 */
MarkerClusterer.prototype.extend = function (obj1, obj2) {
    return (function (object) {
        var property;
        for (property in object.prototype) {
            this.prototype[property] = object.prototype[property];
        }
        return this;
    }).apply(obj1, [obj2]);
};


/**
 * The default function for determining the label text and style
 * for a cluster icon.
 *
 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster.
 * @param {number} numStyles The number of marker styles available.
 * @return {ClusterIconInfo} The information resource for the cluster.
 * @constant
 * @ignore
 */
MarkerClusterer.CALCULATOR = function (markers, numStyles) {
    var index = 0;
    var title = "";
    var realCount = 0;
    var m;
    for (var i = 0, len = markers.length; i < len; i++) {
        m = markers[i];
        realCount += m.count || parseInt(m.label) || 1;
    }
    var count = realCount.toString();// markers.length.toString();

    var dv = count;
    while (dv !== 0) {
        dv = parseInt(dv / 10, 10);
        index++;
    }

    index = Math.min(index, numStyles);
    return {
        text: count,
        index: index,
        title: title
    };
};


/**
 * The number of markers to process in one batch.
 *
 * @type {number}
 * @constant
 */
MarkerClusterer.BATCH_SIZE = 2000;


/**
 * The number of markers to process in one batch (IE only).
 *
 * @type {number}
 * @constant
 */
MarkerClusterer.BATCH_SIZE_IE = 500;


/**
 * The default root name for the marker cluster images.
 *
 * @type {string}
 * @constant
 */
MarkerClusterer.IMAGE_PATH = "../images/m";


/**
 * The default extension name for the marker cluster images.
 *
 * @type {string}
 * @constant
 */
MarkerClusterer.IMAGE_EXTENSION = "png";


/**
 * The default array of sizes for the marker cluster images.
 *
 * @type {Array.<number>}
 * @constant
 */
MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];;function MapExplorerControl(container, searchFn, markerClustererOptions) {
    var self = this;

    //self.CenterLongitude = ko.observable("");
    //self.CenterLatitude = ko.observable("");

    self.Map = null;
    self.Container = container;
    self.MarkerClusterer = null;
    self.InfoWindow = null;

    self.SearchFunction = searchFn;

    self.MarkerClustererOptions = markerClustererOptions;
    self.MarkerClustererOptions.imagePath=ContextPath+ 'Content/images/icons/m';


    self.Markers = ko.observableArray([]);

    self.AddMarkers = function (markers) {
        for (var i = 0, len = markers.length; i < len; i++) {
            self.Markers.push(markers[i]);
        }
        self.MarkerClusterer.addMarkers(markers);
    }
    self.AddMarker = function (marker) {
        self.Markers.push(marker);
        self.MarkerClusterer.addMarker(marker);
    }
    self.RemoveMarker = function (marker) {
        self.MarkerClusterer.removeMarker(marker);
        self.Markers.remove(marker);
    }
    self.ClearMarkers = function () {
        self.Markers.removeAll();
        self.MarkerClusterer.clearMarkers();
    }

    self.initMap = function () {
        if (!self.Map) {
            google.maps.visualRefresh = true;

            //self.Geocoder = new google.maps.Geocoder();
            var mapOptions = {
                zoom: 6,
                center: new google.maps.LatLng(51.9189046, 19.1343786)
            };

            self.Map = new google.maps.Map(self.Container, mapOptions);
            

            if (CurrentUser && CurrentUser.LocationModel().Latitude() && CurrentUser.LocationModel().Longitude()) {
                var pos = new google.maps.LatLng(CurrentUser.LocationModel().Latitude(), CurrentUser.LocationModel().Longitude());
                self.Map.setCenter(pos);
                self.Map.setZoom(8);
            }
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function (position) {
                    var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
                    self.Map.setCenter(pos);
                    self.Map.setZoom(8);
                });
            }
                
            
            self.MarkerClusterer = new MarkerClusterer(self.Map, self.Markers(), self.MarkerClustererOptions);
        }
        if (self.SearchFunction) {
            self.SearchFunction(self);
        }
    }

    self.ShowInfoWindow = function (content, marker, closeCallback) {
        if (!self.InfoWindow) {
            self.InfoWindow = new google.maps.InfoWindow({maxWidth:350});
        }
        if (closeCallback) {
            google.maps.event.addListener(self.InfoWindow, 'closeclick', closeCallback);
        }
        self.InfoWindow.setContent(content);
        self.InfoWindow.open(self.Map, marker);
    }

};var communityPublicationHeader = null;
var communityPublicationType = null;
var managePages = false;
var manageGroups = false;
var publishPages = false;
var fbPages = false;
var fbGroups = false;
var fbLogged = false;
var googleMails = null;
var facebookIds = null;
var facebookCallBack = null;
var facebookAlbumName = null;
var facebookAlbumDescription = null;
var facebookUploadFileIds = null;
var fbPublishLiveStreamImageDataURLParams = null;
var setPreviewHandler = null;
var generatePreviewModelHandler = null;

function fbUpdateLongLivedTokens(facebookId, accessToken) {
    var facebookIdCached = $.jStorage.get('FacebookId');
    var facebookAccessTokenCached = $.jStorage.get('FacebookAccessToken');

    var data = {
        facebookId: facebookIdCached,
        accessToken: facebookAccessTokenCached,
        fbAppId: fbId
    };

    var strData = JSON.stringify(data);

    $.ajax({
        type: "POST",
        url: ContextPath + "Facebook/UpdateLongLivedTokens",
        contentType: "application/json; charset=utf-8",
        data: strData
    })
    .done(function (result) {
        if (!result || result.error) {
            fbHandleError(result);
        }
    });
}

function fbUpdateAccounts(response, accessToken) {
    if (CurrentUser.UserId() == 0) return;

    var facebookId = response.id;
    var businessToken = response.token_for_business;
    var facebookAccounts = $.grep(CurrentUser.FacebookAccounts, function (e) { return e.FacebookId == facebookId && e.AppId == fbId; });

    var facebookIdCached = $.jStorage.get('FacebookId');
    var facebookAccessTokenCached = $.jStorage.get('FacebookAccessToken');

    facebookIdCached = facebookId;
    facebookAccessTokenCached = accessToken;

    $.jStorage.set('FacebookId', facebookIdCached, { TTL: 0 });
    $.jStorage.set('FacebookAccessToken', facebookAccessTokenCached, { TTL: 0 });

    if (facebookAccounts.length == 0) {
        FB.api(
          '/me/ids_for_business',
          'GET',
//          {"fields":"id,app.id"},
          function (response) {
              if (!response || response.error) {
                  fbHandleError(response);
              } else {
                  var key = getCurrentUserCacheKey();
                  var currentUserFromCache = $.jStorage.get(key);
                  var facebookAccountModels = [];
                  for (var i = 0, l = response.data.length; i < l; i++) {
                      var facebookAccounts_loop = $.grep(CurrentUser.FacebookAccounts, function (e) { return e.FacebookId == response.data[i].id && e.AppId == response.data[i].app.id; });

                      if (facebookAccounts_loop.length == 0) {
                          var item = new FacebookAccountModel();
                          item.FacebookId = response.data[i].id;
                          item.BusinessToken = businessToken;
                          item.AppId = response.data[i].app.id;

                          currentUserFromCache.FacebookAccounts.push(item);
                          CurrentUser.FacebookAccounts.push(item);
                          facebookAccountModels.push({ FacebookId: item.FacebookId, BusinessToken: businessToken, AppId: item.AppId });
                      }
                  }
                  $.jStorage.set(key, currentUserFromCache, 60000);

                  if (facebookAccountModels.length > 0) {
                      var data = {
                          facebookAccounts: facebookAccountModels,
                          fbAppId: fbId
                      };

                      var strData = JSON.stringify(data);

                      $.ajax({
                          //traditional: true,
                          type: "POST",
                          url: ContextPath + "Facebook/UpdateFacebookAccounts",
                          contentType: "application/json; charset=utf-8",
                          data: strData
                      })
                      .done(function (result) {
                          if (!result || result.error) {
                              fbHandleError(result);
                          }
                      });
                  }
              }
          }
        );
    }
}

function fbPostOnWall(body, pic, message, pageId, groupId) {

    FB.Event.subscribe('auth.authResponseChange', function (response) {
        // Here we specify what we do with the response anytime this event occurs. 
        if (response.status === 'connected') {

        } else if (response.status === 'not_authorized') {
            console.log("error ");
            //FB.login({ scope: 'publish_actions' });
        } else {
            console.log("error ");
            //FB.login({ scope: 'publish_actions' });
        }
    });


    FB.getLoginStatus(function (statusResponse) {
        var scope = 'publish_actions';//, publish_stream';

        if (pageId) scope = scope + ', manage_pages, publish_pages';
        if (groupId) scope = scope + ', user_managed_groups';
        //if (true) scope = scope + ', user_photos';

        if (statusResponse.status === 'connected') {
            FB.api('/me/permissions', function (response) {
                // Check for permissions
                var publishActions = false;
                if (response.data != null) {
                    for (var i = 0; i < response.data.length; i++) {
                        if (response.data[i].permission == 'publish_actions') {
                            if (response.data[i].status == 'granted')
                                publishActions = true;
                        }
                        if (response.data[i].permission == 'manage_pages') {
                            if (response.data[i].status == 'granted')
                                managePages = true;
                        }
                        if (response.data[i].permission == 'user_managed_groups') {
                            if (response.data[i].status == 'granted')
                                manageGroups = true;
                        }
                        if (response.data[i].permission == 'publish_pages') {
                            if (response.data[i].status == 'granted')
                                publishPages = true;
                        }
                    }
                }
                if (publishActions && (!pageId || (managePages && publishPages)) && (!groupId || manageGroups)) {
                    fbPostImage(statusResponse, body, pic, message, pageId, groupId);
                } else {
                    loginToFacebook(body, pic, message, scope, pageId, groupId);
                }
            });
        } else if (statusResponse.status === 'not_authorized') {
            loginToFacebook(body, pic, message, scope, pageId, groupId);
        } else {
            loginToFacebook(body, pic, message, scope, pageId, groupId);
        }
    });
}

function loginToFacebook(body, pic, message, scope, pageId, groupId) {
    FB.login(function (response) {
        if (response.authResponse) {
            debugger;
            fbLogged = true;
            fbSetUserData(response.authResponse);
            fbPostImage(response, body, pic, message, pageId, groupId);
        } else {
            debugger;
            fbLogged = false;
            fbHandleErrorAndResetOptions(response);
        }
    }, { scope: scope });
}

function fbHandleErrorAndResetOptions(response) {
    $('#fbPrevieModal .fbOptions label .account').text(translations.NotLoggedIn);
    $('#fbPrevieModal .fbOptions .publishFromOtherAccount').text(translations.LogIn);
    fbHandleError(response, false, 'canceledLogin');
}

function logoutFromFacebook(callback, callParam1, callParam2, callParam3) {
    FB.getLoginStatus(function (response) {
        if (response && response.status === 'connected') {
            FB.logout(function (response) {
                //document.location.reload();
            });
            //FB.Auth.setAuthResponse(null, 'unknown');
        } else if (response.status === 'not_authorized') {
            FB.logout(function (response) {
                //document.location.reload();
            });
        }
        if (callback) {
            setTimeout(function () { callback(callParam1, callParam2, callParam3); }, 500);
        }
    });
}

function fbShowPreviewForOtherUser() {
    fbPages = false;
    fbGroups = false;
    //     $('#fbPrevieModal .fbPageOptions').empty(); 
    //     $('#fbPrevieModal .fbGroupOptions').empty(); 
    //     $('#fbPrevieModal .fbPageOptions').hide();
    //     $('#fbPrevieModal .fbGroupOptions').hide();    
    //     $('#fbPrevieModal .fbShareOptions').val('PublishToYoursTimeLine');

    $('#fbPrevieModal .fbOptions .fbPageOptions > ul').empty();
    $('#fbPrevieModal .fbOptions .fbGroupOptions > ul').empty();
    $('#fbPrevieModal .fbOptions .fbPageOptions').hide();
    $('#fbPrevieModal .fbOptions .fbGroupOptions').hide();
    $('#fbPrevieModal .fbPageOptions .btn.ddl > span')[0].id = '';
    $('#fbPrevieModal .fbGroupOptions .btn.ddl > span')[0].id = '';
    $('#fbPrevieModal .fbOptions .fbShareOptions > a > span').text(translations.PublishToYoursTimeLine);
}

function fbPublishFromOtherAccount() {
    logoutFromFacebook(fbLoginToFacebookWithCallBack, 'publish_actions', fbShowPreviewForOtherUser, fbResetOptions);
}

function fbPostImageInCustomStory(response, body, pic, message) {
    try {
        var accessToken = response.authResponse.accessToken;
        var userId = response.authResponse.userID;

        var mimeType = "image/png";
        var pic = pic.replace('data:image/png;base64,', '');
        var blob = dataURItoBlob(pic, mimeType);

        var fd = new FormData();
        fd.append("access_token", accessToken);
        fd.append("source", blob);
        if (message)
            fd.append("message", message);

        $.ajax({
            url: "https://graph.facebook.com/me/staging_resources?access_token=" + accessToken,
            type: "POST",
            data: fd,
            processData: false,
            contentType: false,
            cache: false,
            success: function (data) {
                console.log("picture to album success " + data);
                fbPostObjectInCustomStory(accessToken, body, data.uri);
                //fbPostLink(body, data.uri);
                //fbPostPhotoInCustomStory(accessToken, body, data.uri);
            },
            error: function (shr, status, data) {
                console.log("error " + data + " Status " + shr.status);
            },
            complete: function () {
                console.log("Ajax Complete");
                JsRequestLog('social.js', 'fbPostImageInCustomStory', '');
            }
        });
    } catch (e) {
        alert(e);
    }
}

function fbPostPhotoInCustomStory(accessToken, body, pic_id) {
    FB.api(
      'me/objects/localhostpulsstory:photo',
      'post',
      {
          object: {
              "title": body,
              //"type": 'localhostpulsstory:photo',

              image: {
                  url: pic_id,
                  user_generated: false,
              },
          }
      },
      function (response) {
          if (!response || response.error)
              alert('Error occured: ' + response.error.message);
          else {
              fbSharePhotoCustomStory(accessToken, body, response.id);
          }
      }
    );
}

function fbSharePhotoCustomStory(accessToken, body, pic_id) {
    FB.api(
      'me/localhostpulsstory:share',
      'post',
      {
          photo: pic_id,
      },
      function (response) {
          if (!response || response.error)
              alert('Error occured: ' + response.error.message);
          else {
          }
      }
    );
}

function fbPostObjectInCustomStory(accessToken, body, pic_id) {

    //localhostPulsstory
    //http://localhost:60735/

    FB.api(
      'me/objects/localhostpulsstory:activityobjecttype',
      'post',
      {
          object: {
              "title": body,
              //"picture":'http://www.facebook.com/photo.php?fbid=' + pic_id,
              //"picture":"http://www.maastrichtuniversity.nl/upload_mm/2/3/9/3675823_UM%20Sport%202013%20-%20jump-6_830x455.jpg",

              image: {
                  url: pic_id,
                  user_generated: false,
                  //                type: "image/png"
                  width: 1020,
                  height: 820
              },
              //               "rich_attachment": true,
              "is_scraped": true,
              //"type": "Article",  
              //"image": [{ "url": 'http://www.facebook.com/photo.php?fbid=' + pic_id, "user_generated": "false" }],
              //"url": 'https://www.facebook.com/safe_image.php?url=fbstaging://graph.facebook.com/staging_resources/MDExNTYwNTIwMjQ0MjEwODkxOjQzMDQ5ODk2Mg==',
              //"url": 'http://www.facebook.com/photo.php?fbid=' + pic_id,
              //"url": "https://www.facebook.com/photo.php?fbid=1559612837634965&set=a.1383826855213565.1073741825.100007587912377&type=1&theater",
              //"url": "https://www.facebook.com/safe_image.php?url=" + pic_id,

              //"ref": 'https://www.facebook.com/safe_image.php?url=fbstaging://graph.facebook.com/staging_resources/MDExNTYwNTIwMjQ0MjEwODkxOjQzMDQ5ODk2Mg==',

              //"description": "Opis",
              //"message": "aaaa",

              //"item": "http://localhost:60735/",
              //"start_time": "2014-01-30T15:18:26-08:00",
              //"end_time": "2013-03-06T15:18:27-08:00",

              //               "no_feed_story": false,
              //               "fb:explicitly_shared": true,
              //               "privacy": { 'value': 'EVERYONE' }
          }
      },
      function (response) {
          if (!response || response.error)
              alert('Error occured: ' + response.error.message);
          else {
              //no rights
              fbPostActionInCustomStory(accessToken, body, response.id);
          }
      }
    );
}

function fbPostActionInCustomStory(accessToken, body, object_id) {
    FB.api(
      'me/localhostpulsstory:activitytype',
      'post',
      {
          activityobjecttype: object_id,
          title: body,
          //ref: object_id,
          //"ref": 'https://www.facebook.com/safe_image.php?url=fbstaging://graph.facebook.com/staging_resources/MDExNTYwNTIwMjQ0MjEwODkxOjQzMDQ5ODk2Mg==',          
          //description: "Opis",
          //tags: '100005099586310,100007728726685',
          //message: "aaaa \@[100005099586310],\@[100007728726685] ",
          //"video":"http://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Big_Wood%2C_N2.JPG/1280px-Big_Wood%2C_N2.JPG",

          //no rights
          //message: "aaaa",

          item: object_id,

          //start_time: now_date_string,
          //end_time: "2013-03-06T15:18:27-08:00",

          //method: 'share_open_graph',  

          //no_feed_story: false,
          //no rights
          //          "fb:explicitly_shared": true,
          //"privacy": { "value": "EVERYONE" }
      },
      function (response) {
          if (!response || response.error)
              alert('Error occured: ' + response.error.message);
          else {
              //fbPostActionInCustomStoryOnFriendsWall(0, body, response.id);
              //fbPostLink(accessToken, body, object_id);
              //fbPostActionInCustomStoryOnFriendsWall(0, 'sss', 0);
              //fbSendNotificationsForFriends(body, object_id);
          }
      }
    );
}

function fbPostLink(accessToken, body, object_id) {
    FB.api('/me/localhostpulsstory:activity?access_token=' + accessToken, 'get',
        function (response) {
            for (var i = 0; i < response.data.length; i++) {
                article_title[i] = response.data[i].data.post.title;
                article_url[i] = response.data[i].data.post.url;
            }
        });
    //var client = new XMLHttpRequest();
    //client.open("POST", "/log");
    //client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
    //client.send(message);

    //FB.api('me/activity/1389403367989247', 'get', {
    //    //message: body, 
    //    //link: 'https://www.facebook.com/szymon.kowalski.790/activity/1389403367989247' 
    //    //"fbsdk:create_object": true,
    //    //image: [{ "url": pic_id, "user_generated": "true" }],
    //    }, function (response) {
    //    if (!response || response.error) {
    //        alert('Error occured: ' + response.error.message);
    //    } else {
    //        //fbPosMsg(body, response.id);
    //        // alert('Post ID: ' + response.id);
    //        console.log("post on wall success");
    //    }
    //});
    //$.ajax({
    //    url: "https://graph.facebook.com/me/staging_resources?access_token=" + accessToken,
    //    type: "POST",
    //    data: fd,
    //    processData: false,
    //    contentType: false,
    //    cache: false,
    //    success: function (data) {
    //        console.log("picture to album success " + data);
    //        fbPostObjectInCustomStory(body, data.uri);
    //        //fbPosMsg(body, data.uri);
    //    },
    //    error: function (shr, status, data) {
    //        console.log("error " + data + " Status " + shr.status);
    //    },
    //    complete: function () {
    //        console.log("Ajax Complete");
    //          JsRequestLog('social.js', 'fbPostLink', '');
    //    }
    //});
}

function fbSendNotificationsForFriends(body, object_id) {
    FB.api('me/friends?fields=installed', function (response) {
        if (!response || response.error) {
            alert('Error occured' + response.error.message);
        } else {
            var friendsSale = response.data;
            var len = friendsSale.length;
            for (var x = 0; x < len; x++) {
                fbSendNotificationForFriend(body, friendsSale[x].id);
                //fbPostActionInCustomStoryOnFriendsWall(friendsSale[x].id, body, object_id);
            }
        }
    });
}

function fbSendNotificationForFriend(body, friend_id) {
    FB.api('/' + friend_id + '/notifications',
           'post',
           {
               //app id       
               access_token: '714604305236798',
               href: "", //relative path
               template: body,
           }
    , function (response) {
        if (!response || response.error) {
            alert('Error occured' + response.error.message);
        }
    });
}

function fbPostActionInCustomStoryOnFriendsWall(friend_id, body, object_id) {

    //FB.api('/'+ friend_id + '/feed', 'post', { message: body }, function (response) {
    //    if (!response || response.error) {
    //        alert('Error occured');
    //    } else {
    //        alert('Post ID: ' + response.id);
    //    }
    //});
    // calling the API ...
    //var obj = {
    //    method: 'feed',
    //    //to:  { '100005099586310', ',100007728726685' },
    //    //to: '100007728726685',
    //    link: 'http://www.facebook.com/thepcwizardblog',
    //    picture: 'http://fbrell.com/f8.jpg',
    //    name: 'Feed Dialog',
    //    caption: 'Tagging Friends',
    //    description: 'Using Dialogs for posting to friends timeline.'
    //};

    //function callback(response) {
    //    if (!response || response.error)
    //        alert('Error occured: ' + response.error.message);
    //}

    //FB.ui(obj, callback);

    //FB.ui({
    //    method: 'feed',
    //    link: 'https://developers.facebook.com/docs/dialogs/',
    //    caption: 'An example caption',
    //    post_id: object_id
    //}, function (response) {
    //        if (!response || response.error)
    //            alert('Error occured: ' + response.error.message);
    //});

    //FB.api(
    //  '/'+ friend_id + '/localhostpulsstory:activitytype',
    //  'post',
    //  {
    //      activityobjecttype: object_id,
    //      title: body,
    //      description: "Opis",
    //      message: "aaaa \@[szymon.kowalski.790]",
    //      item: object_id,
    //      "fb:explicitly_shared": true,
    //  },
    //  function (response) {
    //      if (!response || response.error)
    //          alert('Error occured: ' + response.error.message);
    //  }
    //);
}

function fbPostImage(response, body, pic, message, pageId, groupId, albumId) {
    if (pageId) {
        FB.api('/' + pageId, { fields: 'access_token' }, function (resp) {
            if (!resp || resp.error) {
                fbHandleError(resp, true, 'publicationError');
                return;
            }

            fbPostImageBody(resp.access_token, pageId, body, pic, message, true);
        });
    } else
        if (groupId) {
            fbPostImageBody(response.authResponse.accessToken, response.authResponse.userID, body, pic, message, false, groupId);
        } else
            if (albumId) {
                fbPostImageBody(response.authResponse.accessToken, albumId, body, pic, message);
            } else
                fbPostImageBody(response.authResponse.accessToken, response.authResponse.userID, body, pic, message);
}

function fbPostImageBody(accessToken, userId, body, pic, message, toPage, groupId) {
    //var data = {
    //    accessToken: accessToken,
    //    imageDataURL: pic,
    //    comment: body,
    //};

    //var strData = JSON.stringify(data);

    //$.ajax({
    //    type: "POST",
    //    url: ContextPath + "Facebook/PublishImageDataURL",
    //    contentType: "application/json; charset=utf-8",
    //    data: strData
    //})
    //.done(function (result) {

    //});

    //return;

    var mimeType = "image/png";
    var pic = pic.replace('data:image/png;base64,', '');
    var blob = dataURItoBlob(pic, mimeType);

    var fd = new FormData();
    fd.append("access_token", accessToken);
    fd.append("source", blob);

    if (toPage && body) fd.append("message", body);

    if (message != null) {
        fd.append("message", message);
    }
    else {
        //fd.append("message", translations.PublishPulsstoryMessage + " https://pulsstory.com");        
    }

    try {

        $.ajax({
            url: "https://graph.facebook.com/" + userId + "/photos" + "?access_token=" + accessToken,
            type: "POST",
            data: fd,
            processData: false,
            contentType: false,
            cache: false,
            success: function (data) {
                console.log("picture to album success " + data);
                if (toPage) {
                    console.log("post on page success");
                    //fbPosMsgToPage(body, data.id, userId, accessToken);
                } else
                    if (groupId) {
                        fbPosMsgToGroup(body, data.id, groupId);
                    } else {
                        fbPosMsg(body, data.id);
                    }
                fbUpdateLongLivedTokens();
            },
            error: function (shr, status, data, message) {
                var responseObject = JSON.parse(shr.responseText);

                if (responseObject.error.code == 190 && responseObject.error.hasOwnProperty('error_subcode') &&
                    responseObject.error.error_subcode == 467) {
                    //                         FB.login(function (response) {
                    //                             if (response.authResponse) {
                    //                                 fbPostImage(response, body, pic, message);
                    //                             } else {
                    //                                 ShowEmptyMsg(translations.PublicationFailed + ' ' + translations.CanceledLoginToFacebook, translations.Info);
                    //                             }
                    //                         }, { scope: 'publish_actions, publish_stream' });                        
                    ShowEmptyMsg(translations.PublicationFailed + ' ' + translations.YouAreLoggedOutFromFacebook, translations.Error);
                }
                else {
                    ShowEmptyMsg(translations.PublicationFailed, translations.Error);
                }
                var msg = "Facebook error: " + responseObject.error.message + " Status " + shr.status + " ErrorCode " +
                          responseObject.error.code;
                console.log(msg);
                logMsgOnServer(msg, ErrorMessageType);

            },
            complete: function () {
                console.log("Ajax Complete");
                JsRequestLog('social.js', 'fbPostImageBody', '');
                //.done(function (serverModel) { });
            }
        });

    } catch (e) {
        alert(e);
    }
}

function fbPublishWithNewDialog(link, msg) {
    FB.ui({
        method: 'share',
        href: link,
        //quote: msg
    }, function (response) {
        if (!response || response.error) {
            fbHandleError(result);
        } else {
            console.log("post on wall success");
        }
    });
}

function fbPosMsg(body, pic_id) {
    FB.api('/me/feed', 'post', { message: body, link: 'http://www.facebook.com/photo.php?fbid=' + pic_id }, function (response) {
        if (!response || response.error) {
            fbHandleError(response, true, 'notEnoughRights');
            //alert('Error occured');
        } else {
            // alert('Post ID: ' + response.id);
            console.log("post on wall success");
            //fbGetLikes(response.id);
            //console.l og("post id: " + response.id);
        }
    });
}

function fbGetLikes(id) {
    //    FB.api(
    //      //'/' + id + '/likes', //v2.6 -> fan_count - fun page
    //      '/1683965965204345_1930565453877727/likes', //v2.6 -> fan_count
    ////        'post',
    ////        {},
    //      function (response) {
    //        debugger;  
    //        if(!response || response.error) {
    //            fbHandleError(response, true, 'notEnoughRights');            
    //        } else {
    //            var array = response.data;

    //            for (var i = 0, l = response.data.length; i < l; i++) {
    //                var id = response.data[i].id;
    //                var name = response.data[i].name;

    //                console.log("post like user id: " + id);
    //                console.log("post like user name: " + name);
    //            }
    //        }
    //      }
    //    );

    //var data = {
    //    userId: 173,
    //    facebookId: '1683965965204345',
    //    postId: '1683965965204345_1930565453877727'
    //};

    //var strData = JSON.stringify(data);

    //$.ajax({
    //    type: "POST",
    //    url: ContextPath + "Facebook/GetLikes",
    //    contentType: "application/json; charset=utf-8",
    //    data: strData
    //})
    //.done(function (result) {
    //    console.log(result);
    //    if (!result || result.error) {
    //        fbHandleError(result);
    //        //alert('Error occured');
    //    } else {
    //        for (var i = 0, l = result.data.length; i < l; i++) {
    //            var id = result.data[i].id;
    //            var name = result.data[i].name;

    //            console.log("post like user id: " + id);
    //            console.log("post like user name: " + name);
    //        }
    //    }
    //});
}

function fbPosMsgToGroup(body, pic_id, groupId) {
    FB.api('/' + groupId + '/feed', 'post', { message: body, link: 'http://www.facebook.com/photo.php?fbid=' + pic_id }, function (response) {
        if (!response || response.error) {
            fbHandleError(response, true, 'notEnoughRights');
            //alert('Error occured');
        } else {
            // alert('Post ID: ' + response.id);
            console.log("post on group success");
        }
    });
}

function fbPosMsgToPage(body, pic_id, pageId, accessToken) {
    var page_id = pageId;

    FB.api('/' + page_id + '/feed',
           'post',
           {
               message: body,
               link: 'http://www.facebook.com/photo.php?fbid=' + pic_id,
               access_token: accessToken
           }, function (response) {
               if (!response || response.error) {
                   fbHandleError(response, true, 'notEnoughRights');
                   //alert('Error occured');
               } else {
                   // alert('Post ID: ' + response.id);
                   console.log("post on wall success");
               }
           });
}

function fbPosUrlImage(body, pic) {

    FB.api('/photos', 'post', {
        message: 'Chart description',
        url: pic
        //source: pic
    }, function (response) {

        if (!response || response.error) {
            alert('Error occured: ' + response.error.message);

        } else {
            alert('Post ID: ' + response.id);
        }
    });
}

function dataURItoBlob(dataURI, mime) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs

    var byteString = window.atob(dataURI);

    // separate out the mime component
    // write the bytes of the string to an ArrayBuffer
    //var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    var blob = new Blob([ia], { type: mime });
    return blob;
}

function getGenericLogoFbPreviewContent() {
    return '<img src="' + ContextPath + 'Content/images/pulsstory_face_600.png' + '" />';
}

function FB_PreviewManager(content, doNotMove, message, link, useBinding, goToAdress) {
    var self = this;

    //content.find('.uCalT .weekRow').css('height', "60px");
    //self.contentTimeLineScaled = content.clone();
    //var weekRow = self.contentTimeLineScaled.find('.uCalT .weekRow');
    //weekRow.hide();
    //weekRow.attr("height", "30px");

    //changeSelection($('.fbFaceShareButton.shareFace_30'));

    self.content = content;
    self.doNotMove = doNotMove;
    self.link = link;
    self.GoToAdress = goToAdress;

    self.dataURL = null;
    //self.dataURLScaled = null;

    self.ImgSaved = false;
    self.PublicationGuid = null;
    self.PublicationUrl = null;

    self.previewElem = null;
    //self.previewElemScaled = null;

    self.orginalParent = null;

    //$('#fbPrevieModal').css('width', '820px');

    self.msg = message ? message.trim() : '';

    $('#fbPrevieModal .fbMsg').val(self.msg);

    self.openFbPreviewModal = function (waitTillContent) {

        var prev = $('#fbPrevieModal .fbPreview');
        var wait = $('<div class="wait"></div>');

        prev.html(wait);

        self.corePreviewInit();

        if (waitTillContent) {
            setPreviewHandler = setInterval(function () {
                if (self.content) {
                    clearInterval(setPreviewHandler);
                    setPreviewHandler = null;
                    self.setPreview();
                }
            }, 100);
        }
        else {
            self.setPreview();
        }
    }

    self.openFbPreviewModalCustomContent = function (customContentClb) {

        var prev = $('#fbPrevieModal .fbPreview');
        var wait = $('<div class="wait"></div>');

        prev.html(wait);

        self.corePreviewInit();

        if (customContentClb) customContentClb();
    }

    self.corePreviewInit = function () {
        self.PublicationGuid = null;
        self.ImgSaved = false;
        self.PublicationUrl = null;

        var fnc = function () {
            var msg = $('#fbPrevieModal .fbMsg').val();
            if (msg != self.msg) {
                $("#shareButtons").jsSocials("option", "text", msg);
            }
        };

        //$('#fbPrevieModal .fbMsg').on('change', fnc);
        $('#fbPrevieModal .fbMsg').on('keyup', fnc);


        var shares = [
                    "facebook",
                    { share: "twitter", hashtags: "pulsstory" },
                    "googleplus",
                    "linkedin",
                    /*"pinterest", "stumbleupon", "pocket",*/
                    "email",

        ];
        if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|IEMobile|Windows Phone|Silk|Opera Mini/i.test(navigator.userAgent)) {
            shares = shares.concat([
                    "whatsapp",
                    /*"viber",*/
                    "messenger",
                    /*"vkontakte",*/
                    "telegram",
                    "line"
                    /*, "rss"*/
            ]);
        }

        $('#fbPrevieModal').on('shown', function () {
            $("#shareButtons").jsSocials({
                url: self.PublicationUrl,
                showCount: false,
                showLabel: true,
                shareIn: "popup",
                shares: shares,
                on: {
                    click: function () {
                        self.JsSocialsStoreImg(this.share);
                    }
                }
            });
        });
        self.GetGuid();

        $('#fbPrevieModal').modal('show');
        //fbSetLoginStatus(function () { $('#fbPrevieModal').modal('show'); });
    }

    self.GetGuid = function () {
        $.ajax({
            type: 'POST',
            url: ContextPath + "ExternalPublication/GetGuid"
        })
        .done(function (guid) {
            self.PublicationGuid = guid;
            self.PublicationUrl = self.GetUrl(guid);
            $("#shareButtons").jsSocials("option", "url", self.PublicationUrl);
        });
    }

    self.JsSocialsStoreImg = function (target) {
        var data = '';
        var type = '';
        var msg = $('#fbPrevieModal .fbMsg').val();

        if (self.ImgSaved) {
            updateExternalPublication(self.PublicationGuid, msg, null, target);
        } else {
            saveExternalPublication(self.dataURL, data, type, msg, function () { self.ImgSaved = true; }, target, self.PublicationGuid);
        }
    }
    self.getDomain = function () {
        var url = window.location.href;
        var arr = url.split("/");
        var domain = arr[0] + "//" + arr[2]
        return domain;
    }
    self.GetUrl = function (guid) {
        var link = self.link;
        var g = guid || self.PublicationGuid;

        if (link) {
            if (link.indexOf('?') >= 0) {
                link += '&';
            } else {
                link += '?';
            }
            link = link + 'externalPublicationGuid=' + g;

            if (link.startsWith("http")) return link;

            return self.getDomain() + link;
        }

        var url = self.getDomain() + ContextPath + "ExternalPublication/Publication?guid=" + g;
        return url;
    }

    self.NewPublishCallback_step1 = function (dataURL, msg, target) {
        var data = '';
        var type = '';

        if (self.ImgSaved) {
            updateExternalPublication(self.PublicationGuid, msg, self.NewPublishCallback_step2, target);
            self.NewPublishCallback_step2(guid, msg, target);
        } else {
            saveExternalPublication(dataURL, data, type, msg, self.NewPublishCallback_step2, target, self.PublicationGuid);
        }
    }
    self.NewPublishCallback_step2 = function (guid, msg, target) {

        self.PublicationGuid = guid;
        var url = self.GetUrl();

        if (target == 'fb') {
            fbPublishWithNewDialog(url, msg);
        } else if (target == 'twitter') {
            twittUrl(url, msg);
        }
        //fbPublishWithNewDialog('https://pulsstory.com/Content/images/logonBg_sport.jpg', msg);
    }

    self.openFbPreviewModal_noprev = function (waitTillContent, data, type) {
        var prev = $('#fbPrevieModal .fbPreview');
        var wait = $('<div class="wait"></div>');

        prev.html(wait);

        if (waitTillContent) {
            setPreviewHandler = setInterval(function () {
                if (self.content) {
                    clearInterval(setPreviewHandler);
                    setPreviewHandler = null;
                    self.setPreview(self.NewPublishCallback_step1);
                }
            }, 100);
        }
        else {
            self.setPreview(self.NewPublishCallback_step1);
        }
    }

    self.setPreview = function (callback) {
        //previewElem = $('#fbPrevieModal .fbPreview');
        if (doNotMove) {
            self.orginalParent = self.content.parent();
        }
        self.previewElem = $('body > .fbPreview');
        self.previewElem.html(self.content);

        /*
                //fix FB ratio to 1.91:1
                self.previewElem.width('auto');
                var height = self.previewElem.height();
                var width = self.previewElem.width();
                var w = height * 1.91;
                if (w > width) {
                    self.previewElem.width(w);
                }
        */

        communityPublicationHeader = $('.communityPublicationHeader', self.previewElem);
        communityPublicationType = $('.communityPublicationType', self.previewElem);

        if (useBinding) {
            ko.cleanNode(self.previewElem[0]);
            ko.applyBindings(GeneralModel, self.previewElem[0]);
        }

        correctActivityTypeInfoText('.fbPreview', 60, true);

        //self.previewElemScaled = $('body > .fbPreview');
        //self.previewElemScaled.html(self.contentTimeLineScaled);

        setTimeout(function () { self.render(callback); }, 500);
    }

    self.Bg = null;

    self.render = function (callback) {
        //html2canvas(self.previewElemScaled, {
        //        background: self.Bg ? self.Bg : '#ffffff',
        //        onrendered: function (canvas) {
        //            // save canvas image as data url (png format by default)
        //            self.dataURLScaled = canvas.toDataURL();
        //        }
        //    });
        html2canvas(self.previewElem, {
            background: self.Bg ? self.Bg : '#ffffff',
            onrendered: function (canvas) {
                //document.body.appendChild(canvas);                

                // save canvas image as data url (png format by default)
                self.dataURL = canvas.toDataURL();

                if (doNotMove && self.orginalParent) {
                    self.orginalParent.html(self.content);
                }

                self.previewElem.html('');

                var prev = $('#fbPrevieModal .fbPreview');
                var img = $(document.createElement('img'));
                img.attr('src', self.dataURL);
                prev.html(img);
                if (callback) callback(self.dataURL);
            }
        });
    }

    self.genericPublish = function (target) {
        var msg = $('#fbPrevieModal .fbMsg').val();
        self.NewPublishCallback_step1(self.dataURL, msg, target);

        $('#fbPrevieModal').modal('hide');
        $('#fbPrevieModal .fbMsg').val('');
        self.PublicationGuid = null;
    }

    self.publish = function () {
        self.genericPublish('fb');
    }

    self.twitt = function () {
        self.genericPublish('twitter');
    }
}

function fbHandleError(response, logOnServer, errorType) {
    if (errorType == 'canceledLogin') {
        ShowEmptyMsg(translations.PublicationFailed + ' ' + translations.CanceledLoginToFacebook, translations.Info);
    } else
        if (errorType == 'publicationError') {
            ShowEmptyMsg(translations.PublicationFailed, translations.Info);
        } else
            if (errorType == 'notEnoughRights') {
                ShowEmptyMsg(translations.PublicationFailed + ' ' + translations.NotEnoughFacebookRightsForPublish, translations.Info);
            } else
                if (errorType) {
                    ShowEmptyMsg(translations[errorType], translations.Info);
                } else {
                    ShowEmptyMsg(translations.ErrorMsg, translations.Error);
                }

    if (response.error) {
        var msg = "Facebook error: " + response.error.message;
        console.log(msg);
        if (logOnServer) logMsgOnServer(msg, ErrorMessageType);
    }
}

function fbSetLoginStatus(callback) {
    if (!fbLogged) {
        fbLoginToFacebookWithCallBack('', callback, fbHandleErrorAndResetOptions);
        //fbLoginToFacebookWithCallBack('publish_actions', callback, fbHandleErrorAndResetOptions);// publish_actions b�dzie wycofane
    } else {
        callback();
    }
}

function fbShareOptions(value) {
    var shareOptionsSpan = $('#fbPrevieModal .fbShareOptions > a > span');
    $('#fbPrevieModal .fbPageOptions').hide();
    $('#fbPrevieModal .fbGroupOptions').hide();
    if (value == 'PublishToPage') {
        //fbSetAdditionalData("user_photos", fbCreateAlbum, fbResetOptions);
        fbSetAdditionalData('manage_pages, publish_pages', fbSetPages, fbResetOptions);
        shareOptionsSpan.text(translations.PublishToPage);
    }
    if (value == 'PublishToGroup') {
        fbSetAdditionalData('user_managed_groups', fbSetGroups, fbResetOptions);
        shareOptionsSpan.text(translations.PublishToGroup);
    }
    if (value == 'PublishToYoursTimeLine') {
        shareOptionsSpan.text(translations.PublishToYoursTimeLine);
    }

    $('#fbPrevieModal .fbOptions .fbPageOptions').hide();
    $('#fbPrevieModal .fbOptions .fbGroupOptions').hide();
}

function fbShareOptionsSetSelected(optionsSelector, elem) {
    var updateElem = $('#fbPrevieModal ' + optionsSelector + ' .btn.ddl > span');
    updateElem.text($('a', elem).text())
    updateElem[0].id = elem[0].id;
}

function fbSetPages() {
    var select = $('#fbPrevieModal .fbPageOptions');
    var selectUl = $('#fbPrevieModal .fbPageOptions > ul');
    var selectSpan = $('#fbPrevieModal .fbPageOptions .btn.ddl > span');
    if (!fbPages) {
        FB.api('/me/accounts', function (response) {
            if (!response || response.error) {
                fbHandleError(response);
            } else {
                for (var i = 0, l = response.data.length; i < l; i++) {
                    var page = response.data[i];
                    var option = document.createElement('li');
                    //option.appendChild(document.createTextNode(page.name));
                    //option.text = page.name;
                    option.id = page.id;
                    option.innerHTML = '<a tabindex="-1" href="#">' + page.name + '</a>';
                    //                    option.addEventListener("click", function() { $('#fbPrevieModal .fbPageOptions .btn.ddl > span').text($('a',  $(this)).text()) }, false);
                    option.addEventListener("click", function () { fbShareOptionsSetSelected('.fbPageOptions', $(this)); }, false);
                    selectUl.append(option);
                }

                var first = $('#fbPrevieModal .fbPageOptions > ul a:first');
                if (first.length > 0) {
                    selectSpan.text($('#fbPrevieModal .fbPageOptions > ul a:first').text());
                    selectSpan[0].id = $('#fbPrevieModal .fbPageOptions > ul li:first')[0].id;
                }
                fbPages = true;
                select.show();
            }
        });
    } else {
        select.show();
    }
};

function fbSetGroups() {
    var select = $('#fbPrevieModal .fbGroupOptions');
    var selectUl = $('#fbPrevieModal .fbGroupOptions > ul');
    var selectSpan = $('#fbPrevieModal .fbGroupOptions .btn.ddl > span');
    if (!fbGroups) {
        FB.api(
            "/me/groups",
            function (response) {
                if (!response || response.error) {
                    fbHandleError(response);
                } else {
                    for (var i = 0, l = response.data.length; i < l; i++) {
                        var group = response.data[i];
                        //var option = document.createElement('option');
                        var option = document.createElement('li');
                        //                         option.text =  group.name;
                        //                         option.value = group.id;
                        option.id = group.id;
                        option.innerHTML = '<a tabindex="-1" href="#">' + group.name + '</a>';
                        option.addEventListener("click", function () { fbShareOptionsSetSelected('.fbGroupOptions', $(this)); }, false);
                        selectUl.append(option);
                    }
                    var first = $('#fbPrevieModal .fbGroupOptions > ul a:first');
                    if (first.length > 0) {
                        selectSpan.text($('#fbPrevieModal .fbGroupOptions > ul a:first').text());
                        selectSpan[0].id = $('#fbPrevieModal .fbGroupOptions > ul li:first')[0].id;
                    }
                    fbGroups = true;
                    select.show();
                }
            });
    } else {
        select.show();
    }
};

function fbSetUserData(authResponse) {
    var accessToken = authResponse.accessToken;

    FB.api(
        "/" + authResponse.userID, { fields: 'name, token_for_business' },
        function (response) {
            //     debugger;    
            if (!response || response.error) {
                //check if it is old session (user logged out)
                if (response && response.error && (response.error.code == 104 || response.error.code == 190)) {
                    $('#fbPrevieModal .fbOptions label .account').text(translations.NotLoggedIn);
                    $('#fbPrevieModal .fbOptions .publishFromOtherAccount').text(translations.LogIn);
                    fbLogged = false;
                    return;
                }
                fbHandleError(response);
            } else {
                $('#fbPrevieModal .fbOptions label .account').text(response.name);
                $('#fbPrevieModal .fbOptions .publishFromOtherAccount').text(translations.PublishFromOtherAccount);
                fbUpdateAccounts(response, accessToken);
            }
        });
}

function fbLoginToFacebookWithCallBack(scope, callbackIfSuccess, callbackIfFailure) {
    if (typeof FB === 'undefined' || !FB) {
        if (PrivateModeStatus == 'private') {
            ShowEmptyMsg(translations.PublicationFailed + ' ' + translations.PrivateModeSet, translations.Info);
        }
    }

    FB.login(function (response) {
        // debugger;
        if (response.authResponse) {
            fbLogged = true;
            fbSetUserData(response.authResponse);
            callbackIfSuccess();
        }
        else {
            fbLogged = false;
            callbackIfFailure(response);
        }
    }, { scope: scope });
}

function fbCheckRights(scopeStr, callbackIfSuccess, callbackIfFailure) {
    FB.getLoginStatus(function (statusResponse) {
        if (statusResponse.status === 'connected') {
            FB.api('/me/permissions', function (response) {
                // Check for permissions
                var actionAllowed = false;
                if (response.data != null) {
                    var scopes = scopeStr.split(', ');
                    for (var j = 0; j < scopes.length; j++) {
                        var scopeAllowed = false;
                        for (var i = 0; i < response.data.length; i++) {
                            if (response.data[i].permission == scopes[j]) {
                                if (response.data[i].status == 'granted') {
                                    scopeAllowed = true;
                                    break;
                                }
                            }
                        }

                        if (scopeAllowed) actionAllowed = true;
                        else {
                            actionAllowed = false;
                            break;
                        }
                    }
                }
                if (actionAllowed) {
                    callbackIfSuccess();
                } else {
                    callbackIfFailure("noRights");
                }
            });
        } else {
            callbackIfFailure();
        }
    });
}

function fbCheckRighsAndLogged(authResponse, scope, callbackIfSuccess, callbackIfFailure) {
    var accessToken = authResponse.accessToken;

    FB.api(
        "/" + authResponse.userID, { fields: 'token_for_business' },
        function (response) {
            //     debugger;    
            if (!response || response.error) {
                if (response && response.error && (response.error.code == 104 || response.error.code == 190)) {
                    fbLoginToFacebookWithCallBack2(scope, callbackIfSuccess, callbackIfFailure);
                    return;
                }
                fbHandleError(response);
                callbackIfFailure();
            } else {
                fbUpdateAccounts(response, accessToken);
                fbCheckRights(scope, callbackIfSuccess, callbackIfFailure);
            }
        });
}

function fbLoginToFacebookWithCallBack2(scope, callbackIfSuccess, callbackIfFailure) {
    FB.login(function (response) {
        if (response.authResponse) {
            debugger;
            fbCheckRighsAndLogged(response.authResponse, scope, callbackIfSuccess, callbackIfFailure);
        }
        else {
            debugger;
            callbackIfFailure(response);
        }
    }, { scope: scope });
}

function fbResetOptions(noRights) {
    if (!noRights) {
        $('#fbPrevieModal .fbOptions label .account').text(translations.NotLoggedIn);
        $('#fbPrevieModal .fbOptions .publishFromOtherAccount').text(translations.LogIn);
    }
    fbShowPreviewForOtherUser();
}

function fbSetAdditionalData(scopeStr, callbackIfSuccess, callbackIfFailure, rightsChecking) {
    FB.getLoginStatus(function (statusResponse) {
        if (statusResponse.status === 'connected') {
            FB.api('/me/permissions', function (response) {
                // Check for permissions
                var actionAllowed = false;
                if (response.data != null) {
                    var scopes = scopeStr.split(', ');
                    for (var j = 0; j < scopes.length; j++) {
                        var scopeAllowed = false;
                        for (var i = 0; i < response.data.length; i++) {
                            if (response.data[i].permission == scopes[j]) {
                                if (response.data[i].status == 'granted') {
                                    scopeAllowed = true;
                                    break;
                                }
                            }
                        }

                        if (scopeAllowed) actionAllowed = true;
                        else {
                            actionAllowed = false;
                            break;
                        }
                    }
                }
                if (actionAllowed) {
                    if (callbackIfSuccess) callbackIfSuccess();
                } else {
                    if (rightsChecking) {
                        fbLoginToFacebookWithCallBack2(scopeStr, callbackIfSuccess, callbackIfFailure)
                    } else {
                        fbLoginToFacebookWithCallBack(scopeStr, callbackIfSuccess, callbackIfFailure)
                    }
                }
            });
        } else {
            if (rightsChecking) {
                fbLoginToFacebookWithCallBack2(scopeStr, callbackIfSuccess, callbackIfFailure)
            } else {
                fbLoginToFacebookWithCallBack(scopeStr, callbackIfSuccess, callbackIfFailure)
            }
        }
    });
}

function fbGetEmails(callBack) {
    facebookCallBack = callBack;
    //googleClientScopes = googleClientScopeContacts;
    //googleReguestMethod = googleGetContactsWithToken;
    //checkAuth();

    fbSetAdditionalData("user_friends", fbSetEmails, fbSetEmailsCallBackFailure, true);
}

function fbSetEmails() {
    if (!facebookIds) {
        FB.api('/me/friends'
        , function (response) {
            if (!response || response.error) {
                fbHandleError(response);
                facebookCallBack(1);
            } else {
                facebookIds = [];
                for (var i = 0, l = response.data.length; i < l; i++) {
                    facebookIds.push(response.data[i].id);
                }
                facebookCallBack();
            }
        });
    } else {
        facebookCallBack();
    }
};

function fbSetEmailsCallBackFailure(noRights) {
    if (noRights) {
        ShowEmptyMsg(translations.NotGoogleRightsContacts, translations.Info);
    }

    facebookCallBack(1);
}

function publishFromCommunityModal(msg, dataURL, link) {
    //saveCommunityPublication(msg, dataURL);
    var pageOptionsSelectVisible = $("#fbPrevieModal .fbPageOptions").is(":visible");
    var groupOptionsSelectVisible = $("#fbPrevieModal .fbGroupOptions").is(":visible");

    var pageId = 0, groupId = 0;

    if (pageOptionsSelectVisible) {
        //pageId = $("#fbPrevieModal .fbPageOptions :selected").val();
        pageId = $('#fbPrevieModal .fbPageOptions .btn.ddl > span')[0].id;
    }
    if (groupOptionsSelectVisible) {
        //groupId = $("#fbPrevieModal .fbGroupOptions :selected").val();
        groupId = $('#fbPrevieModal .fbGroupOptions .btn.ddl > span')[0].id;
    }

    fbPostOnWall(msg, dataURL, link, pageId, groupId);
}

function afterFBPreviewClose() {
    if (fbPreview && fbPreview.GoToAdress) {
        window.location = fbPreview.GoToAdress;
    }
}

function publishFromModal() {
    if (fbPreview != null) {
        fbPreview.publish();
    } else {
        var self = this;
        var msg = $('#fbPrevieModal .fbMsg').val();
        // post image on wall
        publishFromCommunityModal(msg, self.dataURL);
        $('#fbPrevieModal').modal('hide');
    }

    /*  var fbFaceShareButtonMod = $('.fbFaceShareButton.shareFace_30.mod');   
      var fbMailShareButtonMod = $('.fbMailShareButton.shareMail_30.mod');   
      
      if (fbFaceShareButtonMod.length > 0) {
          fbPreview.publish();
      }
      if (fbMailShareButtonMod.length > 0) {
          $('#fbPrevieModal').modal('hide');
          
          $.ajax({
              type: "POST",
              url: ContextPath + "Sharing/GetViewForEmail",
          })
          .done(function (result) {
              var prev = $('#mailForm .mailFormContent');
              prev.html(result);
              
              $('#mailForm').modal('show');            
          });
      }  */
}

function twittFromModal() {
    if (fbPreview != null) {
        fbPreview.twitt();
    } else {
        alert("no supported yet")
    }
}

var fbPreview = null;

function loadSimpleFbPreviewModal(template, param, lengthType, date) {
    fbPreview = new FB_PreviewManager(null);
    fbPreview.openFbPreviewModal(true);

    generatePreviewModelHandler = setInterval(function () {
        if (!statsHeaderModel.Reloading) {
            clearInterval(generatePreviewModelHandler);
            generatePreviewModelHandler = null;

            lengthType = statsHeaderModel.LengthType();

            if (lengthType != -1) {
                date = statsHeaderModel.CurrentDate();
            }
            else {
                date = statsHeaderModel.CalendarDate() + ' - ' + statsHeaderModel.CalendarEndDate();
            }

            if (template == '_Calories') {
                param = $('.invisibleCaloriesValue')[0].innerText;
            }
            if (template == '_Distance') {
                param = $('.invisibleDistanceValue')[0].innerHTML;
            }

            param = param.replace('&nbsp;', ' ');

            $.ajax({
                type: "POST",
                url: ContextPath + "Sharing/GetSimpleTemplate",
                data: { template: template, param: param, lengthType: lengthType, date: date }
            })
            .done(function (result) {
                fbPreview.content = result;
            });
        }
    }, 100);
}

function loadSimpleFbPreviewForReport(template, lengthType, date) {
    var callendar = $('#userstatreport_wrapper ').first().clone();
    var head = $('<div class="calFbHeadStat  calFbHead"><div class="calFbHeadStatBar"><img src="/Content/images/whiteLogo_60_15.png" class="onFbLogoStat onFbLogo"></div><div class="date dateStat">' + date + '</div></div>');

    var container = $(document.createElement('div'));
    container.append(head);
    container.append(callendar);

    fbPreview = new FB_PreviewManager(container);
    fbPreview.openFbPreviewModal();
}

function loadTargetFbPreviewModal(template, param, lengthType, date, currentTargetIndex) {
    if (currentTargetIndex || currentTargetIndex == 0) {
        var currentUserTarget = clubTableExplorerModel.TargetList()[currentTargetIndex];
        if (currentUserTarget) {
            GeneralModel.TargetModelManager.TargetExplorerManager.SelectedTarget(currentUserTarget.TargetSideTileManage.currentTarget);
        }
    }

    $.ajax({
        type: "POST",
        url: ContextPath + "Sharing/GetSimpleTemplate",
        data: { template: template, param: param, lengthType: lengthType, date: date }
    })
    .done(function (result) {
        fbPreview = new FB_PreviewManager(result, false, null, null, true);
        fbPreview.openFbPreviewModal();
    });
}

function loadFbPreviewModalForContest(competitionsResultId, trainingId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Sharing/GetViewForContest",
        data: { competitionsResultId: competitionsResultId }
    })
    .done(function (result) {
        //textarea class "fbHiddenMsg" value in _Contest view
        var description = $(result)[2].value

        var elem = $('#ContestTile_' + competitionsResultId + '.simpleTile.span12.contestTile');

        var resultClone = $(result).clone();
        var fbSeparator = resultClone.find('.fbSeparator.displayNone');

        if (elem != null && trainingId != null && trainingId != 0) {
            var renderElement = elem.find('.sessionHrChart');

            if (renderElement.height() > 0 && renderElement.width() > 0) {
                fbPreview = new FB_PreviewManager(null, false, description);
                fbPreview.openFbPreviewModalCustomContent(function () {


                    var contestDetailsContainer = resultClone.find('.fbStatContestTraining .contestDetailsContainer');
                    contestDetailsContainer.css('display', 'block');

                    var self = this;
                    self.content = resultClone;
                    self.previewElem = $('body > .fbPreview');
                    self.previewElem.html(self.content);

                    var container = $(self.previewElem).find('.sessionHrChart');
                    var placeholder = $(".hrChart", $(container));
                    if (!placeholder.length) { returnFromChartLoading(); return; }

                    loadCurrentChartFB(trainingId, true, container, returnFromChartLoading, 20, 6, 'hr');
                });
            }
            else {
                fbSeparator.removeClass('displayNone');

                fbPreview = new FB_PreviewManager(resultClone, false, description);
                fbPreview.openFbPreviewModal();
            }
        }
        else {
            fbSeparator.removeClass('displayNone');

            fbPreview = new FB_PreviewManager(resultClone, false, description);
            fbPreview.openFbPreviewModal();
        }
    });
}

function loadUserStatisticsReportChartShare(start, end, lengthType, disciplineId, date) {
    var sampleType = $('#userstatreportchart .item.periodpicker').data('sampletype');

    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetUserStatisticsReportChartShare",
        data: { startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId, sampleType: sampleType, userId: UserId, date: date },
        error: function (shr, status, data) {
            console.log("error " + data + " Status " + shr.status);
        }
    })
    .done(function (result) {
        fbPreview = new FB_PreviewManager(null, false, '');
        fbPreview.openFbPreviewModalCustomContent(function () {

            var self = this;
            self.content = result;
            self.previewElem = $('body > .fbPreview');
            self.previewElem.html(self.content);

            userStatReportChartViewModel.refreshChartForPreview();

            ko.cleanNode(self.previewElem[0]);
            ko.applyBindings(userStatReportChartViewModel, self.previewElem[0]);

            returnFromChartLoading();
        });
    });
}

function loadSimpleFbPreviewModalStatistics(template, startDate, lengthType, trainingId, type, link, loadChart, viewType) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetSimpleTemplateStatistics",
        data: { template: template, startDate: startDate, lengthType: lengthType, trainingId: trainingId, type: type, viewType: viewType },
        error: function (shr, status, data) {
            console.log("error " + data + " Status " + shr.status);
        }
    })
    .done(function (result) {
        if (loadChart) {
            fbPreview = new FB_PreviewManager(null, false, '');
            fbPreview.openFbPreviewModalCustomContent(function () {


                var self = this;
                self.content = result;
                self.previewElem = $('body > .fbPreview');
                self.previewElem.html(self.content);

                var container = $(self.previewElem).find('.collapse');

                if (viewType == 'HRChart') {
                    var placeholder = $(".hrChart", $(container));
                    if (!placeholder.length) { returnFromChartLoading(); return; }

                    loadCurrentChartFB(trainingId, false, container, returnFromChartLoading, null, null, 'hr');
                } else
                    if (viewType == 'SPEEDChart') {
                        var paceParams = container.data();

                        var type = "speed";
                        var contSelector = ".speedChart";
                        var unit = "km/h";
                        var invertFactor = 0;
                        if (paceParams && paceParams.paceunit) {
                            unit = paceParams.paceunit;
                            invertFactor = paceParams.pacefactor;
                        }

                        var placeholder = $(contSelector, $(container));
                        if (!placeholder.length) { returnFromChartLoading(); return; }

                        loadTrainingDataChartFB(trainingId, false, container, type, contSelector, unit, returnFromChartLoading, invertFactor);
                    } else
                        if (viewType == 'CADENCEChart') {
                            var type = "cadence";
                            var contSelector = ".cadenceChart";
                            var unit = "rpm";

                            var placeholder = $(contSelector, $(container));
                            if (!placeholder.length) { returnFromChartLoading(); return; }

                            loadTrainingDataChartFB(trainingId, false, container, type, contSelector, unit, returnFromChartLoading);
                        } else
                            if (viewType == 'ALTITUDEChart') {
                                var type = "altitude";
                                var contSelector = ".altitudeChart";
                                var unit = "M";

                                var placeholder = $(contSelector, $(container));
                                if (!placeholder.length) { returnFromChartLoading(); return; }

                                loadTrainingDataChartFB(trainingId, false, container, type, contSelector, unit, returnFromChartLoading);
                            } else
                                if (viewType == 'POWERChart') {
                                    /*
                                    var type = "power";
                                    var contSelector = ".powerChart";
                                    var unit = "W";
                    
                                    var placeholder = $(contSelector, $(container));
                                    if (!placeholder.length) {returnFromChartLoading(); return;}
                    
                                    loadTrainingDataChartFB(trainingId, false, container, type, contSelector, unit, returnFromChartLoading);
                                    */
                                    var placeholder = $(".powerChart", $(container));
                                    if (!placeholder.length) { returnFromChartLoading(); return; }

                                    loadCurrentChartFB(trainingId, false, container, returnFromChartLoading, null, null, 'power');
                                }

            });
        } else {
            fbPreview = new FB_PreviewManager(result, false, "", link);
            fbPreview.openFbPreviewModal();
        }
    });
}

function returnFromChartLoading() {
    var prev = $('#fbPrevieModal .fbPreview');
    var self = this;
    self.previewElem = $('body > .fbPreview');

    communityPublicationHeader = $('.communityPublicationHeader', self.previewElem);
    communityPublicationType = $('.communityPublicationType', self.previewElem);
    html2canvas(self.previewElem, {
        background: self.Bg ? self.Bg : '#ffffff',
        onrendered: function (canvas) {

            self.dataURL = canvas.toDataURL();

            self.previewElem.html('');

            var prev = $('#fbPrevieModal .fbPreview');
            var img = $(document.createElement('img'));
            img.attr('src', self.dataURL);
            prev.html(img);

            if (fbPreview != null) {
                fbPreview.dataURL = self.dataURL;
            }
        }
    });
}

function loadFbPreviewOptional(startDate, lengthType, currentDate) {
    fbPreview = new FB_PreviewManager(null);
    fbPreview.openFbPreviewModal(true);

    generatePreviewModelHandler = setInterval(function () {
        if (!statsHeaderModel.Reloading) {
            clearInterval(generatePreviewModelHandler);
            generatePreviewModelHandler = null;

            lengthType = statsHeaderModel.LengthType();
            startDate = statsHeaderModel.ParamStart();

            if (lengthType != -1) {
                currentDate = statsHeaderModel.CurrentDate();
            }
            else {
                currentDate = statsHeaderModel.CalendarDate() + ' - ' + statsHeaderModel.CalendarEndDate();
            }

            $.ajax({
                type: "POST",
                url: ContextPath + "SharingStats/GetView",
                data: { startDate: startDate, lengthType: lengthType, currentDate: currentDate }
            })
            .done(function (result) {
                fbPreview.content = result;
            });
        }
    }, 100);
}

//function setStatsModelForFbPreview(startDate, lengthType, currentDate, callback) {
//    if (typeof statsHeaderModel !== 'undefined' && statsHeaderModel) {
//        generatePreviewModelHandler = setInterval(function () {
//            if (!statsHeaderModel.Reloading) {
//                clearInterval(generatePreviewModelHandler);
//                generatePreviewModelHandler = null;

//                callback();
//            }
//        }, 100);

//        lengthType = statsHeaderModel.LengthType();
//        startDate = statsHeaderModel.ParamStart();

//        if (lengthType != -1) {
//            currentDate = statsHeaderModel.CurrentDate();
//        }
//        else {
//            currentDate = statsHeaderModel.CalendarDate() + ' - ' + statsHeaderModel.CalendarEndDate();
//        }
//    }
//}

function loadFbPreviewForMultiSession(template, startDate, lengthType, currentDate, startTime, loadChart, viewType, trainingId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetViewForMultiSession",
        data: { template: template, startDate: startDate, lengthType: lengthType, currentDate: currentDate, startTime: startTime }
    })
    .done(function (result) {
        if (loadChart) {
            fbPreview = new FB_PreviewManager(null, false, '');
            fbPreview.openFbPreviewModalCustomContent(function () {

                var self = this;
                self.content = result;
                self.previewElem = $('body > .fbPreview');
                self.previewElem.html(self.content);

                if (viewType == 'HRChart') {
                    var container = $(self.previewElem).find('.sessionHrChart');
                    var placeholder = $(".hrChart", $(container));
                    if (!placeholder.length) { returnFromChartLoading(); return; }

                    loadCurrentChartFB(trainingId, true, container, returnFromChartLoading, null, null, 'hr');
                }
            });
        } else {
            fbPreview = new FB_PreviewManager(result);
            fbPreview.openFbPreviewModal();
        }
    });
}

function loadFbPreviewForAllActivities(template, startDate, lengthType, currentDate, disciplineId) {
    fbPreview = new FB_PreviewManager(null);
    fbPreview.openFbPreviewModal(true);

    generatePreviewModelHandler = setInterval(function () {
        if (!statsHeaderModel.Reloading) {
            clearInterval(generatePreviewModelHandler);
            generatePreviewModelHandler = null;

            lengthType = statsHeaderModel.LengthType();
            startDate = statsHeaderModel.ParamStart();

            if (lengthType != -1) {
                currentDate = statsHeaderModel.CurrentDate();
            }
            else {
                currentDate = statsHeaderModel.CalendarDate() + ' - ' + statsHeaderModel.CalendarEndDate();
            }

            $.ajax({
                type: "POST",
                url: ContextPath + "SharingStats/GetViewForAllActivities",
                data: { template: template, startDate: startDate, lengthType: lengthType, currentDate: currentDate, disciplineId: disciplineId }
            })
            .done(function (result) {
                fbPreview.content = result;
            });
        }
    }, 100);
}

function setImageFromContent(template, startDate, lengthType, currentDate, disciplineId, divClass, width, height) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetViewForAllActivities",
        data: { template: template, startDate: startDate, lengthType: lengthType, currentDate: currentDate, disciplineId: disciplineId }
    })
    .done(function (content) {
        var self = this;
        self.content = null;
        self.dataURLScaled = null;

        self.content = content;

        var elementDivClass = $("." + divClass);

        var container = document.createElement("div");
        container.className = "divScalePreview";
        container.style.width = width + 'px';
        container.style.height = height + 'px';
        container.style.float = "left";
        container.innerHTML = self.content;

        document.body.appendChild(container);

        setTimeout(function () { self.render2(); }, 500);

        self.render2 = function () {
            html2canvas(container, {
                background: self.Bg ? self.Bg : '#ffffff',
                onrendered: function (canvas) {

                    //var extra_canvas = document.createElement("canvas");
                    //extra_canvas.setAttribute('width', width);
                    //extra_canvas.setAttribute('height', height);
                    //var ctx = extra_canvas.getContext('2d');
                    //ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, elementDivClass.width(), elementDivClass.height());

                    self.dataURLScaled = canvas.toDataURL();

                    document.body.removeChild(container);

                    var elem = $('.' + divClass);
                    var img = $(document.createElement('img'));
                    img.attr('src', self.dataURLScaled);
                    elem.html(img);

                    window.processing = false;
                }
            });
        }
    });
}

function loadZoneCirclesForFbPreview() {
    window.processing = true;
    setImageFromContent('../Stats/singleControls/_ZoneCircles',
    '@(ViewBag.StartDate)', '@(ViewBag.LengthType)', '@(ViewBag.HeaderTime)', null,
    'fbStatSimpleImageSummary', 472, 262);

    (function doOne() {
        if (window.processing) setTimeout(doOne, 100);
    })();
}

function loadUserCalendarFbPreview() {
    var callendar = $('.uCalT ').first().clone();

    var stats = $(userCallendar.MonthStatCache).find('.horizontal');

    if (stats.length == 0) {
        //var stats

        stats = $(userCallendar.MonthStatCache).find('.vertical').removeClass('vertical').addClass('horizontal');
    }

    /*var stats = $('.calStatsContainer .horizontal').first().clone();
    if (stats.length == 0) {
        stats = $('.calStatsContainer .vertical').first().clone();
        stats.removeClass('vertical').addClass('horizontal');
    }
    */

    callendar.find('.weeksCalendar').remove();
    callendar.find('.calHeader').remove();
    callendar.find('.tileFooter').remove();
    callendar.find('.redStripe').remove();
    callendar.find('.uniHeadBar').remove();

    stats.find('.redStripe').remove();
    stats.find('.moreStats').remove();
    stats.find('.shareButton').remove();

    var heartControl = stats.find('.statControl.withHeart');

    if (heartControl != null) {
        heartControl.parent().css({ position: 'static' });
        heartControl.css({ position: 'static', float: 'none', 'margin-top': '19px', 'margin-right': '5px' });
    }

    var date = '', head = '';

    if (GeneralModel.UserCallendarModel.Callendar().Callendar.PeriodType() == 'month') {
        date = GeneralModel.UserCallendarModel.Callendar().MonthName() + ' ' + GeneralModel.UserCallendarModel.Year();

        head = $('<div class="calFbHead"><span class="txt">' + translations.calFbHead + '</span><br/><span class="date">' + date + '</span></div>');
    }
    if (GeneralModel.UserCallendarModel.Callendar().Callendar.PeriodType() == 'week') {
        date = GeneralModel.UserCallendarModel.NaviBarModel().CurrentDate;

        head = $('<div class="calFbHead"><span class="txt">' + translations.calFbHeadWeek + '</span><br/><span class="date">' + date + '</span></div>');
    }


    var logo = $(document.createElement('img'));
    logo.attr('src', ContextPath + "Content/images/whiteLogo.png");
    logo.addClass('onFbLogo');
    head.prepend(logo);

    var container = $(document.createElement('div'));
    container.append(head);
    container.append(callendar);

    container.append(stats);

    container.addClass('fbCallendarContainer').addClass('tile');

    fbPreview = new FB_PreviewManager(container, false);
    fbPreview.Bg = '#29BDB2';
    fbPreview.openFbPreviewModal();
}

function trainingTileSimpleFbPreview(elem, doNotMove, link, clone, addParentClassName) {
    var container = elem;

    if (clone) {
        container = elem.clone();
    }

    if (addParentClassName) {
        var containerTmp = $(document.createElement('div'));
        containerTmp.addClass(addParentClassName);
        containerTmp.append(container);
        container = containerTmp;
    }

    fbPreview = new FB_PreviewManager(container, doNotMove, "", link);
    fbPreview.openFbPreviewModal();
}

function trainingTileFbPreview(elem, chartClassName, startDate, date, trainingId, startTime, link) {
    if (chartClassName == null) {

        trainingTileSimpleFbPreview(elem, true, link);

        return;
    }

    var tile = elem.clone();
    var container = null;
    var basicInfoHeight = '0px';
    var hrChartLoaded = null;
    var singleTraining = chartClassName != '.sessionHrChart';


    if (singleTraining) {
        container = $(".trainingDetailsContainer", elem);
    }
    else {
        container = $(".sessionDetailsContainer", elem);
    }

    hrChartLoaded = container.data("loaded");

    if (singleTraining) {
        basicInfoHeight = $('[id^=basicinfo_' + trainingId + ']').css('height');
        if (basicInfoHeight != '0px') hrChartLoaded = false;
    }

    tile.find('.redStripe').remove();
    tile.find('.grayStripe').remove();
    tile.find('.detailsMenu').remove();
    tile.find('.collapsed .icon30 .route').remove();

    var multiClubLogo = tile.find('.fbStatSimpleTrainingMultiClubLogo.fbStatHidden');
    multiClubLogo.removeClass('fbStatHidden');

    //var dayNr = GeneralModel.UserCallendarModel.SelectedElement.DayNr();
    //var date = dayNr + ' ' + GeneralModel.UserCallendarModel.Callendar().MonthName() + ' ' + GeneralModel.UserCallendarModel.Year();
    var head = $('<div class="simpleFbPostStat"><div class="calFbHeadStat calFbHeadGray calFbHead"><div class="calFbHeadStatBar"></div><div class="date dateStat">' + date + '</div></div></div>');

    var routeClass = elem.find('[id^=route_' + trainingId + ']');
    var autoLapsClass = elem.find('[id^=auto_laps_' + trainingId + ']');
    var manualLapsClass = elem.find('[id^=manual_laps_' + trainingId + ']');

    var logo = $(document.createElement('img'));
    logo.attr('src', ContextPath + "Content/images/whiteLogo_60_15.png");
    logo.addClass('onFbLogoStat onFbLogo');
    head.find('.calFbHeadStatBar').prepend(logo);

    var container = $(document.createElement('div'));
    container.append(head);
    container.append(tile);

    if (hrChartLoaded) {
        tile.find('.trainingDetailsContainer').remove();
        var hrChart = $('<div></div>');

        var renderElement = elem.find(chartClassName);
        var elemToRender = elem.find(renderElement);

        if (renderElement.height() > 0 && renderElement.width() > 0) {

            var routeMap = elem.find('.routeMap');

            if (routeClass && $(routeClass).css('height') != "0px"
                && routeMap.length > 0) {
                $("body").css('cursor', 'wait');
                $('.tab-content.person #history').css('cursor', 'wait');

                html2canvas(routeMap, {
                    //"logging": true,
                    "proxy": "/Image/LoadExternalImageAsDataURLWithIgnoredList",
                    background: self.Bg ? self.Bg : '#ffffff',
                    onrendered: function (canvas) {
                        self.dataURL = canvas.toDataURL();

                        var img = $(document.createElement('img'));
                        img.attr('src', self.dataURL);

                        //                         var img = $(document.createElement('div'));
                        //                         img.attr('style', 'background: white url(' + self.dataURL + ') no-repeat;width: 753px; height: 266px;');

                        loadFbPreviewRouteMap(img, trainingId, link);
                        $("body").css('cursor', 'initial');
                        $(tile).css('cursor', 'initial');
                        $('.tab-content.person #history').css('cursor', 'initial');
                    },
                    error: function (data) {
                        $("body").css('cursor', 'initial');
                        $('.tab-content.person #history').css('cursor', 'initial');
                    },
                });

            } else {
                var viewType = getViewType(elem, trainingId);
                var loadChart = viewType == 'HRChart' || viewType == 'SPEEDChart' || viewType == 'CADENCEChart' || viewType == 'ALTITUDEChart' || viewType == 'POWERChart';

                if (viewType != '') {
                    var plot = getPlot(elem, trainingId);


                    if (singleTraining) {
                        loadSimpleFbPreviewModalStatistics("_SimpleTrainingExpandedShare", null, 0, trainingId, "training", link, loadChart, viewType, plot);
                    } else {
                        loadFbPreviewForMultiSession("_MultiTrainingExpandedShare", startDate, 1, date, startTime, loadChart, viewType, trainingId);
                    }
                } else
                    if (autoLapsClass.length > 0 && $(autoLapsClass).css('height') != "0px") {
                        var trainingLapsAutoFixed = $('#trainingLapsAutoFixed', elemToRender).clone();

                        loadFbPreviewLaps(trainingLapsAutoFixed, trainingId, link);
                    } else
                        if (manualLapsClass.length > 0 && $(manualLapsClass).css('height') != "0px") {
                            var trainingLapsFixed = $('#trainingLapsFixed', elemToRender).clone();

                            loadFbPreviewLaps(trainingLapsFixed, trainingId, link);
                        }
                        else {
                            html2canvas(elemToRender, {
                                background: self.Bg ? self.Bg : '#ffffff',
                                onrendered: function (canvas) {
                                    self.dataURL = canvas.toDataURL();

                                    var img = $(document.createElement('img'));
                                    img.attr('src', self.dataURL);
                                    hrChart.html(img);
                                    tile.find('.simpleTraining').append(hrChart);

                                    trainingTileSimpleFbPreview(container, true, link);
                                },
                                error: function (data) {
                                    console.log("error " + data);
                                },
                            });
                        }
            }
        } else {
            trainingTileSimpleFbPreview(container, true, link);
        }
    } else {
        if (singleTraining) {
            loadSimpleFbPreviewModalStatistics("_UserTrainingShare", null, 0, trainingId, "training", link);
        } else {
            loadFbPreviewForMultiSession("_MultiTrainingShare", startDate, 1, date, startTime);
        }
    }

    //    tile.find('.tileFooter').remove();

    //var container = $(document.createElement('div'));
    //container.append(tile);
    //container.append(logo);

    //container.addClass('fbTrainingContainer');
}

function getViewType(elem, trainingId) {
    var chartClass = elem.find('[id^=sessionHr_' + trainingId + ']');
    var chartElem = elem.find('.hrChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'HRChart';
        }
        return 'HR';
    }


    var chartClass = elem.find('[id^=hrChart_' + trainingId + ']');
    var chartElem = elem.find('.hrChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'HRChart';
        }
        return 'HR';
    }

    chartClass = elem.find('[id^=speed_' + trainingId + ']');
    chartElem = elem.find('.speedChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'SPEEDChart';
        }
        return 'SPEED';
    }

    chartClass = elem.find('[id^=cadence_' + trainingId + ']');
    chartElem = elem.find('.cadenceChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'CADENCEChart';
        }
        return 'CADENCE';
    }

    chartClass = elem.find('[id^=route_' + trainingId + ']');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        return 'ROUTE';
    }

    chartClass = elem.find('[id^=exercises_' + trainingId + ']');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        return 'EXERCISES';
    }

    chartClass = elem.find('[id^=feading_' + trainingId + ']');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        return 'FEADING';
    }

    chartClass = elem.find('[id^=description_' + trainingId + ']');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        return 'DESCRIPTION';
    }

    chartClass = elem.find('[id^=altitude_' + trainingId + ']');
    chartElem = elem.find('.altitudeChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'ALTITUDEChart';
        }
        return 'ALTITUDE';
    }

    chartClass = elem.find('[id^=power_' + trainingId + ']');
    chartElem = elem.find('.powerChart');

    if (chartClass && $(chartClass).length > 0 && $(chartClass).css('height') != "0px") {
        if (chartElem != null && chartElem.length > 0) {
            return 'POWERChart';
        }
        return 'POWER';
    }

    return '';
}

function getPlot(elem, trainingId) {
    if (trainingId != null) {
        container = $(".trainingDetailsContainer", elem);
    }
    else {
        container = $(".sessionDetailsContainer", elem);
    }

    var placeholder = $(".hrChart", $(container));
    if (!placeholder.length) return;
    var plot = placeholder.data("plot");

    return plot;
}

function changeSelection(fbActualShareButton) {
    var fbFaceShareButtonMod = $('.fbFaceShareButton.shareFace_30.mod');
    var fbMailShareButtonMod = $('.fbMailShareButton.shareMail_30.mod');

    if (fbFaceShareButtonMod.length > 0) {
        fbFaceShareButtonMod.removeClass('mod');
    }
    if (fbMailShareButtonMod.length > 0) {
        fbMailShareButtonMod.removeClass('mod');
    }

    fbActualShareButton.addClass('mod');
}

function sendEmail(toEmail, fromEmail, subject, msg) {
    $('#mailForm').modal('hide');

    var imageSRC = $('#fbPrevieModal .fbPreview img').attr('src');

    $.ajax({
        type: "POST",
        url: ContextPath + "Sharing/SenEmail",
        data: { toEmail: toEmail, fromEmail: fromEmail, subject: subject, msg: msg, imageSRC: imageSRC }
    })
    .done(function (result) {
    });
}

function setDivImageFromElement(elem, sourceClassName, outDiv) {

    var renderElement = elem.find(sourceClassName);

    if (renderElement.height() > 0 && renderElement.width() > 0) {
        html2canvas(elem.find(renderElement), {
            background: self.Bg ? self.Bg : '#ffffff',
            onrendered: function (canvas) {
                self.dataURL = canvas.toDataURL();

                var img = $(document.createElement('img'));
                img.attr('src', self.dataURL);
                outDiv.html(img);
            },
            error: function (data) {
                console.log("error " + data);
            },
        });
    }
}

function sizeDivToCover(div, parentDiv) {
    var startWidth = $(div).css('width');

    for (var i = 0; i < 1000; i++) {
        $(div).css('width', startWidth + i);

        //$(div).
    }
}

function loadFbLivePreviewPlan(liveStreamGuid) {
    var link = liveStreamGuid ? ContextPath + 'LiveStream/Stream?guid=' + liveStreamGuid : null;

    var img = '<img src="' + ContextPath + 'Content/images/pulsstory_lsInvit.png' + '" />';

    fbPreview = new FB_PreviewManager(img, false, "", link);
    fbPreview.openFbPreviewModal();
}

function loadFbPreviewPeriodStats(template, periodId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetPeriodView",
        data: { template: template, periodId: periodId }
    })
    .done(function (result) {
        fbPreview = new FB_PreviewManager(result);
        fbPreview.openFbPreviewModal();
        //openFbPreviewModal(result);
    });
}

function loadFbPreviewSharingStats(template, message, goToAdress) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetPartialView",
        data: { template: template }
    })
    .done(function (result) {
        fbPreview = new FB_PreviewManager(result, false, message, null, true, goToAdress);
        fbPreview.openFbPreviewModal();
    });
}


function loadFbPreviewFunPublishLiveShare(periodLogoId, liveUrl) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetPartialViewForfunPublishLiveShare",
        data: { periodLogoId: periodLogoId, liveUrl: liveUrl }
    })
    .done(function (result) {
        fbPreview = new FB_PreviewManager(result, false, null, null, true);
        fbPreview.openFbPreviewModal();
    });
}

function loadFbPreviewViewWithModel(template, elemId, liveStreamGuid) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetPartialViewWithModel",
        data: { template: template, elemId: elemId }
    })
    .done(function (result) {
        if (result == 'NoData') {
            ShowEmptyMsg(translations[result], translations["ReadError"]);
        }
        else {
            var link = liveStreamGuid ? ContextPath + 'LiveStream/Stream?guid=' + liveStreamGuid : null;

            fbPreview = new FB_PreviewManager(result, false, "", link);
            fbPreview.openFbPreviewModal();
        }
    });
}

function loadFbPreviewRouteMap(img, trainingId, link) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetSimpleTemplateStatistics",
        data: { template: "_UserTrainingMapShare", startDate: null, lengthType: 0, trainingId: trainingId, type: "training" },
        error: function (shr, status, data) {
            console.log("error " + data + " Status " + shr.status);
        }
    })
    .done(function (result) {
        var container = $(document.createElement('div'));

        container.append(result);

        $(".routeMapImg", container).html(img);

        fbPreview = new FB_PreviewManager(container, false, "", link);
        fbPreview.openFbPreviewModal();
    });
}

function loadFbPreviewLaps(elem, trainingId, link) {
    $.ajax({
        type: "POST",
        url: ContextPath + "SharingStats/GetSimpleTemplateStatistics",
        data: { template: "_UserTrainingLapsShare", startDate: null, lengthType: 0, trainingId: trainingId, type: "training" },
        error: function (shr, status, data) {
            console.log("error " + data + " Status " + shr.status);
        }
    })
    .done(function (result) {
        var container = $(document.createElement('div'));

        container.append(result);

        $(".routeMapImg", container).html(elem);

        fbPreview = new FB_PreviewManager(container, false, "", link);
        fbPreview.openFbPreviewModal();
    });
}

function saveCommunityPublication(msg, image) {
    image = image.replace('data:image/png;base64,', '');

    if (communityPublicationHeader && communityPublicationType &&
        communityPublicationHeader.length > 0 && communityPublicationType.length > 0) {
        var communityPublicationHeaderString = communityPublicationHeader[0].outerHTML;
        var communityPublicationTypeValue = communityPublicationType[0].value;

        $.ajax({
            type: 'POST',
            url: ContextPath + "Community/SavePublication",
            //data: '{ "imageData" : "' + image + '" }',
            data: {
                msg: msg,
                imageDataURL: image,
                publicationHeader: encodeURI(communityPublicationHeaderString),
                type: communityPublicationTypeValue
            }
        })
        .done(function (serverModel) {

        });
    }

    // 	$.ajax({
    // 		url: 'handler/FileUploader.ashx?FileName=' + file.name, //server script to process data
    // 		type: 'POST',
    // 		xhr: function () {
    // 			myXhr = $.ajaxSettings.xhr();
    // 			if (myXhr.upload) {
    // 				myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
    // 			}
    // 			return myXhr;
    // 		},
    // 		data: file,
    // 		success: function (result) {                    

    // 		}
    // 	});

    // 	function progressHandlingFunction(e) {
    // 				if (e.lengthComputable) {
    // 					var s = parseInt((e.loaded / e.total) * 100);
    // 					$("#progress" + currFile).text(s + "%");
    // 					$("#progbarWidth" + currFile).width(s + "%");
    // 					if (s == 100) {
    // 						triggerNextFileUpload();
    // 					}
    // 				}
    // 			}	
}


function saveExternalPublication(image, data, type, msg, callback, target, guid, link) {
    image = image.replace('data:image/png;base64,', '');
    $.ajax({
        type: 'POST',
        url: ContextPath + "ExternalPublication/SavePublication",
        data: {
            imageDataURL: image,
            data: data,
            type: type,
            msg: msg,
            target: target,
            guid: guid,
            link: link
        }
    })
    .done(function (guid) {

        if (guid) {
            var elem = $("<img id='ExternalPublicationGetImage' src='" + ContextPath + "ExternalPublication/GetImage?guid=" + guid + "' />");
            $('body').append(elem);
            setTimeout(function () { $('#ExternalPublicationGetImage').remove(); }, 500);
        }
        if (callback) callback(guid, msg, target);
    });
}

function updateExternalPublication(guid, msg, callback, target, link) {
    $.ajax({
        type: 'POST',
        url: ContextPath + "ExternalPublication/UpdatePublication",
        data: {
            guid: guid,
            msg: msg,
            target: target,
            link: link
        }
    });
    //.done(function (result) {    });
}

// #region google

var googleClientId = googleId;
var googleClientScopeContacts = 'https://www.googleapis.com/auth/contacts.readonly';
var googleClientScopes = null;
var googleCallBack = null;
var googleReguestMethod = null;
var googleAuthCanceled = false;

function googleGetEmails(callBack) {
    googleCallBack = callBack;
    googleClientScopes = googleClientScopeContacts;
    googleReguestMethod = googleGetContactsWithToken;
    checkAuth();
}

function checkAuth() {
    var authorizeDeferred;
    googleAuthCanceled = false;

    (function (wrapped) {
        window.open = function () {
            // re-assign the original window.open after one usage
            window.open = wrapped;

            var win = wrapped.apply(this, arguments);
            var i = setInterval(function () {
                if (win.closed) {
                    clearInterval(i);
                    // cancel has no effect when the promise is already resolved, e.g. by the success handler
                    // see http://docs.closure-library.googlecode.com/git/class_goog_Promise.html#goog.Promise.prototype.cancel
                    googleAuthCanceled = true;
                    authorizeDeferred.cancel();
                }
            }, 1000);
            return win;
        };
    })(window.open);

    //     authorizeDeferred = gapi.auth.authorize({
    //           'client_id': googleClientId,
    //           'scope': googleClientScopes,
    //           'immediate': false        
    //     }).then(handleAuthResult, googleOnRejected);

    authorizeDeferred = gapi.auth.authorize(
      {
          'client_id': googleClientId,
          'scope': googleClientScopes,
          'immediate': false
      }, handleAuthResult);
}

function handleAuthResult(authResult) {
    if (googleAuthCanceled) {
        authResult.error = "access_denied";
    }

    if (authResult && !authResult.error) {
        googleReguestMethod(gapi.auth.getToken());
    } else {
        if (authResult.error == "access_denied" && googleClientScopes.indexOf("contacts")) {
            googleHandleError(authResult, "NotGoogleRightsContacts", true);
        } else {
            googleHandleError(authResult, null, true);
        }

        if (googleCallBack) googleCallBack(1);
    }
}

function googleGetContactsWithToken(token) {
    token['g-oauth-window'] = null;

    $.ajax({
        url: 'https://www.google.com/m8/feeds/contacts/default/full?alt=json',
        dataType: 'jsonp',
        data: token
    }).done(function (response) {
        if (!response || response.error) {
            googleHandleError(response, null, true);
            if (googleCallBack) googleCallBack(1);
        } else {
            googleMails = [];

            if (response.feed.entry) {
                for (var i = 0, len = response.feed.entry.length; i < len; i++) {
                    if (response.feed.entry[i].gd$email) {
                        for (var j = 0, jlen = response.feed.entry[i].gd$email.length; j < jlen; j++) {
                            googleMails.push(response.feed.entry[i].gd$email[j].address);
                        }
                    }
                }
            }

            if (googleCallBack) googleCallBack();
        }
    }).error(function (error) {
        if (error.status == 404) {
            notHandled = false;
            ShowEmptyMsg(translations.GoogleGetContactsError, translations.Error);
        }
    });
}

function googleHandleError(response, errorType, logOnServer) {
    if (errorType) {
        ShowEmptyMsg(translations[errorType], translations.Info);
    } else
        if (response.error == "access_denied") {
            ShowEmptyMsg(translations.NotGoogleRights, translations.Info);
        } else {
            ShowEmptyMsg(translations.ErrorMsg, translations.Error);
        }

    logResponseError(response, ErrorMessageType, "Google error", true);

    //var msg = "Google error: " + response.error;
    //console.log(msg);
    //if (logOnServer)
    //{
    //    logMsgOnServer(msg, ErrorMessageType);
    //    if (response.errors) {
    //        for (var j = 0, jlen = response.errors.length; j < jlen; j++) {
    //            response.errors[j].message
    //        }
    //    }
    //}
}

//var restRequest = gapi.client.request({
//    'path': '/gmail/v1/people',
//    'params': { 'query': 'Google+', 'orderBy': 'best' }
//});
//restRequest.then(function (resp) {
//    console.log(resp.result);
//}, function (reason) {
//    console.log('Error: ' + reason.result.error.message);
//});

// #endregion

function fbCreateAlbum() {
    //post to album
    //fbPostImage(statusResponse, body, pic, message, pageId, groupId, 1912563135673265);

    FB.api(
      '/me/albums',
      'post',
      {
          message: 'Test album 2 opis',
          name: 'Test album 2'
      },
      function (response) {
          if (!response || response.error)
              alert('Error occured: ' + response.error.message);
          else {
          }
      }
    );
}

function FacebookUploadFileModel(id, description) {
    var self = this;

    self.Id = id;
    self.Description = description;
}

function fbCreateAlbumAndAddImages(albumName, albumDescription, facebookUploadFileModels, callBack) {
    facebookCallBack = callBack;
    facebookAlbumName = albumName;
    facebookAlbumDescription = albumDescription;
    facebookUploadFileIds = facebookUploadFileModels;
    fbSetAdditionalData("publish_actions, user_photos", fbCreateAlbumAndAddImagesCallBack, fbCreateAlbumAndAddImagesCallBackFailure, true);
}

function fbCreateAlbumAndAddImagesCallBack() {
    var facebookAccessTokenCached = $.jStorage.get('FacebookAccessToken');

    var data = {
        accessToken: facebookAccessTokenCached,
        albumName: facebookAlbumName,
        albumDescription: facebookAlbumDescription,
        facebookUploadFileModels: facebookUploadFileIds
    };

    var strData = JSON.stringify(data);

    $.ajax({
        type: "POST",
        url: ContextPath + "Facebook/CreateAlbumAndAddImages",
        contentType: "application/json; charset=utf-8",
        data: strData
    })
    .done(function (result) {
        if (facebookCallBack) facebookCallBack(result);
    });
}

function fbCreateAlbumAndAddImagesCallBackFailure(noRights) {
    if (noRights) {
        ShowEmptyMsg(translations.NotGoogleRights, translations.Info);
    }
}

function fbPublishLiveStreamImageDataURL(senderId, receiverId, releatedId, type, imageDataURL, comment, fileName) {
    fbPublishLiveStreamImageDataURLParams = {
        senderId: senderId,
        receiverId: receiverId,
        releatedId: releatedId,
        type: type,
        imageDataURL: imageDataURL,
        comment: comment,
        fileName: fileName
    };

    fbSetAdditionalData("publish_actions", fbPublishLiveStreamImageDataURLCallBack, fbCreateAlbumAndAddImagesCallBackFailure, true);
}

function fbPublishLiveStreamImageDataURLCallBack() {
    var facebookAccessTokenCached = $.jStorage.get('FacebookAccessToken');

    var data = {
        accessToken: facebookAccessTokenCached,
        senderId: fbPublishLiveStreamImageDataURLParams.senderId,
        receiverId: fbPublishLiveStreamImageDataURLParams.receiverId,
        releatedId: fbPublishLiveStreamImageDataURLParams.releatedId,
        type: fbPublishLiveStreamImageDataURLParams.type,
        imageDataURL: fbPublishLiveStreamImageDataURLParams.imageDataURL,
        comment: fbPublishLiveStreamImageDataURLParams.comment,
        fileName: fbPublishLiveStreamImageDataURLParams.fileName
    };

    var strData = JSON.stringify(data);

    $.ajax({
        type: "POST",
        url: ContextPath + "Facebook/PublishLiveStreamImageDataURL",
        contentType: "application/json; charset=utf-8",
        data: strData
    })
    .done(function (result) {
        if (facebookCallBack) facebookCallBack(result);
    });
}

function fbPublishLiveStreamPhotos(senderId, receiverId, releatedId, type, liveStreamPhotoIds) {
    var data = {
        senderId: senderId,
        receiverId: receiverId,
        releatedId: releatedId,
        type: type,
        liveStreamPhotoIds: liveStreamPhotoIds,
    };

    var strData = JSON.stringify(data);

    $.ajax({
        type: "POST",
        url: ContextPath + "Facebook/PublishLiveStreamPhotos",
        contentType: "application/json; charset=utf-8",
        data: strData
    })
    .done(function (result) {
        if (facebookCallBack) facebookCallBack(result);
    });
}

$(document).ready(function () {
    $('#fbPrevieModal').on('hidden', function () {
        afterFBPreviewClose();
    })

    if (typeof FB !== 'undefined' && FB) {
        var facebookAuthorizationStatus = $.jStorage.get('FacebookAuthorizationStatus');

        if (!facebookAuthorizationStatus) {
            FB.getLoginStatus(function (statusResponse) {
                if (statusResponse.status === 'connected') {
                    FB.login(function (response) {
                        var loggedIn = false;
                        if (response.authResponse) {
                            loggedIn = true;

                            $.jStorage.set('FacebookAuthorizationStatus', 'LoggedIn', 300000);//5 min.
                        }
                    });
                }
                else {
                    $.jStorage.set('FacebookAuthorizationStatus', 'NotLoggedIn', 300000); //5 min.
                }
            });
        }
    }
});

function twittUrl(url, msg) {

    if (msg && msg.length > 140) {
        msg = msg.substr(0, 140);
    }

    var twtLink = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(msg) + '&url=' + encodeURIComponent(url) + '&hashtags=pulsstory';

    var width = 550;
    var height = 420;

    var left = Math.round((screen.width / 2) - (width / 2));
    var top = 0;

    if (screen.height > height) {
        top = Math.round((screen.height / 2) - (height / 2));
    }

    window.open(twtLink, 'intent', 'scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);
}


;function showPageElement(what) {
    var elem = $( what);
    elem.show();

    $.scrollTo(elem, { duration: 1000, easing: 'swing' });
    return false;
}
var currentSlide = null;
var showExpanded = false;
$(document).ready(function () {
    $("#carousel-example-generic").carousel({ interval: 50000000 });
    $('#carousel-example-generic').on('slid.bs.carousel', function () {
        if (showExpanded) {
            currentSlide.show();
            $.scrollTo(currentSlide, { duration: 700, easing: 'swing' });
            showExpanded = false;
        }
    });
});

function showHelp2(what, what2, what3) {
    var obj = typeof what == 'object'
      ? what : document.getElementById(what);

    var triangle_down = typeof what2 == 'object'
      ? what2 : document.getElementById(what2);

    var triangle_up = typeof what3 == 'object'
      ? what3 : document.getElementById(what3);

    if (obj.style.display == 'none') {
        obj.style.display = 'block';
        triangle_down.style.display = 'none';
        triangle_up.style.display = 'block';
    }
    else {
        obj.style.display = 'none';
        triangle_down.style.display = 'block';
        triangle_up.style.display = 'none';
    }
    return false;
}

function showHelp3(elem) {
    var elem = $(elem).closest('.help_list_item');
    var content = elem.find('.helpContent');
    var upDyndol = elem.find('.upDyndol');
    var downDyndol = elem.find('.downDyndol');

    if (content.is(":visible")) {
        content.hide();
        upDyndol.show();
        downDyndol.hide();
    } else {
        content.show();
        upDyndol.hide();
        downDyndol.show();
    }
}


function openFooterModal(label, url, applyBindings, callBackFunction, callBackFunctionParams, onshowCallback, width) {

    var height = $(window).height() - 200;

    $('#footerInfoModalBody').html('<div style="height: ' + height + 'px"></div>');

    var modeal = $('#footerInfoModal');
    if (width) {
        modeal.data('width', width);
    }
    if (onshowCallback) {
        modeal.on('shown', onshowCallback);
    }
    modeal.modal('show');

    modeal.data('width', '900px');

    $('#footerInfoModalLabel').html(label);

    $.ajax({
        type: "POST",
        url: ContextPath + url
    })
    .done(function (result) {
        $('#footerInfoModalBody').html(result);
        
        if (applyBindings)
        {
            GeneralModel.CheckIfApply($('#footerInfoModalBody').children('div')[0]);                    
        }
        
        if (callBackFunction)
        {
            callBackFunction(callBackFunctionParams);
        }
    });
}

function menuClick(slideNr, contentId) {
    currentSlide = $("#" + contentId);
    showExpanded = true;
    $("#carousel-example-generic").carousel(slideNr);
    
    $('.navMenu > li > a').each(function (index) {
        $($('.navMenu > li > a')[index]).removeClass('selected');
    });
    
    $($('.navMenu > li > a')[slideNr]).addClass('selected');
         
    return false;
};DiscDatas = {
    PsTrainingTypes: [],
    ClubTrainingTypes: [],
    PsDisciplines: [],
    ClubDisciplines: []
}
function GetAllTypesModelPostProcess(serverModel) {
    if (serverModel) {
        var _sm = _(serverModel);
        _sm.each(function (elem) {
            if (elem.parent === undefined) {
                elem.subs = _sm.where({ parentId: parseInt(elem.id) });
            }
        });
    }
    return serverModel;
}
if (typeof (ClubId) == 'undefined') ClubId = 0;
cachedAjaxCall({
    type: "POST", url: ContextPath + "TrainingTypes/GetAllTypesModel",
    data: addTimestamToParams({ clubId: 0, disciplines: false, lang: translations.lang })
}, function (serverModel) {
    DiscDatas.PsTrainingTypes = GetAllTypesModelPostProcess(serverModel);
    if (!ClubId) {
        DiscDatas.ClubTrainingTypes = DiscDatas.PsTrainingTypes;
    }
}, 360000000
);
if (ClubId) {
    cachedAjaxCall({
        type: "POST", url: ContextPath + "TrainingTypes/GetAllTypesModel",
        data: addTimestamToParams({ clubId: ClubId || 0, disciplines: false, lang: translations.lang })
    }, function (serverModel) { DiscDatas.ClubTrainingTypes = GetAllTypesModelPostProcess(serverModel); }, 360000000
    );
}
cachedAjaxCall({
    type: "POST", url: ContextPath + "TrainingTypes/GetAllTypesModel",
    data: addTimestamToParams({ clubId: 0, disciplines: true, lang: translations.lang })
}, function (serverModel) {
    DiscDatas.PsDisciplines = GetAllTypesModelPostProcess(serverModel);
    DiscDatas.ClubDisciplines = DiscDatas.PsDisciplines;
}, 360000000
);

if (ClubId) {
    cachedAjaxCall({
        type: "POST", url: ContextPath + "TrainingTypes/GetAllTypesModel",
        data: addTimestamToParams({ clubId: ClubId || 0, disciplines: true, lang: translations.lang })
    }, function (serverModel) { DiscDatas.ClubDisciplines = GetAllTypesModelPostProcess(serverModel); }, 360000000
    );
}

function TrainingTypeControl(mainId, subId, typeChangeCallback, clubId, multi, baseCtrl, disciplines, multiSubs, allInstedNone, dummy, selectNoneIfNoChoise, addNoneSubType, noneIconCss) {

    var self = this;

    self.Enable = true;
    self.AvailableIds = null;

    if (typeof mainId === 'object' && mainId != null) {
        subId = mainId.subId;
        typeChangeCallback = mainId.typeChangeCallback;
        clubId = mainId.clubId;
        multi = mainId.multi;
        baseCtrl = mainId.baseCtrl;
        disciplines = mainId.disciplines;
        multiSubs = mainId.multiSubs;
        allInstedNone = mainId.allInstedNone;
        dummy = mainId.dummy;
        selectNoneIfNoChoise = mainId.selectNoneIfNoChoise;
        addNoneSubType = mainId.addNoneSubType;
        noneIconCss = mainId.noneIconCss;
        if (mainId && mainId.availableIds) {
            self.AvailableIds = ko.observableArray(mainId.availableIds);
        }
        mainId = mainId.mainId;
    }

    self.MainId = mainId;
    self.SubId = subId;

    self.initState = false;

    self.Disciplines = disciplines;

    self.Multi = multi;
    self.MultiSubs = multiSubs;

    self.TypeChangeCallback = typeChangeCallback;

    self.ClubId = clubId;

    self.AllInstedNone = allInstedNone;
    self.SelectNoneIfNoChoise = selectNoneIfNoChoise;
    self.Dummy = dummy;
    self.AddNoneSubType = addNoneSubType;

    self.NoneIconCss = noneIconCss;

    self.SelectedType = ko.observable(null);
    self.SelectedSubType = ko.observable(null);

    self.Types = ko.observableArray([]);
    self.SubTypes = ko.observableArray([]);

    //self.Exercises = ko.observableArray([]);

    self.SelectedTypes = ko.observableArray([]);
    self.SelectedSubTypes = ko.observableArray([]);

    self.Disabled = ko.observable(false);


    self.ShowCategories = ko.pureComputed(self.computeShowCategories, self);

    self.SelectedType.subscribe(self.subscribeSelectedType, self);


    self.SelectedTypes.subscribe(self.subscribeSelectedTypes, self);
    self.SelectedSubType.subscribe(self.subscribeSelectedSubType, self);

    self.SelectedTypeId = ko.computed(self.computeSelectedTypeId, self);
    self.SelectedSubTypeId = ko.computed(self.computeSelectedSubTypeId, self);

    self.SelectedTypeIconCss = ko.pureComputed(self.computeSelectedTypeIconCss, self);
    self.NameOfSelection = ko.pureComputed(self.computeNameOfSelection, self);

    self.SelectedTypeIds = ko.computed(self.computeSelectedTypeIds, self);

    if (self.TypeChangeCallback) {
        self.lastSubId = subId;

        self.SelectedSubType.subscribe(self.subscribeSelectedSubType2, self);
    }

    if (!self.Dummy) {
        if (!baseCtrl) {
            self.SelectValue(self.MainId, self.SubId, self.ClubId);
        } else {
            self.ClubId = baseCtrl.ClubId;
            self.Multi = baseCtrl.Multi;
            self.MultiSubs = baseCtrl.MultiSubs;

            self.Types.removeAll();
            var types = baseCtrl.Types();
            for (var i = 0, len = types.length; i < len; i++) {
                var t = types[i];
                self.Types.push(new TrainingTypeModel(t, self));
            }
        }
    }


    _.bindAll(self, 'SelectMultipleSubTypes', 'SetChangeCallback', 'SelectType', 'SelectNoneType', 'RemoveNoneType', 'RemoveNoneSubType',
        'SelectTypeById', 'GetTypeById', 'SelectSubType', 'SelectSubTypeById', 'DeselectAll', 'SelectFirst', 'SelectValue',
        'computeShowCategories', 'computeSelectedTypeId', 'computeSelectedSubTypeId','computeSelectedTypeIconCss','computeSelectedTypeIds','computeNameOfSelection');
}
TrainingTypeControl.prototype = {
    computeShowCategories: function () {
        var self = this;
        var subTypesLength = self.SubTypes().length;
        return self.AddNoneSubType ? subTypesLength > 2 : subTypesLength > 1;
    },
    subscribeSelectedType: function (newValue) {
        var self = this;
        if (!self.initState && newValue) {
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "TrainingTypes/GetSubTypesModel",
                data: addTimestamToParams({ typeId: newValue.Id || 0, clubId: self.ClubId, disciplines: self.Disciplines, lang: translations.lang, addNoneSubType: self.AddNoneSubType }),
                async: false
            }, function (serverModel) {
                self.initState = true;
                self.SubTypes.removeAll();
                for (var i = 0, len = serverModel.List.length; i < len; i++) {
                    var elem = serverModel.List[i];
                    elem = new TrainingTypeModel(elem, self, true, !serverModel.HasIcons);
                    self.SubTypes.push(elem);
                }
                self.SelectedSubType(self.SubTypes()[0]);
                self.SelectedSubTypes.removeAll();
                self.SelectedSubTypes.push(self.SubTypes()[0]);
                /*
                self.Exercises.removeAll();
                if (serverModel.Exercises) {
                    for (var i = 0, len = serverModel.Exercises.length; i < len; i++) {
                        self.Exercises.push(serverModel.Exercises[i]);
                    }
                }
                */
                if (self.TypeChangeCallback) {
                    self.TypeChangeCallback(newValue.Id, 0);
                }

                self.initState = false;
            },
            360000000 //5min
            );
        }
    },
    subscribeSelectedTypes: function (newValue) {
        var self = this;
        if (!self.initState) {
            if (self.TypeChangeCallback) {
                self.TypeChangeCallback(self);
            }
        }
    },
    subscribeSelectedSubType: function (newValue) {
        var self = this;
        if (!self.initState && self.Disciplines) {
            if (self.TypeChangeCallback) {
                self.TypeChangeCallback();
            }
        }
    },
    computeSelectedTypeId: function () {
        var self = this;
        if (self.SelectedType && self.SelectedType()) return self.SelectedType().Id;
        return 0;
    },
    computeSelectedSubTypeId: function () {
        var self = this;
        if (self.SelectedSubType && self.SelectedSubType()) return self.SelectedSubType().Id;
        return 0;
    },
    computeSelectedTypeIds: function () {
        var self = this;
        var types = self.SelectedTypes();
        if (!types.length) return [];
        return _.pluck(types, 'Id');
    },

    computeSelectedTypeIconCss: function () {
        var self = this;
        var st = self.SelectedType();
        if (st && st.Id) return self.SelectedType().IconCss();

        if (self.NoneIconCss) {
            return self.NoneIconCss;
        } else {
            if (self.AllInstedNone) return 'all';
            return 'none';
        }
    },
    computeNameOfSelection: function () {
        var self = this;
        var st = self.SelectedType();
        if (!st || !st.Id) return '';

        var name = st.Name;

        var sst = self.SelectedSubType();
        if (sst && sst.Name) {
            name += ' - ' + sst.Name;
        }

        return name;
    },

    subscribeSelectedSubType2: function (newValue) {
        var self = this;
        if (newValue && !self.initState && newValue.Id != self.lastSubId) {
            self.lastSubId = newValue.Id;
            self.TypeChangeCallback(self.SelectedType().Id, newValue.Id);
        }
    },
    subscribeSelectedSubTypes: function (newValue) {
        var self = this;
        if (!self.initState && self.Disciplines) {
            if (self.TypeChangeCallback) {
                self.TypeChangeCallback();
            }
        }
    },


    SelectMultipleSubTypes: function (typeId, subTypesArr) {
        var self = this;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingTypes/GetSubTypesModel",
            data: addTimestamToParams({ typeId: typeId || 0, clubId: self.ClubId, disciplines: self.Disciplines, lang: translations.lang, addNoneSubType: self.AddNoneSubType }),
            async: false
        }, function (serverModel) {
            self.initState = true;
            self.SubTypes.removeAll();

            self.SelectTypeById(typeId);

            self.SelectedSubTypes.removeAll();
            for (var i = 0, len = serverModel.List.length; i < len; i++) {
                var elem = serverModel.List[i];
                elem = new TrainingTypeModel(elem, self, true, !serverModel.HasIcons);
                self.SubTypes.push(elem);
                if ($.inArray(elem.Id, subTypesArr) >= 0) self.SelectedSubTypes.push(elem);
            }
            /*
            self.Exercises.removeAll();
            if (serverModel.Exercises) {
                for (var i = 0, len = serverModel.Exercises.length; i < len; i++) {
                    self.Exercises.push(serverModel.Exercises[i]);
                }
            }
            */
            if (self.TypeChangeCallback) {
                self.TypeChangeCallback(typeId, 0);
            }

            self.initState = false;
        },
            36000000 //1h
            );
    },


    SetChangeCallback: function (typeChangeCallback) {
        var self = this;
        self.TypeChangeCallback = typeChangeCallback;

        if (self.MultiSubs) {
            self.SelectedSubTypes.subscribe(self.subscribeSelectedSubTypes, self);
        }
    },

    SelectType: function (type) {
        var self = this;
        if (self.Multi) {
            if (type.Selected()) {
                self.SelectedTypes.remove(type);

                if (self.SelectNoneIfNoChoise) self.SelectNoneType();
            } else {
                if (self.SelectNoneIfNoChoise) {
                    self.RemoveNoneType();
                    if (type.Id == 0) {
                        self.SelectedTypes.removeAll();
                    }
                }
                self.SelectedTypes.push(type);
            }
        } else {
            self.SelectedType(type);
        }
    },

    SelectNoneType: function () {
        var self = this;
        if (self.SelectedTypes().length == 0) {
            var noneExists = false;
            for (var i = 0; i < self.SelectedTypes().length; i++) {
                if (self.SelectedTypes()[i].Id == 0) {
                    noneExists = true;
                    break;
                }
            }
            if (!noneExists) {
                var noneType = self.GetTypeById(0);

                self.SelectedTypes.push(noneType);
            }
        }
    },

    RemoveNoneType: function () {
        var self = this;
        for (var i = 0; i < self.SelectedTypes().length; i++) {
            if (self.SelectedTypes()[i].Id == 0) {
                self.SelectedTypes.remove(self.SelectedTypes()[i]);
            }
        }
    },

    RemoveNoneSubType: function () {
        var self = this;
        for (var i = 0; i < self.SelectedSubTypes().length; i++) {
            if (self.SelectedSubTypes()[i].Id == 0) {
                self.SelectedSubTypes.remove(self.SelectedSubTypes()[i]);
            }
        }
    },

    SelectTypeById: function (id) {
        var self = this;
        var sels = self.Types();
        for (var i = 0, len = sels.length; i < len; i++) {
            if (sels[i].Id == id) {
                self.SelectType(sels[i]);
                return true;
            }
        }
        return false;
    },
    GetTypeById: function (id, afterReload) {
        var self = this;
        var sels = self.Types();
        for (var i = 0, len = sels.length; i < len; i++) {
            if (sels[i].Id == id) {
                return sels[i];
            }
        }
        return null;
    },

    SelectSubType: function (type) {
        var self = this;
        if (self.MultiSubs) {
            if (type.Selected() && self.SubTypes().length > 1) {
                self.SelectedSubTypes.remove(type);
            } else {
                if (self.SelectNoneIfNoChoise) {
                    self.RemoveNoneSubType();
                    if (type.Id == 0) {
                        self.SelectedSubTypes.removeAll();
                    }
                }

                self.SelectedSubTypes.push(type);
            }
        } else {
            self.SelectedSubType(type);
        }
    },
    SelectSubTypeById: function (id, parentId) {
        var self = this;
        if (self.SelectedType() && (!parentId || self.SelectedType().Id == parentId)) {
            var sels = self.SubTypes();
            for (var i = 0, len = sels.length; i < len; i++) {
                if (sels[i].Id == id) {
                    self.SelectSubType(sels[i]);
                    return true;
                }
            }
        }
        return false;
    },

    DeselectAll: function () {
        var self = this;
        self.SelectedTypes.removeAll();
        self.SelectedSubTypes.removeAll();
        self.SelectedSubType(null);
        self.SelectedType(null);
        if (self.SelectNoneIfNoChoise) self.SelectNoneType();
    },

    SelectFirst: function () {
        var self = this;
        self.DeselectAll();
        if (self.Types().length) {
            if (self.Multi) {
                self.SelectedTypes.push(self.Types()[0]);
            } else {
                self.SelectedType(self.Types()[0]);
            }
        }
    },

    SelectValue: function (mainId, subId, clubId, callback, fireOnChange) {
        var self = this;

        if (mainId == null) mainId = self.SelectedTypeId();
        if (subId == null) subId = self.SelectedSubTypeId();

        self.ClubId = clubId;
        self.initState = true;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingTypes/GetTypesModel",
            data: addTimestamToParams({ mainId: mainId, subId: subId, clubId: clubId, disciplines: self.Disciplines, lang: translations.lang, addNoneSubType: self.AddNoneSubType }),
            async: false
        },
        function (serverModel) {
            self.Types.removeAll();
            if (serverModel.Main && serverModel.Main.List) {
                for (var i = 0, len = serverModel.Main.List.length; i < len; i++) {
                    var t = serverModel.Main.List[i];
                    var mT = new TrainingTypeModel(t, self);
                    self.Types.push(mT);
                    if (t.Id == mainId) {
                        self.SelectedType(mT);
                    }
                }
            }
            self.SubTypes.removeAll();
            var selectedSub = false;
            if (serverModel.Sub && serverModel.Sub.List) {
                for (var i = 0, len = serverModel.Sub.List.length; i < len; i++) {
                    var t = new TrainingTypeModel(serverModel.Sub.List[i], self, true, !serverModel.Sub.HasIcons);
                    self.SubTypes.push(t);
                    if (t.Id == subId || (subId == 0 && i == 0)) {
                        selectedSub = true;
                        self.SelectedSubType(t);
                    }
                }
            }
            if (!selectedSub && self.SubTypes().length) {
                self.SelectedSubType(self.SubTypes()[0]);
            }
            /*
            self.Exercises.removeAll();
            if (serverModel.Sub && serverModel.Sub.Exercises) {
                for (var i = 0, len = serverModel.Sub.Exercises.length; i < len; i++) {
                    self.Exercises.push(serverModel.Sub.Exercises[i]);
                }
            }*/
            self.initState = false;

            if (fireOnChange && self.TypeChangeCallback) {
                self.TypeChangeCallback();
            }

            if (callback) callback();
        }, 360000000);
    }

}

function TrainingTypeModel(serverModel, parent, subType, hideIcon, allIcon) {
    var self = this;
    self.Parent = parent;
    self.SubType = subType;
    self.HideIcon = hideIcon;

    if (serverModel) {
        self.ClubId = serverModel.ClubId;
        self.IconId = serverModel.IconId;
        self.Id = serverModel.Id;
        self.ParentId = serverModel.ParentId;
        self.ParamsVisibility = serverModel.ParamsVisibility;
        self.ShortName = serverModel.ShortName;
        self.Name = ((self.Parent && self.Parent.AllInstedNone && serverModel.Id <= 0) || allIcon) ? translations.All : serverModel.Name;
        self.Text = ((self.Parent && self.Parent.AllInstedNone && serverModel.Id <= 0) || allIcon) ? translations.All : serverModel.Text;
        self.Value = serverModel.Value;
        self.Other = serverModel.Other;
        self.ConectedDisciplines = serverModel.ConectedDisciplines;

        if ((self.Parent && self.Parent.AllInstedNone && serverModel.Id <= 0) || allIcon) {
            self.Icon = 'all';
        } else {
            self.Icon = serverModel.Icon;
        }
    } else {
        self.ClubId = 0;
        self.IconId = 0;
        self.Id = 0;
        self.ParentId = 0;
        self.ParamsVisibility = null;
        self.ShortName = allIcon ? translations.All : translations.None;
        self.Name = allIcon ? translations.All : translations.None;
        self.Text = allIcon ? translations.All : translations.None;
        self.Value = 0;
        self.ConectedDisciplines = [];

        if ((self.Parent && self.Parent.AllInstedNone) || allIcon) {
            self.Icon = 'all';
        } else {
            self.Icon = translations.None;
        }
    }
    self._parsedParamsVisibility = null;

    self.Selected = ko.pureComputed(self.computeSelected, self);

    self.IconCss = ko.pureComputed(self.computeIconCss, self);

    self.IconCssUnselected = ko.pureComputed(self.computeIconCssUnselected, self);

    self.IconTextContent = ko.pureComputed(self.computeIconTextContent, self);

    _.bindAll(self, 'Select');
}
TrainingTypeModel.prototype = {
    computeSelected: function () {
        var self = this;
        if (!self.Parent) return false;

        if (!self.SubType) {
            if (self.Parent.Multi) {
                var sels = self.Parent.SelectedTypes();
                for (var i = 0, len = sels.length; i < len; i++) {
                    if (sels[i].Id == self.Id) return true;
                }
                return false;
            } else {
                return self.Parent.SelectedType() != null && self.Parent.SelectedType().Id == self.Id;
            }
        } else {
            if (self.Parent.MultiSubs) {
                var sels = self.Parent.SelectedSubTypes();
                for (var i = 0, len = sels.length; i < len; i++) {
                    if (sels[i].Id == self.Id) return true;
                }
                if (self.Parent.Disciplines
                    && sels.length == 0
                    && self.Parent.SubTypes().length > 0
                    && self.Parent.SubTypes()[0].Id == self.Id) {
                    return true;
                }
                return false;
            } else {
                return self.Parent.SelectedSubType() != null && self.Parent.SelectedSubType().Id == self.Id;
            }
        }
    },
    computeIconCss: function () {
        var self = this;
        if (self.HideIcon) {
            return self.Selected() ? "plainText selected" : 'plainText';
        } else {
            if (self.Parent && self.Parent.Disabled()) {
                return self.Selected() ? self.Icon + " selected disabled" : self.Icon;
            }
            else {
                return self.Selected() ? self.Icon + " selected" : self.Icon;
            }
        }
    },
    computeIconCssUnselected: function () {
        var self = this;
        if (self.HideIcon) {
            return 'plainText';
        } else {
            return self.Icon;
        }
    },
    computeIconTextContent: function () {
        var self = this;
        if (self.HideIcon) {
            return self.Text;
        } else {
            return '';
        }
    },
    Select: function () {
        var self = this;
        if (self.Parent.Enable) {
            if (!self.SubType) {
                self.Parent.SelectType(self);
            } else {
                self.Parent.SelectSubType(self);
            }
        }
    },
    GetParsedParamsVisibility: function () {
        var self = this;
        if (!self._parsedParamsVisibility) {
            self._parsedParamsVisibility = new ParamsVisibility(self.ParamsVisibility);
        }
        return self._parsedParamsVisibility;
    }
}


var CommonTrainingTypesLibControl = new TrainingTypeControl({
    mainId: 0,
    subId: 0,
    typeChangeCallback: null,
    clubId: 0,
    multi: false,
    baseCtrl: null,
    disciplines: false,
    multiSubs: false,
    allInstedNone: true,
    dummy: false,
    selectNoneIfNoChoise: true
});
var CommonDisciplineLibControl = new TrainingTypeControl({
    mainId: 0,
    subId: 0,
    typeChangeCallback: null,
    clubId: 0,
    multi: false,
    baseCtrl: null,
    disciplines: true,
    multiSubs: false,
    allInstedNone: true,
    dummy: false,
    selectNoneIfNoChoise: true
});

;

function ExerciseTypeModel(parent, readOnly) {
    var self = this;
    self.Parent = parent;
    self.readOnly = readOnly;

    if (!readOnly) {
        self.Id = ko.observable(0);
        self.TrainingTypeId = ko.observable(0);
        self.Unit = ko.observable('');
        self.LoadUnit = ko.observable('');
        self.RegenerationUnit = ko.observable('');
        self.RelaxUnit = ko.observable('');

        self.NameTransAble = new translateAble.Text();
        self.DescriptionTransAble = new translateAble.Text();

        self.Name = self.NameTransAble.someValue;
        self.Description = self.DescriptionTransAble.someValue;

        self.Shortcut = ko.observable('');
        self.Url = ko.observable('');
        self.Tags = ko.observableArray([]);
        self.FileIds = ko.observableArray([]);

        self.FileIndex = ko.observable(0);

        self.Repeats = ko.observable('');
        self.Value = ko.observable('');
        self.Series = ko.observable('');
        self.Pace = ko.observable('');
        self.Relax = ko.observable('');
        self.Cadence = ko.observable('');
        self.Regeneration = ko.observable('');

        self.ValueChangeMode = ko.observable('');
        self.LoadChangeMode = ko.observable('');

        self.Intervals = ko.observable(false);
    }
    self.ServerModel = null;

    if (!readOnly) {
        self.Intervals.subscribe(function (newValue) {
            self.Repeats('');
            self.Regeneration('');
        });

        self.openDelete = function () {
            $('#removeExerciseModal').modal('show');
        }
        self.coreDelete = function (data, event) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingTypes/DeleteExercise",
                data: { id: self.Id() }
            })
                 .done(function (result) {
                     if (result) {
                         $('#removeExerciseModal').modal('hide');
                         setTimeout(function () { $('#trainingExerciseModal').modal('hide'); }, 500);
                         $(event.target).parents('.modal ').find('button[data-dismiss="modal"]').first().click();
                         parent.Exercises.remove(self);
                         parent.SelectedExercise().Id(0);
                     }
                 });
        }

        self.Edit = function () {
            if (!self.ServerModel) {
                self.ServerModel = self.getClearData();
            }
            self.Parent.SelectedExercise(self);
            $('#trainingExerciseModal').modal('show');
            self.inintUpload();
        }


        self.getClearData = function () {
            var _Tags = [];
            var _Tags_Iter = self.Tags();
            for (var i = 0, len = _Tags_Iter.length; i < len; i++) {
                var tag = _Tags_Iter[i]();
                if (tag) {
                    _Tags.push(tag);
                }
            }

            var fIds = [];
            var files = self.FileIds();
            for (var i = 0, len = files.length; i < len; i++) {
                var v = files[i]();
                if (v > 0) {
                    fIds.push(v);
                }
            }

            var data = {
                Id: self.Id(),
                Name: self.NameTransAble.getClearData(),
                TrainingTypeId: self.TrainingTypeId(),
                Unit: self.Unit(),
                LoadUnit: self.LoadUnit(),
                Description: self.DescriptionTransAble.getClearData(),
                Shortcut: self.Shortcut(),
                Url: self.Url(),
                Tags: _Tags,
                PhotoIds: fIds,
                Repeats: self.Repeats(),
                Value: self.Value(),
                Series: self.Series(),
                Pace: self.Pace(),
                Relax: self.Relax(),
                Cadence: self.Cadence(),
                RegenerationUnit: self.RegenerationUnit(),
                RelaxUnit: self.RelaxUnit(),
                Regeneration: self.Regeneration(),
                ValueChangeMode: self.ValueChangeMode(),
                LoadChangeMode: self.LoadChangeMode(),
                Intervals: self.Intervals()
            }
            return data;
        };

        self.save = function () {
            if (!self.InitMode) {
                var data = self.getClearData();
                var strData = JSON.stringify(data);
                $.ajax({
                    type: "POST",
                    url: ContextPath + "TrainingTypes/SetExercise",
                    dataType: "json",
                    contentType: "application/json; charset=utf-8",
                    data: strData
                })
                 .done(function (result) {
                     $('#trainingExerciseModal').modal('hide');
                     self.initByModel(result);
                     //ShowSavedAlert();
                 });
            }
        }
        self.CancelChanges = function () {
            if (self.ServerModel) {
                self.initByModel(self.ServerModel);
            }
        }
        self.cancel = function () {
            self.CancelChanges();
            $('#trainingExerciseModal').modal('hide');
        }

        self.addNext = function () {
            if (!self.InitMode) {
                var data = self.getClearData();
                var strData = JSON.stringify(data);
                $.ajax({
                    type: "POST",
                    url: ContextPath + "TrainingTypes/SetExercise",
                    dataType: "json",
                    contentType: "application/json; charset=utf-8",
                    data: strData
                })
                 .done(function (result) {
                     var elem = new ExerciseTypeModel(self.Parent);
                     elem.initByModel(result);
                     elem.Id(0);
                     elem.Name(translations.CopyLabel + ' - ' + elem.Name());
                     self.Parent.Exercises.push(elem);
                     self.Parent.SelectedExercise(elem);
                 });
            }
        }

        self.AddTag = function () {
            self.Tags.push(ko.observable(''));
        }

        self.addFile = function () {
            self.FileIds.push(ko.observable(0));
        }
        self.addFile();
        self.removeFile = function (id) {
            if (self.FileIds().length > 1) {
                removeKoItem(self.FileIds, id);
            }
            else {
                self.FileIds()[0](0);
            }
        }
        self.uploadBtnClick = function (fileIndex) {
            self.FileIndex(fileIndex);
            $('#exerciseFileupload input').click();
        }


        self.inintUpload = function () {

            $("#exerciseFileupload").fileupload({
                dataType: 'json',
                add: function (e, data) {
                    data.submit();
                },
                done: function (e, data) {
                    self.FileIds()[self.FileIndex()](data.result[0].url);
                }
            });
        }
    }

    self.SpeedUnit = ko.pureComputed(self.SpeedUnit_Computed, self);
    self.ProperLoadUnit = ko.pureComputed(self.ProperLoadUnit_Computed, self);
    self.AvailableLoadUnits = ko.pureComputed(self.AvailableLoadUnits_Computed, self);

    _.bindAll(self
            , "initByModel"
            );
}
ExerciseTypeModel.prototype = {
    initByModel: function (serverModel) {
        var self = this;

        if (serverModel) {
            self.ServerModel = serverModel;

            if (!self.readOnly) {
                self.Id(serverModel.Id);
                self.TrainingTypeId(serverModel.TrainingTypeId);
                self.NameTransAble.initByModel(serverModel.NameTransAble);
                self.Unit(serverModel.Unit);
                self.LoadUnit(serverModel.LoadUnit);
                self.RegenerationUnit(serverModel.RegenerationUnit);
                self.RelaxUnit(serverModel.RelaxUnit);

                self.DescriptionTransAble.initByModel(serverModel.DescriptionTransAble);

                self.Shortcut(serverModel.Shortcut);
                self.Url(serverModel.Url);
                koArrayCopy(serverModel.Tags, self.Tags, false, true);
                if (self.Tags().length == 0) {
                    self.AddTag();
                }
                koArrayCopy(serverModel.PhotoIds, self.FileIds, false, true);
                if (self.FileIds().length == 0) {
                    self.addFile();
                }

                self.text = self.Name();
                self.id = self.Id();

                self.Intervals(serverModel.Intervals); //przed repeats i regeneration

                self.Repeats(serverModel.Repeats);
                self.Value(serverModel.Value);
                self.Series(serverModel.Series);
                self.Pace(serverModel.Pace);
                self.Relax(serverModel.Relax);
                self.Cadence(serverModel.Cadence);
                self.Regeneration(serverModel.Regeneration);

                self.ValueChangeMode(serverModel.ValueChangeMode);
                self.LoadChangeMode(serverModel.LoadChangeMode);
            } else {
                self = $.extend(self, serverModel);

                self.ClubLogoUrl = (!self.ClubLogoId) ? '' : ContextPath + "Image/GetImage?id=" + self.ClubLogoId;

                self.text = self.Name;
                self.id = self.Id;

                self.FileIds = serverModel.PhotoIds;

            }
        }
    }

    , AvailableLoadUnits_Computed: function () {
        var self = this;

        var units = trainingExerciseManager.LoadUnits.slice();
        /*
        var newUnits = []
            for (var i = 0; i < units.length; i++) {
                var u = units[i];
                self._availableLoadUnits.push({ text:  ko.observable(u.text), value: u.value });
            }       
*/

        var speed = _.findWhere(units, { value: 'speed' });
        if (speed) {
            speed.text = self.SpeedUnit();
        }

        return units;
    }
    , SpeedUnit_Computed: function () {
        var self = this;

        var defUnit = 'km/h';

        var pv = GetParamsVisibility(ko.unwrap(self.TrainingTypeId));

        if (!pv) return defUnit;

        if (pv.ShowPace) {
            return pv.PaceParams.unit;
        } else {
            return defUnit;
        }
    }
    , ProperLoadUnit_Computed: function () {
        var self = this;

        var u = self.LoadUnit();
        if (u == 'speed') return self.SpeedUnit();

        return u;
    }
}

function TrainingExerciseManager() {
    var self = this;
    self.Pattern = ko.observable('');
    self.OnlyWeighted = false;
    self.TrainingTypeId = ko.observable(0);

    self.ExerciseParent = self;
    self.Exercises = ko.observableArray([]);
    self.ExerciseSeries = ko.observableArray([]);


    //self.SelectExerciseFnc = null;

    self.SelectedForInfo = ko.observable(null);

    self.SearchForExcerciseTypes = function (callback) {
        //if (self.Pattern() && self.Pattern().length > 2) {
        var patt = self.Pattern();
        if (!patt || patt.length < 2) {
            patt = '';
        }
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingTypes/SearchForExcerciseTypes",
            data: { typeId: self.TrainingTypeId(), pattern: patt, onlyWeighted: self.OnlyWeighted }
        }, function (serverModel) {
            self.Exercises.removeAll();
            if (serverModel) {
                /*
                var elem = new ExerciseTypeModel(self.ExerciseParent, true);
                elem.Name = translations.None;
                elem.Id = 10;
                self.Exercises.push(elem);*/

                for (var i = 0, len = serverModel.length; i < len; i++) {
                    var elem = new ExerciseTypeModel(self.ExerciseParent, true);
                    elem.initByModel(serverModel[i]);
                    self.Exercises.push(elem);
                }
            }
            if (callback) callback();
        }, 300000);
        //}
        self.GetInfoLayout();
    }

    self.searchTimeOut = null;
    self.DefferedSeach = function (callback) {
        if (self.searchTimeOut) clearTimeout(self.searchTimeOut);
        self.searchTimeOut = setTimeout(function () { self.SearchForExcerciseTypes(callback); }, 650);
    }

    self.GetInfoLayout = function () {
        if (!self.InfoLayout) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingTypes/SingleStaticExcerciseType"
            })
            .done(function (result) {
                self.InfoLayout = result;
            });
        }
    }


    self.validBySpeedUnit = function (value, exeObj) {
        if (!value) return true;

        var unit = exeObj.SpeedUnit();

        if (!unit) return true;
        var patt;
        unit = unit.toLowerCase();

        if (unit.startsWith("km/")) {
            patt = new RegExp(/^\d+([,.]\d+)?$/);
        } else if (unit.startsWith("min/")) {
            patt = new RegExp(/^(\d+):(\d){2}/);
        } else if (unit.startsWith("s/")) {
            patt = new RegExp(/^(\d+)/);
        }

        if (!patt) return true;
        return patt.test(value);
    }

    self.ValueUnits = [{ text: '', value: '', placeholder: '' },
        { text: 'm', value: 'm', placeholder: '0', ValidRegEx: '^\\d+([\\.,]\\d+)?$' },
        { text: 'km', value: 'km', placeholder: '0', ValidRegEx: '^\\d+([\\.,]\\d+)?$' },
        { text: translations.ShortTimeFormatG, value: 'mm:ss', placeholder: '00:00', ValidRegEx: '^(\\d+):(\\d){2}$' },
        { text: translations.TimeFormatG, value: 'gg:mm', placeholder: '00:00', ValidRegEx: '^(\\d+):(\\d){2}$' },
        { text: translations.STimeFormatG, value: 'gg:mm:ss', placeholder: '00:00:00', ValidRegEx: '^(\\d+):(\\d){2}:(\\d){2}$' },
        { text: translations.excecution, value: 'exe', placeholder: '', ValidRegEx: '^\\d+$' },
        { text: translations.achievement, value: 'achieve', placeholder: '00:00', ValidRegEx: '^(\\d+):(\\d){2}$' },
        { text: translations.Chart, value: 'chart', placeholder: '' },
    ];
    self.LoadUnits = [
        { text: '-', value: '', ico: 'none', placeholder: '', allowMultiMode: false },
        { text: 'speed', value: 'speed', ico: 'pace', placeholder: '', allowMultiMode: true, ValidFnc: self.validBySpeedUnit },
        { text: translations.Speed.toLowerCase(), value: 'speedSpeed', ico: 'pace', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+([\\.,]\\d+)?$', visible: false },
        { text: translations.PaceZoneExceLoad, value: 'paceZone', ico: 'pace', placeholder: '', allowMultiMode: false },
        { text: translations.ExactHrExeLoad, value: 'HR', ico: 'hr', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.strefaHr, value: 'strefaHR', ico: 'hr', placeholder: '', allowMultiMode: false },
        { text: '%LTHR (%FTHR)', value: '%LTHR', ico: 'hr', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.HRmaxPerc, value: '%hr', ico: 'hr', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.PowerExceLoad, value: 'W', ico: 'power', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.strefaFTP, value: 'strefaFTP', ico: 'power', placeholder: '', allowMultiMode: false },
        { text: '%FTP', value: '%FTP', ico: 'power', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.ExceWeight, value: 'kg', ico: 'weight', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.RmPerc, value: 'RM', ico: 'weight', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: translations.ExceCandence, value: 'rpm', ico: 'cadence', placeholder: '0', allowMultiMode: true, ValidRegEx: '^\\d+$' },
        { text: 'RPE (6-20)', value: 'RPE', ico: 'face', placeholder: '0', allowMultiMode: false, ValidFnc: function (val) { var pat = /^\d+$/; if (!pat.test(val)) return false; var v = parseInt(val); return v == val && v >= 6 && v <= 20; } },
        { text: 'KOW (1-10)', value: 'KOW', ico: 'face', placeholder: '0', allowMultiMode: false, ValidFnc: function (val) { var pat = /^\d+$/; if (!pat.test(val)) return false; var v = parseInt(val); return v == val && v >= 1 && v <= 10; } },
    ];
    self.RegenerationUnits = [
       { text: '', value: '', placeholder: '' },
       { text: 'm', value: 'm', placeholder: '0', ValidRegEx: '^\\d+([\\.,]\\d+)?$' },
       { text: 'km', value: 'km', placeholder: '0', ValidRegEx: '^\\d+([\\.,]\\d+)?$' },
       { text: translations.ShortTimeFormatG, value: 'mm:ss', placeholder: '00:00', ValidRegEx: '^(\\d+):(\\d){2}$' },
       { text: translations.ValuePerc, value: '%value', placeholder: '0', ValidRegEx: '^\\d+([\\.,]\\d+)?$' },
       { text: translations.HRmaxPerc, value: '%hr', placeholder: '0', ValidRegEx: '^\\d+$' },
       { text: '%LTHR (%FTHR)', value: '%LTHR', placeholder: '0', ValidRegEx: '^\\d+$' },
    ];
    self.RelaxUnits = [
       { text: '', value: '', placeholder: '' },
       { text: translations.ShortTimeFormatG, value: 'mm:ss', placeholder: '00:00', ValidRegEx: '^(\\d+):(\\d){2}$' },
       { text: translations.HRmaxPerc, value: '%hr', placeholder: '0', ValidRegEx: '^\\d+$' },
       { text: '%LTHR (%FTHR)', value: '%LTHR', placeholder: '0', ValidRegEx: '^\\d+$' },
    ];
    self.LoadModes = [
       { text: 'fixed', value: 'fixed' },
       { text: 'go up to', value: 'upto' },
       { text: 'go down to', value: 'downto' },
       { text: 'between', value: 'between' },
       { text: '...', value: 'series' },
    ];

    //1. aktywna regeneracja
    //2. próg tlenowy
    //3. tempo – subpróg mleczanowy
    //4. próg mleczanowy – wydolność tlenowa
    //5. wydolność beztlenowa

    self.ZonesHr = [
            { zoneId: '1', text: '[1] ' + translations.AktywnaRegeneracja.toLowerCase(), value: '1.', color: '#eee' },
            { zoneId: '8', text: '[2] ' + translations.ProgTlenowy.toLowerCase(), value: '2.', color: '#97C672' },
            { zoneId: '7', text: '[3] ' + translations.SubprogMleczanowy.toLowerCase(), value: '3.', color: '#F8B133' },
            { zoneId: '6', text: '[4] ' + translations.ProgMleczanowy.toLowerCase(), value: '4.', color: '#E7412B' },
            { zoneId: '5', text: '[5] ' + translations.WydolnoscBeztlenowa.toLowerCase(), value: '5.', color: '#454443' },
    ];

    //1. aktywna regeneracja (active revovery)
    //2. próg tlenowy (endurance)
    //3. tempo (tempo)
    //4. próg mleczanowy (lactate threshold)
    //5. VO2max
    //6. wydolność beztlenowa (anaerobic capacity)
    //7. moc nerwowo-mięśniowa (neuromuscular power)
    self.ZonesFtp = [
                { zoneId: '1000', text: '[1] ' + translations.powerZone_1.toLowerCase(), value: '1.', color: '#eee', range: '<55' },
                { zoneId: '1001', text: '[2] ' + translations.powerZone_2.toLowerCase(), value: '2.', color: '#eee', range: '56-75' },
                { zoneId: '1002', text: '[3] ' + translations.powerZone_3.toLowerCase(), value: '3.', color: '#eee', range: '76-90' },
                { zoneId: '1003', text: '[4] ' + translations.powerZone_4.toLowerCase(), value: '4.', color: '#eee', range: '91-105' },
                { zoneId: '1004', text: '[5] ' + translations.powerZone_5.toLowerCase(), value: '5.', color: '#eee', range: '106-120' },
                { zoneId: '1005', text: '[6] ' + translations.powerZone_6.toLowerCase(), value: '6.', color: '#eee', range: '121-150' },
                { zoneId: '1006', text: '[7] ' + translations.powerZone_7.toLowerCase(), value: '7.', color: '#eee', range: 'MAX' },
    ];

    self.BasePaces = [
          { Name: translations.TempospokojneBS, Shortcut: 'BS', DiaryTypeId: 42, AdditionalParam: null, Value: null, Pace: null }
        , { Name: translations.TempodlugiegowysciguM, Shortcut: 'M', DiaryTypeId: 43, AdditionalParam: null, Value: null, Pace: null }
        , { Name: translations.TempoprogoweP, Shortcut: 'P', DiaryTypeId: 44, AdditionalParam: null, Value: null, Pace: null }
        , { Name: translations.InterwalyI, Shortcut: 'I', DiaryTypeId: 45, AdditionalParam: null, Value: null, Pace: null }
        , { Name: translations.RytmyR, Shortcut: 'R', DiaryTypeId: 46, AdditionalParam: null, Value: null, Pace: null }
    ];

    //#region preview
    self.InfoTypeId = null;
    self.PrepareInfoTimeout = null;
    self.InfoLayout = null;
    self.PrepareInfo = function (sourceElem, typeId) {
        self.GetInfoLayout();
        if (self.InfoTypeId !== typeId) {
            self.InfoTypeId = typeId;
            if (self.PrepareInfoTimeout) clearTimeout(self.PrepareInfoTimeout);
            if (self.CancelPrepareInfoTimeout) clearTimeout(self.CancelPrepareInfoTimeout);
            setTimeout(function () { self.ShowSmallInfo(sourceElem, typeId); }, 800);
        }
    }
    self.ShowSmallInfo = function (sourceElem, typeId) {
        var exeType = _(self.Exercises()).findWhere({ id: typeId });
        if (exeType) {
            var result = '<div id="SmallExerciseTypeInfo" class="preview exerciseInfoModal" onmouseover="trainingExerciseManager.CancelCancelPrepareInfo();"  onmouseleave="trainingExerciseManager.CancelPrepareInfo();">';

            if (self.InfoLayout) {
                result += self.InfoLayout;
            } else {
                result += "<img src='" + ContextPath + 'Content/images/ajax-loader.gif' + "' />";
            }

            result += '</div>';

            $('#SmallExerciseTypeInfo').remove();
            $('body').append(result);

            var infoElem = $('#SmallExerciseTypeInfo');

            ko.applyBindings(exeType, infoElem[0]);

            sourceElem = $(sourceElem);
            var srcPos = sourceElem.offset();

            var parent = sourceElem.closest('.select2-results');
            width = parent.width() - 100;

            infoElem.css({ top: srcPos.top, left: srcPos.left - width - 18, width: width });

        }
    }
    self.CancelPrepareInfoTimeout = null;
    self.CancelPrepareInfo = function () {
        if (self.CancelPrepareInfoTimeout) clearTimeout(self.CancelPrepareInfoTimeout);
        if (self.PrepareInfoTimeout) clearTimeout(self.PrepareInfoTimeout);
        self.CancelPrepareInfoTimeout = setTimeout(function () {
            self.InfoTypeId = 0;
            $('#SmallExerciseTypeInfo').remove();
        }, 100);
    }
    self.CancelCancelPrepareInfo = function () {
        if (self.CancelPrepareInfoTimeout) clearTimeout(self.CancelPrepareInfoTimeout);
    }

    //#endregion

    self.OpenExeInfoExternal = function (typeId) {

        loadAndBindTemplate({
            simpleContainer: 'exerciseInfoModalContainer',
            existingElem: '#exerciseInfoModal',
            ajaxPath: 'TrainingTypes/GetGexerciseInfoModal',
            callback: function () {
                $.ajax({
                    type: "POST",
                    url: ContextPath + "TrainingTypes/GetTrainingExcerciseType",
                    data: { typeId: typeId }
                })
                .done(function (det) {
                    if (det) {
                        if (!det.FileIds) {
                            det.FileIds = det.PhotoIds;
                        }
                        trainingExerciseManager.SelectedForInfo(det);
                        $('#exerciseInfoModal').modal('show');
                    }
                });
            }
        });
    }
    self.OpenExeInfo = function () {
        loadAndBindTemplate({
            simpleContainer: 'exerciseInfoModalContainer',
            existingElem: '#exerciseInfoModal',
            ajaxPath: 'TrainingTypes/GetGexerciseInfoModal',
            callback: function () {
                $('#exerciseInfoModal').modal('show');
            }
        });
    }

    self.BuildSeries = function (Exercises) {
        var mgr = new ExerciseSerieManager();
        mgr.init(Exercises);
        return mgr;
    }
}
var trainingExerciseManager = new TrainingExerciseManager();
GeneralModel.addProperty("TrainingExerciseManager", trainingExerciseManager);

TrainingExerciseModel = function (parent) {
    var self = this;
    self.Parent = parent;
    self.initMode = true;
    self.serverModel = null;

    self.Exercises = false;

    self.syncTTypeWithExcerType = true;
    self.onlyWeighted = false;

    self.EditMode = ko.observable(false);
    self.EditMode.subscribe(function (newValue) {
        $('input').blur();
        $('textarea').blur();
    });

    self.TypeManager = new TrainingTypeControl({
        subId: 0,
        typeChangeCallback: null,
        clubId: 0,
        multi: false,
        baseCtrl: null,
        disciplines: false,
        multiSubs: false,
        allInstedNone: true,
        dummy: false,
        selectNoneIfNoChoise: true,
        mainId: self.Parent && self.Parent.TypeManager ? self.Parent.TypeManager.SelectedTypeId() : 0
    });
    self.ParamsVisibility = ko.observable(new ParamsVisibility(''));
    self.TrainingType = ko.computed(self.TrainingType_Computed, self);

    self.ExerciseTypeId = ko.observable(0);
    self.Id = 0;
    self.Repeats = ko.observable('');
    self.Value = ko.observable('');
    self.ValueReCalculated = ko.observable('');
    self.Series = ko.observable('');
    self.SeriesGroup = ko.observable(self.GenerateRandomSeriesGroupId());

    self.Pace = ko.observable('');
    self.Relax = ko.observable('');
    self.Description = ko.observable('');
    self.Comments = ko.observable('');
    self.Cadence = ko.observable('');
    self.Regeneration = ko.observable('');

    self.ValueChangeMode = ko.observable('fixed');
    self.LoadChangeMode = ko.observable('fixed');

    self.Unit = ko.observable('');
    self.LoadUnit = ko.observable('');
    self.RmLoadUnit = ko.observable('');
    self.RegenerationUnit = ko.observable('');
    self.RelaxUnit = ko.observable('');

    self.Index = ko.observable(1000); //1000 żeby dodawał na końcu

    self.Date = ko.observable('');
    self.ExecutionDate = ko.pureComputed(self.ExecutionDate_Computed, self);
    self.ZoneValues = ko.observable('');
    self.RegenerationValue = ko.observable('');

    self.RelaxValue = ko.observable('');

    self.AvailableExercises = ko.observableArray([]);

    self.ExcerciseDetails = ko.observable({ Name: null });

    self.Pattern = ko.observable('');
    self.TypeManager.SelectValue(self.Parent && self.Parent.TypeManager ? self.Parent.TypeManager.SelectedTypeId() : 0, 0, 0);

    self.InSerie = false;
    self.FirstInSerie = false;
    self.LastInSerie = false;
    self.ParentSerie = ko.observable('');

    self.Intervals = ko.observable(false);

    self.LoadTooltip = ko.observable(translations.Intensivity);

    self.TrainingPointsContainerModel = new TrainingPointsContainerModel(self);


    self.Intervals.subscribe(self.Intervals_susbscribe, self);

    self.TypeName = ko.pureComputed(self.TypeName_Computed, self);
    self.TypeShortName = ko.pureComputed(self.TypeShortName_Computed, self);
    self.TypeShortcut = ko.pureComputed(self.TypeShortcut_Computed, self);
    self.TypeUnit = ko.pureComputed(self.TypeUnit_Computed, self);

    self.ZonePaceValue = ko.pureComputed(self.ZonePaceValue_Computed, self);
    self.ZonePaceTooltip = ko.pureComputed(self.ZonePaceTooltip_Computed, self);

    self.ValueWithUnit = ko.pureComputed(self.ValueWithUnit_Computed, self);
    self.LoadWithUnit = ko.pureComputed(self.LoadWithUnit_Computed, self);

    self.SpeedUnit = ko.pureComputed(self.SpeedUnit_Computed, self);
    self.ProperLoadUnit = ko.pureComputed(self.ProperLoadUnit_Computed, self);
    self._availableLoadUnits = ko.observableArray([]);
    self.AvailableLoadUnits = ko.pureComputed(self.AvailableLoadUnits_Computed, self);

    self.PaceZones = ko.observableArray([]);
    self.PaceZonesLoadedTrainingTypeId = -1;
    self.PacePaceType = ko.pureComputed(self.PacePaceType_Computed, self);
    self.PacePaceParam = ko.pureComputed(self.PacePaceParam_Computed, self);

    self.ExerciseTypeId.subscribe(self.ExerciseTypeId_subscribe, self);
    self.LoadUnit.subscribe(self.LoadUnit_subscribe, self);
    self.Unit.subscribe(self.Unit_subscribe, self);

    self.LoadIconCss = ko.pureComputed(self.LoadIconCss_Computed, self);
    self.LoadEntry = ko.pureComputed(self.LoadEntry_Computed, self);
    self.UnitEntry = ko.pureComputed(self.UnitEntry_Computed, self);
    self.RegenerationEntry = ko.pureComputed(self.RegenerationEntry_Computed, self);
    self.RelaxEntry = ko.pureComputed(self.RelaxEntry_Computed, self);
    self.ProperRelaxUnit = ko.pureComputed(self.ProperRelaxUnit_Computed, self);

    self.DisplayLoadUnit = ko.pureComputed(self.DisplayLoadUnit_Computed, self);

    self.DoublePaceValue1 = ko.pureComputed({ read: self.DoublePaceValue1_Computed_read, write: self.DoublePaceValue1_Computed_write }, self);
    self.DoublePaceValue2 = ko.pureComputed({ read: self.DoublePaceValue2_Computed_read, write: self.DoublePaceValue2_Computed_write }, self);

    self.SeriesValuesList = ko.pureComputed(self.SeriesValuesList_Computed, self);

    self.ProperRegenerationUnit = ko.pureComputed(self.ProperRegenerationUnit_Computed, self);
    self.ProperRegenerationValue = ko.pureComputed(self.ProperRegenerationValue_Computed, self);

    self.ProperRelax = ko.pureComputed(self.ProperRelax_Computed, self);

    self.SeriesDisplay = ko.pureComputed(self.SeriesDisplay_Computed, self);

    self.Value_Invalid = ko.pureComputed(self.Value_Invalid_Computed, self);
    self.Pace_Invalid = ko.pureComputed(self.Pace_Invalid_Computed, self);
    self.Regeneration_Invalid = ko.pureComputed(self.Regeneration_Invalid_Computed, self);
    self.Repeats_Invalid = ko.pureComputed(self.Repeats_Invalid_Computed, self);
    self.Series_Invalid = ko.pureComputed(self.Series_Invalid_Computed, self);
    self.Relax_Invalid = ko.pureComputed(self.Relax_Invalid_Computed, self);
    self.DoublePaceValue1_Invalid = ko.pureComputed(self.DoublePaceValue1_Invalid_Computed, self);
    self.DoublePaceValue2_Invalid = ko.pureComputed(self.DoublePaceValue2_Invalid_Computed, self);

    self.mouseUpHandler = null;
    self.bindedContainer = null;


    self.SelectedOnChart = ko.observable(false);

    self.SelectedOnChart.subscribe(self.SelectedOnChart_subscribe, self);
    /*
    self.SelectOnChart = function () {
        if (!self.Parent) return;
        var par = self.Parent;
        if (!(par instanceof TrainingModel)) {
            par = par.Parent;
        }
        if (!(par instanceof TrainingModel)) return;

        var mc = par.MultiChart();
        if (!mc) return;

        mc.selectExeXRange(self.Id);

        $.scrollTo(mc.placeholder.parents('.tile'), { duration: 500, easing: 'swing' });
        //scrollToVisible(mc.placeholder, { duration: 500, easing: 'swing' });
    }*/

    self.initMode = false;

    _.bindAll(self
            , "SearchForTypes"
            , "Select2Format"
            , "Select2Selection"
            //, "Select2Close"
            , "initByModel"
            , "getData"
            , "remove"
            , "GoIntoEditMode"
            , "GoOutEditMode"
            , "OpenExeInfo"
            , "ToggleLoadChangeMode"
            , "GetPaceToStore"
            , "LoadRmUnit"
            , "HrGenPercValue"
            , "GetRepeatsToStore"
            , "ToggleSelectedOnChart"
            , "Copy"
            , "GetAndBindChart"
            );
}
TrainingExerciseModel.prototype = {
    Copy: function () {
        var self = this;
        var data = self.getData();

        var elem = new TrainingExerciseModel(self.Parent);
        elem.initByModel(data);
        elem.EditMode(true);
        elem.Id = 0;
        var idx = elem.Index();
        elem.Index(idx + 1);
        elem.SeriesGroup(elem.SeriesGroup() + elem.GenerateRandomSeriesGroupId());

        _.each(self.Parent.Exercises, function (exe) { if (exe.Index() > idx) { exe.Index(exe.Index() + 1); } });

        self.Parent.Exercises.push(elem)
    }
    , ToggleSelectedOnChart: function () {
        var self = this;
        var curVal = self.SelectedOnChart();
        self.SelectedOnChart(!curVal);

        if (!self.Parent) return;
        var par = self.Parent;
        if (!(par instanceof TrainingModel)) {
            par = par.Parent;
        }
        if (!(par instanceof TrainingModel)) return;

        var mc = par.MultiChart();
        if (!mc) return;

        if (curVal) {
            mc.ResetZoom();
        } else {
            mc.selectExeXRange(self.Id);
            $.scrollTo(mc.placeholder.parents('.tile'), { duration: 500, easing: 'swing' });
        }
    }
    , SelectedOnChart_subscribe: function (newValue) {
        var self = this;
        if (newValue) {
            $(".showSelectOnChart .smalChb").each(function (idx, elem) {
                var mod = ko.dataFor(elem);
                if (mod.Id != self.Id) mod.SelectedOnChart(false);
            });
            //self.SelectOnChart();
        }
    }
    , ExecutionDate_Computed: function () {
        var self = this;
        var d = self.Date();
        if (d && d != '0001-01-01') return d;

        if (self.Parent && self.Parent.Date) return self.Parent.Date();

        return '';
    }
    //#region type info
    , TypeShortcut_Computed: function () {
        var self = this;
        if (!self.ExcerciseDetails()) return '';
        return self.ExcerciseDetails().Shortcut;
    }
    , TypeShortName_Computed: function () {
        var self = this;
        if (!self.ExcerciseDetails()) return '';
        return self.ExcerciseDetails().Shortcut || self.ExcerciseDetails().Name;
    }
     , TypeName_Computed: function () {
         var self = this;
         if (!self.ExcerciseDetails()) return '';
         return self.ExcerciseDetails().Name;
     }
    , TypeUnit_Computed: function () {
        var self = this;
        if (!self.ExcerciseDetails()) return '';
        var unit = self.ExcerciseDetails().Unit;
        if (unit == "mm:ss" || unit == "gg:mm:ss") return '';
        return unit;
    }
    //#endregion
    //#region value recalculations
    , Intervals_susbscribe: function (newValue) {
        var self = this;
        self.Repeats('');
        self.Regeneration('');
    }
    , ZonePaceValue_Computed: function () {
        var self = this;
        var v = self.Value();
        if (!v) return null;
        var u = self.LoadUnit();
        if (u != 'strefaHR' && u != 'strefaFTP') return null;
        if (u == 'strefaHR') {
            return _.findWhere(trainingExerciseManager.ZonesHr, { value: v });
        } else if (u == 'strefaFTP') {
            return _.findWhere(trainingExerciseManager.ZonesFtp, { value: v });
        }
        return null;
    }
    , ZonePaceTooltip_Computed: function () {
        var self = this;
        var v = self.ZonePaceValue();
        if (!v) return '';
        return v.text;
    }
    , ValueWithUnit_Computed: function () {
        var self = this;

        var v = self.Value();
        if (!v) return '';

        var u = self.Unit();

        if (!u && self.ExcerciseDetails()) u = self.ExcerciseDetails().Unit;
        if (!u) return v;

        if (u == 'mm:ss' || u == 'gg:mm:ss' || u == 'gg:mm') {
            if (v.length <= 5 && u != 'gg:mm') v = '00:' + v;
            if (v.length <= 5 && u == 'gg:mm') v = v + ':00';
            var d = new Date(stringToSeconds(v) * 1000);
            return dateToShortDescFromat(d, false);
        }

        if (u == 'achieve') {
            if (!v) return '';
            if (v.length <= 5) v = '00:' + v;
            var d = new Date(stringToSeconds(v) * 1000);
            d = '(max ' + dateToShortDescFromat(d, false) + ')';
            return d;
        }

        return v + u;
    }
    , LoadWithUnit_Computed: function () {
        var self = this;

        self.LoadTooltip(translations.Intensivity);

        var v = self.Pace();
        if (!v) return '';

        var u = self.LoadUnit();
        if (!u && self.ExcerciseDetails()) u = self.ExcerciseDetails().LoadUnit;
        if (!u) return v;
        switch (u) {
            case 'strefaHR':
                self.LoadGenZoneValues(trainingExerciseManager.ZonesHr, HR_zoneTypeId, 'bpm', '%', 'strefaHR');
                return self.ZoneValues();
            case 'strefaFTP':
                self.LoadGenZoneValues(trainingExerciseManager.ZonesFtp, POWER_zoneTypeId, 'W', '%FTP', 'strefaFTP');
                return self.ZoneValues();
            case '%LTHR':
                self.LoadFthrValue(trainingExerciseManager.ZonesHr, 'bpm', '%', '%LTHR', self.Pace, self.ZoneValues);
                return self.ZoneValues();
            case '%hr':
                self.LoadGenPercValue(23, false, 'bpm', '%HRmax');
                return self.ZoneValues();
            case '%FTP':
                self.LoadGenPercValue(41, true, 'W', '%FTP');
                return self.ZoneValues();
            case 'paceZone':
                self.LoadPaceValues();
                return self.ZoneValues();
            case 'RM':
                self.LoadRmValue(self.Pace, self.ZoneValues, true);
                return self.ZoneValues();
        }
        u = self.ProperLoadUnit();
        return v + u;
    }
    , LoadGenZoneValues: function (zones, zoneTypeId, valueUnit, percentUnit, paramName) {
        var self = this;
        var zone = _.findWhere(zones, { value: self.Pace() });
        if (zone) {
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "Zones/GetUserZoneValue",
                data: {
                    zoneId: zone.zoneId,
                    zoneTypeId: zoneTypeId,
                    trainingTypeId: self.TypeManager.SelectedTypeId(),
                    userId: 0,
                    date: self.ExecutionDate()
                }
            }, function (serverModel) {
                if (serverModel && serverModel.zone) {
                    var min = 0;
                    var max = 0;
                    var unit = '';
                    if (serverModel.baseVal) {
                        min = Math.round(serverModel.baseVal * serverModel.zone.LowerBoundNr / 100);
                        var u = serverModel.zone.UpperBoundNr;
                        if (u < 100) {
                            u++;
                            max = Math.round(serverModel.baseVal * (u) / 100 - 1);
                        } else {
                            max = serverModel.baseVal;
                        }
                        unit = valueUnit;
                    } else {
                        min = Math.round(serverModel.zone.LowerBoundNr);
                        max = Math.round(serverModel.zone.UpperBoundNr);
                        unit = percentUnit;
                    }

                    if (min == 0) {
                        self.ZoneValues('<' + max + unit);
                    } else if (max == 0) {
                        self.ZoneValues('>' + min + unit);
                    } else {
                        self.ZoneValues(min + '-' + max + unit);
                    }
                } else {
                    self.ZoneValues(zone.value + paramName);
                }
            }, 3600000 //1h
            );
        }
    }
    , LoadFthrValue: function (zones, valueUnit, percentUnit, paramName, source, target) {
        var self = this;
        var percVal = source();
        var fthrZone = "6";
        var zone = _.findWhere(zones, { zoneId: fthrZone });
        if (zone) {
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "Zones/GetUserZoneValue",
                data: {
                    zoneId: zone.zoneId,
                    zoneTypeId: HR_zoneTypeId,
                    trainingTypeId: self.TypeManager.SelectedTypeId(),
                    userId: 0,
                    date: self.ExecutionDate()
                }
            }, function (serverModel) {
                if (serverModel && serverModel.zone) {
                    var min = 0;
                    var unit = '';
                    if (serverModel.baseVal) {
                        min = Math.round(serverModel.baseVal * serverModel.zone.LowerBoundNr * percVal / 10000);
                        var u = serverModel.zone.UpperBoundNr;
                        unit = valueUnit;
                    }
                    if (min) {
                        target(min + unit);
                    } else {
                        target(percVal + paramName);
                    }
                } else {
                    target(percVal + paramName);
                }
            }, 3600000 //1h
            );
        }
    }
    , LoadGenPercValue: function (diaryTypeId, useTrainingType, valueUnit, percUnit) {
        var self = this;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "Diary/GetUserDaryValue",
            data: {
                diaryId: diaryTypeId,
                userId: 0,
                date: self.ExecutionDate(),
                trainingTypeId: useTrainingType ? self.TypeManager.SelectedTypeId() : 0,
                exact: diaryTypeId == 41 //FTP tylko dla konkretnej dyscypliny
            }
        }, function (serverModel) {
            if (serverModel) {

                var clacFunc = function (val) { return Math.round(serverModel * val / 100); }

                var str = self.Pace();
                str = str.replace(/[0-9]+/g, clacFunc);

                self.ZoneValues(str + valueUnit);
            } else {
                self.ZoneValues(self.Pace() + percUnit);
            }
        }, 3600000 //1h
                    );
    }
    , LoadPaceValue: function () {
        var self = this;
        var _paces = _(self.PaceZones());

        var PacePaceType = self.PacePaceType();
        var PacePaceParam = self.PacePaceParam();

        var pace = _paces.find(function (elem) { return elem.Shortcut == PacePaceType && (!PacePaceParam || PacePaceParam == elem.AdditionalParam) });

        if (pace) {
            self.LoadTooltip(translations.Intensivity + ' - ' + pace.Name);
        }

        if (pace && pace.Pace && pace.Pace != '0' && pace.Pace != '0:00') {
            self.ZoneValues(pace.Pace + self.SpeedUnit());
        } else {
            self.ZoneValues(self.Pace());
        }
    }
    , LoadPaceValues: function () {
        var self = this;

        var pz = self.PaceZones();
        var tTypeId = self.TypeManager.SelectedTypeId();
        if (!self.PaceZonesLoading && (!pz || !pz.length || self.PaceZonesLoadedTrainingTypeId != tTypeId)) {
            self.PaceZonesLoadedTrainingTypeId = tTypeId;
            self.PaceZonesLoading = true;
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "Diary/GetPaceSet",
                data: {
                    date: self.ExecutionDate(),
                    trainingTypeId: tTypeId
                }
            }, function (serverModel) {
                if (serverModel && serverModel.length) {
                    self.CopyPaceValues(serverModel);
                } else {
                    self.CopyPaceValues(trainingExerciseManager.BasePaces);
                }
                self.LoadPaceValue();
                self.PaceZonesLoading = false;
            }, 3600000 //1h
            );
        }

        self.LoadPaceValue();
    }
    , CopyPaceValues: function (paces) {
        var self = this;
        var currPace = self.Pace();
        var PacePaceType = self.PacePaceType();
        var PacePaceParam = self.PacePaceParam();
        var exist = false;
        koArrayCopyComplex(paces, self.PaceZones, false, function (elem) {

            switch (elem.DiaryTypeId) {
                case 42: elem.Shortcut = "BS"; elem.order = 1; break;
                case 43: elem.Shortcut = "M"; elem.order = 2; break;
                case 44: elem.Shortcut = "P"; elem.order = 3; break;
                case 45: elem.Shortcut = "I"; elem.order = 5; break;
                case 46: elem.Shortcut = "R"; elem.order = 4; break;
            }
            elem.DdlVal = elem.Shortcut + (elem.AdditionalParam ? ("[" + elem.AdditionalParam + "]") : "");
            elem.DdlText = elem.Name + (elem.AdditionalParam ? (" [" + elem.AdditionalParam + "m]") : "");
            if (elem.DdlVal == currPace) exist = true;
            elem.AdditionalParamNr = elem.AdditionalParam ? parseInt(elem.AdditionalParam) : 0;

            return elem;
        });
        self.PaceZones.sort(function (left, right) {
            return left.order == right.order ? (left.AdditionalParamNr == right.AdditionalParamNr ? 0 : (left.AdditionalParamNr < right.AdditionalParamNr ? -1 : 1)) : (left.order < right.order ? -1 : 1)
        });

        if (currPace && !exist) {
            var mostsim = _.findWhere(trainingExerciseManager.BasePaces, { Shortcut: PacePaceType });
            var toAdd = { Name: "", Shortcut: '', DiaryTypeId: 0, AdditionalParam: null, Value: null, Pace: null };
            if (mostsim) {
                _.extend(toAdd, mostsim);
            } else {
                toAdd.Shortcut = PacePaceType;
                toAdd.Name = PacePaceType;
            }
            toAdd.AdditionalParam = PacePaceParam;

            self.PaceZones.push(toAdd);
        }
        self.Pace(currPace);
    }
    , PacePaceType_Computed: function () {
        var self = this;
        var str = self.Pace();
        if (str) {
            var parts = str.match(/(\w+)/g);
            if (parts && parts.length) return parts[0];
        }
        return str;
    }
    , PacePaceParam_Computed: function () {
        var self = this;
        var str = self.Pace();
        if (str) {
            var parts = str.match(/(\w+)/g);
            if (parts && parts.length > 1) return parts[1];
        }
        return "";
    }
    , LoadRmValue: function (sourceProperty, targetProperty, withUnit) {
        var self = this;
        //self.ValueReCalculated(self.Value() + '%RM');
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "Diary/GetUserDaryValue",
            data: {
                diaryId: 48, //max weight
                userId: 0,
                date: self.ExecutionDate(),
                additionalParam: self.ExerciseTypeId(),
                exact: true
            }
        }, function (serverModel) {
            if (serverModel) {
                var clacFunc = function (val) { return Math.round(serverModel * val / 100); }

                var str = sourceProperty();
                str = str.replace(/[0-9]+/g, clacFunc);

                if (withUnit) str = str + 'kg';

                targetProperty(str);
            } else {
                var str = sourceProperty();
                if (withUnit) str = str + '%RM';
                targetProperty(str);
            }
        }, 3600000 //1h
        );
    }

    , LoadRmUnit: function () {
        var self = this;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "Diary/GetUserDaryValue",
            data: {
                diaryId: 48, //max weight
                userId: 0,
                date: self.ExecutionDate(),
                additionalParam: self.ExerciseTypeId(),
                exact: true
            }
        }, function (serverModel) {
            if (serverModel) {
                self.RmLoadUnit('kg');
            } else {
                self.RmLoadUnit('%RM');
            }
        }, 3600000 //1h
        );
    }

    , TrainingType_Computed: function () {
        var self = this;
        var type = self.TypeManager.SelectedType();
        self.ParamsVisibility(GetParamsVisibility(type));

        return type;
    }
    , AvailableLoadUnits_Computed: function () {
        var self = this;

        var units = self._availableLoadUnits();
        if (!units || !units.length) {
            units = trainingExerciseManager.LoadUnits;
            for (var i = 0; i < units.length; i++) {
                var u = units[i];
                var nu = {};
                $.extend(nu, u);
                nu.text = ko.observable(u.text);
                nu.visible = ko.observable(true);
                self._availableLoadUnits.push(nu);
            }
        }

        var units = self._availableLoadUnits();

        var pv = self.ParamsVisibility();
        var isPace = pv && pv.ShowPace;

        var speed = _.findWhere(units, { value: 'speed' });
        if (speed) {
            speed.text((isPace ? translations.Pace.toLowerCase() : translations.Speed.toLowerCase()));// + ' (' + self.SpeedUnit() + ')');

            var unit = self.SpeedUnit();
            if (unit.startsWith("km/")) {
                speed.placeholder = '0';
            } else if (unit.startsWith("min/")) {
                speed.placeholder = '00:00';
            } else if (unit.startsWith("s/")) {
                speed.placeholder = '0';
            }
        }

        var speedSpeed = _.findWhere(units, { value: 'speedSpeed' });
        if (speedSpeed) {
            speedSpeed.visible(isPace);
        }

        return units;
    }
    , SpeedUnit_Computed: function () {
        var self = this;

        var defUnit = 'km/h';

        var pv = self.ParamsVisibility();

        if (!pv) return defUnit;

        if (pv.ShowPace) {
            return pv.PaceParams.unit;
        } else {
            return defUnit;
        }
    }
    , ProperLoadUnit_Computed: function () {
        var self = this;

        var u = self.LoadUnit();
        if (u == 'speed' || u == 'pace') return self.SpeedUnit();

        if (u == 'RM') {
            self.LoadRmUnit();
            return self.RmLoadUnit();
        }

        if (u == '%hr') return '%HRmax';

        if (u == 'HR') return 'bpm';

        if (u == 'speedSpeed') return 'km/h';

        return u;
    }
    , LoadUnit_subscribe: function (newValue) {
        var self = this;
        if (!self.initMode && newValue != 'paceZone' && newValue != 'strefaHR' && newValue != 'strefaFTP') {
            setTimeout(function () {
                self.Pace('');
                self.Repeats('');
            }, 50);
        }
        if (!self.initMode) {
            setTimeout(function () {
                self.ResetLoadChangeMode();
            }, 50);
        }
        if (newValue == 'paceZone') self.LoadPaceValues();
    }
    , Unit_subscribe: function (newValue) {
        var self = this;
        if (!self.initMode) {
            setTimeout(function () {
                self.ResetLoadChangeMode();
            }, 50);
        }
    }
    //#endregion

    , ExerciseTypeId_subscribe: function (newValue) {
        var self = this;
        if (newValue) {

            var exercise = _(self.AvailableExercises()).find(function (exe) { return '' + exe.Id === '' + newValue });
            if (exercise) {
                self.ExcerciseDetails(exercise);

                if (newValue == -1) return; //"brak"

                if (self.syncTTypeWithExcerType) {
                    self.TypeManager.SelectValue(exercise.TrainingTypeId, 0, 0);
                }

                self.ResetLoadChangeMode(exercise.Unit);
                self.Repeats(exercise.Repeats);
                self.Value(exercise.Value);
                self.Series(exercise.Series);
                self.Pace(exercise.Pace);
                self.Relax(exercise.Relax);
                self.Unit(exercise.Unit);
                self.LoadUnit(exercise.LoadUnit);
                self.Relax(exercise.Relax);
                self.Regeneration(exercise.Regeneration);
                self.RegenerationUnit(exercise.RegenerationUnit);
                self.RelaxUnit(exercise.RelaxUnit);
            }
        }
    }

    //#region search type
    , SearchForTypes: function (callback, pattern) {
        var self = this;
        if (pattern) self.Pattern(pattern);
        //if (!self.Pattern() && self.ExcerciseDetails()) self.Pattern(self.ExcerciseDetails().Name);
        trainingExerciseManager.ExerciseParent = self;
        trainingExerciseManager.Exercises = self.AvailableExercises;
        trainingExerciseManager.Pattern(self.Pattern());
        trainingExerciseManager.TrainingTypeId(self.TypeManager.SelectedTypeId());
        trainingExerciseManager.OnlyWeighted = self.onlyWeighted;
        trainingExerciseManager.DefferedSeach(callback);
    }
    , Select2Format: function (exeType) {
        var self = this;
        var result = '';
        if (exeType && exeType.Name) {

            if (exeType.TrainingTypeIcon) {
                result += '<span class="disc_15 ' + exeType.TrainingTypeIcon + '" title="' + exeType.TrainingTypeName + '">&nbsp;</span>';
            }

            if (exeType.ClubId) {
                result += '<span class="club"><img src="' + exeType.ClubLogoUrl + '&width=50&height=50" alt="' + exeType.ClubName + '" /></span>';

            }
            result += '<span class="name';
            if (exeType.MatchScore == 1) {
                result += ' maxMatch';
            }
            result += '">' + exeType.Name + '</span>';

            if (exeType.Shortcut) {
                result += '<span class="shortcut">' + exeType.Shortcut + '</span>';
            }


            result += '<span class="previewIcon info_25_18" onmouseenter="trainingExerciseManager.PrepareInfo(this, ' + exeType.Id + ');" onmouseout="trainingExerciseManager.CancelPrepareInfo();">';


        } else {
            result = '<span class="placeholder">' + translations.ChooseExercise + '...</span>';
        }
        return $(result);
    }
    , Select2Selection: function (exeType) {
        var self = this;
        var result = '';
        if (exeType && exeType.Name) {
            result = '<span>' + exeType.Name + '</span>';
        } else {
            result = '<span class="placeholder">' + translations.ChooseExercise + '...</span>';
        }
        return $(result);
    }
    /*, Select2Close: function () {
        var self = this;
        $('#SmallExerciseTypeInfo').remove();
        self.Pattern('');
    }*/

    , ProperRelaxUnit_Computed: function () {
        var self = this;

        var u = self.RelaxUnit();
        if (u == '%hr') return '%HRmax';

        return u;
    }

    //#endregion
    , initByModel: function (serverModel) {
        var self = this;
        if (serverModel) {
            self.initMode = true;

            self.serverModel = serverModel;

            self.Intervals(serverModel.Intervals); //musi byc przed regeneration i repeats

            self.Id = serverModel.Id;
            self.Repeats(serverModel.Repeats);
            self.Value(serverModel.Value);
            self.Series(serverModel.Series);

            self.Pace(serverModel.Pace);
            self.Relax(serverModel.Relax);
            self.Description(serverModel.Description);
            self.Comments(serverModel.Comments);
            self.Cadence(serverModel.Cadence);
            self.Regeneration(serverModel.Regeneration);

            self.ValueChangeMode(serverModel.ValueChangeMode);
            self.LoadChangeMode(serverModel.LoadChangeMode);

            self.Unit(serverModel.Unit);
            self.LoadUnit(serverModel.LoadUnit);
            self.RegenerationUnit(serverModel.RegenerationUnit);
            self.RelaxUnit(serverModel.RelaxUnit);

            if (serverModel.TrainingPointsContainerModel) {
                self.TrainingPointsContainerModel.initByModel(serverModel.TrainingPointsContainerModel);
            } else {
                self.TrainingPointsContainerModel.init();
            }

            self.Index(serverModel.Index);
            self.SeriesGroup(serverModel.SeriesGroup || serverModel.Index);

            self.ExerciseTypeId(serverModel.ExerciseTypeId);

            self.Date(serverModel.Date);

            if (serverModel.ExerciseTypeModel) {
                self.ExcerciseDetails(serverModel.ExerciseTypeModel);
                self.ExcerciseDetails().ClubLogoUrl = (!serverModel.ExerciseTypeModel.ClubLogoId) ? '' : ContextPath + "Image/GetImage?id=" + serverModel.ExerciseTypeModel.ClubLogoId;
                self.TypeManager.SelectValue(serverModel.ExerciseTypeModel.TrainingTypeId, 0, 0);
            } else {
                self.ExcerciseDetails({});
                self.ExcerciseDetails().ClubLogoUrl = (!serverModel.ClubLogoId) ? '' : ContextPath + "Image/GetImage?id=" + serverModel.ClubLogoId;
            }

            self.InSerie = serverModel.InSerie;
            self.FirstInSerie = serverModel.FirstInSerie;
            self.LastInSerie = serverModel.LastInSerie;


            //ustawienie liczby serii cwiczen silowych
            //var vals = self.Pace().split(';');
            //var repets = self.Repeats().split(';');
            //if (vals && vals.length) self.ExeSeries(vals.length);
            //if (repets && repets.length && repets.length > self.ExeSeries()) self.ExeSeries(repets.length);

            self.initMode = false;
        }
    }
    , getData: function () {
        var self = this;
        var pointsContainer = self.TrainingPointsContainerModel;
        if (pointsContainer) pointsContainer = pointsContainer.getClearData();
        return {
            Id: self.Id,
            Repeats: self.GetRepeatsToStore(),
            Value: self.Value(),
            Series: self.Series(),
            ExerciseTypeId: self.ExerciseTypeId(),
            Pace: self.GetPaceToStore(),
            Relax: self.Relax(),
            Description: self.Description(),
            Comments: self.Comments(),
            Cadence: self.Cadence(),
            Regeneration: self.Regeneration(),
            ValueChangeMode: self.ValueChangeMode(),
            LoadChangeMode: self.LoadChangeMode(),
            Unit: self.Unit(),
            LoadUnit: self.LoadUnit(),
            RegenerationUnit: self.RegenerationUnit(),
            RelaxUnit: self.RelaxUnit(),
            Index: self.Index(),
            SeriesGroup: self.SeriesGroup(),
            TrainingTypeId: self.TypeManager.SelectedTypeId(),
            Intervals: self.Intervals(),
            TrainingPointsContainerModel: pointsContainer
        };
    }
    , remove: function () {
        var self = this;
        modalAskModel.Show(translations.DeleteExercise, translations.DeleteExerciseMsg, function () { self.Parent && self.Parent.Exercises.remove(self); }, translations.Delete);
    }
    , GoIntoEditMode: function (data, event) {
        var self = this;
        self.EditMode(true);
        self.bindedContainer = $(event.target).closest('.trainingExercise')[0];
        self.mouseUpHandler = self.GoOutEditMode;
        $(document).one("mouseup", self.mouseUpHandler);
    }
    , GoOutEditMode: function (event) {
        var self = this;
        var realTarget = $(event.target).closest('.trainingExercise');
        if (realTarget.length && realTarget[0] == self.bindedContainer) {
            $(document).one("mouseup", self.mouseUpHandler);
        } else {
            self.mouseUpHandler = null;
            self.EditMode(false);
        }
    }
    , OpenExeInfo: function () {
        var self = this;
        var det = self.ExcerciseDetails();
        if (!det.FileIds) {
            det.FileIds = det.PhotoIds;
        }
        trainingExerciseManager.SelectedForInfo(self.ExcerciseDetails());
        trainingExerciseManager.OpenExeInfo();
    }

    //#region entries
    , LoadIconCss_Computed: function () {
        var self = this;
        return self.LoadEntry().ico;//+ ' ' + self.LoadChangeMode();
        /*
        switch (self.LoadUnit()) {
            case '': return '';
            case 'paceZone': return 'pace';
            case 'speed': return 'pace';
            case 'strefaHR': return 'hr';
            case '%LTHR': return 'hr';
            case 'strefaFTP': return 'power';
            case '%FTP': return 'power';
            case 'W': return 'power';
            case 'RM': return 'weight';
            case 'kg': return 'weight';
            case 'rpm': return 'cadence';
            case 'RPE': return 'face';
            case 'KOW': return 'face';
        }

        return type;*/
    }
    , LoadEntry_Computed: function () {
        var self = this;
        var entries = self.AvailableLoadUnits();
        var lu = self.LoadUnit();

        if (!lu) return entries[0];

        return _.findWhere(entries, { value: lu });
    }
    , UnitEntry_Computed: function () {
        var self = this;
        var entries = self.Parent.ValueUnits || trainingExerciseManager.ValueUnits;
        var lu = self.Unit();
        if (!lu) return entries[0];

        return _.findWhere(entries, { value: lu });
    }
    , RegenerationEntry_Computed: function () {
        var self = this;
        var entries = self.Parent.RegenerationUnits || trainingExerciseManager.RegenerationUnits;
        var lu = self.RegenerationUnit();
        if (!lu) return entries[0];

        return _.findWhere(entries, { value: lu });
    }
    , RelaxEntry_Computed: function () {
        var self = this;
        var entries;
        if (self.Parent) entries = self.Parent.RelaxUnits;
        if (!entries) entries = trainingExerciseManager.RelaxUnits;

        var lu = self.RelaxUnit();
        if (!lu) return entries[0];

        return _.findWhere(entries, {
            value: lu
        });
    }
    //#endregion
    , ResetLoadChangeMode: function (unit) {
        var self = this;
        var vu = unit || self.Unit();
        if (vu == 'achieve') {
            self.LoadChangeMode('downto');
        } else {
            self.LoadChangeMode('fixed');
        }
    }
    , ToggleLoadChangeMode: function () {
        var self = this;
        self.Pace('');
        self.Repeats('');
        var vu = self.Unit();
        var lu = self.LoadEntry();
        var m = self.LoadChangeMode();
        var nM = 'fixed';
        if (lu.allowMultiMode && vu != 'achieve') {
            switch (m) {
                case 'fixed': nM = 'upto'; break;
                case 'upto': nM = 'downto'; break;
                case 'downto': nM = 'between'; break;
                case 'between': nM = (vu == 'exe' ? 'list' : 'fixed'); break;
                case 'list': nM = 'fixed'; break;
            }
        } else {
            switch (m) {
                case 'fixed': nM = 'upto'; break;
                case 'upto': nM = 'downto'; break;
                case 'downto': nM = 'fixed'; break;
            }
        }
        if (nM == 'list') self.Value(1);

        self.LoadChangeMode(nM);
    }
    //#region value recalculations 2
    , DisplayLoadUnit_Computed: function () {
        var self = this;

        return self.LoadUnit() != 'strefaHR' && self.LoadUnit() != 'strefaFTP' && self.LoadUnit() != 'paceZone';
    }
    , DoublePaceValue1_Computed_read: function () {
        var self = this;

        var p = self.Pace();
        if (!p) return '';
        var ps = p.split('-');
        return ps[0];
    }
    , DoublePaceValue1_Computed_write: function (value) {
        var self = this;
        var v2 = self.DoublePaceValue2();
        if (v2 && value) {
            self.Pace(value + '-' + v2);
        } else if (value) {
            self.Pace(value);
        } else if (v2) {
            self.Pace(v2);
        }
    }
    , DoublePaceValue2_Computed_read: function () {
        var self = this;

        var p = self.Pace();
        if (!p) return '';
        var ps = p.split('-');
        if (ps.length > 1) {
            return ps[1];
        } else {
            return ps[0];
        }
    }
    , DoublePaceValue2_Computed_write: function (value) {
        var self = this;
        var v2 = self.DoublePaceValue1();
        if (v2 && value) {
            self.Pace(v2 + '-' + value);
        } else if (value) {
            self.Pace(value);
        } else if (v2) {
            self.Pace(v2);
        }
    }
    , GetPaceToStore: function () {
        var self = this;
        if (self.LoadChangeMode() == 'list') {
            var vals = self.SeriesValuesList();
            if (!vals || !vals.length) return '';
            return _.map(vals, function (elem) { return elem.Pace(); }).join(";")
        } else {
            return self.Pace();
        }
    }
    , GetRepeatsToStore: function () {
        var self = this;
        if (self.LoadChangeMode() == 'list') {
            var vals = self.SeriesValuesList();
            if (!vals || !vals.length) return '';
            return _.map(vals, function (elem) { return elem.Repeats(); }).join(";")
        } else {
            return self.Repeats();
        }
    }
    , SeriesValuesList_Computed: function () {
        var self = this;
        if (self.LoadChangeMode() != 'list') return [];
        var nrOfSeries = self.Value() || 1;
        var result = [];
        var vals = self.Pace().split(';');
        var lu = self.LoadUnit();
        if (lu == 'RM') {

        }
        var repets = self.Repeats().split(';');
        for (var i = 0; i < nrOfSeries; i++) {
            var serVal = {
                Pace: ko.observable('')
                , Repeats: ko.observable('')
                , Pace_Invalid: function () {
                    return self.validByUnit(serVal.Pace(), self.LoadEntry());
                }
            };
            if (vals.length > i) serVal.Pace(vals[i]);
            if (repets.length > i) serVal.Repeats(repets[i]);
            if (lu == 'RM') {
                serVal.DisplayPace = ko.observable('');
                self.LoadRmValue(serVal.Pace, serVal.DisplayPace, false);
            } else {
                serVal.DisplayPace = serVal.Pace;
            }

            result.push(serVal);
        }
        return result;
    }

    , ProperRegenerationUnit_Computed: function () {
        var self = this;

        var ru = self.RegenerationUnit();
        if (ru == '%value') return self.Unit();
        if (ru == '%hr') return '%HRmax';
        // if (ru == '%LTHR') return '';

        return ru;
    }

    , ProperRegenerationValue_Computed: function () {
        var self = this;

        var reg = self.Regeneration();
        var percReg = parseFloat(reg);

        var ru = self.RegenerationUnit();
        if (ru == '%value') {
            var val = self.Value();
            var valUnit = self.Unit();

            if (valUnit == 'm' || valUnit == 'km') {
                var recalc = Math.round(percReg * parseFloat(val) / 100);
                if (valUnit == 'km' && recalc < 1) {
                    return Math.round(percReg * parseFloat(val) * 10) + 'm';
                } else {
                    return recalc + valUnit;
                }
            } else {
                if (valUnit == 'mm:ss') val = '00:' + val;
                if (valUnit == 'gg:mm') val = val + ':00';
                var secs = stringToSeconds(val) * percReg / 100;
                /*
                if (valUnit == 'gg:mm') return MsToString(secs, true);
                if (valUnit == 'mm:ss') return MsToMinutesString(secs);
                return MsToString(secs, false);
                */
                var d = new Date(secs * 1000);
                return dateToShortDescFromat(d, false);
            }
        }
        if (ru == '%hr') {
            self.HrGenPercValue('bpm', ru, self.Regeneration, self.RegenerationValue);
            return self.RegenerationValue();
        }

        if (ru == '%LTHR') {
            self.LoadFthrValue(trainingExerciseManager.ZonesHr, 'bpm', '%', '%LTHR', self.Regeneration, self.RegenerationValue);
            return self.RegenerationValue();
        }

        if (ru == 'mm:ss') {
            reg = '00:' + reg;
            var secs = stringToSeconds(reg);
            var d = new Date(secs * 1000);
            return dateToShortDescFromat(d, false);
        }

        if (ru == 'm' || ru == 'km') return reg + ru;

        return reg;
    }
    , HrGenPercValue: function (valueUnit, percUnit, sourceProperty, targetProperty) {
        var self = this;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "Diary/GetUserDaryValue",
            data: {
                diaryId: 23, //maxhr
                userId: 0,
                date: self.ExecutionDate(),
                trainingTypeId: 0,
                exact: false
            }
        }, function (serverModel) {
            if (serverModel) {

                var clacFunc = function (val) { return Math.round(serverModel * val / 100); }

                var str = sourceProperty();
                str = str.replace(/[0-9]+/g, clacFunc);

                targetProperty(str + valueUnit);
            } else {
                targetProperty(sourceProperty() + percUnit);
            }
        }, 3600000 //1h
      );
    }


    , ProperRelax_Computed: function () {
        var self = this;
        var relax = self.Relax();
        var relEntry = self.RelaxEntry();

        if (relEntry.value == 'mm:ss') {
            var val = '00:' + relax;
            var secs = stringToSeconds(val);
            var d = new Date(secs * 1000);
            return dateToShortDescFromat(d, false);
        } else if (relEntry.value == '%hr') {
            self.HrGenPercValue('bpm', relEntry.value, self.Relax, self.RelaxValue);
            return self.RelaxValue();
        } else if (relEntry.value == '%LTHR') {
            self.LoadFthrValue(trainingExerciseManager.ZonesHr, 'bpm', '%', '%LTHR', self.Relax, self.RelaxValue);
            return self.RelaxValue();
        }
    }

    , SeriesDisplay_Computed: function () {
        var self = this;
        var s = self.Series();
        return s && s.trim() != '1';
    }
    //#endregion

    , GenerateRandomSeriesGroupId: function () {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

        for (var i = 0; i < 5; i++)
            text += possible.charAt(Math.floor(Math.random() * possible.length));

        return text;
    }

    //#region validators
    , validByUnit: function (value, unitEntry) {
        var self = this;
        if (!value) return false;

        if (!unitEntry) return false;

        if (unitEntry.ValidFnc) {
            var isValid = unitEntry.ValidFnc(value, self);
            return !isValid;
        }

        if (!unitEntry.ValidRegEx) return false;
        var patt = new RegExp(unitEntry.ValidRegEx);
        return !patt.test(value);
    }
    , Value_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Value(), self.UnitEntry());
    }
    , Pace_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Pace(), self.LoadEntry());
    }
    , Regeneration_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Regeneration(), self.RegenerationEntry());
    }
    , Relax_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Relax(), self.RelaxEntry());
    }
    , Repeats_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Repeats(), { ValidRegEx: '^\\d+$' });
    }
    , Series_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Series(), { ValidRegEx: '^\\d+$' });
    }
    , DoublePaceValue1_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.DoublePaceValue1(), self.LoadEntry());
    }

    , DoublePaceValue2_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.DoublePaceValue2(), self.LoadEntry());
    }
    //#endregion

    //#region chartExcercise

    , GetAndBindChart: function () {
        var self = this;
        $.ajax({
            type: "POST",
            url: ContextPath + "Chart/GetStoredChartContainerModel"
        })
            .done(function (result) {
                if (result) {
                    self.TrainingPointsContainerModel.initByModel(result);
                } else {
                    alert("nie wybrano wykresu")
                }
            });
    }

    //#endregion
}


ExerciseSerieManager = function () {
    var self = this;

    self.Series = ko.observableArray([]);//.extend({ rateLimit: 50 });

    self.seriesRebuilding = false;
    self.Series.subscribeArrayChanged(
        function (addedItem, index) {
            if (!self.seriesRebuilding) {
                self.seriesRebuilding = true;
                if (addedItem instanceof TrainingExerciseModel) {
                    var prevSerie = addedItem.ParentSerie();

                    addedItem.Relax('');
                    addedItem.Series('');

                    var arr = [];
                    arr.push(addedItem);
                    var ser = new ExerciseSerie(arr, self);
                    self.Series.remove(addedItem);
                    self.Series.splice(index, 0, ser);

                    if (prevSerie) {
                        prevSerie.Exercises.remove(addedItem);
                    }
                }
                self.seriesRebuilding = false;
            }
        },
        function (deletedItem) {
            // self.Series.remove(deletedItem);
        },
        function () { //clearCallback
            //self.Series.removeAll();
        }
    );




    _.bindAll(self
               , "init", "RecalculateExerciseSeries", "SetIndexes"
                );
}
ExerciseSerieManager.prototype = {
    init: function (Exercises) {
        var self = this;
        self.Exercises = Exercises;

        Exercises.extend({ rateLimit: 50 });

        Exercises.subscribe(function (newValue) {
            self.RecalculateExerciseSeries();
        });

        self.RecalculateExerciseSeries();
    }
    , RecalculateExerciseSeries: function () {
        var self = this;
        var series = _.toArray(_.groupBy(self.Exercises(), function (elem) { return elem.SeriesGroup(); }));

        self.Series.removeAll();
        for (var i = 0; i < series.length; i++) {
            var arr = series[i];
            var ser = new ExerciseSerie(arr, self);
            self.Series.push(ser);
        }
        self.Series.sort(function (left, right) { return left.Index() == right.Index() ? 0 : (left.Index() < right.Index() ? -1 : 1) })
    }
    , SetIndexes: function () {
        var self = this;
        var idx = 1;
        _.each(self.Series(), function (elem) {
            _.each(elem.Exercises(), function (subElem) { subElem.Index(idx++); });
        });
    }
    , GetExercisesToStore: function () {
        var self = this;
        var exerces = [];
        var idx = 1;
        var groupIdx = 0;
        _.each(self.Series(), function (elem) {
            var exes = elem.Exercises();
            if (exes.length) {
                groupIdx++;
                _.each(elem.Exercises(), function (subElem) {
                    var tExe = subElem.getData();
                    tExe.Index = idx++;
                    idx++;
                    tExe.SeriesGroup = groupIdx;
                    exerces.push(tExe);
                });
            }
        });
        return exerces;
    }
}

ExerciseSerie = function (subSerie, parent) {
    var self = this;
    self.Parent = parent;

    self.ForceMulti = ko.observable(false);

    self.SeriesGroup = subSerie[0].SeriesGroup();

    self.Exercises = ko.observableArray(_.sortBy(subSerie, function (elem) { return elem.Index(); }));

    self.Exercises.subscribe(self.Exercises_subscribe, self);

    self.Relax = ko.pureComputed({ read: self.Relax_Computed_read, write: self.Relax_Computed_write }, self);
    self.RelaxUnit = ko.pureComputed({ read: self.RelaxUnit_Computed_read, write: self.RelaxUnit_Computed_write }, self);
    self.Series = ko.pureComputed({ read: self.Series_Computed_read, write: self.Series_Computed_write }, self);

    self.RelaxEntry = ko.pureComputed(self.RelaxEntry_Computed, self);

    self.IsSingle = ko.pureComputed(self.IsSingle_Computed, self);
    self.First = ko.pureComputed(self.First_Computed, self);

    self.Index = ko.pureComputed(self.Index_Computed, self);

    self.ShowForceMulti = ko.pureComputed(self.ShowForceMulti_Computed, self);

    self.Relax_Invalid = ko.pureComputed(self.Relax_Invalid_Computed, self);

    self.ProperRelax = ko.pureComputed(self.ProperRelax_Computed, self);

    self.ProperRelaxUnit = ko.pureComputed(self.ProperRelaxUnit_Computed, self);

    self.SetSubElements();
}
ExerciseSerie.prototype = {
    IsSingle_Computed: function () {
        var self = this;

        var hasSeriesVals = self.Relax() || self.Series();

        return self.Exercises().length == 1 && !self.ForceMulti() && !hasSeriesVals;
    }
    , ShowForceMulti_Computed: function () {
        var self = this;

        var hasSeriesVals = self.Relax() || self.Series();

        var first = self.First_Computed();
        var isEdited = first && first.EditMode();

        return self.Exercises().length == 1 && !hasSeriesVals && isEdited;
    }
    , First_Computed: function () {
        var self = this;

        var es = self.Exercises();
        if (!es.length) return null;
        return es[0];
    }

    , Relax_Computed_read: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].Relax();
    }
    , Relax_Computed_write: function (value) {
        var self = this;
        _.each(self.Exercises(), function (elem) { elem.Relax(value); });
    }

    , RelaxUnit_Computed_read: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].RelaxUnit();
    }
    , RelaxUnit_Computed_write: function (value) {
        var self = this;
        _.each(self.Exercises(), function (elem) { elem.RelaxUnit(value); });
    }

    , Series_Computed_read: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].Series();
    }
    , Series_Computed_write: function (value) {
        var self = this;
        _.each(self.Exercises(), function (elem) { elem.Series(value); });
    }

    , RelaxEntry_Computed: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].RelaxEntry();
    }

    , Exercises_subscribe: function (newValue) {
        var self = this;
        self.SetSubElements();

        //self.Parent.SetIndexes();
        //self.Parent.RecalculateExerciseSeries();

        if (!self.Exercises().length) {
            self.Parent.Series.remove(self);
        }
    }
    , SetSubElements: function () {
        var self = this;
        _.each(self.Exercises(), function (elem) {
            elem.Relax(self.Relax());
            elem.RelaxUnit(self.RelaxUnit());
            elem.Series(self.Series());
            elem.ParentList = self.Exercises;
            elem.SeriesGroup(self.SeriesGroup);

            var prevSerie = elem.ParentSerie();
            if (prevSerie && prevSerie != self) {
                prevSerie.Exercises.remove(elem);
            }
            elem.ParentSerie(self);
        });
    }

    , Index_Computed: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return 0;
        return es[0].Index();
    }


    , validByUnit: function (value, unitEntry) {
        if (!value) return false;
        if (!unitEntry || !unitEntry.ValidRegEx) return false;
        var patt = new RegExp(unitEntry.ValidRegEx);
        return !patt.test(value);
    }
    , Relax_Invalid_Computed: function () {
        var self = this;
        return self.validByUnit(self.Relax(), self.RelaxEntry());
    }
    , ProperRelax_Computed: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].ProperRelax();
    }
    , ProperRelaxUnit_Computed: function () {
        var self = this;
        var es = self.Exercises();
        if (es.length == 0) return '';
        return es[0].ProperRelaxUnit();
    }
}

ExcercisesChartContainer = function (baseModel) {
    var self = this;

    if (!baseModel) {
        baseModel = {
            totalLength: null,
            ClubZones: null,
            PlanInfo: null,
            CurrentStepPosition: null,
            BlocksLegendZeroPercent: null,
            BlocksLegendType: null
        }
    }

    self.Manager = baseModel || null;

    self.totalLength = baseModel.totalLength || ko.observable(null);

    self.ClubZones = baseModel.ClubZones || ko.observableArray([]);
    self.TimeAxeEntries = ko.pureComputed(function () {
        var length = self.totalLength();
        var step = 0;
        if (length <= 10 * 60) { //10min - step:1min
            step = 60;
        } else if (length <= 20 * 60) { //20min - step:2min
            step = 60 * 2;
        } else if (length <= 60 * 60) { //60min - step:5min
            step = 60 * 5;
        } else if (length <= 120 * 60) { //120min - step:10min
            step = 60 * 10;
        } else if (length <= 180 * 60) { //180min - step:15min
            step = 60 * 15;
        } else if (length <= 10 * 60) { //300min - step:30min
            step = 60 * 30;
        } else {// step:60min
            step = 60 * 60;
        }

        var result = [];
        for (var t = step; t < length; t += step) {
            result.push({ time: t, percTime: t * 100.0 / length, text: secondsToShortDescFromat(t, true) });
        }
        return result;
    });

    self.ChartDimensionsRatio = ko.observable(890.0 / 130.0);

    self.PlanInfo = baseModel.PlanInfo || ko.observableArray([]).extend({ rateLimit: 50 });

    self.CurrentZoneId = function () {
        if (self.Manager && self.Manager.CurrentZoneId)
            return self.Manager.CurrentZoneId();
        return 0;
    }

    self.PlanInfoPolyline = ko.pureComputed(function () {
        var result = ' ';
        var pis = self.PlanInfo();
        var chartDimensionsRatio = self.ChartDimensionsRatio();
        if (pis) {
            for (var i = 0; i < pis.length; i++) {
                var pi = pis[i];
                if (pi.ChartLine) {
                    result += pi.ChartLine;
                } else {
                    result += pi.PercStartTime * chartDimensionsRatio + ',' + pi.PrevPercTop + ' ' + ((pi.PercStartTime + pi.PercDurationTime) * chartDimensionsRatio) + ',' + pi.PercTop + ' ';
                }
            }
        }
        return result;
    });
    /*
        self.PlanInfoPath = ko.pureComputed(function () {
            var result = '';
            var pis = self.PlanInfo();
            if (pis && pis.length) {
                var pi = pis[0];
                var prevX = pi.PercStartTime;
                var prevY = pi.PrevPercTop;
                result += 'M ' + pi.PercStartTime + ' ' + pi.PrevPercTop + ' ';
                for (var i = 0; i < pis.length; i++) {
                    pi = pis[i];
                    if (i != 0) {
    
                    } else {
                        result += 'q ' + pi.PercDurationTime + ' ' + pi.PercTop + ' ';
                    }
                }
            }
            return result;
        });*/

    self.CurrentStepPosition = baseModel.CurrentStepPosition || ko.observable(0);

    self.Maxes = {};

    self.BlocksLegendZeroPercent = baseModel.BlocksLegendZeroPercent || ko.observable(0);
    self.BlocksLegendType = baseModel.BlocksLegendType || ko.observable(null);
    self.BlocksLegend = ko.pureComputed(function () {
        if (self.BlocksLegendType() == 'hr') {
            var zero = self.BlocksLegendZeroPercent();
            var modifier = 100.0 / (100 - zero);
            var zonesMap = []
            var l = _.filter(self.ClubZones(), function (z) {
                if (zonesMap['' + z.BaseId]) return false;
                zonesMap['' + z.BaseId] = z;
                return z.ZoneTypeId == 1 && z.BaseId != 1;
            });

            var minLower = _.min(l, function (z) { return z.LowerBoundNr });
            if (minLower.LowerBoundNr && zero && minLower.LowerBoundNr > zero) {
                l.push({
                    BaseId: 1
                   , Color: "#999"
                   , Id: 0
                   , LowerBound: zero
                   , LowerBoundNr: zero
                   , Name: ""
                   , Selected: false
                   , Text: ""
                   , TrainingTypeId: minLower.TrainingTypeId
                   , UpperBound: minLower.LowerBound
                   , UpperBoundNr: minLower.LowerBoundNr - 1
                   , Value: "0"
                   , ZoneSetId: 0
                   , ZoneTypeId: 1
                });
            }
            l = _.map(l, function (z) {
                var res = {
                    Color: z.Color,
                    Top: (100 - z.UpperBoundNr - 1) * modifier,
                    Height: (z.UpperBoundNr - z.LowerBoundNr + (z.UpperBoundNr < 100 ? 1 : 0)) * modifier,
                    BaseId: z.BaseId,
                    Current: ko.pureComputed(function () {
                        return z.BaseId == self.CurrentZoneId();
                    })
                };

                if (res.Top < 0) {
                    res.Top = 0;
                }
                return res;
            });
            return l;
        }
    });
    self.ZonesColorMap = baseModel.ZonesColorMap || {};
    if (baseModel.getZoneColor) self.getZoneColor = baseModel.getZoneColor;

    self.getExcercise = function (id) { return self.Manager && self.Manager.getExcercise ? self.Manager.getExcercise(id) : null; };

    _.bindAll(self
              , "initByModel", "setZones", "getZoneColor"
               );
}
ExcercisesChartContainer.prototype = {
    setZones: function (result) {
        if (result.Zones) {
            koArrayCopy(result.Zones, this.ClubZones);
            this.SetZonesColorMap();
        }
    },
    initByModel: function (result) {
        var self = this;

        if (result) {

            var p = result.plan;
            if (p && p.length) {
                var types = _.groupBy(p, 'Type');
                var maxes = self.Maxes;

                for (var k in types) {
                    maxes[k] = _.max(types[k], function (elem) { return Math.max(elem.LowerValue, elem.UpperValue); }).UpperValue;
                }
                var last = p[p.length - 1];
                var totalLength = last.StartTime + last.DurationTime;

                self.totalLength(totalLength);

                if (types.hr) {
                    maxes.hr = 100.0 * types.hr[0].UpperValue / (types.hr[0].UpperPercentValue || 1);
                    if (maxes.hr > 220) maxes.hr = 220;
                }
                var mostComonn = _.max(types, function (t) { return t.length; })[0];
                self.BlocksLegendType(mostComonn.Type);
                self.BlocksLegendZeroPercent(mostComonn.ZeroPercentValue);

                var prev = null;
                koArrayCopyComplex(p, self.PlanInfo, false, function (elem) {
                    var max = maxes[elem.Type];
                    var block = new CalcInfo(totalLength, elem, max, self.Manager, prev, self);
                    prev = block;
                    return block;
                });
            } else {
                self.BlocksLegendType('hr');
                self.BlocksLegendZeroPercent(40);
                self.totalLength(result.eventLength || 0);
            }
        }

    },

    getZoneColor: function (zoneId) {
        var self = this;
        var color = self.ZonesColorMap['' + zoneId];
        if (color) return color;

        switch (zoneId) {
            case 1: return '#eee';
            case 5: return '#454443';
            case 6: return '#E7412B';
            case 7: return '#F8B133';
            case 8: return '#97C672';

            case 1000: return '#eee';
            case 1001: return '#97C672';
            case 1002: return '#F8B133';
            case 1003: return '#E7412B';
            case 1004: return '#5B5A59';
            case 1005: return '#454443';
            case 1006: return '#333231';
        }
    },
    SetZonesColorMap: function () {
        var self = this;
        var clubZones = self.ClubZones();
        if (clubZones) {
            _.each(clubZones, function (zone) {
                self.ZonesColorMap['' + zone.Id] = zone.Color;
                self.ZonesColorMap['' + zone.BaseId] = zone.Color;
            });
        }
    }
}




CalcInfo = function (totlaLength, serverModel, maxVal, manager, prev, parent) {
    var self = $.extend(this, serverModel);

    self.Manager = manager;
    self.Parent = parent;

    self.Current = ko.observable(false);
    self.Current.subscribe(self.UpdateChartValue, self);

    if (self.UpperValue < self.LowerValue) {
        self.UpperValue = serverModel.LowerValue;
        self.LowerValue = serverModel.UpperValue;
    }

    self.TotlaLength = totlaLength;

    var zeroedMax = (maxVal || serverModel.ZeroValue) - serverModel.ZeroValue;

    self.PercStartTime = 100.0 * serverModel.StartTime / totlaLength;
    self.PercDurationTime = 100.0 * serverModel.DurationTime / totlaLength;

    self.PercUpperValue = 100.0 * (serverModel.UpperValue - serverModel.ZeroValue) / zeroedMax;
    self.PercLowerValue = 100.0 * (serverModel.LowerValue - serverModel.ZeroValue) / zeroedMax;

    if (self.PercUpperValue < 0) self.PercUpperValue = 0;
    if (self.PercLowerValue < 0) self.PercLowerValue = 0;

    self.PercTop = 100 - self.PercUpperValue;
    if (self.PercTop < 0) self.PercTop = 0;

    if (self.LoadChangeMode == 'upto' || self.LoadChangeMode == 'downto') {
        self.PrevPercTop = prev ? prev.PercTop : 100;
    } else {
        self.PrevPercTop = self.PercTop;
    }


    self.PercLowerTop = 100 - self.PercLowerValue;
    if (self.PercLowerTop < 0) self.PercLowerTop = 0;

    if (self.LoadChangeMode == 'upto' || self.LoadChangeMode == 'downto') {
        self.PrevPercLowerTop = prev ? prev.PercLowerTop : 100;
    } else {
        self.PrevPercLowerTop = self.PercLowerTop;
    }

    self.TrainingPointsContainerModel = null;
    if (serverModel.TrainingPointsContainerModel) {
        self.TrainingPointsContainerModel = new TrainingPointsContainerModel(self);
        self.TrainingPointsContainerModel.initByModel(serverModel.TrainingPointsContainerModel);
    }

    var chartDimensionsRatio = self.Parent.ChartDimensionsRatio();
    if (serverModel.TrainingPointsContainerModel && serverModel.TrainingPointsContainerModel.Points && serverModel.TrainingPointsContainerModel.Points.length) {
        var line = '';
        var ps = serverModel.TrainingPointsContainerModel.Points;
        for (var i = 0, len = ps.length; i < len; i++) {
            var p = ps[i];
            line += self.GetPercPointTime(p[0], chartDimensionsRatio) + ',' + self.GetPercPointVal(p[1], zeroedMax) + ' ';
        }
        self.ChartLine = line;
    }

    self.PoligonPoints = '' + (self.PercStartTime * chartDimensionsRatio) + ',' + self.PrevPercLowerTop + ' ' + (self.PercStartTime * chartDimensionsRatio) + ',' + self.PrevPercTop + ' ' + ((self.PercStartTime + self.PercDurationTime) * chartDimensionsRatio) + ',' + self.PercTop + ' ' + ((self.PercStartTime + self.PercDurationTime) * chartDimensionsRatio) + ',' + self.PercLowerTop;

    self.LowerPoligonPoints = '' + (self.PercStartTime * chartDimensionsRatio) + ',100 ' + (self.PercStartTime * chartDimensionsRatio) + ',' + self.PrevPercLowerTop + ' ' + ((self.PercStartTime + self.PercDurationTime) * chartDimensionsRatio) + ',' + self.PercLowerTop + ' ' + ((self.PercStartTime + self.PercDurationTime) * chartDimensionsRatio) + ',100';
    self.BlockRectangleX = self.PercStartTime * chartDimensionsRatio;
    self.BlockRectangleWidth = self.PercDurationTime * chartDimensionsRatio;


    self.ZoneColor = self.Parent.getZoneColor(self.ZoneId);
    self.Color = ko.pureComputed(self.Color_compute, this);

    self.Excercise = self.Parent.getExcercise(self.ExerciseId);

    self.SecondsToEnd = ko.pureComputed(self.SecondsToEnd_compute, this);

    self.ChartValue = ko.observable(0);
    self.CurrentChartValue = ko.pureComputed(self.CurrentChartValue_compute, this);
    self.ChartValueTimeout = null;
}
CalcInfo.prototype.Color_compute = function () {
    /*if (this.Current()) {
        return this.ZoneColor || '#fff';
    } else {
        return '';
    }*/
    return this.ZoneColor || '#fff';
}

CalcInfo.prototype.SecondsToEnd_compute = function () {
    var self = this;
    if (!self.Current()) return 0;

    var secs = (self.Manager && self.Manager.SecondsFromStart) ? self.Manager.SecondsFromStart() : 0;

    if (secs <= self.StartTime) return self.DurationTime;

    var diff = self.DurationTime - (secs - self.StartTime);
    if (diff < 0) return 0;

    return diff;
}

CalcInfo.prototype.GetPercPointTime = function (val, chartDimensionsRatio) {
    var self = this;
    return 100.0 * (self.StartTime + val) / self.TotlaLength * chartDimensionsRatio;
}
CalcInfo.prototype.GetPercPointVal = function (val, zeroedMax) {
    var self = this;
    return 100 - 100.0 * (100.0 * val - self.ZeroPercentValue) / (100 - self.ZeroPercentValue);
}

CalcInfo.prototype.ChartValueCompute = function () {
    var self = this;
    if (!self.TrainingPointsContainerModel || !self.Current()) return 0;

    var secs = (self.Manager && self.Manager.SecondsFromStart) ? self.Manager.SecondsFromStart() : 0;

    if (secs <= self.StartTime) return 0;

    var diff = secs - self.StartTime;
    if (diff > self.DurationTime) return 0;

    var points = self.TrainingPointsContainerModel.Points();
    var pointsLength = points.length;
    var lastValueIdx = self.LastChartValueIdx || 0;

    for (var i = lastValueIdx; i < pointsLength; i++) {
        var p = points[i];
        if (p[0] > diff) {
            var pp = p;
            if (i > 0) pp = points[i - 1];
            self.LastChartValueIdx = i;
            var result = Math.round((p[1] + pp[1]) / 0.02);
            return result;
        }
    }
    return 0;
}
CalcInfo.prototype.UpdateChartValue = function (newValue) {
    var self = this;
    if (self.ChartValueTimeout) clearTimeout(self.ChartValueTimeout);
    self.ChartValue(self.ChartValueCompute());
    if (self.Current()) {
        self.ChartValueTimeout = setTimeout(function () { self.UpdateChartValue(); }, 1000);
    }
}

CalcInfo.prototype.CurrentChartValue_compute = function () {
    var self = this;
    if (!self.TrainingPointsContainerModel || !self.Current()) {
        if (self.ChartValueTimeout) clearTimeout(self.ChartValueTimeout);
        return 0;
    }

    return self.ChartValue();

}
;function ClubsListModel() {
    var self = this;

    self.MyClubs = ko.observableArray([]);
    self.UserId = 0;
    self.SimpleSignIn = false;

    self.ShowMap = ko.observable(false);

    self.ClubKind = ko.observable('');

    self.TypeManager =
            new TrainingTypeControl({
                mainId: null,
                subId: null,
                typeChangeCallback: null,
                clubId: null,
                multi: false,
                baseCtrl: null,
                disciplines: false,
                multiSubs: false,
                allInstedNone: true,
                dummy: null,
                selectNoneIfNoChoise: true,
                addNoneSubType: false
            });

    self.MarkersCount = ko.observable(0);
    self.NoResults = ko.pureComputed(function () {
        var mc = self.MyClubs();
        return (self.ShowMap() && !self.MarkersCount())
            || (!self.ShowMap() && (mc.length == 0 || mc.length == 1 && mc[0].Id == 0)); //1-pulsstory
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.UserId = serverModel.UserId;

            self.MarkersCount(0);
            if (self.MapExplorerControl && serverModel.MarkersList) {
                var markers = [];
                for (var i = 0, len = serverModel.MarkersList.length; i < len; i++) {
                    var data = serverModel.MarkersList[i];
                    if (data.LocationModel) {
                        var elem = self.CreateClubMarker(data);
                        markers.push(elem);
                    }
                }
                self.MapExplorerControl.ClearMarkers();
                self.MapExplorerControl.AddMarkers(markers);
                self.MarkersCount(markers.length);
            }


            self.MyClubs.removeAll();
            if (serverModel.MyClubs) {
                for (var i = 0, len = serverModel.MyClubs.length; i < len; i++) {
                    var club = new ClubModel(serverModel.MyClubs[i], self);
                    self.MyClubs.push(club);
                }
            }
        }
    };

    self.Init = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Club/GetClubsListModel",
            data: { clubsType: clubExplorerType }
        })
         .done(function (result) {
             self.initByModel(result);
         });
    };



    self.LoadSingleClub = function (clubId, callback) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Club/SingleClubModel",
            data: { clubId: clubId }
        })
         .done(function (result) {
             self.initByModel(result);
             setTimeout(function () {
                 $('.clubs .expander_11.collapsed').click();
             }, 200);
             if (callback) callback();
         });
    };

    self.CurrentClub = ko.observable(null);

    self.SearchPattern = ko.observable('');
    self.Search = function () {
        var selType = self.TypeManager.SelectedType();
        var selTypeId = 0;
        if (selType) selTypeId = selType.Id;
        $.ajax({
            type: "POST",
            url: ContextPath + "Club/GetClubsListModel",
            data: { clubsType: clubExplorerType, pattern: self.SearchPattern(), forMap: self.ShowMap(), kind: self.ClubKind(), trainingTypeId: selTypeId }
        })
         .done(function (result) {
             self.initByModel(result);
         });
    }

    self.TypeManager.TypeChangeCallback = function () {
        self.Search();
    }

    self.ClubKind.subscribe(self.Search);

    self.MapExplorerControl = null;

    self.ToggleMap = function () {
        var initMap = !self.ShowMap();
        self.ShowMap(!self.ShowMap());

        if (initMap) {
            if (!self.MapExplorerControl) {
                var container = $('.exploreMap')[0];
                setTimeout(function () {
                    var mcOptions = { gridSize: 50, maxZoom: 15 };
                    self.MapExplorerControl = new MapExplorerControl(container, self.Search, mcOptions);
                    self.MapExplorerControl.initMap();
                }, 100);
            } else {
                self.MapExplorerControl.initMap();
            }
        } else {
            self.Search();
        }
    }
    self.CreateClubMarker = function (markerData) {
        var marker = new google.maps.Marker({
            map: self.MapExplorerControl.Map,
            position: new google.maps.LatLng(markerData.LocationModel.Latitude, markerData.LocationModel.Longitude),
            label: ('' + markerData.Ids.length),
            count: markerData.Ids.length
        });

        google.maps.event.addListener(marker, 'click', function () {

            self.MapExplorerControl.ShowInfoWindow(markerData.Name, marker, null);
            self.LoadSelectedClubs(markerData.Ids);
        });

        return marker;
    }

    self.LoadSelectedClubs = function (ids) {
        var strData = JSON.stringify(ids);
        $.ajax({
            type: "POST",
            url: ContextPath + "Club/GetClubsByIds",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (serverModel) {
           self.MyClubs.removeAll();
           for (var i = 0, len = serverModel.MyClubs.length; i < len; i++) {
               var club = new ClubModel(serverModel.MyClubs[i], self);
               self.MyClubs.push(club);
           }
       });
    }

}

function ClubModel(serverModel, parent) {
    var self = this;
    self.Parent = parent;

    self.UserDataAcces = ko.observable(false);
    self.AccessCode = ko.observable("");
    self.AcceptTermsAndConditions = ko.observable(false);
    self.InvalidAccessCode = ko.observable(false);
    self.RequireUserDataAccesNotChecked = ko.observable(false);
    self.RequireAcceptTermsAndConditions = ko.observable(false);
    self.FileIds = ko.observableArray([]);
    self.SocialServices = ko.observableArray([]);
    self.Status = ko.observable("");
    self.UserMessage = ko.observable("");
    self.AdminResponse = ko.observable("");
    self.ClubType = ko.observable("");
    self.CoachId = ko.observable(0);
    self.Active = ko.observable(false);

    self.initByModel = function (serverModel) {

        self.Id = serverModel.Id;
        self.Name = serverModel.Name ? serverModel.Name.trim() : '';
        self.LogoId = serverModel.LogoId;
        self.DefaultClub = serverModel.DefaultClub;
        self.Status(serverModel.Status);
        self.PersonId = serverModel.PersonId;
        self.NeedAccessCode = serverModel.NeedAccessCode;
        self.CanRegister = serverModel.CanRegister;
        self.RegisterUrl = serverModel.RegisterUrl;
        self.RequireUserDataAcces = serverModel.RequireUserDataAcces;
        self.OfficialName = serverModel.OfficialName;
        self.RulesFileId = serverModel.RulesFileId;

        self.HeadLine = serverModel.HeadLine;
        self.InfoText = serverModel.InfoText;
        self.FullName = serverModel.FullName;
        self.Altitude = serverModel.Altitude;
        self.Longtitude = serverModel.Longtitude;
        self.PromoText = serverModel.PromoText;
        self.City = serverModel.City;
        self.PostalCode = serverModel.PostalCode;
        self.Street = serverModel.Street;
        self.Phone = serverModel.Phone;
        self.MobilePhone = serverModel.MobilePhone;
        self.MainActivityIcon = serverModel.MainActivityIcon;
        self.MainActivityText = serverModel.MainActivityText;
        self.WebAdress = serverModel.WebAdress;
        self.ContactEmail = serverModel.ContactEmail;
        self.LinkActive = false;
        self.WebAdressLink = serverModel.WebAdressLink;

        self.Active(serverModel.Active);

        self.UserDataAcces(serverModel.UserDataAcces);
        self.AccessCode("");
        self.AcceptTermsAndConditions(false);

        self.InvalidAccessCode(false);
        self.RequireUserDataAccesNotChecked(false);
        self.RequireAcceptTermsAndConditions(false);

        self.UserMessage = ko.observable(serverModel.UserMessage);
        self.AdminResponse = ko.observable(serverModel.AdminResponse);

        self.ClubType(serverModel.ClubType);

        if (self.Status() == '' || self.Status() == 'Resign')
            self.LinkActive = true;

        self.FileIds.removeAll();
        if (serverModel.FileIds) {
            for (var i = 0; i < serverModel.FileIds.length; i++) {
                self.FileIds.push(ko.observable(serverModel.FileIds[i]));
            }
        }

        self.SocialServices.removeAll();
        if (serverModel.SocialServices) {
            for (var i = 0, len = serverModel.SocialServices.length; i < len; i++) {
                var elemList = serverModel.SocialServices[i];
                var elem = new SocialServiceModel(self, elemList.Id, elemList.Name, elemList.Link);
                self.SocialServices.push(new ko.observable(elem));
            }
        }
    }
    if (!serverModel) serverModel = {};
    self.initByModel(serverModel);

    self.TranslatedStatus = ko.computed(function () {
        switch (self.Status()) {
            case "IsIn": return translations['YouAreInClubCommunity'];
            case "Rejected": return translations['NotAddedToClubCommunity'];
            case "WasAndRejected": return translations['RemovedFromClubCommunity'];
            case "Wants": return translations['WaitingForAccept'] + "<br /> ";
            default /*"none"*/: return translations['JoinToClub'];
        }
        return '';
    });
    self.StateIcon = ko.computed(function () {

        if (self.Id == 0) {
            return "zapiszsie_ok_50 noBottomIcon";
        }
        else {
            switch (self.Status()) {
                case "IsIn": return "zapiszsie_ok_50";
                case "Rejected": return "log_out_55 rejected";
                case "WasAndRejected": return "log_out_55 rejected";
                case "Wants": return "porady_45";
                default /*"none"*/: return "zapiszsie_50";
            }
        }

        return '';
    }, this);

    self.LogoUrl = ko.computed(function () {
        return ContextPath + "Image/GetImage?id=" + self.LogoId;
    });
    self.RulesUrl = ko.pureComputed(function () {
        return ContextPath + "Document/GetImage?id=" + self.RulesFileId;
    });


    self.IsIn = ko.computed(function () {
        return self.Status() == "IsIn";
    });

    self.GroupsUrl = ko.computed(function () {
        return ContextPath + "Club/ChooseGroups?clubId=" + self.Id;
    });

    self.joinResign = function () {
        var stat = self.Status();
        if (stat == "IsIn" || stat == "Wants") {
            if (self.LinkActive) {
                self.resign();
            }
        }
        else {
            if (self.RegisterUrl) {
                var win = window.open(self.RegisterUrl, '_blank');
                win.focus();
            } else {
                self.join();
            }
        }
    }

    self.join = function () {
        if (self.Parent.UserId != 0) {
            self.Parent.CurrentClub(self);

            var mod = $('#joinModalAccessCode');
            mod.one('shown', function () {
                JsRequestLog('club.js', 'join opened', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
            });
            mod.one('hidden', function () {
                JsRequestLog('club.js', 'join hidden', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
            });
            mod.modal('show');
            JsRequestLog('club.js', 'join', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
        }
    }

    self.resign = function () {
        if (self.Parent.UserId != 0) {
            self.Parent.CurrentClub(self);

            var mod = $('#resignModal');
            mod.one('shown', function () {
                JsRequestLog('club.js', 'resign opened', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
            });
            mod.one('hidden', function () {
                JsRequestLog('club.js', 'resign hidden', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
            });
            mod.modal('show');
            JsRequestLog('club.js', 'resign', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
        }
    }

    self.AfterJoinCallback = null;
    self.ValidDataAccess = function () {
        self.RequireUserDataAccesNotChecked(self.RequireUserDataAcces && !self.UserDataAcces());
        return !self.RequireUserDataAccesNotChecked();
    }
    self.coreJoin = function () {

        self.InvalidAccessCode(self.NeedAccessCode && !self.AccessCode());
        self.RequireUserDataAccesNotChecked(self.RequireUserDataAcces && !self.UserDataAcces());
        self.RequireAcceptTermsAndConditions(!self.AcceptTermsAndConditions());

        JsRequestLog('club.js', 'coreJoin',
            ' InvalidAccessCode:' + self.InvalidAccessCode()
            + ' DataAccesNotChecked:' + self.RequireUserDataAccesNotChecked()
            + ' RulesNotChecked:' + self.RequireAcceptTermsAndConditions());

        if (self.InvalidAccessCode() || self.RequireUserDataAccesNotChecked() || self.RequireAcceptTermsAndConditions()) return;

        $.ajax({
            type: "POST",
            url: ContextPath + "Club/Join",
            data: {
                clubId: self.Id,
                accessCode: self.AccessCode(),
                clubCanAccessData: self.UserDataAcces(),
                UserMessage: self.UserMessage(),
                coachId: self.CoachId()
            }
        })
         .done(function (result) {
             self.SimpleSignIn = false;
             if (typeof (result.InvalidAccessCode) != "undefined") {
                 self.InvalidAccessCode(result.InvalidAccessCode);
                 self.RequireUserDataAccesNotChecked(result.RequireUserDataAcces);
             } else {
                 self.initByModel(result);
                 userBarModel.init(true);
                 $('#joinModalAccessCode').modal('hide');
                 $('.modal').modal('hide');
                 if (self.AfterJoinCallback) self.AfterJoinCallback(result);
             }
         });
    }

    self.coreResign = function () {
        var expType = 'none';
        if (typeof (clubExplorerType) != 'undefined') expType = clubExplorerType;

        $.ajax({
            type: "POST",
            url: ContextPath + "Club/Resign",
            data: { clubId: self.Id, clubsType: expType }
        })
         .done(function (result) {
             if (result != "0") {
                 if (self.Parent) self.Parent.initByModel(result);

                 $('#resignModal').modal('hide');
                 ReloadClubs();
             }
         });
    }

    self.CloseJoinModal = function () {
        JsRequestLog('club.js', 'CloseJoinModal', 'UserId=' + self.Parent.UserId + '&clubId=' + self.Id);
        $('#joinModal').modal('hide');
    }

    self.ChangeUserDataAcces = function () {
        if (self.UserDataAcces() && self.RequireUserDataAcces) {
            //show msg  - zgoda jest wymagana przez, mozesz sie wypisa�
            //alert("nie mo�na")
            ShowEmptyMsg(translations.CannotRevokeAcceptanceClubDataAccess, translations.CannotRevokeAcceptance)
        } else {
            self.UserDataAcces(!self.UserDataAcces());
            $.ajax({
                type: "POST",
                url: ContextPath + "Club/ChangeUserDataAccess",
                data: { clubId: self.Id, accept: self.UserDataAcces() }
            })
         .done(function (result) {
             if (result && result != "0") {
                 self.initByModel(result);
             }
         });
        }
    }

    self.markDefault = function () {
        if (self.Parent.UserId != 0 && !self.DefaultClub && self.Status() == "IsIn") {

            $.ajax({
                type: "POST",
                url: ContextPath + "Club/SetCurrent",
                data: { clubId: self.Id }
            })
             .done(function (result) {
                 self.Parent.initByModel(result);
             });
        }
        return true;
    }

    self.PromoTextFormatted = ko.computed(function () {
        if (self.PromoText != null) {
            return self.PromoText.replace(/(?:\r\n|\r|\n)/g, '<br />');
        }
        else {
            return '';
        }
    });

    self.HasImage = ko.computed(function () {
        return (self.FileIds().length > 0 && self.FileIds()[0]() != 0);
    });

    self.ShowAddress = ko.computed(function () {
        var result = false;

        if (self.FullName != null && self.FullName != "")
            result = true;

        if (self.City != null && self.City != "")
            result = true;

        if (self.Street != null && self.Street != "")
            result = true;

        if (self.Phone != null && self.Phone != "")
            result = true;

        if (self.MobilePhone != null && self.MobilePhone != "")
            result = true;

        return result;
    });

    self.ShowSeparator = ko.computed(function () {
        var result = false;

        if (self.City != null && self.City != "" &&
            self.Street != null && self.Street != "")
            result = true;

        return result;
    });

    self.HeadLineDisplayText = ko.pureComputed(function () {
        if (!self.Active()) return translations.CommunityIsInactive.replace("###", self.Name);
        switch (self.Status()) {
            case "IsIn": return translations.YouAreInCommunity + ' ' + self.Name;
            case "Rejected": return translations['NotAddedToClubCommunity_' + self.ClubType()];
            case "WasAndRejected": return translations['RemovedFromClubCommunity_' + self.ClubType()];
            case "Wants": return translations['WaitingForAccept_' + self.ClubType()];
            default /*"none"*/: return self.HeadLine;
        }
        return '';
    });

    self.HeadLineClass = ko.pureComputed(function () {
        if (!self.Active()) return 'gray';
        switch (self.Status()) {
            case "IsIn": return ((typeof (currentClubId) != 'undefined' && currentClubId != 0) ? 'modrak' : 'gray');
            case "Rejected": return 'white';
            case "WasAndRejected": return 'white';
            case "Wants": return 'white';
            default /*"none"*/: return 'red';
        }
        return '';
    });

    self.activateLink = function () {
        self.LoadCoachesInfo();

        if (self.Status() == 'IsIn') {
            self.LinkActive = !self.LinkActive;
        }
    }

    self.CoachesList = ko.observableArray([]);
    self.CoachesListLoaded = false;
    self.LoadCoachesInfo = function () {
        if (!self.CoachesListLoaded) {
            self.CoachesListLoaded = true;

            $.ajax({
                type: "POST",
                url: ContextPath + "Coaches/GetClubCoaches",
                data: { clubId: self.Id }
            })
            .done(function (result) {
                self.CoachesList.removeAll();
                if (result) {
                    for (var i = 0, len = result.length; i < len; i++) {
                        var elem = new CoachModel(self, true);
                        elem.initByModel(result[i]);
                        self.CoachesList.push(elem);
                    }
                }
            });
        }
    }
    self.RealCoaches = ko.computed(function () {
        return _(self.CoachesList()).filter(function (coach) { return coach.RealCoach(); });
    });
    self.Instructors = ko.computed(function () {
        return _(self.CoachesList()).filter(function (coach) { return !coach.RealCoach(); });
    });

    self.ClubRulesText = ko.pureComputed(function () {
        return translations.TermsAndConditionsClub.replace('{0}', self.Name).replace('{1}', (ContextPath + "Document/GetImage?id=" + self.RulesFileId));
    });
    self.ClubDataAccesText = ko.pureComputed(function () {
        return translations.AgreeToUseOfPersonalDataClub.replace('{0}', self.Name).replace('{0}', self.Name).replace('{1}', self.OfficialName);
    });
}

var clubsListModel = new ClubsListModel();
GeneralModel.addProperty("ClubsListModel", clubsListModel);

function clubExplorerExpanderClick(clubExploreContent) {
    var tileFooterButtonsIcon = clubExploreContent.find('.tileFooterButtonsIcon');
    var saveInfo = clubExploreContent.find('.publishInfoCloudContainer .hrCloud .saveInfo');

    if ($(tileFooterButtonsIcon).hasClass('zapiszsie_ok_50')) {
        $(tileFooterButtonsIcon).removeClass('zapiszsie_ok_50');
        $(tileFooterButtonsIcon).addClass('log_out_55');
        $(saveInfo).html(translations['UnSignFromClub']);
    } else
        if ($(tileFooterButtonsIcon).hasClass('log_out_55') && !$(tileFooterButtonsIcon).hasClass('rejected')) {
            $(tileFooterButtonsIcon).removeClass('log_out_55');
            $(tileFooterButtonsIcon).addClass('zapiszsie_ok_50');
            $(saveInfo).html(translations['YouAreInClubCommunity']);
        }
    returnToPositionAfterDzynClick(clubExploreContent, '.expander_11')
}

;
function ClubInfoModel(parent, coachMode) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.LogoId = ko.observable(0);
    self.FullName = ko.observable('');
    self.Altitude = ko.observable('');
    self.Longtitude = ko.observable('');
    self.Name = ko.observable('');

    self.Status = ko.observable('');
    self.CoachStatus = ko.observable('');
    self.CanRegister = ko.observable(false);
    self.RegisterUrl = ko.observable('');
    self.ClubType = ko.observable('');

    self.CoachId = ko.observable(0);
    self.CoachMode = ko.observable(coachMode);
    self.UserMsg = ko.observable('');
    self.CoachResponse = ko.observable('');

    self.DisplayImage = ko.computed(function () { return self.LogoId() != 0; });
    self.ImageUrl = ko.computed(function () { return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.LogoId(); });

    self.CurrentClub = ko.pureComputed(function () {
        return self.Id() == ClubId;
    });

    self.IsNotIn = ko.computed(function () {
        return self.Status() != "IsIn";
    });

    self.TranslatedStatus = ko.computed(function () {
        switch (self.Status()) {
            case "IsIn":
                if (!self.Parent.Collapsed || self.Parent.Collapsed()) {
                    if (self.CoachMode()) {
                        return self.CoachTranslatedStatus();
                    } else {
                        return translations['YouAreInClubCommunity'];
                    }
                } else {
                    return translations.SignOut;
                }
            case "Rejected": return translations['NotAddedToClubCommunity'];
            case "Wants": return translations['WaitingForAccept'];
            default /*"none"*/: return self.ClubType() == 'coach' ? translations.JoinCoach : translations.JoinCoachClub;
        }
        return '';
    });
    self.CoachTranslatedStatus = ko.computed(function () {
        switch (self.CoachStatus()) {
            case "IsIn":
                if (!self.Parent.Collapsed || self.Parent.Collapsed()) {                    
                    return translations.YouAreInCoachClubCommunity;                    
                } else {
                    return translations.SignOut;
                }
            case "Rejected": return translations['NotAddedToClubCommunity'];
            case "Wants": return translations['WaitingForAccept'];
            default /*"none"*/: return translations.JoinCoach;
        }
        return '';
    });
    self.StateIcon = ko.computed(function () {
        switch (self.Status()) {
            case "IsIn":
                if (!self.Parent.Collapsed || self.Parent.Collapsed()) {
                    if (self.CoachMode()) {
                        return self.CoachStateIcon();
                    } else {
                        return "zapiszsie_ok_50";
                    }
                } else {
                    return "log_out_55";
                }
            case "Rejected": return "log_out_55 rejected";
            case "Wants": return "porady_45";
            default /*"none"*/: return "zapiszsie_50";
        }
        return '';
    }, this);
    self.CoachStateIcon = ko.computed(function () {
        switch (self.CoachStatus()) {
            case "IsIn":
                if (!self.Parent.Collapsed || self.Parent.Collapsed()) {
                    return "zapiszsie_ok_50";
                } else {
                    return "log_out_55";
                }
            case "Rejected": return "log_out_55 rejected";
            case "Wants": return "porady_45";
            default /*"none"*/: return "zapiszsie_50";
        }
        return '';
    }, this);

    self.ShowJoin = ko.computed(function () {
        return self.Id() != 0 && self.CanRegister();// && (self.Status() == "none" || self.Status() == "");
    });

    self.join = function () {
        var stat = self.Status();
        var joinToCoach = false;
        if (self.CoachMode()) {
            var cStat = self.CoachStatus();
            if (stat == 'IsIn' || stat == 'Wants') {
                stat = cStat;
                joinToCoach = true;
            }
        }
        if (stat != 'IsIn' && stat != 'Wants' && self.CanRegister()) {
            if (joinToCoach) {
                self.CoachJoin();
            } else {
                if (self.RegisterUrl()) {
                    var win = window.open(self.RegisterUrl, '_blank');
                    win.focus();
                } else {
                    if (self.Parent && self.Parent.Parent && self.Parent.Parent.CurrentClub) {
                        self.Parent.Parent.CurrentClub(self);
                    }
                    self.ExternalJoin();
                }
            }
        } else {
            if (self.Parent.Collapsed && !self.Parent.Collapsed() && (stat == "IsIn" || stat == "Wants")) {
                if (self.Parent && self.Parent.Parent && self.Parent.Parent.CurrentClub) {
                    self.Parent.Parent.CurrentClub(self);
                }
                if (joinToCoach) {
                    self.CoachResign();
                } else {
                    self.ExternalResign();
                }
            }
        }
    }
    self.Close = function () {
        $('#joinModal').modal('hide');
    }

    if (typeof (CoachModel) != 'undefined') {
        self.CoachJoin = function () {
            simpleClubJoin.JoinCallback = self.coreCoachJoin;
            simpleClubJoin.UserMsg(self.UserMsg());
            simpleClubJoin.ClubResponse(self.CoachResponse());
            simpleClubJoin.CoachJoin(true);
            simpleClubJoin.Show();
        };
        self.coreCoachJoin = function () {
            self.UserMsg(simpleClubJoin.UserMsg());
            $.ajax({
                type: "POST",
                url: ContextPath + "Coaches/Join",
                data: { coachId: self.CoachId(), msg: self.UserMsg() }
            })
            .done(function (result) {
                self.CoachStatus(result);
            });
        };

        self.CoachResign = function () {

            modalAskModel.Show(translations.CoachResignTitle, translations.CoachResignMsg, function () {
            $.ajax({
                            type: "POST",
                            url: ContextPath + "Coaches/Resign",
                            data: { coachId: self.CoachId() }
                        })
                        .done(function (result) {
                            self.CoachStatus(result);
                        });
            }, translations.CoachResing)

            
        };
    }

    if (typeof (ClubModel) != 'undefined') {
        self.ClubModel = ko.observable(new ClubModel());
        self.AfterJoinCallback = function (serverModel) {
            self.Status(serverModel.Status);
            if (self.CoachMode()) {
                if (self.Parent && self.Parent.Parent && self.Parent.Parent.Search) {
                    self.Parent.Parent.Search();
                }
            }
        };
        self.LoadClubDetailsModel = function (callback) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Club/GetClubInfoModel",
                data: { id: self.Id() }
            })
            .done(function (serverModel) {
                self.ClubModel(new ClubModel(serverModel));
                self.ClubModel().CoachId(self.CoachId());
                if (callback) callback(serverModel);
            });
        }
        self.ExternalJoin = function () {
            if (!self.ClubModel() || !self.ClubModel().Id) {

                self.LoadClubDetailsModel(function (serverModel) {
                    self.ClubModel().AfterJoinCallback = self.AfterJoinCallback;
                    $('#joinModalAccessCode').modal('show');
                });

            } else {
                $('#joinModalAccessCode').modal('show');
            }
        }

        self.ExternalResign = function () {
            if (!self.ClubModel() || !self.ClubModel().Id) {

                self.LoadClubDetailsModel(function (serverModel) {
                    $('#resignModal').modal('show');
                });

            } else {
                $('#resignModal').modal('show');
            }
        }
    }


    self.init = function () {
        self.Id(0);
        self.LogoId(0);
        self.FullName('');
        self.Altitude('');
        self.Longtitude('');
        self.Name('');
        self.CoachStatus('');
        self.CoachId(0);
        self.UserMsg ('');
        self.CoachResponse('');
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.LogoId(serverModel.LogoId);
            self.FullName(serverModel.FullName);
            self.Altitude(serverModel.Altitude);
            self.Longtitude(serverModel.Longtitude);
            self.Name(serverModel.Name);

            self.Status(serverModel.Status);
            self.CoachStatus(serverModel.CoachStatus);
            self.CanRegister(serverModel.CanRegister);
            self.RegisterUrl(serverModel.RegisterUrl);
            self.ClubType(serverModel.ClubType);

            self.CoachId(serverModel.CoachId);

            self.UserMsg(serverModel.UserMsg);
            self.CoachResponse(serverModel.CoachResponse);
        }
    };

}

function SimpleClubJoin() {
    var self = this;
    self.JoinCallback = null;

    self.CoachJoin = ko.observable(false);
    self.LabelTxt = ko.computed(function () { return self.CoachJoin() ? translations.JoinCoachLabel : translations.Join; });

    self.UserMsg = ko.observable('');
    self.ClubResponse = ko.observable('');

    self.Show = function () {
        $('#joinGroupModal').modal('show');
    }
    self.Join = function () {
        if (self.JoinCallback) {
            self.JoinCallback();
            $('#joinGroupModal').modal('hide');
        }
    }
}
var simpleClubJoin = new SimpleClubJoin();
GeneralModel.addProperty("SimpleClubJoin", simpleClubJoin);;$(document).ready(function () {
    $('table.reportCollapse').collapsible();
});

function showTrainingsModals(modalDialog) {
    $(modalDialog).modal("show");
}

function StatChartData(parent, powerLevelType) {
    var self = this;

    self.parent = parent;
    self.colorIndex = 1;
    self.name = '';
    self.label = '';
    self.unit = '';
    self.icon = '';
    self.description = '';
    self.defaultFlag = '';
    self.windowLength = 0;
    self.level = 0;
    self.series = 0;
    self.colors = [];
    self.values = [];
    self.timeLabels = [];
    self.extendedTimeLabels = [];
    self.selected = ko.observable(false);
    self.tooltipValueVisible = ko.observable(false);

    self.powerLevelType = powerLevelType

    self.categoryClickEvent = function () {
        $.each(self.parent.charts(), function (key, value) {
            value.selected(false);
        });
        self.selected(true);
        self.parent.selectedChart(self);
        self.parent.refreshChart();
    };

    self.isLineType = function () {
        return self.name == "MeanMaximalPower" || self.name == "PowerLevels";
    };

    self.getOffset = function () {
        if (self.name.startsWith("PowerLevel")) {
            return 0.0;
        } else {
            switch (self.name) {
                case "PowerLevels":
                case "MeanMaximalPower":
                    return 0.0;
                default:
                    return 0.0;
            }
        }
    };

    self.selectClick = function () {
        self.selected(!self.selected());
        self.parent.refreshChart();
    };

    self.deselectClick = function () {
        self.selected(false);
        self.parent.refreshChart();
    };

    self.initByData = function (colorIndex, data) {
        self.colorIndex = colorIndex;
        self.name = data.Name;
        self.label = data.Label;
        self.unit = data.Unit;
        self.icon = data.Icon;
        self.smallIcon = data.SmallIcon;
        self.unibarIconClass = data.UnibarIconClass;
        self.colors = data.Colors;
        self.series = data.Series;
        self.values = data.Values;
        self.timeLabels = data.TimeLabels;
        self.extendedTimeLabels = data.ExtendedTimeLabels;
        self.description = data.Description;
        self.level = data.Level;
        self.yTicksFormatter = self.createYTicksFormatter();
        self.defaultFlag = data.Default;
        self.windowLength = data.WindowLength;
    };

    self.getColor = function () {
        return self.colors[0];
    }

    self.getSubLabelText = function () {
        return translations["ShowChart"] + ": " + self.description;
    }

    self.getDataSeriesNoneZeroMin = function () {
        var min = Number.MAX_VALUE;
        for (var i = 0, len = self.values.length; i < len; i++) {
            var values = self.values[i];
            var series = values.length;
            for (var s = 0; s < self.series; s++) {
                if (values[s] > 0) {
                    min = Math.min(min, values[s]);
                }
            }
        }
        return min;
    }

    self.getDataSeriesMax = function (sumSeries) {
        var max = 0.0;
        for (var i = 0, len = self.values.length; i < len; i++) {
            var values = self.values[i];
            if (sumSeries) {
                var sum = 0.0;
                var series = values.length;
                for (var s = 0; s < self.series; s++) {
                    sum = sum + values[s];
                }
                max = Math.max(max, sum);
            } else {
                var series = values.length;
                for (var s = 0; s < self.series; s++) {
                    max = Math.max(max, values[s]);
                }
            }
        }
        return max;
    }

    self.createYTicksFormatter = function () {
        if (self.name == "DurationTime") {
            return function (value) {
                var hours = Math.floor(value / 3600);
                var minute = Math.floor(value / 60 - 60 * hours);
                if (minute < 10) {
                    minute = '0' + minute;
                }
                return hours + ":" + minute;
            };
        } else if (self.name == "Calories") {
            return function (value) {
                return value.formatNumber(0);
            };
        } else if (self.name == "Trimp") {
            return function (value, axis, details) {
                return value.formatNumber(details ? 1 : 0);
            };
        } else if (self.name == "Distance") {
            return function (value, axis, details) {
                return value.formatNumber(details ? 1 : 0);
            };
        } else if (self.name == "HeartRate") {
            return function (value) {
                return value.formatNumber(0);
            };
        } else if (self.name == "Speed") {

            if (self.pace && self.pace.available) {
                return function (value) {
                    return calculatePaceString(value, self.pace.unit, self.pace.factor, false, true);
                };
            } else {
                return function (value, axis, details) {
                    return value.formatNumber(details ? 1 : 0);
                };
            }
        } else {
            return function (value) {
                return value.formatNumber(0);
            };
        }

    }

    self.getIcon = function () {
        return ContextPath + 'Content/images/icons/' + self.icon + '.png';
    }

    self.getSmallIcon = function () {
        return ContextPath + 'Content/images/icons/' + self.smallIcon + '.png';
    }

    self.getSmallIconURL = function () {
        return 'url(' + self.getSmallIcon() + ')';
    }

    self.getSelectableIcon = function () {
        if (self.selected()) {
            return ContextPath + 'Content/images/icons/' + self.icon.replace("_cze", "") + '_mod' + '.png';
        } else {
            return ContextPath + 'Content/images/icons/' + self.icon + '.png';
        }
    }

    self.getName = function () {
        return translations[self.label];
    }

    self.getNameWithUnit = function () {
        return translations[self.label] + ' ' + self.unit;
    }
}

function StatLevelManager(parent) {
    var self = this;
    self.Parent = parent;

    self.Charts = [];

    self.Name = ko.observable('');

    self.Color = ko.observable('');

    self.SmallIconURL = ko.observable('');

    self.tooltipValueVisible = ko.observable(false);

    self.defaultChart = new StatChartData(parent, true);

    self.addLevel = function (colorIndex, chartData) {
        var chart = new StatChartData(parent, true);
        chart.initByData(colorIndex, chartData);
        self.Charts.push(chart);
        if (self.defaultChart.name == '') {
            self.defaultChart = chart;
            self.Name(self.defaultChart.getNameWithUnit());
            self.Color(self.defaultChart.getColor());
            self.SmallIconURL(self.defaultChart.getSmallIconURL());
        }
        self.Charts.sort(function (chartA, chartB) {
            return chartA.windowLength - chartB.windowLength;
        });
    };

    self.currentLevel = function () {
        var current = self.defaultChart;
        $.each(self.Charts, function (index, chart) {
            if (chart.selected()) {
                current = chart;
            }
        });
        return current;
    };

    self.getLevels = function () {
        return self.Charts;
    };

    self.getSelectedLevels = function () {
        var result = [];
        $.each(self.Charts, function (index, chart) {
            if (chart.selected()) {
                result.push(chart);
            }
        });
        return result;
    };

    self.deselectLevelsClick = function () {
        $.each(self.Charts, function (index, chart) {
            chart.selected(false);
        });
        self.Parent.refreshChart();
    };

    self.getDataSeriesMax = function () {
        var yMax = Number.MIN_VALUE;
        $.each(self.Charts, function (index, chart) {
            if (chart.selected()) {
                yMax = Math.max(yMax, chart.getDataSeriesMax());
            }
        });
        return yMax;
    };
}

function StatChartContainer() {
    var self = this;

    self.categoryLinesOptions = {
        show: false,
        fill: true,
        steps: false
    };
    self.categoryBarsOptions = {
        show: true,
        align: "center",
        barWidth: 0.5,
        lineWidth: 1
    };
    self.auxilaryLinesOptions = {
        show: true,
        lineWidth: 1
    };
    self.auxilaryBarsOptions = {
        show: false
    };

    self.customLabelsVisible = false;

    self.charts = ko.observableArray([]);

    self.selectedChart = ko.observable(new StatChartData(self, false));

    self.speedChart = ko.observable(new StatChartData(self, false));

    self.averageHrChart = ko.observable(new StatChartData(self, false));

    self.powerChart = ko.observable(new StatChartData(self, false));

    self.statLevelManager = ko.observable(new StatLevelManager(self));

    self.xValue = ko.observable('');

    self.InitByData = function (data, selectedCharts) {
        var previousSelection = (selectedCharts.length > 0);

        for (var i = 0, len = data.TrainingStatCharts.length; i < len; i++) {
            var chart = new StatChartData(self, false);
            chart.initByData(i + 2, data.TrainingStatCharts[i]);
            self.charts.push(chart);
            if (self.checkSelection(chart, selectedCharts, i == 0)) {
                self.selectedChart(chart);
            }
            if (chart.name == "DurationTime") {
                self.durationChart = chart;
            }
        }
        self.DurationInHrZones = data.DurationInHrZones;
        self.DurationInPowerZones = data.DurationInPowerZones;
        var lastColorIndex = data.TrainingStatCharts.length;
        self.speedChart().pace = {};
        self.speedChart().pace.available = data.PaceMetaData.Available;
        self.speedChart().pace.unit = data.PaceMetaData.Unit;
        self.speedChart().pace.factor = data.PaceMetaData.Factor;
        self.speedChart().initByData(lastColorIndex + 1, data.SpeedChart);
        self.averageHrChart().initByData(lastColorIndex + 2, data.AvarageHrChart);
        self.powerChart().initByData(lastColorIndex + 3, data.PowerChart);
        // power charts
        $.each(data.LevelCharts, function (index, levelChartData) {
            self.statLevelManager().addLevel(lastColorIndex + index, levelChartData);
        });
        // check selection
        self.checkSelection(self.speedChart(), selectedCharts, false);
        self.checkSelection(self.averageHrChart(), selectedCharts, false);
        self.checkSelection(self.powerChart(), selectedCharts, false);
        $.each(self.statLevelManager().Charts, function (index, levelChartData) {
            self.checkSelection(levelChartData, selectedCharts, levelChartData.defaultFlag);
        });
        // max HR
        self.maxHr = data.MaxHr;
    };

    self.getCurrentLevelChart = ko.computed(function () {
        return self.statLevelManager().currentLevel();
    }, self);

    self.checkSelection = function (chart, selectedCharts, defaultSelection) {
        var selected = defaultSelection;
        if (selectedCharts.length > 0) {
            selected = ($.inArray(chart.name, selectedCharts) > -1);
        }
        chart.selected(selected);
        return selected;
    }

    self.getSelectedCharts = function () {
        var result = [];
        $(self.charts()).each(function (index, chart) {
            if (chart.selected()) {
                result.push(chart.name);
            }
        });
        if (self.speedChart().selected()) {
            result.push(self.speedChart().name);
        }
        if (self.averageHrChart().selected()) {
            result.push(self.averageHrChart().name);
        }
        if (self.powerChart().selected()) {
            result.push(self.powerChart().name);
        }

        $.each(self.statLevelManager().Charts, function (index, chart) {
            if (chart.selected()) {
                result.push(chart.name);
            }
        });

        return result;
    };

    self.getTimeLabels = function (chart) {
        var offset = chart.getOffset();
        var timeLabels = [];
        var len = chart.timeLabels.length;
        var mod = 1;
        if (len > 12) {
            mod = Math.round(0.1 * len - 0.5);
        }
        for (var i = 0; i < len; i++) {
            if (i % mod == 0) {
                timeLabels.push([i + offset, chart.timeLabels[i]]);
            }
        }
        return timeLabels;
    }

    self.fillDataSeries = function (chart, emptyChart) {
        var offset = chart.getOffset();
        var dataSeries = [];
        for (var i = 0, len = chart.values.length; i < len; i++) {
            var values = chart.values[i];
            var series = values.length;
            for (var s = 0; s < chart.series; s++) {
                if (i == 0) {
                    var chartData = emptyChart[s];
                    dataSeries.push(chartData);
                }
                var v = 0;
                if (s < series) {
                    v = values[s];
                }
                dataSeries[s].data.push([i + offset, v]);
            }
        }
        return dataSeries;
    }

    self.getEmptyChart = function (chart, colorsVct, applyStack, linesOptions, barsOptions, yAxisNumber) {
        var result = [];
        if (chart.values && chart.values.length && chart.values.length > 0) {
            var series = chart.values[0].length;
            for (var s = 0; s < series; s++) {
                var colorIndex = chart.colorIndex;
                if (chart.colors != null && s < chart.colors.length) {
                    colorIndex = colorsVct.push(chart.colors[s]) - 1;
                }
                var chartData = {
                    data: [],
                    color: colorIndex,
                    lines: linesOptions,
                    bars: barsOptions,
                    yaxis: yAxisNumber,
                    label: chart.getName()
                };
                if (applyStack) {
                    chartData.stack = 0;
                }
                result.push(chartData);
            }
        }
        return result;
    }

    self.createAuxilaryChart = function (chart, colorsVct, index) {
        var tmp = self.getEmptyChart(chart, colorsVct, false, self.auxilaryLinesOptions, self.auxilaryBarsOptions, index);
        if (chart.selected()) {
            tmp = self.fillDataSeries(chart, tmp);
        }
        self.dataSeries = self.dataSeries.concat(tmp);
        return {
            maxY: chart.getDataSeriesMax(!chart.name.startsWith("PowerLevel")),
            minY: chart.getDataSeriesNoneZeroMin(),
            colorIndex: tmp[0].color,
            ticksFormatter: chart.yTicksFormatter
        };
    }

    self.getTooltipForTimeZoneChart = function (currentIndex, valueFormatter, seriesIndex) {
        var tooltipText = '';
        if (self.DurationInHrZones) {
            tooltipText = self.DurationInHrZones[currentIndex][4 - seriesIndex];
        }
        return tooltipText;
    }

    self.getTooltipForPowerZoneChart = function (currentIndex, valueFormatter, seriesIndex) {
        var tooltipText = '';
        if (self.DurationInPowerZones) {
            tooltipText = self.DurationInPowerZones[currentIndex][6 - seriesIndex];
        }
        return tooltipText;
    }

    self.getTooltipForChart = function (s, currentIndex, valueFormatter, unit) {
        var tooltipText = '';
        if (self.dataSeries && self.dataSeries[s] && self.dataSeries[s].data.length > 0) {
            var y = self.dataSeries[s].data[currentIndex][1];
            var tooltipUnit = '';
            var indexTooltip = s - self.currentSeries + 1;
            if (unit.length > 0) {
                tooltipUnit = ' ' + unit;
            }
            var valueText = valueFormatter(y, null, true);
            if ((/\d/.test(valueText))) {
                tooltipText = valueText + tooltipUnit;
            } else {
                tooltipText = valueText;
            }
        }
        return tooltipText;
    }

    self.ticksGenerator = function (min, max) {
        if (max - min > 9) {
            var maxInt = Math.floor(max - min);
            var divider = maxInt / 5;
            var ticks = [];
            for (var i = 0; i < 5; i++) {
                ticks.push(Math.floor(i * divider + min));
            }
            ticks.push(Math.floor(max + 0.5));
            return ticks;
        } else if (min == 0) {
            var ticks = [0];
            for (var i = 1; i < 9; i++) {
                if (max < i) {
                    return ticks;
                }
                ticks.push(i);
            }
            ticks.push(Math.floor(max + 0.5));
            return ticks;
        } else {
            return [Math.floor(min), Math.floor(max)];
        }
    }

    self.drawLabels = function (plot, canvascontext) {
        if (self.selectedChart().name != 'TimeInZonesShort'
            && self.selectedChart().name != 'PowerInZones'
            && self.selectedChart().name != 'PowerLevels') {

            var series = plot.getData()[0];
            if (series) {

                var plotOffset = plot.getPlotOffset();

                var ctx = canvascontext;
                ctx.save();
                ctx.translate(plotOffset.left, plotOffset.top);

                var ps = series.datapoints.pointsize;
                var ax = [series.xaxis, series.yaxis];
                var points = series.datapoints.points;
                if (points.length / ps < 10) {
                    self.customLabelsVisible = true;
                    for (var i = 0; i < points.length; i += ps) {
                        //data coordinates
                        var x = points[i],
                            y = points[i + 1];
                        if (y > 1e-3) {
                            var xPixels = ax[0].p2c(x);
                            var yPixels = ax[1].p2c(y);
                            ctx.font = "15px aller_lightregular";
                            ctx.textAlign = "center";
                            ctx.fillText(self.selectedChart().yTicksFormatter(y, null, true), xPixels, yPixels - 10);
                        }
                    }
                }
                else {
                    self.customLabelsVisible = false;
                }

                ctx.restore();
            }

        }
    };

    self.visiblePowerLevels = ko.computed(function () {
        return self.selectedChart().name == 'PowerLevels';
    }, self);

    self.notVisiblePowerLevels = ko.computed(function () {
        return self.selectedChart().name != 'PowerLevels';
    }, self);

    self.visibleAuxilaryCharts = ko.computed(function () {
        return self.selectedChart().name != 'MeanMaximalPower';
    }, self);

    self.refreshChart = function () {
        var colorsVct = getChartColors(false);

        var selectedChart = self.selectedChart();

        var maxYmain = 100.0;
        if (selectedChart.name == 'PowerLevels') {
            maxYmain = Math.floor(1.1 * self.statLevelManager().getDataSeriesMax() + 1.5);
        } else if (selectedChart.name != 'TimeInZonesShort' && selectedChart.name != 'PowerInZones') {
            maxYmain = Math.floor(1.1 * selectedChart.getDataSeriesMax() + 1.5);
        }
        var minYmain = 0.0;
        if (selectedChart.name == 'MeanMaximalPower') {
            minYmain = 0.9 * selectedChart.getDataSeriesNoneZeroMin();
        }

        var emptyChart;
        if (selectedChart.isLineType()) {
            emptyChart = self.getEmptyChart(selectedChart, colorsVct, selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones", self.auxilaryLinesOptions, self.auxilaryBarsOptions, 1);
        } else {
            emptyChart = self.getEmptyChart(selectedChart, colorsVct, selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones", self.categoryLinesOptions, self.categoryBarsOptions, 1);
        }
        self.dataSeries = self.fillDataSeries(selectedChart, emptyChart);
        var yLabelMain = selectedChart.getName() + ' ' + selectedChart.unit;

        var deltaX = (selectedChart.isLineType() ? 0.0 : 0.25);

        var maxX = selectedChart.values.length - 1;
        var timeLabels = self.getTimeLabels(selectedChart);

        var yaxesData = [];
        yaxesData.push({
            // main category chart
            min: minYmain,
            max: maxYmain,
            labelWidth: 30,
            tickLength: 0,
            ticks: self.ticksGenerator(minYmain, maxYmain),
            tickFormatter: self.selectedChart().yTicksFormatter
        });

        var speedOptions, avgHrOptions, powerOptions, levelOptions;
        if (self.visibleAuxilaryCharts()) {

            if (self.notVisiblePowerLevels()) {
                speedOptions = self.createAuxilaryChart(self.speedChart(), colorsVct, 2);
                avgHrOptions = self.createAuxilaryChart(self.averageHrChart(), colorsVct, 3);
                powerOptions = self.createAuxilaryChart(self.powerChart(), colorsVct, 4);
                var maxY = Math.floor(1.1 * speedOptions.maxY + 1.5);
                yaxesData.push({
                    // speed chart
                    tickLength: 0,
                    position: "right",
                    labelWidth: 30,
                    ticks: self.ticksGenerator(0, maxY),
                    min: 0.0,
                    max: maxY,
                    tickFormatter: speedOptions.ticksFormatter
                });
                yaxesData.push({
                    // averageHR chart                    
                    position: "right",
                    tickLength: 0,
                    labelWidth: 30,
                    ticks: self.ticksGenerator(0.4 * self.maxHr, self.maxHr),
                    min: 0.4 * self.maxHr,
                    max: self.maxHr,
                    tickFormatter: avgHrOptions.ticksFormatter
                });
                maxY = Math.floor(1.1 * powerOptions.maxY + 1.5);
                yaxesData.push({
                    // power chart                    
                    position: "right",
                    tickLength: 0,
                    labelWidth: 30,
                    ticks: self.ticksGenerator(0, maxY),
                    min: 0,
                    max: maxY,
                    tickFormatter: powerOptions.ticksFormatter
                });

            } else {
                // power charts does not have own axis
                $.each(self.statLevelManager().Charts, function (index, levelChart) {
                    if (levelChart.selected()) {
                        self.createAuxilaryChart(levelChart, colorsVct, 1);
                    }
                });
            }
        }

        var series = {
            shadowSize: 0,
        };
        if (selectedChart.isLineType()) {
            series['points'] = {
                show: true,
                radius: 3,
                symbol: "circle"
            }
        }

        var container = $("#userstatreportchart div.placeholderReport");
        $.plot(container, self.dataSeries, {
            xaxis: {
                ticks: timeLabels,
                min: -deltaX,
                max: maxX + deltaX,
                tickLength: 0,
            },
            yaxes: yaxesData,
            grid: {
                show: true,
                backgroundColor: '#ffffff',
                borderWidth: 1,
                hoverable: true,
                marking: [],
            },
            bars: {
                show: true,
                lineWidth: 0,
                fill: true,
                fillColor: { colors: [{ opacity: 1.0 }, { opacity: 1.0 }] }
            },
            legend: {
                show: false
            },
            series: series,
            colors: colorsVct,
            hooks: { drawOverlay: [self.drawLabels] }
        });
        var yaxisLabelMain = $("<div class='axisLabel yaxisLabel labelMain'></div>")
            .text(yLabelMain)
            .appendTo(container);
        yaxisLabelMain.css("margin-top", yaxisLabelMain.width() / 2 - 20);

        container.bind("plothover", function (event, pos, item) {

            if (item) {
                var selectedChart = self.selectedChart();
                var currentSeries = selectedChart.series;
                if (currentSeries > 1 || currentSeries <= item.seriesIndex || !self.customLabelsVisible) {
                    var currentIndex = item.datapoint[0] - selectedChart.getOffset();
                    if (currentSeries > item.seriesIndex) {
                        var tooltipText;
                        if (selectedChart.name == "TimeInZonesShort") {
                            tooltipText = self.getTooltipForTimeZoneChart(currentIndex, self.durationChart.yTicksFormatter, item.seriesIndex);
                        } else if (selectedChart.name == "PowerInZones") {
                            tooltipText = self.getTooltipForPowerZoneChart(currentIndex, self.durationChart.yTicksFormatter, item.seriesIndex);
                        } else {
                            tooltipText = self.getTooltipForChart(0, currentIndex, selectedChart.yTicksFormatter, selectedChart.unit);
                        }
                        $("#userstatreportchart div.tooltip span.dataSeries0").text(tooltipText);
                        self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - selectedChart.getOffset()]);
                        selectedChart.tooltipValueVisible(true);
                        self.averageHrChart().tooltipValueVisible(false);
                        self.speedChart().tooltipValueVisible(false);
                        self.powerChart().tooltipValueVisible(false);
                        self.statLevelManager().tooltipValueVisible(false);
                    } else {
                        var tooltipText = self.getTooltipForChart(currentSeries, currentIndex, self.speedChart().yTicksFormatter, self.speedChart().unit);
                        $("#userstatreportchart div.tooltip span.dataSeries1").text(tooltipText);
                        tooltipText = self.getTooltipForChart(currentSeries + 1, currentIndex, self.averageHrChart().yTicksFormatter, self.averageHrChart().unit);
                        $("#userstatreportchart div.tooltip span.dataSeries2").text(tooltipText);
                        tooltipText = self.getTooltipForChart(currentSeries + 2, currentIndex, self.powerChart().yTicksFormatter, self.powerChart().unit);
                        $("#userstatreportchart div.tooltip span.dataSeries3").text(tooltipText);

                        var chartLevels = self.statLevelManager().getSelectedLevels();
                        if (item.seriesIndex < chartLevels.length) {
                            var chartLevel = chartLevels[item.seriesIndex];
                            if (chartLevel.yTicksFormatter) {
                                tooltipText = self.getTooltipForChart(item.seriesIndex, currentIndex, chartLevel.yTicksFormatter, chartLevel.unit);
                                $("#userstatreportchart div.tooltip span.dataSeries4").text(chartLevel.description + " - " + tooltipText);
                            }
                        }

                        self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - selectedChart.getOffset()]);
                        selectedChart.tooltipValueVisible(false);
                        if (self.visiblePowerLevels()) {
                            self.statLevelManager().tooltipValueVisible(true);
                            self.averageHrChart().tooltipValueVisible(false);
                            self.speedChart().tooltipValueVisible(false);
                            self.powerChart().tooltipValueVisible(false);
                        } else {
                            self.statLevelManager().tooltipValueVisible(false);
                            self.averageHrChart().tooltipValueVisible(self.averageHrChart().selected());
                            self.speedChart().tooltipValueVisible(self.speedChart().selected());
                            self.powerChart().tooltipValueVisible(self.powerChart().selected());
                        }
                    }
                    var left = $("#userstatreportchart div.tooltip").width() + 50;
                    if ((Math.floor(pos.x + 0.5) - 1) / selectedChart.extendedTimeLabels.length < 0.5) {
                        left = 0;
                    }
                    var offset = $('div.simpleTile.userstatreportchart').offset()
                    $("#userstatreportchart div.tooltip")
                        .css({ top: item.pageY - offset.top + 5, left: item.pageX - offset.left + 5 + 25 - left })
                        .fadeIn(200);
                } else {
                    $("#userstatreportchart div.tooltip").stop(true, true).hide();
                }
            } else {
                $("#userstatreportchart div.tooltip").stop(true, true).hide();
            }

        });

        if (self.visibleAuxilaryCharts()) {
            if (self.notVisiblePowerLevels()) {
                $('#userstatreportchart div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('color', colorsVct[speedOptions.colorIndex]);
                $('#userstatreportchart div.flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('color', colorsVct[avgHrOptions.colorIndex]);
                $('#userstatreportchart div.flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('color', colorsVct[powerOptions.colorIndex]);
            } /*else {
                $('#userstatreportchart div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('color', colorsVct[levelOptions.colorIndex]);
            }*/
        }
    };

    self.refreshChartForPreview = function () {
        var colorsVct = getChartColors(false);

        var selectedChart = self.selectedChart();

        var maxYmain = 100.0;
        if (selectedChart.name == 'PowerLevels') {
            maxYmain = Math.floor(1.1 * self.statLevelManager().getDataSeriesMax() + 1.5);
        } else if (selectedChart.name != 'TimeInZonesShort' && selectedChart.name != 'PowerInZones') {
            maxYmain = Math.floor(1.1 * selectedChart.getDataSeriesMax() + 1.5);
        }
        var minYmain = 0.0;
        if (selectedChart.name == 'MeanMaximalPower') {
            minYmain = 0.9 * selectedChart.getDataSeriesNoneZeroMin();
        }

        var emptyChart;
        if (selectedChart.isLineType()) {
            emptyChart = self.getEmptyChart(selectedChart, colorsVct, selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones", self.auxilaryLinesOptions, self.auxilaryBarsOptions, 1);
        } else {
            emptyChart = self.getEmptyChart(selectedChart, colorsVct, selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones", self.categoryLinesOptions, self.categoryBarsOptions, 1);
        }
        self.dataSeries = self.fillDataSeries(selectedChart, emptyChart);
        var yLabelMain = selectedChart.getName() + ' ' + selectedChart.unit;

        var deltaX = (selectedChart.isLineType() ? 0.0 : 0.25);

        var maxX = selectedChart.values.length - 1;
        var timeLabels = self.getTimeLabels(selectedChart);

        var yaxesData = [];
        yaxesData.push({
            // main category chart
            min: minYmain,
            max: maxYmain,
            labelWidth: 30,
            tickLength: 0,
            ticks: self.ticksGenerator(minYmain, maxYmain),
            tickFormatter: self.selectedChart().yTicksFormatter
        });

        var speedOptions, avgHrOptions, powerOptions, levelOptions;
        if (self.visibleAuxilaryCharts()) {

            if (self.notVisiblePowerLevels()) {
                speedOptions = self.createAuxilaryChart(self.speedChart(), colorsVct, 2);
                avgHrOptions = self.createAuxilaryChart(self.averageHrChart(), colorsVct, 3);
                powerOptions = self.createAuxilaryChart(self.powerChart(), colorsVct, 4);
                var maxY = Math.floor(1.1 * speedOptions.maxY + 1.5);
                yaxesData.push({
                    // speed chart
                    tickLength: 0,
                    position: "right",
                    labelWidth: 30,
                    //labelWidth: self.speedChart().selected() ? 30 : 0,
                    ticks: self.ticksGenerator(0, maxY),
                    //ticks: self.speedChart().selected() ? self.ticksGenerator(0, maxY) : [],
                    min: 0.0,
                    max: maxY,
                    tickFormatter: speedOptions.ticksFormatter,
                });
                yaxesData.push({
                    // averageHR chart                    
                    position: "right",
                    tickLength: 0,
                    labelWidth: 30,
                    //labelWidth: self.averageHrChart().selected() ? 30 : 0,
                    ticks: self.ticksGenerator(0.4 * self.maxHr, self.maxHr),
                    //ticks: self.averageHrChart().selected() ? self.ticksGenerator(0.4 * self.maxHr, self.maxHr) : [],
                    min: 0.4 * self.maxHr,
                    max: self.maxHr,
                    tickFormatter: avgHrOptions.ticksFormatter
                });
                maxY = Math.floor(1.1 * powerOptions.maxY + 1.5);
                yaxesData.push({
                    // power chart                    
                    position: "right",
                    tickLength: 0,
                    labelWidth: 30,
                    //labelWidth: self.powerChart().selected() ? 30 : 0,
                    ticks: self.ticksGenerator(0, maxY),
                    //ticks: self.powerChart().selected() ? self.ticksGenerator(0, maxY) : [],
                    min: 0,
                    max: maxY,
                    tickFormatter: powerOptions.ticksFormatter
                });

            } else {
                // power charts does not have own axis
                $.each(self.statLevelManager().Charts, function (index, levelChart) {
                    if (levelChart.selected()) {
                        self.createAuxilaryChart(levelChart, colorsVct, 1);
                    }
                });
            }
        }

        var series = {
            shadowSize: 0,
        };
        if (selectedChart.isLineType()) {
            series['points'] = {
                show: true,
                radius: 3,
                symbol: "circle"
            }
        }

        var container = $(".fbStatSimplePostLarger.reportStats div.placeholderReportShare");
        $.plot(container, self.dataSeries, {
            xaxis: {
                ticks: timeLabels,
                min: -deltaX,
                max: maxX + deltaX,
                tickLength: 0,
            },
            yaxes: yaxesData,
            grid: {
                show: true,
                backgroundColor: '#ffffff',
                borderWidth: 1,
                hoverable: true,
                marking: [],
            },
            bars: {
                show: true,
                lineWidth: 0,
                fill: true,
                fillColor: { colors: [{ opacity: 1.0 }, { opacity: 1.0 }] }
            },
            legend: {
                show: false
            },
            series: series,
            colors: colorsVct,
            hooks: { drawOverlay: [self.drawLabels] }
        });
        var yaxisLabelMain = $("<div class='axisLabel yaxisLabel labelMain'></div>")
            .text(yLabelMain)
            .appendTo(container);
        yaxisLabelMain.css("margin-top", yaxisLabelMain.width() / 2 - 20);

        container.bind("plothover", function (event, pos, item) {

            if (item) {
                var selectedChart = self.selectedChart();
                var currentSeries = selectedChart.series;
                if (currentSeries > 1 || currentSeries <= item.seriesIndex || !self.customLabelsVisible) {
                    var currentIndex = item.datapoint[0] - selectedChart.getOffset();
                    if (currentSeries > item.seriesIndex) {
                        var tooltipText;
                        if (selectedChart.name == "TimeInZonesShort") {
                            tooltipText = self.getTooltipForTimeZoneChart(currentIndex, self.durationChart.yTicksFormatter, item.seriesIndex);
                        } else if (selectedChart.name == "PowerInZones") {
                            tooltipText = self.getTooltipForPowerZoneChart(currentIndex, self.durationChart.yTicksFormatter, item.seriesIndex);
                        } else {
                            tooltipText = self.getTooltipForChart(0, currentIndex, selectedChart.yTicksFormatter, selectedChart.unit);
                        }
                        $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries0").text(tooltipText);
                        self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - selectedChart.getOffset()]);
                        selectedChart.tooltipValueVisible(true);
                        self.averageHrChart().tooltipValueVisible(false);
                        self.speedChart().tooltipValueVisible(false);
                        self.powerChart().tooltipValueVisible(false);
                        self.statLevelManager().tooltipValueVisible(false);
                    } else {
                        var tooltipText = self.getTooltipForChart(currentSeries, currentIndex, self.speedChart().yTicksFormatter, self.speedChart().unit);
                        $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries1").text(tooltipText);
                        tooltipText = self.getTooltipForChart(currentSeries + 1, currentIndex, self.averageHrChart().yTicksFormatter, self.averageHrChart().unit);
                        $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries2").text(tooltipText);
                        tooltipText = self.getTooltipForChart(currentSeries + 2, currentIndex, self.powerChart().yTicksFormatter, self.powerChart().unit);
                        $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries3").text(tooltipText);

                        var chartLevels = self.statLevelManager().getSelectedLevels();
                        if (item.seriesIndex < chartLevels.length) {
                            var chartLevel = chartLevels[item.seriesIndex];
                            if (chartLevel.yTicksFormatter) {
                                tooltipText = self.getTooltipForChart(item.seriesIndex, currentIndex, chartLevel.yTicksFormatter, chartLevel.unit);
                                $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries4").text(chartLevel.description + " - " + tooltipText);
                            }
                        }

                        self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - selectedChart.getOffset()]);
                        selectedChart.tooltipValueVisible(false);
                        if (self.visiblePowerLevels()) {
                            self.statLevelManager().tooltipValueVisible(true);
                            self.averageHrChart().tooltipValueVisible(false);
                            self.speedChart().tooltipValueVisible(false);
                            self.powerChart().tooltipValueVisible(false);
                        } else {
                            self.statLevelManager().tooltipValueVisible(false);
                            self.averageHrChart().tooltipValueVisible(self.averageHrChart().selected());
                            self.speedChart().tooltipValueVisible(self.speedChart().selected());
                            self.powerChart().tooltipValueVisible(self.powerChart().selected());
                        }
                    }
                    var left = $(".fbStatSimplePostLarger.reportStats div.tooltip").width() + 50;
                    if ((Math.floor(pos.x + 0.5) - 1) / selectedChart.extendedTimeLabels.length < 0.5) {
                        left = 0;
                    }
                    var offset = $('div.simpleTile.userstatreportchart').offset()
                    $(".fbStatSimplePostLarger.reportStats div.tooltip")
                        .css({ top: item.pageY - offset.top + 5, left: item.pageX - offset.left + 5 + 25 - left })
                        .fadeIn(200);
                } else {
                    $(".fbStatSimplePostLarger.reportStats div.tooltip").stop(true, true).hide();
                }
            } else {
                $(".fbStatSimplePostLarger.reportStats div.tooltip").stop(true, true).hide();
            }

        });

        if (self.visibleAuxilaryCharts()) {
            if (self.notVisiblePowerLevels()) {
                //                if (self.speedChart().selected()) {
                $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('color', self.speedChart().getColor());
                //}
                //                if (self.averageHrChart().selected()) {
                $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('color', self.averageHrChart().getColor());
                //                }
                //                if (self.powerChart().selected()) {
                $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('color', self.powerChart().getColor());
                //}
            }
        }

        $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('font-weight', 'bold');
        $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('font-weight', 'bold');
        $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('font-weight', 'bold');
        $('.fbStatSimplePostLarger.reportStats div.flot-x-axis.flot-x1-axis.xAxis.x1Axis').css('font-weight', 'bold');
        $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y1-axis.yAxis.y1Axis').css('font-weight', 'bold');

        $('.fbStatSimplePostLarger.reportStats div.flot-x-axis.flot-x1-axis.xAxis.x1Axis').css('color', 'black');
        $('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y1-axis.yAxis.y1Axis').css('color', 'black');


        var labelMain = $('.fbStatSimplePostLarger.reportStats .axisLabel.yaxisLabel.labelMain').clone();
        $('.fbStatSimplePostLarger.reportStats .axisLabel.yaxisLabel.labelMain').remove();
        labelMain.attr('id', 'placeholderReportShareLabelMain');
        labelMain.attr('width', '20');
        labelMain.attr('height', '150');
        var labelMainText = labelMain.html();
        labelMain.html("");
        var html = labelMain[0].outerHTML.replace("div", "canvas").replace("div", "canvas");
        $('.placeholderReportShare').append(html);

        if (self.notVisiblePowerLevels()) {
            displayVerticalTextOnCanvas('speedChartReportLabelVertical', self.speedChart().getNameWithUnit(), self.speedChart().getColor(), 'bottom');
            displayVerticalTextOnCanvas('averageHrChartReportLabelVertical', self.averageHrChart().getNameWithUnit(), self.averageHrChart().getColor(), 'bottom');
            displayVerticalTextOnCanvas('powerChartReportLabelVertical', self.powerChart().getNameWithUnit(), self.powerChart().getColor(), 'bottom');

            //if (!self.powerChart().selected()) {
            //$('.fbStatSimplePostLarger.reportStats #userstatreportchart .flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('display', 'none');
            //}
            //if (!self.averageHrChart().selected()) {
            // $('.fbStatSimplePostLarger.reportStats #userstatreportchart .flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('display', 'none');
            //}
            //if (!self.speedChart().selected()) {
            // $('.fbStatSimplePostLarger.reportStats #userstatreportchart .flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('display', 'none');
            //}
        }

        //if (self.visiblePowerLevels()) {
        //    displayVerticalTextOnCanvas('powerChartReportLabelVertical', self.statLevelManager().Name(), self.statLevelManager().Color(), 'bottom');
        //}

        displayVerticalTextOnCanvas('placeholderReportShareLabelMain', labelMainText, 'black', 'middle');


        //var colorsVct = getChartColors(false);

        //var selectedChart = self.selectedChart();

        //var maxYmain = 100.0;
        //if (selectedChart.name != 'TimeInZonesShort') {
        //    maxYmain = Math.floor(1.1 * self.getDataSeriesMax(selectedChart) + 1.5);
        //}
        //var emptyChart = self.getEmptyChart(selectedChart, colorsVct, selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones", self.categoryLinesOptions, self.categoryBarsOptions, 1);
        //self.dataSeries = self.fillDataSeries(selectedChart, emptyChart);
        //var yLabelMain = selectedChart.getName() + ' ' + selectedChart.unit;

        //var maxX = selectedChart.values.length + 1;
        //var timeLabels = self.getTimeLabels(selectedChart);

        //var speedOptions = self.createAuxilaryChart(self.speedChart(), colorsVct, 2);
        //var avgHrOptions = self.createAuxilaryChart(self.averageHrChart(), colorsVct, 3);
        //var powerOptions = self.createAuxilaryChart(self.powerChart(), colorsVct, 4);

        //var container = $(".fbStatSimplePostLarger.reportStats div.placeholderReportShare");
        //$.plot(container, self.dataSeries, {
        //    xaxis: {
        //        ticks: timeLabels,
        //        min: 0.0,
        //        max: maxX,
        //        tickLength: 0,
        //    },
        //    yaxes: [
        //        {
        //            // main category chart
        //            min: 0.0,
        //            max: maxYmain,
        //            labelWidth: 30,
        //            tickLength: 0,
        //            ticks: self.ticksGenerator(0, maxYmain),
        //            tickFormatter: self.selectedChart().yTicksFormatter
        //        }, {
        //            // speed chart
        //            tickLength: 0,
        //            position: "right",
        //            labelWidth: 30,
        //            ticks: self.ticksGenerator(0, speedOptions.maxY),
        //            min: 0.0,
        //            max: speedOptions.maxY,
        //            tickFormatter: speedOptions.ticksFormatter
        //        }, {
        //            // averageHR chart                    
        //            position: "right",
        //            tickLength: 0,
        //            labelWidth: 30,
        //            ticks: self.ticksGenerator(0.4 * self.maxHr, self.maxHr),
        //            min: 0.4 * self.maxHr,
        //            max: self.maxHr,
        //            tickFormatter: avgHrOptions.ticksFormatter
        //        }, {
        //            // power chart                    
        //            position: "right",
        //            tickLength: 0,
        //            labelWidth: 30,
        //            ticks: self.ticksGenerator(0, powerOptions.maxY),
        //            min: 0,
        //            max: powerOptions.maxY,
        //            tickFormatter: powerOptions.ticksFormatter
        //        }],
        //    grid: {
        //        show: true,
        //        backgroundColor: '#ffffff',
        //        borderWidth: 1,
        //        hoverable: true,
        //        marking: [],
        //    },
        //    bars: {
        //        show: true,
        //        lineWidth: 0,
        //        fill: true,
        //        fillColor: { colors: [{ opacity: 1.0 }, { opacity: 1.0 }] }
        //    },
        //    legend: {
        //        show: false
        //    },
        //    series: {
        //        shadowSize: 0
        //    },
        //    colors: colorsVct,
        //    hooks: { drawOverlay: [self.drawLabels] }
        //});
        //var yaxisLabelMain = $("<div class='axisLabel yaxisLabel labelMain'></div>")
        //    .text(yLabelMain)
        //    .appendTo(container);
        //yaxisLabelMain.css("margin-top", yaxisLabelMain.width() / 2 - 20);

        //container.bind("plothover", function (event, pos, item) {

        //    if (item) {
        //        var currentSeries = selectedChart.series;
        //        if (currentSeries > 1 || currentSeries <= item.seriesIndex || !self.customLabelsVisible) {
        //            var currentIndex = item.datapoint[0] - 1;
        //            if (currentSeries > item.seriesIndex) {
        //                var tooltipText;
        //                if (selectedChart.name == "TimeInZonesShort" || selectedChart.name == "PowerInZones") {
        //                    tooltipText = self.getTooltipForTimeZoneChart(currentIndex, self.durationChart.yTicksFormatter, item.seriesIndex);
        //                } else {
        //                    tooltipText = self.getTooltipForChart(0, currentIndex, selectedChart.yTicksFormatter, selectedChart.unit);
        //                }
        //                $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries0").text(tooltipText);
        //                self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - 1]);
        //                selectedChart.tooltipValueVisible(true);
        //                self.averageHrChart().tooltipValueVisible(false);
        //                self.speedChart().tooltipValueVisible(false);
        //                self.powerChart().tooltipValueVisible(false);
        //            } else {
        //                var tooltipText = self.getTooltipForChart(currentSeries, currentIndex, self.speedChart().yTicksFormatter, self.speedChart().unit);
        //                $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries1").text(tooltipText);
        //                tooltipText = self.getTooltipForChart(currentSeries + 1, currentIndex, self.averageHrChart().yTicksFormatter, self.averageHrChart().unit);
        //                $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries2").text(tooltipText);
        //                tooltipText = self.getTooltipForChart(currentSeries + 2, currentIndex, self.powerChart().yTicksFormatter, self.powerChart().unit);
        //                $(".fbStatSimplePostLarger.reportStats div.tooltip span.dataSeries3").text(tooltipText);
        //                self.xValue(selectedChart.extendedTimeLabels[Math.floor(pos.x + 0.5) - 1]);
        //                selectedChart.tooltipValueVisible(false);
        //                self.averageHrChart().tooltipValueVisible(self.averageHrChart().selected());
        //                self.speedChart().tooltipValueVisible(self.speedChart().selected());
        //                self.powerChart().tooltipValueVisible(self.powerChart().selected());
        //            }
        //            var left = $(".fbStatSimplePostLarger.reportStats div.tooltip").width() + 50;
        //            if ((Math.floor(pos.x + 0.5) - 1) / selectedChart.extendedTimeLabels.length < 0.5) {
        //                left = 0;
        //            }
        //            var offset = $('div.simpleTile.userstatreportchart').offset()
        //            $(".fbStatSimplePostLarger.reportStats div.tooltip")
        //                .css({ top: item.pageY - offset.top + 5, left: item.pageX - offset.left + 5 + 25 - left })
        //                .fadeIn(200);
        //        } else {
        //            $(".fbStatSimplePostLarger.reportStats div.tooltip").stop(true, true).hide();
        //        }
        //    } else {
        //        $(".fbStatSimplePostLarger.reportStats div.tooltip").stop(true, true).hide();
        //    }

        //});

        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('color', colorsVct[speedOptions.colorIndex]);
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('color', colorsVct[avgHrOptions.colorIndex]);
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('color', colorsVct[powerOptions.colorIndex]);

        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y2-axis.yAxis.y2Axis').css('font-weight', 'bold');
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y3-axis.yAxis.y3Axis').css('font-weight', 'bold');
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y4-axis.yAxis.y4Axis').css('font-weight', 'bold');
        //$('.fbStatSimplePostLarger.reportStats div.flot-x-axis.flot-x1-axis.xAxis.x1Axis').css('font-weight', 'bold');
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y1-axis.yAxis.y1Axis').css('font-weight', 'bold');

        //$('.fbStatSimplePostLarger.reportStats div.flot-x-axis.flot-x1-axis.xAxis.x1Axis').css('color', 'black');
        //$('.fbStatSimplePostLarger.reportStats div.flot-y-axis.flot-y1-axis.yAxis.y1Axis').css('color', 'black');


        //var labelMain = $('.fbStatSimplePostLarger.reportStats .axisLabel.yaxisLabel.labelMain').clone();
        //$('.fbStatSimplePostLarger.reportStats .axisLabel.yaxisLabel.labelMain').remove();
        //labelMain.attr('id', 'placeholderReportShareLabelMain');
        //labelMain.attr('width', '20');
        //labelMain.attr('height', '150');
        //var labelMainText = labelMain.html();
        //labelMain.html("");
        //var html = labelMain[0].outerHTML.replace("div", "canvas").replace("div", "canvas");
        //$('.placeholderReportShare').append(html);

        //displayVerticalTextOnCanvas('powerChartReportLabelVertical', self.powerChart().getNameWithUnit(), colorsVct[powerOptions.colorIndex], 'bottom');
        //displayVerticalTextOnCanvas('averageHrChartReportLabelVertical', self.averageHrChart().getNameWithUnit(), colorsVct[avgHrOptions.colorIndex], 'bottom');
        //displayVerticalTextOnCanvas('speedChartReportLabelVertical', self.speedChart().getNameWithUnit(), 'black', 'bottom');
        //displayVerticalTextOnCanvas('placeholderReportShareLabelMain', labelMainText, 'black', 'middle');
    };
}

function displayVerticalTextOnCanvas(elemId, text, color, positionType) {
    var ctx = document.getElementById(elemId).getContext('2d');

    ctx.rotate(90 * Math.PI / 180);
    ctx.font = 'bold 8pt Aller';
    ctx.fillStyle = color;

    var twidth = ctx.measureText(text).width;
    var posX = 0;

    if (positionType == 'bottom') {
        var posX = ctx.canvas.height - twidth;
    }
    if (positionType == 'middle') {
        var posX = ctx.canvas.height / 2 - twidth / 2;
    }

    ctx.fillText(text, posX, -7);
    //    ctx.fillText(text, 0, 0);        
}

var userStatReportChartViewModel = null;

function registerChartReport(container) {
    if (userStatReportChartData != null) {
        var selectedCharts = [];
        if (userStatReportChartViewModel != null) {
            selectedCharts = userStatReportChartViewModel.getSelectedCharts();
        }
        userStatReportChartViewModel = new StatChartContainer();
        userStatReportChartViewModel.InitByData(userStatReportChartData, selectedCharts);
        userStatReportChartViewModel.refreshChart();
        ko.applyBindings(userStatReportChartViewModel, $("body #userstatreportchart")[0]);
        getStatsHeaderModel().UserStatReportChartViewModel(userStatReportChartViewModel);
    }
}

function registerDatePicker(container) {

    container.find('#yearBoxPicker').each(function () {
        var tElem = $(this);
        tElem.datepicker({
            weekStart: 1,
            viewMode: tElem.data("date-view-mode"),
            minViewMode: tElem.data("min-view-mode")
        }).on('changeDate', function (ev) {
            var menuitem = $('div.calHeader.userStats div.selected.menuitem');
            if (menuitem.hasClass('stat')) {
                loadStats(ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            } else if (menuitem.hasClass('chartstat')) {
                loadChart(ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            } else if (menuitem.hasClass('competitionreport')) {
                loadCompetitionStats(ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            } else {
                loadReport(ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            }
            tElem.datepicker('hide');
        });
    });
}

function hideDatePicker(container) {
    container.find('.datepicker').each(function () {
        var tElem = $(this);
        tElem.datepicker('hide');
    });
}

function clickRangeBox(lengthType, disciplineId, sampleType) {
    var from = $('#rangeBox_from').val();
    var to = $('#rangeBox_to').val();
    var menuitem = $('div.calHeader.userStats div.selected.menuitem');
    if (menuitem.hasClass('stat')) {
        loadStats(from, to, lengthType, disciplineId);
    } else if (menuitem.hasClass('chartstat')) {
        loadChart(from, to, lengthType, disciplineId, sampleType);
    } else if (menuitem.hasClass('competitionreport')) {
        loadCompetitionStats(from, to, lengthType, disciplineId);
    } else {
        loadReport(from, to, lengthType, disciplineId);
    }
}

function registerRangeDatePicker(container) {

    var pickerFrom = container.find('#rangeBox_from');
    pickerFrom.datepicker({
        weekStart: 1,
        viewMode: pickerFrom.data("date-view-mode"),
        minViewMode: pickerFrom.data("min-view-mode")
    });
    pickerFrom.on('changeDate', function (ev) {
        pickerFrom.datepicker('hide');
    });
    var pickerTo = container.find('#rangeBox_to');
    pickerTo.datepicker({
        weekStart: 1,
        viewMode: pickerTo.data("date-view-mode"),
        minViewMode: pickerTo.data("min-view-mode"),
        left: true,
        leftOffset: -180
    });
    pickerTo.on('changeDate', function (ev) {
        pickerTo.datepicker('hide');
    });
}

function registerClubStatsDatePicker(page, container) {
    container.find('.datepicker').each(function () {
        var tElem = $(this);
        tElem.datepicker({
            weekStart: 1,
            viewMode: tElem.data("date-view-mode"),
            minViewMode: tElem.data("min-view-mode")
        }).on('changeDate', function (ev) {
            clubStatShowMore('');
            $('.mainColumn.span12').css('cursor', 'wait');
            $('div.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'block');
            clubStatsModel.init(page, ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            loadClubStats(page, ev.date.toJSON(), null, tElem.data("lengthtype"), tElem.data("disciplineid"));
            tElem.datepicker('hide');
        });
    });
}

function loadStats(start, end, lengthType, disciplineId, sampleType) {
    $('body').css('cursor', 'wait');
    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/getPartialUserStatistics",
        data: { startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId, userId: UserId }
    })
        .done(function (result) {
            var container = $("#userStats");
            hideDatePicker(container);
            container.html(result);
            registerDatePicker(container);
            registerRangeDatePicker(container);
            $('body').css('cursor', 'default');
        });
}

function loadReport(start, end, lengthType, disciplineId, sampleType) {
    if (sampleType === undefined) {
        sampleType = $('#userstatreport th.periodpicker').data('sampletype');
    }
    var container = $("#userStats");
    container.css('cursor', 'wait');
    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/getUserStatisticsReport",
        data: { startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId, sampleType: sampleType, userId: UserId }
    })
    .done(function (result) {
        hideDatePicker(container);
        container.html(result);
        registerDatePicker(container);
        registerRangeDatePicker(container);
        $('table.reportCollapse').collapsible();
        container.css('cursor', 'auto');
    });
}

function loadChart(start, end, lengthType, disciplineId, sampleType) {
    if (sampleType === undefined) {
        sampleType = $('#userstatreport th.periodpicker').data('sampletype');
    }
    var container = $("#userStats");
    container.css('cursor', 'wait');
    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/getUserStatisticsReportChart",
        data: { startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId, sampleType: sampleType, userId: UserId }
    })
    .done(function (result) {
        hideDatePicker(container);
        container.html(result);
        registerDatePicker(container);
        registerRangeDatePicker(container);
        $('table.reportCollapse').collapsible();
        registerChartReport(container);
        container.css('cursor', 'auto');
    });
}

function loadCompetitionStats(start, end, lengthType, disciplineId, sampleType) {
    if (sampleType === undefined) {
        sampleType = $('#userstatreport th.periodpicker').data('sampletype');
    }
    var container = $("#userStats");
    container.css('cursor', 'wait');
    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/getUserStatisticsCompetitionReport",
        data: { startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId, sampleType: sampleType, userId: UserId }
    })
    .done(function (result) {
        hideDatePicker(container);
        container.html(result);
        registerDatePicker(container);
        registerRangeDatePicker(container);
        $('table.reportCollapse').collapsible();
        container.css('cursor', 'auto');
    });
}

function loadClubStats(page, start, end, lengthType, disciplineId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/GetPartialClubStatistics",
        data: { page: page, startDate: start, endDate: end, lengthType: lengthType, disciplineId: disciplineId }
    })
        .done(function (result) {
            var clubStatistics = $("#clubStatistics");
            clubStatistics.html(result);
            GeneralModel.CheckIfApply(clubStatistics.children('div')[0]);
            var clubStats = $("#clubStats");
            if (lengthType != -1) {
                registerClubStatsDatePicker(page, clubStats);
            }
            else {
                $(".yearBox.statsPicker").css('display', 'none');
            }
        });
}

function clickDatePicker() {
    var dp = $('.statsPicker .datepicker');
    if (dp) {
        dp.datepicker('show');
    }
};

function ClubStatsModel() {
    var self = this;

    self.initValues = function () {
        self.EventsCount = ko.observable(0);
        self.EventsContestCount = ko.observable(0);
        self.EventsCompetitionsCount = ko.observable(0);
        self.EventsProgramCount = ko.observable(0);
        self.EventsSchoolsCount = ko.observable(0);
        self.EventsLeagueCount = ko.observable(0);
        self.EventsActionCount = ko.observable(0);
        self.EventsOtherCount = ko.observable(0);
        self.EventsParticipantsCount = ko.observable(0);
        self.OrganizedTrainingsCount = ko.observable(0);
        self.RegisteredTrainingsCount = ko.observable(0);
        self.Distance = ko.observable(0);
        self.Duration = ko.observable(0);
        self.HeartBeats = ko.observable(0);
        self.Calories = ko.observable(0);
        self.Targets = ko.observable(0);
        self.ClubStatsPage = ko.observable('');
        self.NaviBar = new StatsNaviBarModel();
        self.OrganizedTraininngs = ko.observableArray([]);
        self.RegisteredTraininngs = ko.observableArray([]);
        self.EventsContestCountPercent = ko.observable(0);
        self.EventsCompetitionsCountPercent = ko.observable(0);
        self.EventsProgramCountPercent = ko.observable(0);
        self.EventsSchoolsCountPercent = ko.observable(0);
        self.EventsLeagueCountPercent = ko.observable(0);
        self.EventsActionCountPercent = ko.observable(0);
        self.EventsOtherCountPercent = ko.observable(0);
        self.DurationStr = ko.observable('');
        self.ClubName = ko.observable('');
        self.LogoFileId = ko.observable(0);
        self.SharingLogoId = ko.observable(0);
        self.DurationStrShare = ko.observable('');
        self.Persons = ko.observable(0);
        self.AVGHRPerPerson = ko.observable(0);
        self.EventsPerPerson = ko.observable(0);
        self.DurationPerPerson = ko.observable(0);
        self.CaloriesPerPerson = ko.observable(0);
        self.HeartBeatsPerPerson = ko.observable(0);
        self.DistancePerPerson = ko.observable(0);
        self.ActivitiesPerPerson = ko.observable(0);
        self.DurationPerPersonStr = ko.observable('');
        self.DistancePerPersonStr = ko.observable('');
        self.PersonalTargets = ko.observable(0);
        self.ClubersCount = ko.observable(0);
        self.MalePercent = ko.observable(0);
        self.FemalePercent = ko.observable(0);
        self.GroupTargetName = ko.observable('');
        self.GroupTargetStartDate = ko.observable('');
        self.DurationInDays = ko.observable(0);
        self.DistanceInEarthCircuit = ko.observable(0);
        self.HeartBeatsInCisterns = ko.observable(0);
        self.CaloriesInMeals = ko.observable(0);
    }

    self.initValues();

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.EventsCount(serverModel.EventsCount);
            self.EventsContestCount(serverModel.EventsContestCount);
            self.EventsCompetitionsCount(serverModel.EventsCompetitionsCount);
            self.EventsProgramCount(serverModel.EventsProgramCount);
            self.EventsSchoolsCount(serverModel.EventsSchoolsCount);
            self.EventsLeagueCount(serverModel.EventsLeagueCount);
            self.EventsActionCount(serverModel.EventsActionCount);
            self.EventsOtherCount(serverModel.EventsOtherCount);
            self.EventsParticipantsCount(serverModel.EventsParticipantsCount);
            self.OrganizedTrainingsCount(serverModel.OrganizedTrainingsCount);
            self.RegisteredTrainingsCount(serverModel.RegisteredTrainingsCount);
            self.Distance(serverModel.Distance);
            self.Duration(serverModel.Duration);
            self.HeartBeats(serverModel.HeartBeats);
            self.Calories(serverModel.Calories);
            self.Targets(serverModel.Targets);
            self.NaviBar.initByModel(serverModel.NaviBar);
            self.EventsContestCountPercent(serverModel.EventsContestCountPercent);
            self.EventsCompetitionsCountPercent(serverModel.EventsCompetitionsCountPercent);
            self.EventsProgramCountPercent(serverModel.EventsProgramCountPercent);
            self.EventsSchoolsCountPercent(serverModel.EventsSchoolsCountPercent);
            self.EventsLeagueCountPercent(serverModel.EventsLeagueCountPercent);
            self.EventsActionCountPercent(serverModel.EventsActionCountPercent);
            self.EventsOtherCountPercent(serverModel.EventsOtherCountPercent);
            self.DurationStr(serverModel.DurationStr);
            self.ClubName(serverModel.ClubName);
            self.LogoFileId(serverModel.LogoFileId);
            self.SharingLogoId(serverModel.SharingLogoId);
            self.DurationStrShare(serverModel.DurationStrShare);
            self.Persons(serverModel.Persons);
            self.AVGHRPerPerson(serverModel.AVGHRPerPerson);
            self.EventsPerPerson(serverModel.EventsPerPerson);
            self.DurationPerPerson(serverModel.DurationPerPerson);
            self.CaloriesPerPerson(serverModel.CaloriesPerPerson);
            self.HeartBeatsPerPerson(serverModel.HeartBeatsPerPerson);
            self.DistancePerPerson(serverModel.DistancePerPerson);
            self.ActivitiesPerPerson(serverModel.ActivitiesPerPerson);
            self.DurationPerPersonStr(serverModel.DurationPerPersonStr);
            self.DistancePerPersonStr(serverModel.DistancePerPersonStr);
            self.PersonalTargets(serverModel.PersonalTargets);
            self.ClubersCount(serverModel.ClubersCount);
            self.MalePercent(serverModel.MalePercent);
            self.FemalePercent(serverModel.FemalePercent);
            self.GroupTargetName(serverModel.GroupTargetName);
            self.GroupTargetStartDate(serverModel.GroupTargetStartDate);
            self.DurationInDays(serverModel.DurationInDays);
            self.DistanceInEarthCircuit(serverModel.DistanceInEarthCircuit);
            self.HeartBeatsInCisterns(serverModel.HeartBeatsInCisterns);
            self.CaloriesInMeals(serverModel.CaloriesInMeals);

            self.OrganizedTraininngs.removeAll();
            if (serverModel.OrganizedTraininngs) {
                for (var i = 0, len = serverModel.OrganizedTraininngs.length; i < len; i++) {
                    var elem = new EventActivity();
                    elem.initByModel(serverModel.OrganizedTraininngs[i]);
                    self.OrganizedTraininngs.push(elem);
                }
            }

            self.RegisteredTraininngs.removeAll();
            if (serverModel.RegisteredTraininngs) {
                for (var i = 0, len = serverModel.RegisteredTraininngs.length; i < len; i++) {
                    var elem = new EventActivity();
                    elem.initByModel(serverModel.RegisteredTraininngs[i]);
                    self.RegisteredTraininngs.push(elem);
                }
            }
        }
    }

    self.LogoUrlComputed = ko.computed(function () {
        return ContextPath + "Image/GetImage?id=" + self.LogoFileId();
    });

    self.LogoUrl = function () {
        return ContextPath + "Image/GetImage?id=" + self.LogoFileId();
    }

    self.SharingLogoUrl = function () { return ContextPath + "Image/GetImage?id=" + self.SharingLogoId(); };

    self.init = function (page, startDate, endDate, lengthType, disciplineId) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetClubStatsModel",
            data: { startDate: startDate, endDate: endDate, lengthType: lengthType, disciplineId: disciplineId }
        })
         .done(function (result) {
             self.initByModel(result);
             $('div.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'none');
             $('.mainColumn.span12').css('cursor', 'initial');
         });
    }

    self.initForGroupTarget = function (targetId, doNotBind) {
        self.initValues();
        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetClubStatsModelForGroupTarget",
            data: { targetId: targetId }
        })
         .done(function (result) {
             self.initByModel(result);
             if (!doNotBind) {
                 GeneralModel.CheckIfApply($('#footerInfoModalBody').children('div')[0]);
                 $('#footerInfoModalBody .groupStats.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'none');
                 $('#footerInfoModalBody').css('cursor', 'initial');
             }
         });
        self.loadHistoryProgressChart(targetId);
    }

    self.EventsCountVisible = ko.computed(function () {
        return self.EventsCount() != 0;
    });

    self.PreviousMonth = function () {
        clubStatShowMore('');
        $('.mainColumn.span12').css('cursor', 'wait');
        $('div.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'block');
        self.init(self.ClubStatsPage(), self.NaviBar.PreviousParam(), null, self.NaviBar.LengthType(), self.NaviBar.DisciplineParam());
    }

    self.NextMonth = function () {
        clubStatShowMore('');
        $('.mainColumn.span12').css('cursor', 'wait');
        $('div.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'block');
        self.init(self.ClubStatsPage(), self.NaviBar.NextParam(), null, self.NaviBar.LengthType(), self.NaviBar.DisciplineParam());
    }

    self.LoadStats = function (length) {
        clubStatShowMore('');
        $('.mainColumn.span12').css('cursor', 'wait');
        $('div.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'block');
        self.init(self.ClubStatsPage(), self.NaviBar.StartDate(), null, length);
    }

    self.getPeriodsIconSrc = function (length) {
        var path = ContextPath + 'Content/images/icons/';

        switch (length) {
            case "1": { path = path + translations['CallendarDayIcon']; break; }
            case "7": { path = path + translations['CallendarWeekIcon']; break; }
            case "30": { path = path + translations['CallendarMonthIcon']; break; }
            case "365": { path = path + translations['CallendarYearIcon']; break; }
            case "-1": { path = path + translations['CallendarPeriodIcon']; break; }
            default: path = path + translations['CallendarMonthIcon'] + 'cze';
        }

        if (self.NaviBar.LengthType() == length) {
            path = path + 'cze';
        }

        path = path + '.png';

        return path;
    }

    self.PeriodsDayIconSrc = ko.computed(function () {
        return self.getPeriodsIconSrc('1');
    });

    self.PeriodsWeekIconSrc = ko.computed(function () {
        return self.getPeriodsIconSrc('7');
    });

    self.PeriodsMonthIconSrc = ko.computed(function () {
        return self.getPeriodsIconSrc('30');
    });

    self.PeriodsYearIconSrc = ko.computed(function () {
        return self.getPeriodsIconSrc('365');
    });

    self.PeriodsListIconSrc = ko.computed(function () {
        return self.getPeriodsIconSrc('-1');
    });

    self.DisplayClubLogo = ko.computed(function () {
        return self.LogoFileId() != null && self.LogoFileId() != 0;// && self.ClubName() != 'pulsstory';
    });

    self.DisplayLogo = function () {
        return self.LogoFileId() != null && self.LogoFileId() != 0 && self.ClubName() != 'pulsstory';
    }

    self.DisplayLogo = function () {
        return self.LogoFileId() != null && self.LogoFileId() != 0 && self.ClubName() != 'pulsstory';
    }

    self.DisplaySharingLogo = function () {
        return self.SharingLogoId() != null && self.SharingLogoId() != 0 && self.ClubName() != 'pulsstory';
    }

    self.EventsParticipantsCountValue = ko.computed(function () {
        var eventsParticipantsCount = self.EventsParticipantsCount();

        if (typeof eventsParticipantsCount == 'string' || eventsParticipantsCount instanceof String) {
            return parseInt(eventsParticipantsCount).formatNumber(0);
        }
        else {
            return eventsParticipantsCount.formatNumber(0);
        }
        return '';
    });

    self.loadHistoryProgressChart = function ( targetId) {
        //var totalProgress = $('#groupTargetDetails_' + targetId + ' .charts .hisotry');
        var totalProgress = $('#targetHistory');

        if (totalProgress.length) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Stats/GetTargetHistoryChart",
                data: { targetId: targetId }
            })
             .done(function (result) {

                 loadGoogleChart();
                 google.charts.setOnLoadCallback(drawChart);
                 function drawChart() {
                     //result.ArrayData.unshift([translations.Day, translations.TargetDayShift, translations.TargetDayValue, translations.TargetDayPrognoza, translations.TargetDayAvgValue]);
                     //var data = google.visualization.arrayToDataTable(result.ArrayData);

                     var data = new google.visualization.DataTable();
                     data.addColumn('string', translations.Day);
                     data.addColumn('number', translations.TargetDayShift);
                     data.addColumn('number', translations.TargetDayValue);
                     data.addColumn('number', translations.TargetDayPrognoza);
                     data.addColumn('number', translations.TargetDayAvgValue);
                     
                     data.addRows(result.ArrayData);

                     var options = {
                         title: '',
                         seriesType: 'bars',
                         series: {
                             0: { type: 'bar', targetAxisIndex: 0 },
                             1: { type: 'area', targetAxisIndex: 1 },
                             2: { type: 'area', targetAxisIndex: 1 },
                             3: { type: 'line', targetAxisIndex: 1 }
                         },

                         height: 200,
                         legend: 'none',
                         colors: ["#bbb", "#E44532", "#78CFC7"],
                         hAxis: { textPosition: 'none', gridlines: { count: 0 } },
                         vAxis: { textPosition: 'none', gridlines: { count: 0 } },
                         chartArea: { width: '80%', height: '80%' },
                         bar: {
                             groupWidth: 4
                         }
                     };

                     var chart = new google.visualization.ComboChart(totalProgress[0]);
                     chart.draw(data, options);
                 }
             });
        }
    }
}

function PeriodsListIconClick(page, length) {
    clubStatsModel.LoadStats(length);

    //     var viewMode = 0;

    //     switch (length) {
    //         case 1: viewMode = 0;
    //         case 7: viewMode = 0;
    //         case 30: viewMode = 1;
    //         case 365: viewMode = 2;
    //         case -1: viewMode = 1;
    //         default: viewMode = 1;
    //     }      

    var clubStats = $("#clubStats");
    //      clubStats.find('.datepicker').each(function () {
    //         var tElem = $(this);

    //         tElem.data("min-view-mode", viewMode);
    //         tElem.data("date-view-mode", viewMode);

    // //         tElem.datepicker({
    // //             weekStart: 1,
    // //             viewMode: viewMode,
    // //         })
    //     });

    if (length != -1) {
        //         $(".yearBox.statsPicker").css('display', 'block');        
        registerClubStatsDatePicker(page, clubStats);
    }
    else {
        $(".yearBox.statsPicker").css('display', 'none');
    }
}

function clubStatShowMore(subStats) {
    $('.subStats > div').each(function () {
        $(this).css('display', 'none');;
    });

    $(subStats).css('display', 'block');
}


var clubStatsModel = new ClubStatsModel();
GeneralModel.addProperty("ClubStatsModel", clubStatsModel);

function TargetStatsModel() {
    var self = this;

    self.initValues = function () {
        self.UserStats = ko.observableArray([]);
        self.Friends = ko.observableArray([]);

        self.TotalProgress = ko.observable();
        self.TotalScore = ko.observable();
        self.TotalScoreLabel = ko.observable();
        self.TotalUsers = ko.observable();
        self.UseProgress = ko.observable();
        self.TargetValueIcon = ko.observable();
        self.TargetIconClass = ko.observable();
        self.TargetValueUnit = ko.observable();
        self.TargetValueName = ko.observable();
        self.UserTargetValue = ko.observable(0);
        self.Ready = ko.observable(false);
        self.UserRulesMsg = ko.observable();
        self.TotalForecast = ko.observable(0);

        self.TotalProgresVal = ko.observable(0);
        self.TotalForecastVal = ko.observable(0);
        self.TotalMissingVal = ko.observable(100);

        self.FriendsProgresVal = ko.observable(0);
        self.FriendsForecastVal = ko.observable(0);
        self.FriendsMissingVal = ko.observable(100);

        self.UserProgresVal = ko.observable(0);
        self.UserForecastVal = ko.observable(0);
        self.UserMissingVal = ko.observable(100);

        self.TotalTargetVal = ko.observable(0);

        self.Last3Trainings = ko.observableArray([]);

        self.UserOk = ko.observable(false);
        self.GroupOk = ko.observable(false);

        self.UserTargetStatus = ko.observable('');
        self.GroupTargetStatus = ko.observable('');

        /*
        self.TotalProgressData = ko.observable({
            datasets: [{
                data: [self.ProgresVal, self.ForecastVal, self.MissingVal],
                backgroundColor: [
								"#E44532",
								"#78CFC7",
								"#eeeeee"
                ]
            }],

            // These labels appear in the legend and in the tooltips when hovering different arcs
            labels: [
                translations.TargetChartProgress,
                translations.TargetForecast,
                translations.TargetMissing
            ]            
        });
*/
        //self.TotalProgressDeg = ko.observable('');
        //self.TotalForecastDeg = ko.observable('');
        //self.MissDeg = ko.observable('');
    }

    self.initByModel = function (model) {
        self.TotalProgress(model.TotalProgress + '%');
        self.TotalScore(model.TotalScore);
        self.TotalScoreLabel(translations['scorelabel_' + model.TargetCategory]);
        self.TotalUsers(model.TotalUsers);
        self.UseProgress(model.UseProgress);
        self.TargetValueIcon("/Content/images/icons/" + model.TargetValueIcon + "_32.png");
        self.TargetIconClass(model.TargetValueIcon);
        self.TargetValueUnit(model.TargetValueUnit);
        self.TargetValueName(model.TargetValueName);
        self.UserTargetValue(model.UserTargetValue);
        self.UserRulesMsg(model.UserRulesMsg);
        self.TotalForecast(model.TotalForecast);
        $(model.UserStats).each(function (index, item) {
            item.AvatarUrl = ContextPath + 'Image/GetImage?id=' + item.AvatarId;
            item.AvatarHover = function (callback) {
                var content = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
                    + item.AvatarUrl
                    + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
                if (callback) callback(content);
            }

            self.UserStats.push(item);
            if (item.IsFriend) self.Friends.push(item);
        });

        //self.TotalProgressDeg ( 'rotate(' + (360.0 * model.TotalProgress / 100) + 'deg)');
        //self.TotalForecastDeg ( 'rotate(' + (360.0 * (model.TotalProgress + model.TotalForecast) / 100) + 'deg)');
        //self.MissDeg ( 'rotate(' + (-360.0 * (model.TotalProgress + model.TotalForecast) / 100) + 'deg)');

        self.TotalProgresVal(model.TotalProgressVal);
        self.TotalForecastVal(model.TotalForecastVal);
        self.TotalMissingVal(model.TotalMissingVal);

        self.FriendsProgresVal(model.FriendsProgressVal);
        self.FriendsForecastVal(model.FriendsForecastVal);
        self.FriendsMissingVal(model.FriendsMissingVal);

        self.UserProgresVal(model.UserProgressVal);
        self.UserForecastVal(model.UserForecastVal);
        self.UserMissingVal(model.UserMissingVal);

        self.TotalTargetVal(model.TotalTargetVal);

        self.UserOk(( model.UserForecastVal) >= model.UserTargetValue);
        self.GroupOk(( model.TotalForecastVal) >= model.TotalTargetVal);

        self.UserTargetStatus(model.UserTargetStatus);
        self.GroupTargetStatus(model.GroupTargetStatus);

        self.Ready(true);
    }

    self.DataTable = null;

    self.DtRedraw = function () {
        if (self.DataTable) {
            self.DataTable.draw();
            if (self.DataTable.fnAdjustColumnSizing) self.DataTable.fnAdjustColumnSizing();
        }
    }

    self.initForGroupTarget = function (targetId, doNotBind) {
        self.initValues();

        if (!doNotBind) GeneralModel.CheckIfApply($('#footerInfoModalBody').children('div')[0]);
        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetTargetStatModelForGroupTarget",
            data: { targetId: targetId }
        })
         .done(function (result) {
             self.initByModel(result);
             // GeneralModel.CheckIfApply($('#footerInfoModalBody').children('div')[0]);


             self.DataTable = $('#groupTargetRanking').DataTable({
                 "oLanguage": dataTableLanguageOptions,
                 "data": self.UserStats(),
                 "bAutoWidth": false,
                 "aoColumnDefs": [
                       { "sWidth": "5%", "aTargets": [0] },
                       { "sWidth": "20%", "aTargets": [0, 3, 4] },
                       { "sWidth": "25%", "iDataSort": 5, "aTargets": [2] },
                       { "visible": result.ShowTargetValue, "targets": 3 },
                       { "visible": false, "targets": 5 }
                 ],
                 "columnDefs": [{
                     "searchable": false,
                     "orderable": false,
                     "targets": 0
                 }],
                 "columns": [
                     { "data": null, "defaultContent": "", "title": translations.Position },
                     { "data": "UserName", "title": translations.Nick },
                     { "data": "CurrentValueFormatted", "title": translations.Score },
                     { "data": "TargetValueFormatted", "title": translations.TargetValue },
                     { "data": "Progress", "title": '% ' + translations.ProgressInGroupTarget },
                     { "data": "CurrentValueNormalized", "title": '' },
                     { "data": "UserRulesMsg", "title": translations.Rules }
                 ],
                 "order": [[2, "desc"]],
                 "scrollY": "360px",
                 "scrollCollapse": true,
                 "paging": false,
                 "deferRender": true
             });
             self.DataTable.on('order.dt search.dt', function () {
                 self.DataTable.column(0, { search: 'applied', order: 'applied' }).nodes().each(function (cell, i) {
                     cell.innerHTML = i + 1;
                 });
             }).draw();

             if (!doNotBind) {
                 $('#footerInfoModalBody .groupStats.clubStats.generationStatsInfo .publishInfoCloudContainer').css('display', 'none');
                 $('#footerInfoModalBody').css('cursor', 'initial');
             }
         });
    }


    self.initForGroupTargetInfo = function (targetId) {
        self.initValues();
        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetTargetStatModelForGroupTarget",
            data: { targetId: targetId }
        })
         .done(function (result) {
             self.initByModel(result);

             $(".groupTargetStats.ranking.mCustomScrollbar ").mCustomScrollbar();

             var parentId = '#groupTargetDetails_' + targetId;
             var parent = $(parentId);
             if (parent.length) {
                 self.loadTotalProgressChart(parent);
                 self.loadFriendsProgressChart(parent);
                 self.loadUserProgressChart(parent);
                 self.loadCurrentProgressChart(parent);
                 self.loadPrognozaProgressChart(parent);

                 self.loadHistoryProgressChart(parent, targetId);
             }
         });
    }

    self.loadHistoryProgressChart = function (parent, targetId) {
        //var totalProgress = $('#groupTargetDetails_' + targetId + ' .charts .hisotry');

        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetTargetHistoryChart",
            data: { targetId: targetId }
        })
         .done(function (result) {

             koArrayCopyComplex(result.Last3Trainings, self.Last3Trainings, false, function (item) {
                 item.AvatarUrl = ContextPath + 'Image/GetImage?id=' + item.AvatarId;
                 item.AvatarHover = function (callback) {
                     var content = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
                         + item.AvatarUrl
                         + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
                     if (callback) callback(content);
                 }
                 return item;
             });

             var totalProgress = parent.find('.charts .hisotry');

             if (totalProgress.length) {
                 loadGoogleChart();
                 google.charts.setOnLoadCallback(drawChart);
                 function drawChart() {
                     result.ArrayData.unshift([translations.Day, translations.TargetDayShift, translations.TargetDayValue, translations.TargetDayPrognoza, translations.TargetDayAvgValue]);
                     var data = google.visualization.arrayToDataTable(result.ArrayData);

                     var options = {
                         title: '',
                         seriesType: 'bars',
                         series: {
                             0: { type: 'bar', targetAxisIndex: 0 },
                             1: { type: 'area', targetAxisIndex: 1 },
                             2: { type: 'area', targetAxisIndex: 1 },
                             3: { type: 'line', targetAxisIndex: 1 }
                         },

                         height: 200,
                         legend: 'none',
                         colors: ["#bbb", "#E44532", "#78CFC7"],
                         hAxis: { textPosition: 'none', gridlines: { count: 0 } },
                         vAxis: { textPosition: 'none', gridlines: { count: 0 } },
                         chartArea: { width: '80%', height: '80%' },
                         bar: {
                             groupWidth: 4
                         }
                     };

                     var chart = new google.visualization.ComboChart(totalProgress[0]);
                     chart.draw(data, options);
                 }
             }
         });

    }

    self.loadTotalProgressChart = function (parent) {
        var totalProgress = parent.find('.charts .totalProgress');

        if (totalProgress.length) {

            loadGoogleChart();
            google.charts.setOnLoadCallback(drawChart);
            function drawChart() {

                var v1 = v2 = v3 = 0;
                v1 = plusOr0(self.TotalProgresVal());
                if (self.TotalProgresVal() < self.TotalTargetVal()) {
                    v2 = plusOr0(self.TotalForecastVal() - self.TotalProgresVal());
                    if (v1 + v2 > self.TotalTargetVal()) {
                        v2 = self.TotalTargetVal() - v1;
                    } else {
                        v3 = plusOr0(self.TotalMissingVal());
                    }
                }

                var data = google.visualization.arrayToDataTable([
                  ['Group', 'Value'],
                  [translations.TargetChartProgress, v1],
                  [v3 ? translations.TargetForecast : translations.leftToEndOfTarget, v2],
                  [translations.TargetMissing, v3]
                ]);

                var options = {
                    title: '',
                    pieHole: 0.4,
                    width: 200,
                    height: 200,
                    legend: 'none',
                    colors: ["#E44532", "#78CFC7", "#999"],
                    chartArea: { width: '80%', height: '80%' },
                    /*
                    animation: {
                        duration: 10000,
                        easing: 'out',
                        startup: true
                    },*/
                };

                var chart = new google.visualization.PieChart(totalProgress[0]);
                chart.draw(data, options);
            }
        }
    }
    self.loadFriendsProgressChart = function (parent) {
        var totalProgress = parent.find('.charts .friendsProgress');

        if (totalProgress.length) {

            loadGoogleChart();
            google.charts.setOnLoadCallback(drawChart);
            function drawChart() {
                var data = google.visualization.arrayToDataTable([
                  ['Group', 'Value'],
                  [translations.TargetChartProgress, plusOr0(self.FriendsProgresVal())],
                  [translations.TargetForecast, plusOr0(self.FriendsForecastVal() - self.FriendsProgresVal())],
                  [translations.TargetMissing, plusOr0(self.FriendsMissingVal())]
                ]);

                var options = {
                    title: '',
                    pieHole: 0.4,
                    width: 200,
                    height: 200,
                    legend: 'none',
                    colors: ["#E44532", "#78CFC7", "#999"],
                    chartArea: { width: '80%', height: '80%' }
                };

                var chart = new google.visualization.PieChart(totalProgress[0]);
                chart.draw(data, options);
            }
        }
    }
    self.loadUserProgressChart = function (parent) {
        var totalProgress = parent.find('.charts .userProgress');

        if (totalProgress.length) {

            loadGoogleChart();
            google.charts.setOnLoadCallback(drawChart);
            function drawChart() {
                var v1= v2= v3=0;
                v1 = plusOr0(self.UserProgresVal());
                if (self.UserProgresVal() < self.UserTargetValue()) {
                    v2 = plusOr0(self.UserForecastVal() - self.UserProgresVal());
                    if (v1 + v2 > self.UserTargetValue()) {
                        v2 = self.UserTargetValue() - v1;
                    } else {
                        v3 = plusOr0(self.UserMissingVal());
                    }
                }

                var data = google.visualization.arrayToDataTable([
                  ['Group', 'Value'],
                  [translations.TargetChartProgress, v1],
                  [v3 ? translations.TargetForecast : translations.leftToEndOfTarget, v2],
                  [translations.TargetMissing,v3]
                ]);

                var options = {
                    title: '',
                    pieHole: 0.4,
                    width: 200,
                    height: 200,
                    legend: 'none',
                    colors: ["#E44532", "#78CFC7", "#999"],
                    chartArea: { width: '80%', height: '80%' }
                };

                var chart = new google.visualization.PieChart(totalProgress[0]);
                chart.draw(data, options);
            }
        }
    }
    self.loadCurrentProgressChart = function (parent) {
        var totalProgress = parent.find('.charts .currentProgress');

        if (totalProgress.length) {

            loadGoogleChart();
            google.charts.setOnLoadCallback(drawChart);
            function drawChart() {
                var data = google.visualization.arrayToDataTable([
                  ['Group', 'Value'],
                  ["me", plusOr0(self.UserProgresVal())],
                  ["Friends", plusOr0(self.FriendsProgresVal() - self.UserProgresVal())],
                  ["all", plusOr0(self.TotalProgresVal() - self.FriendsProgresVal())],
                  [translations.TargetCurrentMissing, plusOr0(self.TotalTargetVal() - self.TotalProgresVal())]
                ]);

                var options = {
                    title: '',
                    pieHole: 0.4,
                    width: 200,
                    height: 200,
                    legend: 'none',
                    colors: ["#702118","#AF3426","#E44532",   "#999"],
                    chartArea: { width: '80%', height: '80%' },
                    //slices: { 0: { offset: 0.2 } }
                };

                var chart = new google.visualization.PieChart(totalProgress[0]);
                chart.draw(data, options);
            }
        }
    }
    self.loadPrognozaProgressChart = function (parent) {
        var totalProgress = parent.find('.charts .prognozaProgress');

        if (totalProgress.length) {

            loadGoogleChart();
            google.charts.setOnLoadCallback(drawChart);
            function drawChart() {
                var data = google.visualization.arrayToDataTable([
                  ['Group', 'Value'],
                  ["me", plusOr0(self.UserForecastVal())],
                  ["Friends", plusOr0(self.FriendsForecastVal() - self.UserForecastVal())],
                  ["all", plusOr0(self.TotalForecastVal() - self.FriendsForecastVal())],
                  [translations.TargetMissing, plusOr0(self.TotalTargetVal() - self.TotalForecastVal())]
                ]);

                var options = {
                    title: '',
                    pieHole: 0.4,
                    width: 200,
                    height: 200,
                    legend: 'none',
                    colors: ["#4F8780","#69B5AD","#78CFC7",   "#999"],
                    chartArea: { width: '80%', height: '80%' }
                };

                var chart = new google.visualization.PieChart(totalProgress[0]);
                chart.draw(data, options);
            }
        }
    }
}

var targetStatsModel = new TargetStatsModel();
GeneralModel.addProperty("TargetStatsModel", targetStatsModel);



$(document).ready(function () {
    registerDatePicker($('.statsPicker'));
    registerRangeDatePicker($('.statsRangePicker'));
});


StatsHeaderModel = function () {
    var self = this;

    self.InitMode = true;

    self.TileLabel = ko.observable('');

    self.TypeManager =
        new TrainingTypeControl({
            mainId: null,
            subId: null,
            typeChangeCallback: null,
            clubId: null,
            multi: false,
            baseCtrl: null,
            disciplines: false,
            multiSubs: false,
            allInstedNone: true,
            dummy: null,
            selectNoneIfNoChoise: true,
            addNoneSubType: true,
            availableIds: []
        });

    self.UserStatReportChartViewModel = ko.observable(null);

    self.loadStats = function () {
        var lt = self.LengthType();
        var end = lt == -1 ? self.CalendarEndDate() : null;
        var tId = self.TypeManager.SelectedType();

        loadStats(self.ParamStart(), end, lt, tId ? tId.Id : null, self.sampleType());
    }
    self.loadReport = function () {
        var lt = self.LengthType();
        var end = lt == -1 ? self.CalendarEndDate() : null;
        var tId = self.TypeManager.SelectedType();

        loadReport(self.ParamStart(), end, lt, tId ? tId.Id : null, self.sampleType());
    }
    self.loadChart = function () {
        var lt = self.LengthType();
        var end = lt == -1 ? self.CalendarEndDate() : null;
        var tId = self.TypeManager.SelectedType();

        loadChart(self.ParamStart(), end, lt, tId ? tId.Id : null, self.sampleType());
    }

    self.Modes = [
        { name: translations.StatisticsGrafika, click: self.loadStats, ico: 'statystyki_40', id: 'stats' },
        { name: translations.Summary, click: self.loadReport, ico: 'lista_38', id: 'rep' },
        { name: translations.Chart, click: self.loadChart, ico: 'wykres_blok_32', id: 'chart' }
    ];
    self.SelectedMode = ko.observable(self.Modes[0]);

    self.Reload = function () {
        if (!self.InitMode) {
            self.Reloading = true;
            self.SelectedMode().click();
        }
    }
    self.TypeManager.SetChangeCallback(self.Reload);

    self.NextDate = ko.observable('');
    self.PreviousDate = ko.observable('');
    self.ParamStart = ko.observable('');
    self.LengthType = ko.observable(0);
    self.sampleType = ko.observable(0);
    self.CalendarDate = ko.observable();
    self.CalendarEndDate = ko.observable();
    self.CurrentDate = ko.observable('');
    self.Reloading = false;

    self.CalendarDate.subscribe(function (newValue) {
        if (!self.InitMode) {
            var date = self.CalendarDate();
            self.ParamStart(date);
            self.Reload();
        }
    });
    self.CalendarEndDate.subscribe(self.Reload);

    self.clickDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.yearBox').find('.datepicker');
            if (dp) {
                dp.datepicker('show');
            }
        }
    };
    self.RegisterDataPicker = function () {

        var dp = $('.periodsExplorer .yearBox .datepicker');
        if (dp) {
            dp.datepicker().on('show', function (ev) {
                var mode = 0;
                var newValue = self.LengthType();
                if (newValue == 30) { mode = 1 }
                else if (newValue == 365) { mode = 2 }
                if (mode - 1) {
                    dp.datepicker('showMode', mode - 1);
                }
            });
        }
    }
    self.GoToNextDate = function () {
        self.ParamStart(self.NextDate());
        self.Reload();
    }
    self.GoToPreviousDate = function () {
        self.ParamStart(self.PreviousDate());
        self.Reload();
    }
    self.LengthType.subscribe(self.Reload);
    /*
    self.getCallendarIcon = function (length) {
        var ico = translations.CallendarDayIcon;
        switch (length) {
            case 1: ico = translations.CallendarDayIcon; break;
            case 7: ico = translations.CallendarWeekIcon; break;
            case 30: ico = translations.CallendarMonthIcon; break;
            case 365: ico = translations.CallendarYearIcon; break;
            case -1: ico = translations.CallendarPeriodIcon; break;
        }
        return ContextPath + 'Content/images/icons/' + ico + (self.LengthType() == length ? 'cze' : '') + '.png';
    }

    self.DayIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(1); });
    self.WeekIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(7); });
    self.MonthIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(30); });
    self.YearIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(365); });
    self.ListIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(-1); });
    self.CurrentIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(self.LengthType()); });
*/

    self.PeriodLengthLabel = ko.pureComputed(function () {
        switch (self.LengthType()) {
            case 1: return translations.Day; break;
            case 7: return translations.Week; break;
            case 30: return translations.Month; break;
            case 365: return translations.Year; break;
            case -1: return translations.Period; break;
        }
    });

    self.SetValues = function (model) {
        self.InitMode = true;

        self.TileLabel(model.HeaderLabel);
        self.LengthType(model.LengthType);
        self.CalendarDate(model.StartDate);
        self.ParamStart(model.StartDate);
        self.CalendarEndDate(model.EndDate);
        self.PreviousDate(model.PrevDate);
        self.NextDate(model.NextDate);
        self.CurrentDate(model.CurrentDate);
        switch (model.ViewType) {
            case 'Stats': self.SelectedMode(self.Modes[0]); break;
            case 'Report': self.SelectedMode(self.Modes[1]); break;
            case 'Chart': self.SelectedMode(self.Modes[2]); break;
        }
        self.sampleType(model.SampleType);

        self.TypeManager.AvailableIds.removeAll();
        $.each(model.TrainingTypeIds, function (index, id) {
            self.TypeManager.AvailableIds.push(id);
        });
        self.TypeManager.SelectTypeById(model.TrainingTypeId);

        //model.DisciplineId;
        //model.DisciplineIds   ;

        self.InitMode = false;
        self.Reloading = false;

        hideDatePicker($(".uniHeadBar"));
    }

    self.GetSampleLabel = function (sampleLength) {
        if (sampleLength == null || sampleLength === undefined) {
            sampleLength = self.sampleType();
        }
        switch (sampleLength) {
            case 1: return translations.Day;
            case 7: return translations.Week;
            case 30: return translations.Month;
            case 365: return translations.Year;
        }
    }
    self.GetSampleIcon = function (sampleLength) {
        if (sampleLength == null || sampleLength === undefined) {
            sampleLength = self.sampleType();
        }
        return 'czas_' + sampleLength + '_25';
    }

    self.SampleLabel = ko.pureComputed(function () {
        return self.GetSampleLabel();
    });
    self.SampleIcon = ko.pureComputed(function () {
        return self.GetSampleIcon();
    });

    self.loadSampleType = function (val) {
        self.sampleType(val);
        self.Reload();
    }

    self.InitMode = false;
}
var statsHeaderModel = null;
getStatsHeaderModel = function (silent) {
    if (!statsHeaderModel) {
        statsHeaderModel = new StatsHeaderModel();
        GeneralModel.addProperty("StatsHeaderModel", statsHeaderModel, silent);
    }
    return statsHeaderModel;
}

;
function TemplateMask(templateMask) {
    var self = this;

    self.TypeChoose = ko.observable(true);
    self.DisciplineActivityChoose = ko.observable(true);
    self.CategoryChoose = ko.observable(true);
    self.ValueChoose = ko.observable(true);
    self.EventChoose = ko.observable(true);
    self.DeadlineChoose = ko.observable(true);
    self.StepBeneficientChoose = ko.observable(true);

    self.ClearDeadlineChoose = ko.observable(false);

    self.LastStep = ko.observable(8);

    if (templateMask) {
        visibilityConf = JSON.parse(templateMask);
        if (typeof (visibilityConf.TypeChoose) != "undefined") self.TypeChoose(visibilityConf.TypeChoose);
        if (typeof (visibilityConf.DisciplineActivityChoose) != "undefined") self.DisciplineActivityChoose(visibilityConf.DisciplineActivityChoose);
        if (typeof (visibilityConf.CategoryChoose) != "undefined") self.CategoryChoose(visibilityConf.CategoryChoose);
        if (typeof (visibilityConf.ValueChoose) != "undefined") self.ValueChoose(visibilityConf.ValueChoose);
        if (typeof (visibilityConf.EventChoose) != "undefined") self.EventChoose(visibilityConf.EventChoose);
        if (typeof (visibilityConf.DeadlineChoose) != "undefined") self.DeadlineChoose(visibilityConf.DeadlineChoose);
        if (typeof (visibilityConf.StepBeneficientChoose) != "undefined") self.StepBeneficientChoose(visibilityConf.StepBeneficientChoose);
        if (typeof (visibilityConf.ClearDeadlineChoose) != "undefined") self.ClearDeadlineChoose(visibilityConf.ClearDeadlineChoose);
        if (typeof (visibilityConf.LastStep) != "undefined") self.LastStep(visibilityConf.LastStep);
    }

    self.getClearData = function () {
        var data = {
            TypeChoose: self.TypeChoose(),
            DisciplineActivityChoose: self.DisciplineActivityChoose(),
            CategoryChoose: self.CategoryChoose(),
            ValueChoose: self.ValueChoose(),
            EventChoose: self.EventChoose(),
            DeadlineChoose: self.DeadlineChoose(),
            StepBeneficientChoose: self.StepBeneficientChoose(),
            ClearDeadlineChoose: self.ClearDeadlineChoose(),
            LastStep: self.LastStep()
        }
        return data;
    };
}

function SimplePeriodModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.LogoId = ko.observable(0);
    self.Date = ko.observable('');
    self.EndDate = ko.observable('');
    self.Name = ko.observable('');
    self.Type = ko.observable('');

    self.LogoUrl = ko.computed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.LogoId();
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.LogoId(serverModel.LogoId);
            self.Date(serverModel.Date);
            self.EndDate(serverModel.EndDate);
            self.Name(serverModel.Name);
            self.Type(serverModel.Type);
        }
    };
    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            LogoId: self.LogoId(),
            Date: self.Date(),
            EndDate: self.EndDate(),
            Name: self.Name(),
            Type: self.Type()
        }
        return data;
    };
}

function SimpleEventModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Date = ko.observable('');
    self.Name = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Date(serverModel.Date);
            self.Name(serverModel.Name);
        }
    };
}

function TargetCategoryModel(parent, serverModel) {
    var self = this;
    self.Parent = parent;

    if (serverModel) {
        self.Id = serverModel.Id;
        self.Icon = serverModel.Icon;
        self.Name = serverModel.Name;
        self.TargetType = serverModel.TargetType;
        self.TargetValueIcon = serverModel.TargetValueIcon;
        self.TargetValueName = serverModel.TargetValueName;
        self.TargetValueType = serverModel.TargetValueType;
        self.TargetValueUnit = serverModel.TargetValueUnit;
        self.ValueIdentifire = serverModel.ValueIdentifire;
        self.DefaultAccepHigher = serverModel.DefaultAccepHigher;
        self.DistanceCategory = serverModel.DistanceCategory;
        self.UseProgress = serverModel.UseProgress;
        self.UserMaleSex = serverModel.UserMaleSex;
    } else {
        self.Id = 0;
        self.Icon = '';
        self.Name = '';
        self.TargetType = '';
        self.TargetValueIcon = '';
        self.TargetValueName = '';
        self.TargetValueType = '';
        self.TargetValueUnit = '';
        self.ValueIdentifire = '';
        self.DefaultAccepHigher = true;
        self.DistanceCategory = false;
        self.UseProgress = true;
        self.UserMaleSex = 'none';
    }

    self.SuccessMainText = ko.computed(function () {
        return translations['IDidIt_' + self.UserMaleSex];
    });

    self.FailedMainText = ko.computed(function () {
        return translations['IFailed_' + self.UserMaleSex];
    });

    self.SuccessText = ko.computed(function () {
        return translations['TargetSuccess_' + self.ValueIdentifire + '_' + self.UserMaleSex];
    });

    /*
    self.TargetValueIconClass = ko.computed(function () {
        if (self.TargetValueIcon) {
            return self.TargetValueIcon + '_40';
        } else {
            return ''
        }
    });
    */
}

function BeneficientModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Age = ko.observable(0);
    self.ClubId = ko.observable(0);
    self.Id = ko.observable(0);
    self.PhotoFileId = ko.observable(0);
    self.Description = ko.observable('');
    self.DidForText = ko.observable('');
    self.Name = ko.observable('');

    self.init = function () {
        self.Age(0);
        self.ClubId(0);
        self.Id(0);
        self.PhotoFileId(0);
        self.Description('');
        self.DidForText('');
        self.Name('');
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Age(serverModel.Age);
            self.ClubId(serverModel.ClubId);
            self.Id(serverModel.Id);
            self.PhotoFileId(serverModel.PhotoFileId);
            self.Description(serverModel.Description);
            self.DidForText(serverModel.DidForText);
            self.Name(serverModel.Name);
        }
    };

    self.getClearData = function () {
        var data = {
            Age: self.Age(),
            ClubId: self.ClubId(),
            Id: self.Id(),
            PhotoFileId: self.PhotoFileId(),
            Description: self.Description(),
            DidForText: self.DidForText(),
            Name: self.Name()
        }
        return data;
    };
}

function TargetModel(parent, readOnly) {
    var self = this;

    var today = (new Date()).toISOString().substr(0, 10);

    self.Parent = parent;

    self.autoChange = true;
    self.ReadOnly = readOnly;

    self.EnableBeneficients = false;

    self.DisciplineManager = self.Parent ? self.Parent.DisciplineManager : null;
    self.TrainingTypeManager = self.Parent ? self.Parent.TrainingTypeManager : null;

    self.CountDownTimer = ko.observable(new CountDownTimer());

    self.BeneficientId = ko.observable(0);
    //self.DisciplineCategoryId = ko.observable(0);
    self.Id = ko.observable(0);
    self.Index = ko.observable(0);
    self.OwnerPeriodId = ko.observable(0);
    self.OwnerUserId = ko.observable(0);
    self.Progres = ko.observable(0);
    self.TargetCategoryId = ko.observable(0);
    //self.TrainingTypeId = ko.observable(0);
    self.CurrentValue = ko.observable('');
    self.InitValue = ko.observable('');
    self.Status = ko.observable('');
    self.TargetSpan = ko.observable('');
    self.TargetType = ko.observable('');
    self.TargetValue = ko.observable('');
    self.AcceptHigherValue = ko.observable(false);
    self.Template = ko.observable(false);
    self.TemplateId = ko.observable(-1);
    self.TemplateMask = ko.observable(new TemplateMask());
    self.MaskForNewTemplate = ko.observable(new TemplateMask());
    self.Beneficient = ko.observable(new BeneficientModel(self));
    self.Deadline = ko.observable(today);

    self.Name = ko.observable('');
    self.Description = ko.observable('');
    self.LogoId = ko.observable(0);
    self.SharingLogoId = ko.observable(0);
    self.SharingClubLogoId = ko.observable(0);
    self.SponsorLogoId = ko.observable(0);
    self.FromClub = ko.observable(false);
    self.ClubGradientColor = ko.observable('');
    self.ClubTextColor = ko.observable('');
    self.PeriodName = ko.observable('');
    self.ParentPeriodId = ko.observable(0);

    self.PersonalTargetModel = ko.observable(null);

    self.LastUpdate = ko.observable('');
    self.StartDate = ko.observable('');
    self.EventIds = ko.observableArray([]);
    self.Events = ko.observableArray([]);
    self.PeriodIds = ko.observableArray([]);
    self.Periods = ko.observableArray([]);
    self.ContestIds = ko.observableArray([]);
    self.Contests = ko.observableArray([]);

    self.Fans = ko.observable(new FanModelForPublication(self));
    self.ForExplorer = ko.observable(false);
    self.ForUserBarTile = ko.observable(false);
    self.CallBackForDeleting = null;

    self.GroupTarget = ko.observable(false);
    self.ParentGroupTarget = ko.observable(false);
    self.CanJoin = ko.observable(false);

    self.DisplayDetails = ko.observable(false);
    self.ParentPeriodModel = ko.observable(null);
    self.StatsModel = ko.observable(null);
    self.RanksModel = ko.observable(null);

    self._userTargetModel = null;
    self.UserTargetModel = ko.pureComputed(function () {
        if (!self._userTargetModel) {
            self._userTargetModel = new UserTargetModel(null, self.Index(), self);
        }

        return self._userTargetModel;
    });

    self.TextNotFitClass = ko.computed(function () {
        return self.ForExplorer() ? 'textNotFit' : self.ForUserBarTile() ? 'textNotFitUserBar' : '';
    });

    self.targetTileClick = function () {
        var id = self.ParentPeriodId();
        if (!id) return '';
        window.location = ContextPath + "Period/SinglePeriodExplorer?periodId=" + id;
    }

    self.ShowTextNotFitClass = ko.computed(function () {
        if (self.ForExplorer() || self.ForUserBarTile()) {
            return true;
        }

        return false;
    });

    self.Deadline.subscribe(function (newValue) {
        if (newValue && !self.autoChange) {
            if (self.Id() <= 0) {
                var t = self.StartDate();
                if (newValue < t) {
                    self.Deadline(t);
                }
            }
        }
    });
    self.StartDate.subscribe(function (newValue) {
        if (newValue && !self.autoChange) {
            if (self.Id() <= 0) {
                if (newValue < today && !self.PeriodAdmin()) {
                    self.StartDate(today);
                }
                if (self.Deadline() < newValue) {
                    self.Deadline(newValue);
                }
            }
        }
    });
    self.GroupFinished = ko.pureComputed(function () {
        if (!self.GroupTarget()) return false;
        var d = new Date(self.Deadline());
        d.setHours(23);
        d.setMinutes(59);
        d.setSeconds(59);
        return d < new Date();
    });

    self.ShowContentOpen = ko.computed(function () {
        return (self.Status() == 'Open' && self.CountDownTimer().Show());
    });

    self.ShowContentSuccess = ko.computed(function () {
        return self.Status() == 'Success';
    });

    self.ShowContentFailed = ko.computed(function () {
        return (self.Status() == 'Failed' || (self.Status() == 'Open' && !self.CountDownTimer().Show()));
    });

    self.TargetStatusBackgroundColor = ko.computed(function () {
        return self.ClubGradientColor() ? self.ClubGradientColor() : self.ShowContentSuccess() ? '#E24631' : '#484848';
    });

    self.TargetStatusTextColor = ko.computed(function () {
        return self.ClubTextColor() ? self.ClubTextColor() : 'white';
    });


    self.HiDurationInformed = -1;
    self.TargetValue.subscribe(function (newValue) {
        if (newValue) {
            var tt = self.TargetCategory().TargetValueType;
            if ((tt == 'int' || tt == 'counter') && isNaN(newValue)) {
                self.TargetValue(0);
            }
            if (tt == 'time' && !self.autoChange) {
                var a = self.TargetValue().split(':');
                if ((+a[0]) >= 10) {
                    if (self.HiDurationInformed != self.Id()) {
                        var modal = $('#emptyMsgModal');
                        modal.find('#emptyMsgModalLabel').html(translations.SuspiciousValue);
                        modal.find('.askMsg').html(translations.TimeSuspiciousValue10.replace('###', a[0]));
                        modal.modal("show");
                        self.HiDurationInformed = self.Id();
                    }
                } else {
                    self.HiDurationInformed = -1;
                }
            }
        }
    });

    self.CurrentValueFormatted = function (c, d, t) {
        var n = new Number(self.CurrentValue());
        var x = n.formatNumber(c, d, t);

        return x;
    };
    self.TargetValueFormatted = function (c, d, t) {
        var n = new Number(self.TargetValue());
        var x = n.formatNumber(c, d, t);

        return x;
    };



    self.CustomTarget = ko.observable(false);
    self.CustomTarget.subscribe(function (newValue) {
        if (newValue) {
            var changeAuto = !self.autoChange;
            if (changeAuto) self.autoChange = true;
            self.UsedTemplate(null);
            self.GoNextStep(null, null, 0);
            if (changeAuto) self.autoChange = false;
        }
    });
    self.UsedTemplate = ko.observable(null);
    self.UsedTemplate.subscribe(function (newValue) {
        if (newValue) {
            var changeAuto = !self.autoChange;
            if (changeAuto) self.autoChange = true;
            self.CustomTarget(false);
            self.GoNextStep(null, null, 0);
            if (changeAuto) self.autoChange = false;
        }
    });

    self.TargetType.subscribe(function (newValue) {
        if (newValue) {
            self.TargetCategoryId(0);
            self.clearEvents();
            var changeAuto = !self.autoChange;
            if (changeAuto) self.autoChange = true;
            self.GoNextStep(null, null, 1);
            if (changeAuto) self.autoChange = false;
        }
    });

    self.EventsTarget = ko.computed(function () {
        return self.TargetType() == 'Events';
    });
    self.ActivitiesTarget = ko.computed(function () {
        return self.TargetType() == 'Activities' || self.TargetType() == 'Group';
    });
    self.GroupTargetType = ko.computed(function () {
        return self.TargetType() == 'Group';
    });

    self.PeriodAdmin = ko.computed(function () {
        return self.OwnerPeriodId() != null && self.OwnerPeriodId() != 0;
    });

    self.NewTarget = ko.computed(function () {
        return self.Id() == 0;
    });

    self.AvailableCategories = ko.computed(function () {
        var result = [];
        if (self.Parent) {
            var all = self.Parent.TargetCategories();
            var tType = self.TargetType();
            var currentTargetCategoryId = self.TargetCategoryId();
            //var currentMatch = false;
            var distance = true;
            if (tType == 'Activities' || tType == 'Group') {
                var t = self.TrainingTypeManager.SelectedType();
                if (t != null && t.ParamsVisibility) {
                    var visibilityConf = JSON.parse(t.ParamsVisibility);
                    if (typeof (visibilityConf.RouteGroup) != "undefined") {
                        distance = visibilityConf.RouteGroup;
                    }
                }
            }

            if (tType == 'Group') tType = 'Activities';
            for (var i = 0, len = all.length; i < len; i++) {
                var c = all[i];
                if (c.TargetType == tType && (!c.DistanceCategory || (distance && c.DistanceCategory))) {
                    result.push(c);
                }
            }
        }
        return result;
    });

    self.TargetCategoryId.subscribe(function (newValue) {
        if (newValue && newValue > 0) {
            var changeAuto = !self.autoChange;
            if (changeAuto) self.autoChange = true;

            self.TargetValue(null);

            self.GoNextStep(null, null, 3);
            if (changeAuto) self.autoChange = false;
        }
    });

    self.GetTargetCategoryById = function (currentTargetCategoryId) {
        if (currentTargetCategoryId > 0) {
            var all = self.AvailableCategories();
            for (var i = 0, len = all.length; i < len; i++) {
                var c = all[i];
                if (c.Id == currentTargetCategoryId) {
                    self.initTimePicker();
                    return c;
                }
            }
        }
        return new TargetCategoryModel(self);
    }

    self.LoadedTargetCategory = ko.observable(new TargetCategoryModel(self));
    self.TargetCategory = ko.computed(function () {
        if (self.ReadOnly) {
            return self.LoadedTargetCategory();
        }
        var currentTargetCategoryId = self.TargetCategoryId();
        return self.GetTargetCategoryById(currentTargetCategoryId);
    });

    self.CurrentStep = ko.observable(0);

    self.PeriodType = ko.observable('Competitions');
    self.SingleEvent = ko.computed(function () {
        if (self.Periods().length) {
            return self.Periods()[0];
        } else if (self.Contests().length) {
            return self.Contests()[0];
        }
        return null;
    });
    self.EventDisplayName = ko.computed(function () {
        var eve = self.SingleEvent();

        return eve != null ? eve.Name() : translations.chooseEventInfo;
    });

    self.LongDeadline = ko.computed(function () {
        var d = self.Deadline();
        if (d) {
            return dateToLongHtml(d);
        } else {
            return '';
        }
    });

    self.LoadedDisciplineCategory = ko.observable(new TrainingTypeModel(null, null, null, null, true));
    self.DisciplineCategory = ko.computed(function () {
        if (self.ReadOnly) {
            return self.LoadedDisciplineCategory();
        } else {
            return self.DisciplineManager.SelectedSubType() != null ? self.DisciplineManager.SelectedSubType() : self.LoadedDisciplineCategory();
        }
    });
    self.LoadedTrainingType = ko.observable(new TrainingTypeModel(null, null, null, null, true));
    self.TrainingType = ko.computed(function () {
        if (self.ReadOnly) {
            return self.LoadedTrainingType();
        } else {
            return self.TrainingTypeManager.SelectedType() != null ? self.TrainingTypeManager.SelectedType() : self.LoadedTrainingType();
        }
    });


    self.FormatedValue = function (value) {
        var n = new Number(value);
        var x = n.formatNumber(0, 3, ' ' /*c, d, t*/);
        return x;
    }
    self.FormatedValuewithUnit = function (value, TargetValueType, Unit) {

        if (TargetValueType == 'int') {
            return self.FormatedValue(value) + ' ' + Unit;
        } else {
            return value + ' ' + Unit;
        }
    }
    self.FormattedTargetValue = ko.computed(function () {
        return self.FormatedValuewithUnit(self.TargetValue(), self.TargetCategory().TargetValueType, self.TargetCategory().TargetValueUnit);
    });
    self.FormattedCurrentValue = ko.computed(function () {
        return self.FormatedValuewithUnit(self.CurrentValue(), self.TargetCategory().TargetValueType, self.TargetCategory().TargetValueUnit);
    });

    self.CurrentValueLabelText = ko.computed(function () {
        return self.GroupTarget() ? translations["ResultOfGroup"] : translations["CurrentValue"];
    });

    self.TargetValueLabelText = ko.computed(function () {
        return self.GroupTarget() ? translations["TargetOfGroup"] : translations["TargetValue"];
    });

    self.LogoUrl = ko.computed(function () { return ContextPath + "Image/GetImage?id=" + self.LogoId(); });
    self.SharingLogoUrl = ko.computed(function () { return ContextPath + "Image/GetImage?id=" + self.SharingLogoId(); });
    self.SharingClubLogoUrl = ko.computed(function () { return ContextPath + "Image/GetImage?width=447&height=80&id=" + self.SharingClubLogoId(); });
    self.SponsorLogoUrl = ko.computed(function () { return ContextPath + "Image/GetImage?width=447&height=67&id=" + self.SponsorLogoId(); });

    self.StatsUrl = ko.pureComputed(function () {
        return ContextPath + "Stats/TargetStatistics?targetId=" + self.Id();
    });


    if (!self.ReadOnly) {

        self.GroupTargetValue = ko.observable('');

        self.TargetValueForAccept = ko.computed(function () {
            if (self.GroupTargetType()) {
                return self.TargetValue() + '/' + self.GroupTargetValue();
            } else {
                return self.TargetValue();
            }
        });

        self.EventsNotSelected = ko.computed(function () {
            return self.EventIds().length == 0 && self.PeriodIds().length == 0 && self.ContestIds().length == 0;
        });

        self.DisplayStep_TypeChoose = ko.computed(function () {
            return self.CurrentStep() >= 1 && (self.CustomTarget() || self.UsedTemplate());
        });
        self.ValidateStep_TypeChoose = function (data, event) {
            var t = self.TargetType();
            var valid = (t == 'Events' || t == 'Activities' || t == 'Group');

            if (!valid) {
                self.DisplayAlert(translations.ChooseTragetTypeMsg);
            }

            return valid;
        }
        self.DisplayStep_DisciplineChoose = ko.computed(function () {
            var t = self.TargetType();
            return self.CurrentStep() >= 2 && t == 'Events';
        });
        self.DisplayStep_ActivityChoose = ko.computed(function () {
            var t = self.TargetType();
            return self.CurrentStep() >= 2 && (t == 'Activities' || t == 'Group');
        });
        self.ValidateStep_DisciplineActivityChoose = function (data, event) {
            /*
            var t = self.TargetType();
            var valid = ((t == 'Events' && self.DisciplineManager.SelectedSubType() != null && self.DisciplineManager.SelectedSubType().Id != 0)
                || (t == 'Activities' && self.TrainingTypeManager.SelectedType() != null && self.TrainingTypeManager.SelectedType().Id != 0));

            if (!valid) {
                self.DisplayAlert(translations.ChooseDisciplineMsg);
            }

            return valid;
            */
            return true;
        }
        self.DisplayStep_CategoryChoose = ko.computed(function () {
            /*
            var t = self.TargetType();
            var disc = self.DisciplineManager.SelectedSubType();
            var acti = self.TrainingTypeManager.SelectedType();
            */
            var result = self.CurrentStep() >= 3;
            //&& ((t == 'Events' && disc != null && disc.Id != 0)
            //|| (t == 'Activities' && acti != null && acti.Id != 0));
            return result;
        });
        self.ValidateStep_CategoryChoose = function (data, event) {
            var valid = (self.TargetCategoryId() > 0);

            if (!valid) {
                self.DisplayAlert(translations.ChooseTragetCategoryMsg);
            }

            return valid;
        }
        self.DisplayStep_ValueChoose = ko.computed(function () {
            return self.CurrentStep() >= 4 && self.TargetCategoryId() > 0 && self.TargetCategory().TargetValueType != 'none';
        });
        self.ValidateStep_ValueChoose = function (data, event) {
            var valid = self.TargetValue() || (self.TargetCategoryId() > 0 && self.TargetCategory().TargetValueType == 'none');

            if (!valid) {
                self.DisplayAlert(translations.GiveTargetValueMsg);
            }

            return valid;
        }
        self.DisplayStep_EventChoose = ko.computed(function () {
            return self.CurrentStep() >= 5 && self.EventsTarget() && self.TargetCategory().ValueIdentifire != 'eventCount';
        });
        self.DisplayStep_DeadlineChoose = ko.computed(function () {
            return self.CurrentStep() >= 6
                && (self.TargetValue() || (self.TargetCategoryId() > 0 && self.TargetCategory().TargetValueType == 'none'))
                && self.EventsNotSelected()
            ;
        });
        self.ValidateStep_DeadlineChoose = function (data, event) {
            var valid = self.Deadline() && self.StartDate();

            if (!valid) {
                self.DisplayAlert(translations.ChooseDeadlineMsg);
            }

            return valid;
        }
        self.DisplayStep_BeneficientChoose = ko.computed(function () {
            return self.EnableBeneficients && self.CurrentStep() >= 7 && self.Deadline();
        });
        self.DisplayStep_Accept = ko.computed(function () {
            return self.CurrentStep() >= 8;
        });

        self.StepNr_EventChoose = ko.computed(function () {
            return self.DisplayStep_ValueChoose() ? 5 : 4;
        });
        self.StepNr_DeadlineChoose = ko.computed(function () {
            var base = 6;
            if (!self.DisplayStep_ValueChoose()) base--;
            if (!self.DisplayStep_EventChoose()) base--;
            return base;
        });
        self.StepNr_BeneficientChoose = ko.computed(function () {
            var base = 7;
            if (!self.DisplayStep_DeadlineChoose()) base--;
            if (!self.DisplayStep_ValueChoose()) base--;
            if (!self.DisplayStep_EventChoose()) base--;
            return base;
        });
        self.StepNr_Accept = ko.computed(function () {
            var base = 8;
            if (!self.DisplayStep_DeadlineChoose()) base--;
            if (!self.DisplayStep_ValueChoose()) base--;
            if (!self.DisplayStep_EventChoose()) base--;
            if (!self.EnableBeneficients) base--;
            return base;
        });

    }

    self.GoNextStep = function (data, event, currentStep, forceScroll) {

        if (self.ReadOnly) return;

        var step = 1;
        var actualStep = self.CurrentStep();
        currentStep = actualStep > currentStep ? actualStep : currentStep;
        var valid = true;

        var targetCategoryId = self.TargetCategoryId();
        var targetCategory = self.GetTargetCategoryById(targetCategoryId);

        if ((currentStep + 1) == 1 && !self.TemplateMask().TypeChoose()) currentStep = 1;
        if ((currentStep + 1) == 2 && !self.TemplateMask().DisciplineActivityChoose()) currentStep = 2;
        if ((currentStep + 1) == 3 && !self.TemplateMask().CategoryChoose()) currentStep = 3;
        if ((currentStep + 1) == 4 && !self.TemplateMask().ValueChoose()) currentStep = 4;
        if ((currentStep + 1) == 5 && !self.TemplateMask().EventChoose()) currentStep = 5;
        if ((currentStep + 1) == 6 && !self.TemplateMask().DeadlineChoose()) currentStep = 6;
        if ((currentStep + 1) == 7 && !self.TemplateMask().StepBeneficientChoose()) currentStep = 7;

        if (currentStep >= 1) {
            valid = self.ValidateStep_TypeChoose(data, event);
            if (valid) step = 2;
        }
        if (valid && currentStep >= 2) {
            valid = self.ValidateStep_DisciplineActivityChoose(data, event);
            if (valid) step = 3;
        }
        if (valid && currentStep >= 3) {
            valid = self.ValidateStep_CategoryChoose(data, event);
            if (valid) {
                step = (targetCategoryId > 0 && targetCategory.TargetValueType != 'none') ? 4 : 5;
            }
        }
        if (valid && currentStep >= 4) {
            valid = self.ValidateStep_ValueChoose(data, event);
            if (valid) {
                step = (self.ActivitiesTarget() || (targetCategory.ValueIdentifire == 'eventCount')) ? 6 : 5;
            }
        }
        if (valid && currentStep >= 5) {
            step = self.EventsNotSelected() ? 6 : 7;
        }
        if (valid && currentStep >= 6) {
            valid = self.ValidateStep_DeadlineChoose(data, event);
            if (valid) step = 7;
        }
        if (!self.EnableBeneficients && step == 7) {
            step = 8;
        }
        if (valid && currentStep >= 7) {
            step = 8;
        }

        self.StepScroll(step, forceScroll);

        self.CurrentStep(step);
    }

    self.clearEvents = function () {
        self.EventIds.removeAll();
        self.Events.removeAll();
        self.PeriodIds.removeAll();
        self.Periods.removeAll();
        self.ContestIds.removeAll();
        self.Contests.removeAll();
    }

    self.init = function (templateClear) {
        self.autoChange = true;

        self.TargetType('');
        self.TargetCategoryId(0);
        self.BeneficientId(0);
        self.Index(0);
        self.Progres(0);
        self.CurrentValue('');
        self.InitValue('');
        self.Status('');
        self.TargetSpan('');
        self.TargetValue('');
        self.AcceptHigherValue(true);
        self.TemplateId(0);
        self.TemplateMask(new TemplateMask());
        self.MaskForNewTemplate(new TemplateMask());

        self.DisciplineManager.SelectValue(0, 0);
        self.TrainingTypeManager.SelectValue(0, 0);

        self.PersonalTargetModel(null);

        self.GroupTarget(false);
        self.ParentGroupTarget(false);

        if (!templateClear) {
            self.Id(0);
            self.OwnerPeriodId(0);
            self.OwnerUserId(0);
            self.Template(false);
            self.Name('');
            self.Description('');
        }
        /*
        if (serverModel.TrainingType) {
            self.TrainingTypeManager.SelectValue(serverModel.TrainingType.Id, null, 0, function () {
                self.GoToAutoLastStep();
            });
        }
        if (serverModel.DisciplineCategory) {
            self.DisciplineManager.SelectValue(serverModel.DisciplineCategory.ParentId, serverModel.DisciplineCategory.Id, 0, function () {
                self.GoToAutoLastStep();
            });
        }
        */

        self.clearEvents();

        self.TrainingTypeManager.Enable = true;
        self.DisciplineManager.Enable = true;

        self.Deadline(today);
        self.StartDate(today);

        self.CurrentStep(1);


        self.autoChange = false;
    };

    //     self.initById = function (id) {
    //         $.ajax({
    //             type: "POST",
    //             url: ContextPath + "Target/GetTargetModel",
    //             contentType: "application/json; charset=utf-8",
    //             dataType: "json",            
    //             data: { id: id }
    //         })
    //          .done(function (result) {
    //              if (result) {
    //                  self.initByModel(result);
    //              }
    //          });
    //     }       

    self.initByModel = function (serverModel, templateLoading, dontScrol) {
        if (serverModel) {
            self.autoChange = true;

            if (!templateLoading) {
                self.Id(serverModel.Id);
                self.Index(serverModel.Index);
                self.OwnerPeriodId(serverModel.OwnerPeriodId);
                self.OwnerUserId(serverModel.OwnerUserId);
                self.Template(serverModel.Template);
                if (serverModel.Template) {
                    self.MaskForNewTemplate(new TemplateMask(serverModel.TemplateMask));
                }
            }

            self.TargetType(serverModel.TargetType);
            self.TargetCategoryId(serverModel.TargetCategoryId);

            self.LoadedTargetCategory(new TargetCategoryModel(self, serverModel.TargetCategory));

            self.BeneficientId(serverModel.BeneficientId);
            self.Progres(serverModel.Progres);
            self.CurrentValue(serverModel.CurrentValue);
            self.InitValue(serverModel.InitValue);
            self.Status(serverModel.Status);
            self.TargetSpan(serverModel.TargetSpan);
            self.TargetValue(serverModel.TargetValue);
            self.AcceptHigherValue(serverModel.AcceptHigherValue);
            self.TemplateId(serverModel.TemplateId);
            self.TemplateMask(new TemplateMask(serverModel.TemplateMask));
            self.Beneficient().initByModel(serverModel.Beneficient);
            self.Deadline(serverModel.Deadline);
            self.LastUpdate(serverModel.LastUpdate);
            self.StartDate(serverModel.StartDate);
            self.LogoId(serverModel.LogoId);
            self.SharingLogoId(serverModel.SharingLogoId);
            self.SharingClubLogoId(serverModel.SharingClubLogoId);
            self.SponsorLogoId(serverModel.SponsorLogoId);
            self.FromClub(serverModel.FromClub);
            self.ClubGradientColor(serverModel.ClubGradientColor);
            self.ClubTextColor(serverModel.ClubTextColor);
            self.PeriodName(serverModel.PeriodName);
            self.ParentPeriodId(serverModel.ParentPeriodId);

            self.GroupTarget(serverModel.GroupTarget);
            self.ParentGroupTarget(serverModel.ParentGroupTarget);
            self.CanJoin(serverModel.CanJoin);

            if (serverModel.PersonalTargetModel) {
                if (!self.PersonalTargetModel()) {
                    self.PersonalTargetModel(new TargetModel(self.Parent, true));
                }
                self.PersonalTargetModel().initByModel(serverModel.PersonalTargetModel, false, true);
            } else {
                self.PersonalTargetModel(null);
            }

            if (self.PeriodAdmin() && !templateLoading) {
                self.Name(serverModel.Name);
                self.Description(serverModel.Description);
            }

            //self.TargetCategory().initByModel(serverModel.TargetCategory);
            //self.TrainingType().initByModel(serverModel.TrainingType);
            //self.TrainingTypeId(serverModel.TrainingTypeId);
            //self.DisciplineCategoryId(serverModel.DisciplineCategoryId);
            //self.DisciplineCategory().initByModel(serverModel.DisciplineCategory);            

            if (serverModel.TrainingType) {
                self.LoadedTrainingType(new TrainingTypeModel(serverModel.TrainingType));
            } else {
                self.LoadedTrainingType(new TrainingTypeModel(null, null, null, null, true));
            }
            if (serverModel.DisciplineCategory) {
                self.LoadedDisciplineCategory(new TrainingTypeModel(serverModel.DisciplineCategory));
            } else {
                self.LoadedDisciplineCategory(new TrainingTypeModel(null, null, null, null, true));
            }

            if (serverModel.TrainingType && !self.ReadOnly) {
                self.TrainingTypeManager.SelectValue(serverModel.TrainingType.Id, null, 0, function () {
                    self.GoToAutoLastStep();
                });
            } else {
                if (self.TrainingTypeManager) {
                    self.TrainingTypeManager.SelectTypeById(0);
                }
            }
            if (serverModel.DisciplineCategory && !self.ReadOnly) {
                self.DisciplineManager.SelectValue(serverModel.DisciplineCategory.ParentId, serverModel.DisciplineCategory.Id, 0, function () {
                    self.GoToAutoLastStep();
                });
            } else {
                if (self.DisciplineManager) {
                    self.DisciplineManager.SelectSubTypeById(0, 0);
                }
            }

            self.EventIds.removeAll();
            if (serverModel.EventIds) {
                for (var i = 0, len = serverModel.EventIds.length; i < len; i++) {
                    self.EventIds.push(serverModel.EventIds[i]);
                }
            }

            self.Events.removeAll();
            if (serverModel.Events) {
                for (var i = 0, len = serverModel.Events.length; i < len; i++) {
                    var elem = new SimpleEventModel(self);
                    elem.initByModel(serverModel.Events[i]);
                    self.Events.push(elem);
                }
            }

            self.PeriodIds.removeAll();
            if (serverModel.PeriodIds) {
                for (var i = 0, len = serverModel.PeriodIds.length; i < len; i++) {
                    self.PeriodIds.push(serverModel.PeriodIds[i]);
                }
            }

            self.Periods.removeAll();
            if (serverModel.Periods) {
                for (var i = 0, len = serverModel.Periods.length; i < len; i++) {
                    var elem = new SimplePeriodModel(self);
                    elem.initByModel(serverModel.Periods[i]);
                    self.Periods.push(elem);
                }
                if (self.Periods().length) {
                    self.PeriodType(self.Periods()[0].Type);
                }
            }


            self.ContestIds.removeAll();
            if (serverModel.ContestIds) {
                for (var i = 0, len = serverModel.ContestIds.length; i < len; i++) {
                    self.ContestIds.push(serverModel.ContestIds[i]);
                }
            }

            self.Contests.removeAll();
            if (serverModel.Contests) {
                for (var i = 0, len = serverModel.Contests.length; i < len; i++) {
                    var elem = new SimplePeriodModel(self);
                    elem.initByModel(serverModel.Contests[i]);
                    self.Contests.push(elem);
                }

                if (self.Contests().length) {
                    self.PeriodType('Competitions');
                }
            }

            if (self.TrainingTypeManager && self.DisciplineManager) {
                var disEn = self.TemplateMask().DisciplineActivityChoose();
                self.TrainingTypeManager.Enable = disEn;
                self.DisciplineManager.Enable = disEn;
            }

            if (templateLoading && self.TemplateMask().ClearDeadlineChoose()) {
                self.StartDate(today);
                self.Deadline(today);
            }

            if (self.ReadOnly) {
                self.CountDownTimer(new CountDownTimer(self.Deadline()));
            }

            if (!dontScrol) {
                self.GoToAutoLastStep();
            }

            if (serverModel.FanModel) {
                self.Fans().initByModel(serverModel.FanModel);
            }

            self.autoChange = false;
        }
    };

    self.GoToAutoLastStep = function () {
        if (!self.ReadOnly) {
            var step = self.TemplateMask().LastStep();
            self.autoChange = true;
            self.GoNextStep(null, null, step);
            self.autoChange = false;
            self.CurrentStep(step);
            self.StepScroll(step, true, true);
        }
    }

    self.StepScrollTimeOut = null;
    self.StepScroll = function (step, forceScroll, setExacly) {
        if (self.Id() == 0 || forceScroll) {
            if (self.CurrentStep() < step || setExacly) { /* - Magdzi sie na podoba bungee */

                if (self.StepScrollTimeOut) clearTimeout(self.StepScrollTimeOut);

                self.StepScrollTimeOut = setTimeout(function () {
                    var q = $(".step" + step + ":visible").first();
                    if (q) {
                        $.scrollTo(q, { duration: 800, easing: 'swing' });
                    }
                }, 550);
            }
        }
    }

    self.getClearData = function () {
        var _EventIds = []; var _EventIds_Iter = self.EventIds(); for (var i = 0, len = _EventIds_Iter.length; i < len; i++) { _EventIds.push(_EventIds_Iter[i]); }
        var _PeriodIds = []; var _PeriodIds_Iter = self.PeriodIds(); for (var i = 0, len = _PeriodIds_Iter.length; i < len; i++) { _PeriodIds.push(_PeriodIds_Iter[i]); }
        var _ContestIds = []; var _ContestIds_Iter = self.ContestIds(); for (var i = 0, len = _ContestIds_Iter.length; i < len; i++) { _ContestIds.push(_ContestIds_Iter[i]); }
        var _Contests = []; var _Contests_Iter = self.Contests(); for (var i = 0, len = _Contests_Iter.length; i < len; i++) { _Contests.push(_Contests_Iter[i].getClearData()); }
        var data = {
            BeneficientId: self.BeneficientId(),
            DisciplineCategoryId: self.DisciplineManager.SelectedSubType() != null ? self.DisciplineManager.SelectedSubType().Id : 0,
            Id: self.Id(),
            OwnerPeriodId: self.OwnerPeriodId(),
            OwnerUserId: self.OwnerUserId(),
            //Progres: self.Progres(),
            TargetCategoryId: self.TargetCategoryId(),
            TrainingTypeId: self.TrainingTypeManager.SelectedType() != null ? self.TrainingTypeManager.SelectedType().Id : 0,
            CurrentValue: self.CurrentValue(),
            InitValue: self.InitValue(),
            //Status: self.Status(),
            TargetSpan: self.TargetSpan(),
            TargetType: self.TargetType(),
            TargetValue: self.TargetValue(),
            GroupTargetValue:self.GroupTargetValue(),
            AcceptHigherValue: self.AcceptHigherValue(),
            Template: self.Template(),
            Deadline: self.Deadline(),
            StartDate: self.StartDate(),
            TemplateId: self.TemplateId(),
            TemplateMask: JSON.stringify(self.MaskForNewTemplate().getClearData()),
            Name: self.Name(),
            Description: self.Description(),
            EventIds: _EventIds,
            PeriodIds: _PeriodIds,
            ContestIds: _ContestIds,
            Contests: _Contests
        }
        return data;
    };

    self.SaveLabel = ko.computed(function () {
        if (!self.PeriodAdmin()) {
            return translations.YesIllDoIt;
        } else {
            if (self.NewTarget() && !self.Template()) {
                return translations.SaveAndGoToTemplate;
            } else {
                return translations.Save;
            }
        }
        return translations.Save;
    });

    self.saving = false;
    self.trySave = function (data, event, callback) {
        if (self.saving) return;
        self.saving = true;

        var infoContainer = (event) ? $(event.target) : null;

        var data = self.getClearData();
        var strData = JSON.stringify(data);
        var groupUserTarget = self.GroupTargetValue();

        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "Target/SetTarget",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.saving = false;
            if (result && !result.SaveMsg) {
                self.initByModel(result);
                if (infoContainer) {
                    ShowSavedAlert(infoContainer);
                }
                if (callback) {
                    callback();
                } if (groupUserTarget) {
                    self.openUserPeriodPage(result.OwnerPeriodId);
                } else {
                    if (self.OwnerPeriodId()) {
                        if (self.NewTarget() && !self.Template()) {
                            self.initByModel(result.AsociatedTemplate);
                            $.scrollTo($(".targetDescript").first(), { duration: 1000, easing: 'swing' });
                        } else {
                            loadTargets();
                        }
                    } else {                   
                        self.openTargetPage();
                    }
                }
            } else {
                var modal = $('#emptyMsgModal');
                modal.find('.askMsg').html(result.SaveMsg);
                modal.find('#emptyMsgModalLabel').html(translations.SaveError);
                modal.modal("show");
            }
        });
    }

    self.openTargetPage = function () {
        self.saving = true;
        setTimeout(function () {
            window.location = ContextPath + "Target/Explorer?id=" + self.Id();
        }, 2000);
    }
    self.openUserPeriodPage = function (ownerPeriodId) {
        self.saving = true;
        setTimeout(function () {
            window.location = ContextPath + "Period/UserContestPage?periodId=" + ownerPeriodId;
        }, 200);
    }

    self.openTemplate = function (templateId) {
        self.TemplateId(templateId);
        if (templateId == 0) {
            self.CustomTarget(true);
            self.UsedTemplate(false);
            self.init(true);
        } else {
            self.init(true);
            self.CustomTarget(false);
            self.UsedTemplate(true);
            $.ajax({
                type: "POST",
                url: ContextPath + "Target/GetTemplate",
                data: { id: templateId }
            })
            .done(function (result) {
                if (result) {
                    self.initByModel(result, true);
                }
            });
        }
    }

    self.openEditEventInfo = function () {
        if (self.TemplateMask().EventChoose()) {
            if (self.PeriodType() == 'Competitions') {
                self.Parent.openEditContestInfo();
            } else {
                self.Parent.openEditPeriodInfo();
            }
        }
    }
    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    }

    self.DisplayAlert = function (msg) {
        if (!self.autoChange) {
            var modal = $('#emptyMsgModal');
            modal.find('.askMsg').html(msg);
            modal.find('#emptyMsgModalLabel').html(translations.SaveError);
            modal.modal("show");
        }
    }

    self.initTimePicker = function () {
        setTimeout(function () {
            var orgVal = self.TargetValue();
            initTimePicker($('.step4'));
            if (!orgVal) {
                self.TargetValue('');
            }
        }, 500);
    }

    self.ExplorerSelect = function () {
        self.Parent.TargetExplorerManager.SelectedTarget(self);
        self.Parent.TargetExplorerManager.Index(self.Index());
    }

    self.OpenDeleteTarget = function (callBack) {
        self.CallBackForDeleting = callBack;
        GeneralModel.TargetModelManager.TargetExplorerManager.SelectedTarget(self);
        $('#removeTargetModal').modal('show');
    }

    self.CoreDeleteTarget = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Target/DeleteTarget",
            data: { targetId: self.Id() }
        })
         .done(function (result) {
             if (result) {
                 setTimeout(function () { $('.removeTargetModal').modal('hide') }, 100);

                 if (self.CallBackForDeleting) {
                     self.CallBackForDeleting();
                 }
             }
         });
    }
    /*
    self.fitValueTxt = function (prefix) {
        $(prefix + ' .fbShareTarget .currentValue.open .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '30px', padding: '13' });
        $(prefix + ' .fbShareTarget .targetValue.open .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '30px', padding: '13' });
        $(prefix + ' .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '42px', padding: '13', elemWidth: '302' });
        $(prefix + ' .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '42px', padding: '13' });
        $(prefix + ' .fbShareTarget .currentValue.open .value.textNotFit').removeClass('textNotFit');
        $(prefix + ' .fbShareTarget .targetValue.open .value.textNotFit').removeClass('textNotFit');
        $(prefix + ' .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFit').removeClass('textNotFit');
        $(prefix + ' .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFit').removeClass('textNotFit');
    }
    self.DisplayDetails.subscribe(function (newValue) {
        if (newValue) {
            var id = '#groupTargetDetails_' + self.Id();
            setTimeout(function () { self.fitValueTxt(id); }, 100);
        }
    });
    */
    self.ToggleDetails = function () {
        var dd = self.DisplayDetails();
        if (!dd && self.GroupTarget() && self.OwnerPeriodId()) {
            self.LoadGroupDetails();
        }
        if (dd && self.DisplayDetails() && self.RanksModel().DataTable && self.RanksModel().DataTable.destroy) {
            //$('#groupTargetRanking_' + targetId)
            self.RanksModel().DataTable.destroy();
        }
        self.DisplayDetails(!dd);
    }
    self.LoadGroupDetails = function () {
        if (!self.StatsModel()) {
            var clubstatsModel = new ClubStatsModel();
            clubStatsModel.initForGroupTarget(self.Id(), true);
            self.StatsModel(clubStatsModel);
        }

        if (!self.RanksModel()) {
            var ranksModel = new TargetStatsModel();
            self.RanksModel(ranksModel);
        }
        self.RanksModel().initForGroupTargetInfo(self.Id());

        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetSinglePeriodsModel",
            data: { periodId: self.OwnerPeriodId() }
        })
        .done(function (result) {
            if (result) {
                var pm = self.ParentPeriodModel();
                if (!pm) {
                    pm = new PeriodModel();
                }
                pm.initByModel(result);
                pm.Collapsed(false);
                pm.SinglePeriodExplore(true);
                self.ParentPeriodModel(pm);
            }
        });
    }

    self.autoChange = false;
}


function CountDownTimer(date) {
    var self = this;

    self.days = ko.observable(0);
    self.hours = ko.observable(0);
    self.mins = ko.observable(0);
    self.secs = ko.observable(0);

    self.Show = ko.observable(true);

    if (date) {

        self.TargetDate = new Date(date);

        self.Update = function () {
            var targetDeadline = self.TargetDate;
            targetDeadline.setHours(23);
            targetDeadline.setMinutes(59);
            targetDeadline.setSeconds(59);
            var amount = self.TargetDate.getTime() - (new Date()).getTime();	//calc milliseconds between dates

            self.Show(amount > 0);

            if (amount > 0) {

                amount = Math.floor(amount / 1000);//kill the "milliseconds" so just secs

                var days = Math.floor(amount / 86400);//days
                amount = amount % 86400;

                var hours = Math.floor(amount / 3600);//hours
                amount = amount % 3600;

                var mins = Math.floor(amount / 60);//minutes
                amount = amount % 60;

                var secs = Math.floor(amount);//seconds

                self.days(days);
                self.hours(hours);
                self.mins(mins);
                self.secs(secs);

                setTimeout(function () { self.Update(); }, 1000);
            }
        }

        self.Update();

    }
}



function TargetSideTileManage(existingTargetModel) {
    var self = this;
    self.Parent = parent;

    self.DisciplineManager = new TrainingTypeControl({
        dummy: true
    });
    self.TrainingTypeManager = new TrainingTypeControl({
        dummy: true
    });
    self.TargetCategories = ko.observableArray([]);

    self.currentTarget = existingTargetModel || new TargetModel(self, true);
    /*
    self.LoadTarget = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Target/LoadTileTarget"
        })
         .done(function (result) {
             if (result) {
                 self.currentTarget.initByModel(result);
             }
         });
    }
    */

    //$(document).ready(function () {
    //    if (typeof tileTarget !== 'undefined' && tileTarget) {
    //        //self.currentTarget.initByModel(tileTarget);
    //    }
    //});
}

if (typeof GeneralModel !== 'undefined' && GeneralModel) {
    var targetSideTileManage = new TargetSideTileManage();
    GeneralModel.addProperty("TargetSideTileManage", targetSideTileManage);
}

$.fn.modalmanager.defaults.backdropLimit = 0;

function showAccomplishedTarget(modal) {
    var self = this;

    $.ajax({
        type: "POST",
        url: ContextPath + "Home/GetCurrentAccomplishedTargetModel"
    })
     .done(function (result) {
         if (result != "nic") {
             var targetModel = new TargetModel(GeneralModel.TargetSideTileManage, true);
             targetModel.initByModel(result);

             GeneralModel.TargetModelManager.TargetExplorerManager.SelectedTarget(targetModel);
             $(self.accomplishedTargetModal).modal('show');

             //GeneralModel.CheckIfApply(modal.find('.modal-body').children('div')[0]);                                                               
         }
     });
}

function initAccomplishedTargetWindow() {
    var self = this;

    self.accomplishedTargetModal = $('#accomplishedTargetModal');

    //     self.accomplishedTargetModal.on('shown', function (e) {
    //         GeneralModel.TargetModelManager.TargetExplorerManager.SelectedTarget(self.accomplishedTarget);
    //     });

    self.accomplishedTargetModal.on('hidden', function (e) {
        showAccomplishedTarget();
    });
}

ko.components.register('count-down-timer', {
    viewModel: function (date) {
        var val = date.value;
        if (_.isFunction(date.value)) {
            val = date.value();
        }

        if (_.isDate(val)) {
            this.CountDownTimer = new CountDownTimer(val);
        } else {
            this.CountDownTimer = val;

        }
    },
    template:
        '<div class="countDownTimer" data-bind="with: CountDownTimer">'
                    + '<!-- ko if: days -->'
                    + '<span class="value" data-bind="text: days"></span>'
                    + '<span class="unit">d</span>'
                    + '<!-- /ko -->'
                    + '<!-- ko if: days() + hours() -->'
                    + '<span class="value" data-bind="text: hours"></span>'
                    + '<span class="unit">h</span>'
                    + '<!-- /ko -->'
                    + ' <!-- ko if: days() + hours() + mins() -->'
                    + '<span class="value" data-bind="text: mins"></span>'
                    + '<span class="unit">m</span>'
                    + '<!-- /ko -->'
                    + '<!-- ko if: days() + hours() + mins() + secs() -->'
                    + ' <span class="value" data-bind="text: secs"></span>'
                    + '<span class="unit">s</span>'
                    + '<!-- /ko --> '
                    + '</div>'
});;

function SimpleTrainingModelExtender(model, parent) {

    var self = model;
    self.Parent = parent;

    self.HeadName = self.DisplayName ? self.TimeStr + ' - ' + self.DisplayName : self.TimeStr;

    self.EditHref = ContextPath + 'User/TrainingDetails?treningId=' + self.Id;

    self.DisplayAvgHr = self.AVGHR > 0;
    self.DisplayDuration = self.Duration > 0;
    self.DisplayEnergy = self.Energy > 0;
    self.DisplayDistance = self.Distance > 0 &&
            ((self.DisplayAvgHr ? 1 : 0) + (self.DisplayDuration ? 1 : 0) + (self.DisplayEnergy ? 1 : 0)) < 3;

    self.DisplayAvgSpeed = self.AvgSpeed > 0 &&
            ((self.DisplayAvgHr ? 1 : 0) + (self.DisplayDuration ? 1 : 0) + (self.DisplayEnergy ? 1 : 0) + (self.DisplayDistance ? 1 : 0)) < 3;

    self.Selected = ko.observable(jQuery.inArray(self.Id, self.Parent.TrainingIds()) >= 0);
    self.Toggle = function () {
        var tid = self.Id;
        if (self.Selected()) {
            self.Parent.TrainingIds.splice(jQuery.inArray(tid, self.Parent.TrainingIds()), 1);
        } else {
            self.Parent.TrainingIds.push(tid);
        }
        self.Selected(!self.Selected());
    };
    self.GoEdit = function () {
        var goFn = function () {
            window.location = self.EditHref;
        };
        self.Parent.saveAndGo(goFn);
    };

    return self;
}

function ContestResultModel(parent) {
    var self = this;
    self.Parent = parent;

    self.InitStingified = '';

    self.CategoryId = ko.observable(0);
    self.DisciplineId = ko.observable(0);
    self.CompetitionId = ko.observable(0);
    self.CompetitionClubId = ko.observable(0);
    self.CompetitionLogoId = ko.observable(0);
    self.CustomCompetition = ko.observable(true);
    self.Id = ko.observable(0);
    self.OpenPlace = ko.observable(0);
    self.PersonId = ko.observable(0);
    self.Place = ko.observable(0);
    self.BestResult = ko.observable('');
    self.OpenParticipantNumber = ko.observable(0);
    self.ParticipantNumber = ko.observable(0);
    self.CompetitionName = ko.observable('');
    self.Date = ko.observable('');
    self.Time = ko.observable('');
    self.Description = ko.observable('');
    self.Value1 = ko.observable('');
    self.Value2 = ko.observable('');
    self.Result = ko.observable('');
    self.FileIds = ko.observableArray([]);
    self.TrainingIds = ko.observableArray([]);
    //     self.CategoryIcon = ko.observable('');
    //     self.HasDescription = ko.observable(false);
    //     self.DescriptionAsHTML = ko.observable('');

    self.NextContestId = ko.observable(0);
    self.PreviousContestId = ko.observable(0);

    self.DayTrainings = ko.observableArray([]);

    self.LiveContent = ko.observable(null);
    self.DisplayLiveContent = ko.observable(false);
    self.LiveContentAvailable = ko.observable(null);

    //self.CategoryModel = ko.observable(new SimpleTrainingTypeModel(self));

    self.FileIndex = ko.observable(0);

    self.SortedDayTrainings = ko.computed(function () {
        var result = [];
        var ts = self.DayTrainings();
        var tids = self.TrainingIds();
        for (var i = 0, len = ts.length; i < len; i++) {
            var elem = ts[i];
            if (jQuery.inArray(elem.Id, tids) >= 0) {
                result.push(elem);
            }
        }
        for (var i = 0, len = ts.length; i < len; i++) {
            var elem = ts[i];
            if (jQuery.inArray(elem.Id, tids) < 0) {
                result.push(elem);
            }
        }
        return result;
    });

    self.DisciplineManager = parent.DisciplineManager;
    self.ParamsVisibility = ko.observable(null);

    self.UpdateVisibility = function () {
        var type = self.DisciplineManager.SelectedSubType();
        if (type && type.ParamsVisibility) {
            self.ParamsVisibility(type.ParamsVisibility);
        } else {
            self.ParamsVisibility({
                Value1Type: 'none',
                Value1Name: null,
                Value1Icon: null,
                Value2Type: 'none',
                Value2Name: null,
                Value2Icon: null,
                ResultType: 'none',
                ResultName: null,
                ResultIcon: null,
                PlaceVisible: false,
                PlaceName: null,
                OpenPlaceVisible: false,
                OpenPlaceName: null
            });
        }
    };
    self.DisciplineManager.SetChangeCallback(function () {
        self.UpdateVisibility();
        self.initTimePicker();
    });

    self.DisableDisciplines = ko.computed(function () {
        return !self.CustomCompetition() && self.DisciplineManager.SelectedSubType() != null && self.DisciplineManager.SelectedSubType().Id != 0;
    });

    self.ContestHasLogo = ko.computed(function () {
        return self.CompetitionLogoId() != null && self.CompetitionLogoId() != 0;
    });
    self.ContestLogoUrl = ko.computed(function () {
        if (self.ContestHasLogo()) {
            return ContextPath + "Image/GetImage?id=" + self.CompetitionLogoId();
        } else {
            return '';
        }
    });

    self.ContestLogoBgStyle = ko.computed(function () {
        if (self.ContestHasLogo()) {
            return "url('../Image/GetImage?width=1000&height=600&id=" + self.CompetitionLogoId() + "')";
        } else {
            return '';
        }
    });

    self.ContestDisplayName = ko.computed(function () {
        var name = self.CompetitionName();
        return name ? name : translations.chooseContestInfo;
    });
    self.ContestHasName = ko.computed(function () {
        return self.CompetitionName() ? true : false;
    });


    self.Value1IconUrl = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().Value1Icon) {
            return ContextPath + 'Content/images/icons/' + self.ParamsVisibility().Value1Icon + '_40.png';
        } else {
            return '';
        }
    });
    self.Value2IconUrl = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().Value2Icon) {
            return ContextPath + 'Content/images/icons/' + self.ParamsVisibility().Value2Icon + '_40.png';
        } else {
            return '';
        }
    });
    self.ResultIconUrl = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().ResultIcon) {
            return ContextPath + 'Content/images/icons/' + self.ParamsVisibility().ResultIcon + '_40.png';
        } else {
            return '';
        }
    });
	self.BestResultIconUrl = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().ResultIcon) {
            return ContextPath + 'Content/images/icons/best_time_40.png';
        } else {
            return '';
        }
    });

    self.HasPhoto = ko.computed(function () {
        return self.FileIds() != null && self.FileIds().length > 0 && self.FileIds()[0]() > 0;
    });
    self.PhotoUrl = ko.computed(function () {
        if (self.HasPhoto()) {
            return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.FileIds()[0]();
        } else {
            return '';
        }
    });
    self.BgPhotoStyle = ko.computed(function () {
        if (self.HasPhoto()) {
            return "url('../Image/GetImage?width=1000&height=600&id=" + self.FileIds()[0]() + "')";
        } else {
            return null;
        }
    });

    self.ContestSubTypes = ko.observable([]);
    self.AvailableSubTypes = ko.computed(function () {
        var result = [];
        var contestSubTypes = self.ContestSubTypes();
        var all = self.DisciplineManager.SubTypes();
        if (all.length) {
            if (contestSubTypes.length && !self.CustomCompetition()) {
                var _contestSubTypes = _(contestSubTypes);
                result = _(all).filter(function (elem) { return _contestSubTypes.find(function (elem2) { return elem.Id == elem2.CategoryId; }); });
            } else {
                result = all;
            }
        }
        return result;
    });

    self.DescriptionBred = ko.computed(function () {
        if (!self.Description()) return self.Description();
        return self.Description().replace(/\n/g, '<br />');
    });

    self.ResultName = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().ResultName) {
            return self.ParamsVisibility().ResultName;
        }
        return '';
    });
    self.OpenPlaceName = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().OpenPlaceName) {
            return self.ParamsVisibility().OpenPlaceName;
        }
        return '';
    });
    self.PlaceName = ko.computed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().PlaceName) {
            return self.ParamsVisibility().PlaceName;
        }
        return '';
    });


    self.removePhoto = function () {
        self.FileIds.shift();
    }

    self.openEditContestInfo = function () {
        self.Parent.openEditContestInfo();
    }

    self.openDataCtrl = function (data, event) {
        // if (self.CanEditDate()) {
        OpenDataCtrlFromKo(data, event);
        // }
    }

    self.LongHtmlDate = ko.computed(function () {
        return dateToLongHtml(self.Date());
    });

    self.BestResultNameLabel = ko.computed(function () {
        if (self.ParamsVisibility() != null) {
            return translations.best + ' ' + self.ParamsVisibility().ResultName;
        } else {
            return '';
        }
    }, self);

    self.initNew = function () {
        //self.CategoryId(0);
        self.CompetitionId(0);
        self.CompetitionLogoId(0);
        self.Id(0);
        self.OpenPlace('');
        self.BestResult('');
        self.OpenParticipantNumber('');
        self.ParticipantNumber('');
        //self.PersonId(0);
        self.Place('');
        self.CompetitionName('');
        //self.Date('');
        self.Description('');
        self.Value1('');
        self.Value2('');
        self.Result('');
        self.FileIds.removeAll();
        self.FileIds.push(ko.observable(0));
        self.TrainingIds.removeAll();
        self.CustomCompetition(true);
        self.NextContestId(0);
        self.PreviousContestId(0);
        //self.DayTrainings.removeAll();
        //         self.CategoryIcon('');
        //         self.HasDescription(false);
        //         self.DescriptionAsHTML('');
        self.ContestSubTypes([]);

        self.LiveContent(null);
        self.DisplayLiveContent(false);
        self.LiveContentAvailable(false);

        self.InitStingifiedField();
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.CategoryId(serverModel.CategoryId);
            self.DisciplineId(serverModel.DisciplineId);
            self.CompetitionId(serverModel.CompetitionId);
            self.CompetitionLogoId(serverModel.CompetitionLogoId);
            self.CompetitionClubId(serverModel.CompetitionClubId);
            self.CustomCompetition(serverModel.CustomCompetition);
            self.Id(serverModel.Id);
            self.OpenPlace(serverModel.OpenPlace);
            self.PersonId(serverModel.PersonId);
            self.Place(serverModel.Place);
            self.BestResult(serverModel.BestResult);
            self.OpenParticipantNumber(serverModel.OpenParticipantNumber);
            self.ParticipantNumber(serverModel.ParticipantNumber);
            self.CompetitionName(serverModel.CompetitionName);
            self.Date(serverModel.Date);
            self.Time(serverModel.Time);
            self.Description(serverModel.Description);
            self.Value1(serverModel.Value1);
            self.Value2(serverModel.Value2);
            self.Result(serverModel.Result);
            self.NextContestId(serverModel.NextContestId);
            self.PreviousContestId(serverModel.PreviousContestId);
            //             self.CategoryIcon(serverModel.CategoryIcon);
            //             self.HasDescription(serverModel.HasDescription);
            //             self.DescriptionAsHTML(serverModel.DescriptionAsHTML);

            //self.CategoryModel().initByModel(serverModel.CategoryModel);            

            self.ContestSubTypes(serverModel.DisciplineCategories);

            self.LiveContentAvailable(serverModel.LiveContentAvailable);
            self.DisplayLiveContent(serverModel.DisplayLiveContent);
            try {
                if (serverModel.LiveContent) {
                    var pObj = JSON.parse(serverModel.LiveContent);
                    if (pObj) self.LiveContent(new ParticipDetails(pObj, true));
                }
            } catch (exc) { }

            self.FileIds.removeAll();
            if (serverModel.FileIds) {
                for (var i = 0, len = serverModel.FileIds.length; i < len; i++) {
                    self.FileIds.push(ko.observable(serverModel.FileIds[i]));
                }
            }
            if (!serverModel.FileIds || !serverModel.FileIds.length) self.FileIds.push(ko.observable(0));

            self.TrainingIds.removeAll();
            if (serverModel.TrainingIds) {
                for (var i = 0, len = serverModel.TrainingIds.length; i < len; i++) {
                    self.TrainingIds.push(serverModel.TrainingIds[i]);
                }
            }

            self.DisciplineManager.SelectValue(self.DisciplineId(), self.CategoryId(), 0, self.UpdateVisibility);

            self.initTimePicker();

            self.InitStingifiedField();
        }
    };

    self.initTimePicker = function () {
        setTimeout(function () { initTimePicker($('#resultControl')); }, 500);
    }

    self.initById = function (competitionResultId, periodId, callback) {
        if (competitionResultId == 0 && !periodId) {
            self.initNew();
        } else {
            $.ajax({
                type: "POST",
                url: ContextPath + "Contest/GetCompetitionResultModel",
                data: { competitionsResultId: competitionResultId, periodId: periodId }
            })
            .done(function (result) {
                self.initByModel(result);

                if (callback) {
                    callback();
                }
            });
        }
    };

    self.getClearData = function () {
        var _FileIds = []; var _FileIds_Iter = self.FileIds(); for (var i = 0, len = _FileIds_Iter.length; i < len; i++) { _FileIds.push(_FileIds_Iter[i]()); }
        var _TrainingIds = []; var _TrainingIds_Iter = self.TrainingIds(); for (var i = 0, len = _TrainingIds_Iter.length; i < len; i++) { _TrainingIds.push(_TrainingIds_Iter[i]); }
        var data = {
            CategoryId: self.DisciplineManager.SelectedSubType() ? self.DisciplineManager.SelectedSubType().Id : 0,
            CompetitionId: self.CompetitionId(),
            CompetitionLogoId: self.CompetitionLogoId(),
            Id: self.Id(),
            OpenPlace: self.OpenPlace(),
            PersonId: self.PersonId(),
            Place: self.Place(),
            BestResult: self.BestResult(),
            OpenParticipantNumber: self.OpenParticipantNumber(),
            ParticipantNumber: self.ParticipantNumber(),
            CompetitionName: self.CompetitionName(),
            Date: self.Date(),
            Time: self.Time(),
            Description: self.Description(),
            Value1: self.Value1(),
            Value2: self.Value2(),
            Result: self.Result(),
            FileIds: _FileIds,
            TrainingIds: _TrainingIds,
            DisplayLiveContent: self.DisplayLiveContent()
        }
        return data;
    };

    self.InitStingifiedField = function () {
        self.InitStingified = JSON.stringify(self.getClearData());
    }

    self.HasChanges = function () {
        return self.InitStingified != JSON.stringify(self.getClearData());
    }

    self.trySave = function (data, event, callback) {

        if (!self.DisciplineManager.SelectedSubType() || self.DisciplineManager.SelectedSubType().Id == 0) {
            self.ShowDisciplineMsg();
            return false;
        }

        if (!self.CompetitionId() && !self.CompetitionName()) {
            self.ShowCompetitionMsg();
            return false;
        }

        var infoContainer = (event) ? $(event.target) : null;
        var data = self.getClearData();
        var strData = JSON.stringify(data);
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "Contest/SetCompetitionResultModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            if ($.isPlainObject(result)) {
                self.initByModel(result);
                if (infoContainer) {
                    ShowSavedAlert(infoContainer);
                }
                if (callback) {
                    callback();
                }
            } else {
                var modal = $('#emptyMsgModal');
                modal.find('.askMsg').html(result);
                modal.find('#emptyMsgModalLabel').html(translations.SaveError);
                modal.modal("show");
            }
        });
        return false;
    };

    self.ShowDisciplineMsg = function () {
        var modal = $('#emptyMsgModal');
        modal.find('.askMsg').html(translations.YouHaveToChooseDiscipline);

        modal.find('#emptyMsgModalLabel').html(translations.SaveError);
        modal.modal("show");
    }

    self.ShowCompetitionMsg = function () {
        var modal = $('#emptyMsgModal');
        modal.find('.askMsg').html(translations.YouHaveToChooseCompetition);

        modal.find('#emptyMsgModalLabel').html(translations.SaveError);
        modal.modal("show");
    }


    self.OpenDeleteContest = function (trainingId) {
        $('#removeContestModal').modal('show');
    }
    self.CoreDeleteContest = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Contest/DeleteCompetitionResultModel",
            data: { id: self.Id() }
        })
         .done(function (result) {
             if (result) {
                 $('#removeContestModal').modal('hide');
                 if (!self.NextContestId() && !self.PreviousContestId()) {
                     window.location = ContextPath + "Home/Home";
                 } else {
                     ShowDeletedAlert();
                     self.initById(self.NextContestId() ? self.NextContestId() : self.PreviousContestId());
                 }
             }
         });
    }

    self.ReloadDayTrainings = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "User/GetDayTrainings",
            data: { date: self.Date() }
        })
        .done(function (serverModel) {
            self.DayTrainings.removeAll();

            if (serverModel) {
                var tids = self.TrainingIds();
                for (var i = 0, len = serverModel.length; i < len; i++) {
                    var train = serverModel[i];
                    var tid = train.Id;

                    train = SimpleTrainingModelExtender(train, self);

                    self.DayTrainings.push(train);
                }
            }
        });
    }
    var today = (new Date()).toISOString().substr(0, 10);
    self.Date.subscribe(function (newValue) {
        if (newValue) {
            /*if (self.Id() <= 0) {*/
            if (newValue <= today) {
                self.ReloadDayTrainings();
            } else {
                self.Date(today);
            }
            /* }*/
        }
    });


    self.addTrainingFromPulsometr = function (data, event) {
        var goFn = function () {
            window.location = ContextPath + 'Upload?contestResultId=' + self.Id();
        };
        self.saveAndGo(goFn);
    }
    self.addTrainingManualy = function (data, event) {
        var goFn = function () {
            window.location = ContextPath + 'User/TrainingDetails?contestResultId=' + self.Id() + '&date=' + self.Date();
        };
        self.saveAndGo(goFn);
    }

    self.addNewContest = function () {
        var goFn = function () {
            self.initNew();
            inintUpload();
        };
        self.saveAndGo(goFn);
    }

    self.saveAndGo = function (goFn) {
        if (goFn) {
            if (self.HasChanges()) {
                self.trySave(null, null, goFn);
            } else {
                goFn();
            }
        }
    }

    self.addNewFile = function () {
        if (self.FileIds()[self.FileIds().length - 1]()) {
            self.FileIds.push(ko.observable(0));
        }
        uploadUserContestFileBtnClick(self.FileIds().length - 1);
    }

    self.InitStingifiedField();
}



function UserContestManager() {
    var self = this;

    self.DisciplineManager = new TrainingTypeControl(0, 0, 0, null, false, null, true); // 6-bieg na posbe magdy, jako najpopularniejsze

    self.currentContest = new ContestResultModel(self);

    self.editCompetitionsResult = function (competitionsResultId, date, ClubId, periodId, callback, noUpload) {
        if (periodId) {
            self.currentContest.initById(null, periodId, callback);
        } else
            if (competitionsResultId) {
                self.currentContest.initById(competitionsResultId, null, callback);
            } else {
                self.currentContest.initNew();
                self.currentContest.Date(date);

                if (callback) {
                    callback();
                }
            }

        if (!noUpload) {
            inintUpload();
        }
    }

    self.ContestChooserModel = ko.observable(null);

    self.openEditContestInfo = function () {
        if (self.ContestChooserModel() == null) {
            var contestChooserModel = new ContestChooserModel(self);
            contestChooserModel.OnlyPast = true;
            //contestChooserModel.LimitCategories = false;
            contestChooserModel.CanChangeDate = false;
            self.ContestChooserModel(contestChooserModel);
            contestChooserModel.inintDataTables();
        }
        self.ContestChooserModel().openEditContestInfo();
    }

    self.addNewContest = function () {
        self.currentContest.addNewContest();
    }

    self.LoadNextContest = function () {
        if (self.currentContest.NextContestId()) {
            self.editCompetitionsResult(self.currentContest.NextContestId());

            inintUpload();
        }
        return false;
    }
    self.LoadPreviousContest = function () {
        if (self.currentContest.PreviousContestId()) {
            self.editCompetitionsResult(self.currentContest.PreviousContestId());

            inintUpload();
        }
        return false;
    }
    self.goToCallendar = function () {
        /*
        var goFn = function () {
            window.location = ContextPath + 'Home/Home';
        };
        self.currentContest.saveAndGo(goFn);
        */
        window.location = ContextPath + 'Home/Home';
        return false;
    }
}


var userContestManager = new UserContestManager();
GeneralModel.addProperty("UserContestManager", userContestManager);


function inintUpload() {
    /*
    $('#contestResult_fileupload').fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            userContestManager.currentContest.FileIds.removeAll();
            userContestManager.currentContest.FileIds.push(data.result[0].url);

            $('#contestResult_fileupload .progress .bar').css(
                'width',
                0 + '%'
            );
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            $('#contestResult_fileupload .progress .bar').css(
                'width',
                progress + '%'
            );
        }
    });
    */
    $("#userPhotosFileupload").fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            if (data.result[0].url) {
                var selP = userContestManager.currentContest;
                if (data.originalFiles.length > 1) {
                    if (data.result[0].name == data.originalFiles[0].name) {
                        selP.FileIds()[selP.FileIndex()](data.result[0].url);
                    } else {
                        selP.FileIds.push(ko.observable(data.result[0].url));
                    }
                } else {
                    selP.FileIds()[selP.FileIndex()](data.result[0].url);
                }
            }
        }
    });

}

function uploadUserContestFileDrop(fileIndex) {
    userContestManager.currentContest.FileIndex(fileIndex);
}
function uploadUserContestFileBtnClick(fileIndex) {
    userContestManager.currentContest.FileIndex(fileIndex);
    $('#userPhotosFileupload input').click();
}
function removeUserContestFile(id) {
    if (userContestManager.currentContest.FileIds().length > 1) {
        removeKoItem(userContestManager.currentContest.FileIds, id);
    }
    else {
        userContestManager.currentContest.FileIds()[0](0);
    }
};PeriodPlanModelPeriodSubstitute = function () {
    var self = this;
    self.PeriodId = ko.observable(0);
    self.CompetitionId = ko.observable(0);
    self.Name = ko.observable('');
    self.ShowPlanOnUserCal = ko.observable(false);
    self.LogoId = ko.observable(0);
    self.StartDate = ko.observable('');
    self.StartTime = ko.observable('');
    self.DisciplineId = ko.observable(0);
    self.CategoryId = ko.observable(0);

    self.CompetitionAdded = ko.observable(false);
    self.CompetitionResult = ko.observable('');
    self.CompetitionOpenPlace = ko.observable('');
    self.CompetitionAddedId = ko.observable(0);

    self.LogoUrl = ko.computed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.LogoId();
    });
    self.HasLogo = ko.computed(function () {
        return self.LogoId() != 0;
    });
    self.Active = ko.computed(function () {
        return true;
    });

    self.Ended = ko.observable(false);
    self.ExtendedTitle = ko.pureComputed(function () {
        if (self.Active()) {
            return self.Name();
        } else {
            return translations.VeryficationStatus;
        }
    });
    self.CompetitionLink = ko.computed(function () {
        if (self.CompetitionAddedId() > 0) {
            return "/Contest/UserAdd?competitionsResultId=" + self.PeriodPlan().PeriodModel().CompetitionAddedId();
        } else {
            return "#";
        }
    }, self);

    self.TitleMode = ko.computed(function () {
        if (!self.Ended()) {
            return 'before';
        } else {
            if (self.CompetitionAdded()) {
                return 'completed';
            } else {
                return 'uncompleted';
            }
        }
    }, self);
}
PeriodPlanModelPeriodSubstitute.prototype = {
    ParamsVisibility: function () { return null; },
    UpdateVisibility: function () { },
    initByCompetitionModel: function (serverModel) {
        var self = this;

        if (serverModel) {
            self.PeriodId(serverModel.PeriodId);
            self.CompetitionId(serverModel.CompetitionId);
            self.Name(serverModel.Name);
            self.ShowPlanOnUserCal(true);
            self.LogoId(serverModel.LogoId);
            self.StartDate(serverModel.DateStr);
            self.StartTime(serverModel.TimeStr);
            self.DisciplineId(0);
            self.CategoryId(0);

            self.CompetitionAdded(serverModel.CompetitionAdded);
            self.CompetitionResult(serverModel.CompetitionResult);
            self.CompetitionOpenPlace(serverModel.CompetitionOpenPlace);
            self.CompetitionAddedId(serverModel.CompetitionAddedId);
        }
    }
}


PeriodPlanModel = function (period) {
    var self = this;
    //self.Parent = parent;

    self.SaveCallBack = null;

    self.Id = ko.observable(0);
    self.Place = ko.observable('');
    self.Priority = ko.observable(3);
    self.UserId = ko.observable(0);
    self.CreationDate = ko.observable('');
    self.Result = ko.observable('');
    self.Status = ko.observable('Planned');
    self.TargetDescription = ko.observable('');
    self.PeriodModel = ko.observable(period);// ? period : new PeriodModel(periodsModel, true));

    self.LiveStream = ko.observable(true);
    self.LiveStreamVisibility = ko.observable('Friends');
    self.LiveStreamStartTime = ko.observable(period ? (period.StartTime ? period.StartTime() : period.TimeStr) : '12:00');
    self.LiveStreamGuid = ko.observable('');
    self.LiveStreamApp = ko.observable(false);

    self.LiveStreamUrl = ko.computed(function () {
        return ContextPath + 'LiveStream/Stream?guid=' + self.LiveStreamGuid();
    });

    self.BaseObj = null;
    /*
    self.InfoIcon = ko.computed(function () {
        return (self.FileIds().length > 0 && self.FileIds()[0]() != 0);
    });
    self.StatusMgs = ko.computed(function () {
        return (self.FileIds().length > 0 && self.FileIds()[0]() != 0);
    });
*/

    self.ParamsVisibility = ko.computed(function () {
        var pm = self.PeriodModel();
        if (pm) {
            return pm.ParamsVisibility();
        }
        return null;
    });

    self.ResultIconUrl = ko.computed(function () {
        var vis = self.ParamsVisibility();
        if (vis && vis.ResultIcon) {
            return ContextPath + 'Content/images/icons/' + vis.ResultIcon + '_40.png';
        } else {
            return '';
        }
    });
    self.StatusName = ko.computed(function () {
        var s = _.findWhere(self.Statuses, { value: self.Status() });
        if (s) return s.text;
        return '';
    });
    self.StatusIcon = ko.computed(function () {
        var s = _.findWhere(self.Statuses, { value: self.Status() });
        if (s) return '/Content/images/icons/' + s.icon + '.png';
        return '';
    });
    self.PriorityName = ko.computed(function () {
        var s = _.findWhere(self.Priorities, { value: self.Priority() });
        if (s) return s.text;
        return '';
    });
    self.PriorityIcon = ko.computed(function () {
        var s = _.findWhere(self.Priorities, { value: self.Priority() });
        if (s) return '/Content/images/icons/' + s.icon + '.png';
        return '';
    });

    self.HasTargetDescription = ko.computed(function () {
        return typeof self.TargetDescription() === 'string' && self.TargetDescription().length > 0;
    }, self);

    self.HasResult = ko.computed(function () {
        return self.ParamsVisibility() && self.ParamsVisibility().ResultType != 'none' && typeof self.Result() === 'string' && self.Result().length > 0;
    });

    self.HasPlace = ko.computed(function () {
        return self.ParamsVisibility() && self.ParamsVisibility().OpenPlaceVisible && self.Place() > 0;
    }, self);

    _.bindAll(self, 'init', 'CancelChanges', 'initByModel', 'getClearData', 'OpenEdit');
}
PeriodPlanModel.prototype = {
    Priorities: [{ value: 1, text: translations.Priority_High, icon: 'prio_high_19' }
               , { value: 2, text: translations.Priority_Medium, icon: 'prio_med_19' }
               , { value: 3, text: translations.Priority_Low, icon: 'prio_low_19' }],
    Statuses: [{ value: 'Planned', text: translations.PeriodPlanStatus_Planned, icon: 'status_zaplan_30' }
                , { value: 'Registered', text: translations.PeriodPlanStatus_Registered, icon: 'status_zarej_30' }
                , { value: 'Payed', text: translations.PeriodPlanStatus_Payed, icon: 'status_oplac_30' }
                , { value: '', text: translations.PeriodPlanStatus_, icon: 'status_nieznany_30' }],
    init: function () {
        var self = this;
        self.Id(0);
        //self.PeriodId(0);
        self.Place(0);
        self.Priority(0);
        self.UserId(0);
        self.CreationDate('');
        self.Result('');
        self.Status('');
        self.TargetDescription('');
        self.PeriodModel(new PeriodModel(periodsModel, true));
        self.LiveStream(true);
        self.LiveStreamVisibility('Friends');
        self.LiveStreamStartTime('12:00');
        self.LiveStreamGuid('');
        self.LiveStreamApp(false)
    },
    CancelChanges: function () {
        var self = this;
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    },
    initByModel: function (serverModel) {
        var self = this;
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            //self.PeriodId(serverModel.PeriodId);
            self.Place(serverModel.Place ? serverModel.Place : '');
            self.Priority(serverModel.Priority);
            self.UserId(serverModel.UserId);
            self.CreationDate(serverModel.CreationDate);
            self.Result(serverModel.Result);
            self.Status(serverModel.Status);
            self.TargetDescription(serverModel.TargetDescription);

            //self.LiveStream(serverModel.LiveStream);
            self.LiveStreamVisibility(serverModel.LiveStreamVisibility || 'Friends');
            self.LiveStreamGuid(serverModel.LiveStreamGuid);
            self.LiveStreamApp(serverModel.LiveStreamApp);

            var pm = self.PeriodModel();
            if (serverModel.PeriodModel) {
                if (pm && pm.initByModel) {
                    pm.initByModel(serverModel.PeriodModel);
                } else {
                    pm = new PeriodModel(self, true);
                    pm.initByModel(serverModel.PeriodModel);
                    self.PeriodModel(pm);
                }
            } else {               
                if (!pm) {
                    pm = new PeriodModel(self, true);
                    self.PeriodModel(pm);
                }
                if (serverModel.CompetitionModel && self.PeriodModel().initByCompetitionModel) {
                    pm.initByCompetitionModel(serverModel.CompetitionModel);
                }
            }

            if (serverModel.LiveStreamStartTime) {
                self.LiveStreamStartTime(serverModel.LiveStreamStartTime);
            } else {
                if (pm && pm.StartTime) self.LiveStreamStartTime(pm.StartTime());
            }
            
        }
    },
    getClearData: function () {
        var self = this;

        var data = {
            Id: self.Id(),
            PeriodId: ko.unwrap(self.PeriodModel().PeriodId ? self.PeriodModel().PeriodId : self.PeriodModel().Id),
            CompetitionId: ko.unwrap(self.PeriodModel().CompetitionId ? self.PeriodModel().CompetitionId : self.CompetitionId),
            Place: self.Place() ? self.Place() : 0,
            Priority: self.Priority(),
            UserId: self.UserId(),
            Result: self.Result(),
            Status: self.Status(),
            TargetDescription: self.TargetDescription(),
            LiveStream: self.LiveStream(),
            LiveStreamVisibility: self.LiveStreamVisibility(),
            LiveStreamStartTime: self.LiveStreamStartTime(),
            LiveStreamApp: self.LiveStreamApp(),
            CompetitionModel: compModel
        }
        if (!data.PeriodId && !data.CompetitionId) {
            var compModel = {
                DateStr: self.PeriodModel().StartDate(),
                Name: self.PeriodModel().Name(),
                CategoryId: self.PeriodModel().CategoryId(),
                DisciplineId: self.PeriodModel().DisciplineId()
            };

            data.CompetitionModel = compModel;
        }
        return data;
    },
    OpenEdit: function (saveCallBack) {
        var self = this;

        self.SaveCallBack = saveCallBack;

        self.PeriodModel().UpdateVisibility();

        periodPlanManager.Current(self);

        loadAndBindTemplate({
            simpleContainer: 'periodPlanModalContainer',
            existingElem: '#periodPlanModal',
            ajaxPath: 'Period/GetPeriodPlanModal',
            callback: function () {
                $('#periodPlanModal').modal('show');
            }
        });

        setTimeout(function () {
            initTimePicker('#periodPlanModal div.bootstrap-timepicker');
        }, 500);

    },
    Save: function () {
        var self = this;

        $.ajax({
            type: "POST",
            url: ContextPath + "Period/SetPeriodPlan",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(self.getClearData())
        })
            .done(function (result) {
                self.initByModel(result);
                self.PeriodModel().ShowPlanOnUserCal(true);
                $('.periodPlanModal').modal('hide');
                if (self.SaveCallBack && _.isFunction(self.SaveCallBack)) self.SaveCallBack();
            });
    },
    Load: function (id) {
        var self = this;

        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GrtPeriodPlan",
            data: { id: id || self.Id() }
        })
            .done(function (result) {
                self.initByModel(result);
            });
    },
    Delete: function () {
        var self = this;

        modalAskModel.Show(translations.RemovePlanToCallendar, translations.RemovePlanToCallendarMsg, function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/RemovePlanToCallendar",
                data: {
                    id: self.Id()
                }
            })
            .done(function (result) {
                self.PeriodModel().ShowPlanOnUserCal(false);
                if (self.DeleteFdbk) {
                    self.DeleteFdbk();
                }
                if (userCallendar) {
                    userCallendar.coreRefresh();
                }
                $('.periodPlanModal').modal('hide');
            });
        },
            translations.Delete
         );
    }
}

function PeriodPlanManager() {
    var self = this;
    self.Current = ko.observable(null);
}
var periodPlanManager = new PeriodPlanManager();
GeneralModel.addProperty("PeriodPlanManager", periodPlanManager);;
var mailerPeriodsAoColumns = [
          { "bVisible": true }, //1 - 
          { "sWidth": null }, //2 -         
          { "sWidth": null }, //3 - 
          { "sWidth": null }, //4 - 
          { "sWidth": null }, //5 - 
];

function getMailerPeriodsTableConfig(periodId) {
    return {
        "bDestroy": true,
        "oLanguage": dataTableLanguageOptions,
        "aoColumnDefs": [
              { "bSortable": false, "aTargets": [0] }
        ],
        "lengthMenu": [5, 10, 25, 50],
        "aaSorting": [[2, "asc"]],
        "bAutoWidth": false,
        "aoColumns": mailerPeriodsAoColumns,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": ContextPath + "Period/getMailHistoryListJson",
        "sServerMethod": "POST",
        "bStateSave": true,
        "fnServerParams": function (aoData) {
            $('.datapicker').datepicker('hide');
            var startDate = $('#startDate').val();
            var endDate = $('#endDate').val();

            var successStatus = $('#success_status').is(':checked');
            var failedStatus = $('#failed_status').is(':checked');

            aoData.push({ name: "startDate", value: startDate });
            aoData.push({ name: "endDate", value: endDate });
            aoData.push({ name: "successStatus", value: successStatus });
            aoData.push({ name: "failedStatus", value: failedStatus });
            aoData.push({ name: "periodId", value: periodId });

        },
        "fnDrawCallback": function (oSettings) {
            if ($('#PeriodsAdminTable_container tr.selected').length == 0) {
                $('.params .toDetails .paramValue textarea').text('');
                $('.params .contentDetails .paramValue textarea').text('');
                $('.params .errors .paramValue textarea').text('');
                $('.params .attachements a').remove();
            }
        }
    };
}

function loadMailerPeriodsTable(periodId) {
    var container = $('#PeriodsAdminTable_container');
    var customConfig = getMailerPeriodsTableConfig(periodId);
    var table = $('.mailerPeriodsDataTable', container).dataTable(customConfig);

    bindMailerPeriodsTableNavi(table, container);
}

function bindMailerPeriodsTableNavi(table, container) {
    reloadFunction = function () { table.fnDraw(false); }

    $('.datapicker', container).datepicker().on('changeDate', function (ev) {
        table.fnDraw();
    });
    $('.datapicker', container).change(function (ev) {
        table.fnDraw();
    });
    $('#success_status').change(function () {
        table.fnDraw();
    });
    $('#failed_status').change(function () {
        table.fnDraw();
    });
    $('#contestPageContainer .comunications li').click(function () {
        table.fnDraw();
    });

    $('#PeriodsAdminTable_container tbody').on('click', 'tr', function () {
        $('#PeriodsAdminTable_container tbody tr').removeClass('selected');
        $(this).addClass('selected');

        var textTo = $(this).find('.toDetails').text();
        $('.params .toDetails .paramValue textarea').text(textTo);

        var textDetails = $(this).find('.contentDetails').text();
        $('.params .contentDetails .paramValue textarea').text(textDetails);

        var textDetails = $(this).find('.errors').text();
        $('.params .errors .paramValue textarea').text(textDetails);

        $('.params .attachements a').remove();
        var attachementsJSON = $(this).find('.attachements').text();
        if (attachementsJSON && attachementsJSON.length > 0) {
            var attachements = JSON.parse(attachementsJSON);
            if (attachements && attachements.length > 0) {
                var parent = $('.params .attachements .paramValue');
                $.each(attachements, function (index, attachement) {
                    parent.append('<a href="/Document/GetImage?id=' + attachement.Id + '">' + attachement.FileName + '</a>');
                });
            }
        }
    });
}


function initMailerUpload() {
    var selectedPeriod = GeneralModel.PeriodsModel.SelectdPeriod();
    if (selectedPeriod) {
        selectedPeriod.MailAttachements.removeAll();
    }

    $('#articlePhotoUpload').fileupload({
        dataType: 'json',
        pasteZone: null,
        dropZone: null,
        add: function (e, data) {
            GeneralModel.PeriodsModel.SelectdPeriod().ButtonsDisable(true);
            data.submit();
        },
        done: function (e, data) {
            GeneralModel.PeriodsModel.SelectdPeriod().MailAttachements.push(data.result.Data[0]);
            GeneralModel.PeriodsModel.SelectdPeriod().ButtonsDisable(false);
            $('#progress .bar').css(
                'width',
                0 + '%'
            );
        },
        fail: function (e, data) {
            GeneralModel.PeriodsModel.SelectdPeriod().ButtonsDisable(false);
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            $('#progress .bar').css(
                'width',
                progress + '%'
            );
        }
    });
}






function InternalPeriodsTableModel() {
    var self = this;

    self.Visible_status = ko.observable(true);
    self.Hidden_status = ko.observable(true);

    self.SelectedDiscipline = ko.observable(0);
    self.SelectedPeriodType = ko.observable('');

    self.SelectedDiscipline.subscribe(function (newValue) {
        self.refresh();
    });
    self.SelectedPeriodType.subscribe(function (newValue) {
        self.refresh();
    });


    self.DisciplineModels = ko.observableArray([]);
    self.PeriodTypeModels = ko.observableArray([]);

    self.TypeManager =
       new TrainingTypeControl({
           mainId: null,
           subId: null,
           typeChangeCallback: function () {
               var selDisc = self.TypeManager.SelectedType();
               self.SelectedDiscipline(selDisc ? selDisc.Id : 0);
           },
           clubId: null,
           multi: false,
           baseCtrl: null,
           disciplines: true,
           multiSubs: false,
           allInstedNone: true,
           dummy: null,
           selectNoneIfNoChoise: true,
           addNoneSubType: true
       });

    self.InitMode = false;

    // uni header
    self.Start = false;
    self.NextDate = ko.observable('');
    self.PreviousDate = ko.observable('');
    self.ParamStart = ko.observable('');
    self.CurrentDate = ko.observable('');
    self.LengthType = ko.observable(365);

    self.internalPeriodsAoColumns = [
          { "sWidth": "40px", "bVisible": true }, //1 - 
          { "sWidth": "120px" }, //2 - 
          { "sWidth": "60px" }, //3 - 
          { "sWidth": "40px" }, //4 - 
          { "sWidth": "60px" }, //5 - 
          { "sWidth": "40px" }, //6 - 
          { "sWidth": "60px" }, //7 - 
          { "sWidth": "60px" }, //8 - 
          { "sWidth": "60px" }, //9 - 
          { "sWidth": "60px" }, //10 - 
    ];

    self.getInternalPeriodsTableConfig = function () {
        return {
            "bDestroy": true,
            "oLanguage": dataTableLanguageOptions,
            "aoColumnDefs": [
                  { "bSortable": false, "aTargets": [5, 7, 8, 9] }
            ],
            "aaSorting": [[2, "asc"]],
            "bAutoWidth": false,
            "aoColumns": self.internalPeriodsAoColumns,
            "bProcessing": true,
            "bServerSide": true,
            "sAjaxSource": ContextPath + "Period/getInternalPeriodListJson",
            "fnServerData": function (sAjaxSource, map, callback, oSettings) {
                var data = oSettings['oAjaxData'];
                $.ajax({
                    type: "POST",
                    url: ContextPath + "Period/getInternalPeriodListJson",
                    data: data
                })
                .done(function (serverModel) {
                    self.InitMode = true;
                    // header - dates
                    self.CurrentDate(serverModel.CurrentDate);
                    self.NextDate(serverModel.Next);
                    self.PreviousDate(serverModel.Previous);
                    self.ParamStart(serverModel.ParamStart);

                    self.CalendarDate(serverModel.Start);
                    self.CalendarStartDate(serverModel.Start);
                    self.CalendarEndDate(serverModel.End);

                    // filters
                    var selectedDiscipline = self.SelectedDiscipline();
                    self.DisciplineModels.removeAll();
                    $.each(serverModel.DisciplineModels, function (index, value) {
                        self.DisciplineModels.push(value);
                    });
                    if (selectedDiscipline == 0) {
                        self.SelectedDiscipline(self.DisciplineModels()[0].Value);
                    } else {
                        self.SelectedDiscipline(selectedDiscipline);
                    }
                    self.TypeManager.SelectTypeById(self.SelectedDiscipline());

                    var selectedPeriodType = self.SelectedPeriodType();
                    self.PeriodTypeModels.removeAll();
                    $.each(serverModel.PeriodTypeModels, function (index, value) {
                        self.PeriodTypeModels.push(value);
                    });
                    if (selectedPeriodType == '') {
                        self.SelectedPeriodType(self.PeriodTypeModels()[0].Value);
                    } else {
                        self.SelectedPeriodType(selectedPeriodType);
                    }

                    // call back
                    callback(serverModel.DataTableResult);
                    self.InitMode = false;
                });
            },
            "sServerMethod": "POST",
            "bStateSave": true,
            "fnServerParams": function (aoData) {
                $('.datapicker').datepicker('hide');

                var kind = self.SelectedPeriodType();
                if (kind == null || kind == '') {
                    kind = 'all';
                }
                var disciplineId = self.SelectedDiscipline();
                if (disciplineId == null || disciplineId == 0) {
                    disciplineId = 0;
                }

                var lengthType = self.LengthType();

                var startDate;
                var endDate;
                if (lengthType != -1 || self.useParam) {
                    startDate = self.ParamStart();
                    endDate = null;
                    self.useParam = false;
                } else {
                    startDate = self.CalendarStartDate();
                    endDate = self.CalendarEndDate();
                }

                var visibleStatus = true;
                var visChb = $('#visible_status');
                if (visChb.length) { visibleStatus = visChb.is(':checked'); }
                else { visibleStatus = self.Visible_status(); }

                var hiddenStatus = true;
                var hidChb = $('#hidden_status');
                if (hidChb.length) { hiddenStatus = hidChb.is(':checked'); }
                else { hiddenStatus = self.Hidden_status(); }

                aoData.push({ name: "startDate", value: startDate });
                aoData.push({ name: "endDate", value: endDate });
                aoData.push({ name: "lengthType", value: lengthType });
                aoData.push({ name: "kind", value: kind });
                aoData.push({ name: "disciplineId", value: disciplineId });
                aoData.push({ name: "visibleStatus", value: visibleStatus });
                aoData.push({ name: "hiddenStatus", value: hiddenStatus });
            },
            "fnDrawCallback": function (oSettings) {
            }
        };
    };

    self.init = function (container) {
        self.RegisterDataPicker();
        var customConfig = self.getInternalPeriodsTableConfig();
        self.DataTable = $('.externalPeriodsDataTable', container).dataTable(customConfig);
        self.bindInternalPeriodsTableNavi(self.DataTable, container);
    };

    self.refresh = function () {
        if (!self.InitMode && self.DataTable) {
            self.DataTable.fnDraw();
        }
    }

    self.bindInternalPeriodsTableNavi = function (container) {
        $('.datapicker', container).datepicker().on('changeDate', function (ev) {
            self.refresh();
        });
        $('.datapicker', container).change(function (ev) {
            self.refresh();
        });
        $('#disciplineSelect').change(function () {
            self.refresh();
        });
        $('#kindSelect').change(function () {
            self.refresh();
        });
        $('#visible_status').change(function () {
            self.refresh();
        });
        $('#hidden_status').change(function () {
            self.refresh();
        });
        $('#hidden_status').change(function () {
            self.refresh();
        });
    };

    self.Search = function () {
        self.refresh();
    }

    self.CalendarDate = ko.observable();
    self.CalendarDate.subscribe(function (newValue) {
        if (!self.InitMode) {
            var date = self.CalendarDate();
            self.ParamStart(date);
            self.Start = true;

            self.Search();
        }
    });
    self.CalendarStartDate = ko.observable();
    self.CalendarEndDate = ko.observable();
    self.CalendarEndDate.subscribe(function (newValue) {
        if (!self.InitMode) {
            self.InitMode = true;
            self.CalendarDate(newValue);
            self.InitMode = false;
        }
        self.Search();
    });
    self.CalendarStartDate.subscribe(function (newValue) {
        self.Search();
    });

    self.clickDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.yearBox').find('.datepicker');
            if (dp) {
                dp.datepicker('show');
            }
        }
    };
    self.RegisterDataPicker = function () {

        var dp = $('#PeriodsAdminTable_container .yearBox .datepicker');
        if (dp) {
            dp.datepicker().on('show', function (ev) {
                var mode = 0;
                var newValue = self.LengthType();
                if (newValue == 30) { mode = 1 }
                else if (newValue == 365) { mode = 2 }
                if (mode - 1) {
                    dp.datepicker('showMode', mode - 1);
                }
            });
        }
    }

    self.GoToNextDate = function () {
        self.ParamStart(self.NextDate());
        self.Search();
    };
    self.NextPeriod = function () {
        self.GoToNextDate();
        self.useParam = true;
    };
    self.GoToPreviousDate = function () {
        self.ParamStart(self.PreviousDate());
        self.Search();
    };
    self.PreviousPeriod = function (selectLast) {
        self.GoToPreviousDate();
        self.useParam = true;
    };

    self.LengthType.subscribe(self.refresh);

    self.getPeriodTypeIcon = function (type) {
        var ico = 'allPeriodTypes';
        switch (type) {
            case 'Contest': ico = 'zawody'; break;
            case 'Competitions': ico = 'wyniki'; break;
            case 'Program': ico = 'programy'; break;
            case 'Schools': ico = 'schools'; break;
            case 'League': ico = 'league'; break;
            case 'Action': ico = 'action'; break;
            case 'Group': ico = 'group'; break;
            case 'Other': ico = 'otherPeriodTypes'; break;
        }
        return ico + '_39';// + (self.PeriodType() == type ? ' selected' : '');
    }
    self.CurrentTypeIconClass = ko.pureComputed(function () { return self.getPeriodTypeIcon(self.SelectedPeriodType()); });
    self.PeriodTypeLabel = ko.pureComputed(function () {
        switch (self.SelectedPeriodType()) {
            case 'Contest': return translations.Contests; break;
            case 'Competitions': return translations.CompetitionsChalenges; break;
            case 'Program': return translations.Programs; break;
            case 'Schools': return translations.Schools; break;
            case 'League': return translations.League; break;
            case 'Action': return translations.Actions; break;
            case 'Group': return translations.Group; break;
            case 'Other': return translations.OtherPeriodTypes; break;
            case '': return translations.All; break;
        }
        return translations.All;
    });
    self.SelectedVisibility = ko.pureComputed(function () {
        if (self.Visible_status() && self.Hidden_status()) return translations.VisibleAndHiddenEvents;
        if (self.Visible_status() && !self.Hidden_status()) return translations.OnlyVisibleEvents;
        if (!self.Visible_status() && self.Hidden_status()) return translations.OnlyHiddenEvents;
    });
}
var internalPeriodsTableModel = new InternalPeriodsTableModel();
GeneralModel.addProperty("InternalPeriodsTableModel", internalPeriodsTableModel);

function loadInternalPeriodsTable() {
    var container = $('#PeriodsAdminTable_container');
    internalPeriodsTableModel.init(container);
}

var externalPeriodsAoColumns = [
          { "bVisible": true }, //1 - 
          { "sWidth": "70px" }, //2 - 
          { "sWidth": "60px" }, //3 - 
          { "sWidth": "60px" }, //4 - 
          { "sWidth": null }, //5 - 
          { "sWidth": null }, //6 - 
          { "sWidth": null }, //7 - 
];

function getExternalPeriodsTableConfig() {
    return {
        "bDestroy": true,
        "oLanguage": dataTableLanguageOptions,
        "aoColumnDefs": [
              { "bSortable": false, "aTargets": [0, 6] }
        ],
        "aaSorting": [[3, "asc"]],
        "bAutoWidth": false,
        "aoColumns": externalPeriodsAoColumns,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": ContextPath + "Period/getExternalPeriodListJson",
        "sServerMethod": "POST",
        "bStateSave": true,
        "fnServerParams": function (aoData) {
            $('.datapicker').datepicker('hide');
            var startDate = $('#startDate').val();
            var endDate = $('#endDate').val();

            var visibleStatus = $('#visible_status').is(':checked');
            var hiddenStatus = $('#hidden_status').is(':checked');

            aoData.push({ name: "startDate", value: startDate });
            aoData.push({ name: "endDate", value: endDate });
            aoData.push({ name: "visibleStatus", value: visibleStatus });
            aoData.push({ name: "hiddenStatus", value: hiddenStatus });
        },
        "fnDrawCallback": function (oSettings) {
        }
    };
}

function loadExternalPeriodsTable() {
    var container = $('#PeriodsAdminTable_container');
    var customConfig = getExternalPeriodsTableConfig();
    var table = $('.externalPeriodsDataTable', container).dataTable(customConfig);

    bindExternalPeriodsTableNavi(table, container);
}

function bindExternalPeriodsTableNavi(table, container) {
    reloadFunction = function () { table.fnDraw(false); }

    $('.datapicker', container).datepicker().on('changeDate', function (ev) {
        table.fnDraw();
    });
    $('.datapicker', container).change(function (ev) {
        table.fnDraw();
    });
    $('#visible_status').change(function () {
        table.fnDraw();
    });
    $('#hidden_status').change(function () {
        table.fnDraw();
    });
}

function PeriodModel(parent, initTypes) {

    var self = this;

    self.parent = parent;

    //#region properties   

    self.CreatedBy = ko.observable(false);

    self.TypeManager = (initTypes || !self.parent || !self.parent.TypeManager) ? new TrainingTypeControl({
        mainId: null,
        subId: null,
        typeChangeCallback: null,
        clubId: null,
        multi: false,
        baseCtrl: self.parent ? self.parent.TypeManager : null,
        disciplines: true,
        multiSubs: true,
        allInstedNone: true
    }) : self.parent.TypeManager;

    self.ParamsVisibility = ko.observable(null);
    self.UpdateVisibility = function () {
        var types = self.TypeManager.SelectedSubTypes();
        if (types && types.length == 1 && types[0].ParamsVisibility) {
            self.ParamsVisibility(types[0].ParamsVisibility);
        } else {
            self.ParamsVisibility({
                Value1Type: 'none',
                Value1Name: null,
                Value1Icon: null,
                Value2Type: 'none',
                Value2Name: null,
                Value2Icon: null,
                ResultType: 'none',
                ResultName: null,
                ResultIcon: null,
                PlaceVisible: false,
                PlaceName: null,
                OpenPlaceVisible: false,
                OpenPlaceName: null
            });
        }
    };
    self.TypeManager.SetChangeCallback(function () {
        self.UpdateVisibility();
    });

    if (typeof (ClubInfoModel) != 'undefined') {
        self.ClubInfo = ko.observable(new ClubInfoModel(self));
    }

    self.Id = ko.observable("");
    self.NameTransAble = new translateAble.Text();
    self.HiddenNotes = ko.observable("");
    self.StartDate = ko.observable(new Date().toJSON().substring(0, 10));
    self.EndDate = ko.observable(new Date().toJSON().substring(0, 10));
    self.StartTime = ko.observable("");
    self.Group = ko.observable("");
    self.Deadline = ko.observable("");
    self.Active = ko.observable(true);
    self.UseGrafik = ko.observable(true);
    self.DefaultEventType = ko.observable('');
    self.DefaultLiveEvents = ko.observable(false);
    self.GrafikLengthType = ko.observable('');
    self.Ended = ko.observable(true);
    self.LogoId = ko.observable(0);
    self.ClubLogoId = ko.observable(0);
    self.PartnersLogosFileId = ko.observable(0);
    self.AccessCode = ko.observable("");
    self.IsRanked = ko.observable(false);
    self.Type = ko.observable("");
    self.Intensivity = ko.observable("");
    self.Place = ko.observable("");
    self.CategoryIds = ko.observableArray([]);
    self.DisciplineId = ko.observable(0);
    self.DescriptionTransAble = new translateAble.Text();
    self.CategoryValue1 = ko.observable("");
    self.CategoryValue2 = ko.observable("");

    self.SignAccessCode = ko.observable(""); //pole podczas zapisywania się
    self.RankingsModel = ko.observable(null);
    self.RequireAccessCode = ko.observable(false);
    self.InvalidCode = ko.observable(false);
    self.Saved = ko.observable(false);
    self.SimpleJoin = ko.observable(false);

    self.RegisterQuestionareId = ko.observable(0);
    self.AvailableQuests = ko.observableArray([]);

    self.UserStatus = ko.observable("");
    self.CanJoin = ko.observable(false);

    self.CanDelete = ko.observable(false);
    self.IsSignUps = ko.observable(false);
    self.PlacesLimit = ko.observable(null);
    self.WebAdressTransAble = new translateAble.Text();
    self.WebAdressLink = ko.observable("");
    self.Organisers = ko.observableArray([]);
    self.ClubOficialName = ko.observable("");
    self.PeriodRoutes = ko.observableArray([]);
    self.PeriodRouteIndex = ko.observable(0);
    self.FileIds = ko.observableArray([]);
    self.FileIndex = ko.observable(0);
    self.FileId = ko.observable(0);
    self.Public = ko.observable(false);
    self.PublicAccepted = ko.observable(false);
    self.OnlyForParticipants = ko.observable(false);

    self.StartDateInactive = ko.observable(true);
    self.EndDateInactive = ko.observable(true);
    self.TwoDates = ko.observable(true);
    self.TwoYears = ko.observable(true);
    self.TwoMonths = ko.observable(true);
    self.Started = ko.observable(false);

    self.TargetsNr = ko.observable(0);
    self.ParticipsNr = ko.observable(0);
    self.LiguesNr = ko.observable(0);
    self.ExternalResultsNr = ko.observable(0);
    self.QuestsNr = ko.observable(0);
    self.NewsNr = ko.observable(0);

    self.Coaches = ko.observableArray([]);

    self.Collapsed = ko.observable(true);
    self.CollapsedTargetDesc = ko.observable(true);

    self.ShowPlanOnUserCal = ko.observable(false);

    self.Source = ko.observable('');

    self.ContactEmailTransAble = new translateAble.Text();

    self.MailAnalRequests = ko.observable(false);

    self.LocationModel = ko.observable(new LocationModel(self));

    self.DocumentIdTransAble = new translateAble.Text(0);
    self.DocumentId = self.DocumentIdTransAble.someValue;

    self.EmailFooter = ko.observable("");
    self.GalleryLinks = ko.observable("");

    self.SinglePeriodExplore = ko.observable(false);

    self.PeriodPlanModel = new PeriodPlanModel(self);
    self.CompetitionAdded = ko.observable(false);
    self.CompetitionAddedId = ko.observable(0);
    self.CompetitionResult = ko.observable('');
    self.CompetitionPlace = ko.observable(false);
    self.CompetitionOpenPlace = ko.observable(false);

    self.EndDateIsToday = ko.observable(false);

    self.TrainingPlanId = ko.observable(0);

    self.ChildPeriodIds = ko.observableArray([]);
    self.IsParent = ko.observable(false);
    self.AvailablePeriods = ko.observableArray([]);
    self.AvailableBindPeriods = ko.observableArray([]);
    self.ParentId = ko.observable(0);
    self.ParentName = ko.observable('');
    self.AvailablePeriodsPattern = ko.observable('');

    self.JoinInfoImgFileId = ko.observable(0);

    self.AuthorEmail = ko.observable('');
    self.AuthorName = ko.observable('');

    self.IgnoreDates = ko.observable(false);

    self.Targets = ko.observableArray([]);

    self.SponsorName = ko.observable('');
    self.SponsorLink = ko.observable('');
    self.SponsorLogofileId = ko.observable(0);

    self.NrOfLiveStreams = ko.observable(0);

    //#endregion

    self.DefaultEventType.subscribe(function (newValue) {
        if (newValue && typeof (eventManager) != 'undefined' && eventManager) {
            eventManager.DefaultEventType(newValue);
        }
    });
    self.DefaultLiveEvents.subscribe(function (newValue) {
        if (newValue && typeof (eventManager) != 'undefined' && eventManager) {
            eventManager.DefaultLiveEvents(newValue);
        }
    });

    //#region computed

    self.Name = self.NameTransAble.someValue;
    self.Description = self.DescriptionTransAble.someValue;
    self.WebAdress = self.WebAdressTransAble.someValue;
    self.ContactEmail = self.ContactEmailTransAble.someValue;

    self.EnableGroup = ko.computed(function () {
        return self.Id() != 0;
    });

    self.ComplexName = ko.pureComputed(function () {
        return self.Name() + ' (od: ' + self.StartDate + ' do: ' + self.EndDate + ')';
    });

    self.ExtendedTitle = ko.pureComputed(function () {
        if (self.Active()) {
            return self.Name;
        } else {
            return translations.VeryficationStatus;
        }
    });

    self.HasDocument = ko.pureComputed(function () {
        return self.DocumentId() != 0;
    });
    self.DocumentUrl = ko.pureComputed(function () {
        return ContextPath + "Document/GetImage?id=" + self.DocumentId();
    });

    self.HasLogo = ko.pureComputed(function () {
        return self.LogoId() != 0;
    });
    self.LogoUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.LogoId();
    });
    self.SponsorLogoUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.SponsorLogofileId();
    });
    self.HasClubLogo = ko.pureComputed(function () {
        return self.ClubLogoId() != 0;
    });
    self.ClubLogoUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.ClubLogoId();
    });
    self.PartnersLogosUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.PartnersLogosFileId();
    });

    self.HasImage = ko.pureComputed(function () {
        return (self.FileIds().length > 0 && self.FileIds()[0]() != 0);
    });
    self.ImageUrl = ko.pureComputed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.FileId();
    });

    self.JoinInfoImgFileUrl = ko.pureComputed(function () {
        if (self.JoinInfoImgFileId()) {
            return ContextPath + "Image/GetImage?id=" + self.JoinInfoImgFileId();
        } else {
            return ContextPath + "Content/images/info/" + translations.infoZgloszenieImg;
        }

    });

    self.ExternalJoinPageUrl = ko.pureComputed(function () {
        return ContextPath + "Period/SinglePeriodExplorer?periodId=" + self.Id();
    });

    self.LiveStreamsUrl = ko.pureComputed(function () {
        return ContextPath + "LiveStream/CompetitionStreams?periodId=" + self.Id();
    });

    self.ParentPeriodUrl = ko.pureComputed(function () {
        return ContextPath + "Period/ContestPage?periodId=" + self.ParentId();
    });


    self.ActivityLabel = ko.pureComputed(function () {
        return self.Active() ? translations.StateVisible : translations.StateInvisible;
    });
    self.GrafikActivityLabel = ko.pureComputed(function () {
        return self.UseGrafik() ? translations.UseGrafik : translations.DoNotUseGrafik;
    });

    self.TypeLabel = ko.pureComputed(function () {
        return translations["PeriodType" + (self.Type() ? self.Type() : "Other")];
    });

    self.Planned = ko.pureComputed(function () {
        var sy = new Date(self.StartDate());
        var now = new Date();
        return sy > now;
    });
    self.ProgresDescription = ko.pureComputed(function () {
        if (self.Ended()) return translations.Ended;
        if (self.Planned()) return translations.InPlans;
        return translations.InProgress;
    });



    self.ToggleGroupActivity = function () {
        self.Active(!self.Active());
        self.SaveActivitnes();
    }
    
    self.ToggleGrafikActivity = function () {
        self.UseGrafik(!self.UseGrafik());
        self.EnsureEventManager();
    }

    self.EnsureEventManager = function () {
        if (self.UseGrafik()) {
            initEventManager();
            eventManager.FulfillEventManager();
        }
    }
    

    self.SaveActivitnes = function () {
        if (self.Id()) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/SetPeriodActivity",
                data: { id: self.Id(), groupActive: self.Active() }//, grafickActive: self.UseGrafik() }
            });
        }
    }

    self.eventTypeIconClass = ko.pureComputed(function () {
        return self.Type();
    });

    self.IntensToolBarHovered = ko.observable(0);
    self.DisplayIntensivity = ko.pureComputed(function () {
        var hIntens = self.IntensToolBarHovered();
        if (hIntens) {
            return hIntens;
        } else {
            return self.Intensivity();
        }
    });
    self.IntensivityLabel = ko.pureComputed(function () {
        return translations["Intensivity_" + self.DisplayIntensivity()];
    });
    self.IntensivitySubLabel = ko.pureComputed(function () {
        return '';//translations["IntensivitySub_" + self.DisplayIntensivity()];
    });
    self.IntensivityRPE = ko.pureComputed(function () {
        return '';//translations["IntensivityRPE_" + self.DisplayIntensivity()];
    });

    self.IntensToolBarMouseIn = function (value) {
        self.IntensToolBarHovered(value);
    }
    self.IntensToolBarMouseOut = function () {
        self.IntensToolBarHovered(0);
    }
    self.IntensToolBarMouseClick = function (value) {
        self.Intensivity(value);
    }

    self.LongHtmlDate = ko.pureComputed(function () {
        return dateToLongHtml(self.StartDate());
    });
    self.LongHtmlDateSpan = ko.pureComputed(function () {
        return datesToLongHtml(self.StartDate(), self.EndDate());
    });

    self.ShowTime = ko.pureComputed(function () {
        var t = self.Type();
        return !t || t == 'Competitions' || t == 'Other';
    });

    self.StartDay = ko.pureComputed(function () {
        return getDayFromDate(self.StartDate());
    });

    self.StartMonth = ko.pureComputed(function () {
        return getMonthFromDate(self.StartDate());
    });

    self.StartYear = ko.pureComputed(function () {
        return getYearFromDate(self.StartDate());
    });

    self.EndDay = ko.pureComputed(function () {
        return getDayFromDate(self.EndDate());
    });

    self.EndMonth = ko.pureComputed(function () {
        return getMonthFromDate(self.EndDate());
    });

    self.EndYear = ko.pureComputed(function () {
        return getYearFromDate(self.EndDate());
    });

    self.StartDate.subscribe(function (newValue) {
        if (newValue) {
            self.CheckEndDate();
        }
    });
    self.EndDate.subscribe(function (newValue) {
        if (newValue) {
            self.CheckEndDate();
        }
    });
    self.CheckEndDate = function () {
        var ey = new Date(self.EndDate());
        var sy = new Date(self.StartDate());
        if (ey < sy) {
            self.EndDate(self.StartDate());
        }
    }

    self.PublicityLabel = ko.pureComputed(function () {
        if (self.OnlyForParticipants()) {
            return translations.OnlyForParticipants;
        } else if (self.Public()) {
            if (self.PublicAccepted()) {
                return translations.PublicityPublicAccepted;
            } else {
                return translations.PublicityPublicNotAccepted;
            }
        } else {
            return translations.PublicityInClub;
        }
    });

    self.EnableGrafikLabel = ko.pureComputed(function () {
        if (self.UseGrafik()) {
            return translations.UseGrafik;
        } else {
            return translations.DoNotUseGrafik;
        }
    });


    self.Value1IconUrl = ko.pureComputed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().Value1Icon) {
            return ContextPath + 'Content/images/icons/' + self.ParamsVisibility().Value1Icon + '_40.png';
        } else {
            return '';
        }
    });
    self.Value2IconUrl = ko.pureComputed(function () {
        if (self.ParamsVisibility() && self.ParamsVisibility().Value2Icon) {
            return ContextPath + 'Content/images/icons/' + self.ParamsVisibility().Value2Icon + '_40.png';
        } else {
            return '';
        }
    });

    self.EndedBarClass = ko.pureComputed(function () {
        var result = '';

        if (self.Type() == 'Competitions' && (!self.IsSignUps() || self.UserStatus() == 'IsIn')) {
            result += 'withAddContest';

            if (self.CompetitionAdded()) {
                result += ' added';
            }
        }

        return result;
    });

    self.DisplayAddContestResult = ko.pureComputed(function () {
        return self.EndedBarClass().indexOf("withAddContest") != -1;
    });
    self.DisplayAddedContestResult = ko.pureComputed(function () {
        return self.EndedBarClass().indexOf("withAddContest added") != -1;
    });

    self.AddedContestResultHref = ko.pureComputed(function () {
        if (self.EndedBarClass().indexOf("withAddContest added") != -1) {
            return ContextPath + "Contest/UserAdd?competitionsResultId=" + self.CompetitionAddedId();
        } else {
            return ContextPath + "Contest/UserAdd?periodId=" + self.Id();
        };
    });

    self.CategoryLabel = ko.pureComputed(function () {
        var result = '';

        if (self.TypeManager.SelectedSubTypes().length > 0) {
            for (var i = 0, len = self.TypeManager.SelectedSubTypes().length; i < len; i++) {
                result += self.TypeManager.SelectedSubTypes()[i].IconTextContent() + ' ';
            }
        }

        if (self.ParamsVisibility() && self.ParamsVisibility().Value1Type != 'none' && self.CategoryValue1()) {
            if (self.CategoryValue1().trim().replace(/\s/gi, "") != result.trim().replace(/\s/gi, "")) {
                result += '(' + self.CategoryValue1().trim() + ')';
            }
        }

        return result;
    });

    self.IsProgram = ko.pureComputed(function () {
        return self.Type() == 'Program';
    });

    self.SingleTarget = ko.pureComputed(function () {
        var targets = self.Targets();
        if (!targets || targets.length != 1) return null;
        return targets[0];
    });

    self.SelectedCoaches = ko.pureComputed(function () {
        var result = [];
        if (parent.AvailableCoaches) {
            var _avCoaches = _(parent.AvailableCoaches());
            var cIds = self.Coaches();

            for (var i = 0; i < cIds.length; i++) {

                var cId = cIds[i];
                var coach = _avCoaches.find(function (elem) { return elem.Id == cId });
                if (coach) {
                    result.push(coach);
                }
            }
        }
        return result;
    });
    self.SelectedCoacheEmail = ko.pureComputed(function () {
        if (parent.AvailableCoaches) {
            var _avCoaches = _(parent.AvailableCoaches());
            var cIds = self.Coaches();

            for (var i = 0; i < cIds.length; i++) {

                var cId = cIds[i];
                var coach = _avCoaches.find(function (elem) { return elem.Id == cId });
                if (coach) {
                    return coach.Email
                }
            }
        }
        return '';
    });
    self.DisplayCoachEmails = ko.observable(false);
    self.UnsetCoachEmail = ko.pureComputed(function () {
        var scs = self.SelectedCoaches();
        if (scs && scs.length) {
            for (var i = 0; i < scs.length; i++) {
                var sc = scs[i];
                if (!sc.Email) return true;
            }
        }
        return false;
    });


    self.PeriodVisibility = ko.pureComputed({
        read: function () {
            if (self.OnlyForParticipants()) return 'OnlyForParticipants';
            if (self.Public()) return 'Public';
            return 'InClub';
        },
        write: function (value) {
            switch (value) {
                case 'OnlyForParticipants': self.OnlyForParticipants(true); break;
                case 'Public': self.OnlyForParticipants(false); self.Public(true); break;
                case 'InClub': self.OnlyForParticipants(false); self.Public(false); break;
            }
        },
        owner: self
    });

    //#endregion

    // events

    self.PeriodChangeEvent = null;

    self.StartDate.subscribe(function (newValue) {
        if (self.PeriodChangeEvent) {
            self.PeriodChangeEvent();
        }
    });
    self.TypeManager.SelectedType.subscribe(function (newValue) {
        if (self.PeriodChangeEvent) {
            self.PeriodChangeEvent();
        }
    });

    //#region init, save, delete

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.NameTransAble.initByModel(serverModel.NameTransAble);
            self.HiddenNotes(serverModel.HiddenNotes);
            self.StartTime(serverModel.StartTime);
            if (self.PeriodPlanModel) self.PeriodPlanModel.LiveStreamStartTime(serverModel.StartTime);
            self.StartDate(serverModel.StartDate);
            self.EndDate(serverModel.EndDate);
            self.Group(serverModel.GroupId);
            self.Deadline(serverModel.Deadline);
            self.Active(serverModel.Active);
            self.UseGrafik(serverModel.UseGrafik);
            self.DefaultEventType(serverModel.GrafikDefaultEventType);
            self.DefaultLiveEvents(serverModel.GrafikDefaultLiveEvents);
            self.GrafikLengthType(serverModel.GrafikLengthType);
            self.Ended(serverModel.Ended);
            //self.DocumentId(serverModel.DocumentId);
            self.DocumentIdTransAble.initByModel(serverModel.DocumentIdTransAble);
            self.LogoId(serverModel.LogoId);
            self.ClubLogoId(serverModel.ClubLogoId);
            self.PartnersLogosFileId(serverModel.PartnersLogosFileId);
            self.AccessCode(serverModel.AccessCode);
            self.IsRanked(serverModel.IsRanked);
            self.Type(serverModel.Type);
            self.Intensivity(serverModel.Intensivity);

            koArrayCopy(serverModel.AvailableQuests, self.AvailableQuests);
            self.AvailableQuests.unshift({ Text: '', Value: '0' });
            self.RegisterQuestionareId('' + serverModel.RegisterQuestionareId);

            koArrayCopyComplex(serverModel.Organisers, self.Organisers, false, function (elem) {
                elem.Name = ko.observable(elem.Name);
                return elem;
            });
            if (!self.Organisers().length) self.addOrganiser();

            self.ClubOficialName(serverModel.ClubOficialName);

            self.Place(serverModel.Place);
            self.DescriptionTransAble.initByModel(serverModel.DescriptionTransAble);
            self.RequireAccessCode(serverModel.RequireAccessCode);
            self.SimpleJoin(serverModel.SimpleJoin);
            self.DisciplineId(serverModel.DisciplineId);
            self.CategoryValue1(serverModel.CategoryValue1);
            self.CategoryValue2(serverModel.CategoryValue2);

            self.UserStatus(serverModel.UserStatus);
            self.CanJoin(serverModel.CanJoin);

            self.CanDelete(serverModel.CanDelete);
            self.IsSignUps(serverModel.IsSignUps);
            self.PlacesLimit(serverModel.PlacesLimit);
            self.WebAdressTransAble.initByModel(serverModel.WebAdressTransAble);
            self.WebAdressLink(serverModel.WebAdressLink);

            self.Public(serverModel.Public);
            self.PublicAccepted(serverModel.PublicAccepted);
            self.OnlyForParticipants(serverModel.OnlyForParticipants);

            self.StartDateInactive(serverModel.StartDateInactive);
            self.EndDateInactive(serverModel.EndDateInactive);
            self.TwoDates(serverModel.TwoDates);
            self.TwoYears(serverModel.TwoYears);
            self.TwoMonths(serverModel.TwoMonths);
            self.Started(serverModel.Started);

            self.TargetsNr(serverModel.TargetsNr);
            self.ParticipsNr(serverModel.ParticipsNr);
            self.LiguesNr(serverModel.LiguesNr);
            self.ExternalResultsNr(serverModel.ExternalResultsNr);
            self.QuestsNr(serverModel.QuestsNr);
            self.NewsNr(serverModel.NewsNr);

            self.ShowPlanOnUserCal(serverModel.ShowPlanOnUserCal);

            self.Source(serverModel.Source);
            self.ContactEmailTransAble.initByModel(serverModel.ContactEmailTransAble);

            self.EmailFooter(serverModel.EmailFooter);
            self.GalleryLinks(serverModel.GalleryLinks);

            self.LocationModel().initByModel(serverModel.LocationModel);

            if (self.ClubInfo) {
                self.ClubInfo().initByModel(serverModel.ClubInfo);
            }

            //self.DisciplineIds.removeAll();
            self.TypeManager.DeselectAll();
            self.TypeManager.SelectMultipleSubTypes(self.DisciplineId(), serverModel.CategoryIds);

            self.FileIds.removeAll();
            for (var i = 0; i < serverModel.FileIds.length; i++) {
                self.FileIds.push(ko.observable(serverModel.FileIds[i]));
            }

            self.FileId(serverModel.FileIds[0]);



            self.PeriodRoutes.removeAll();
            for (var i = 0; i < serverModel.PeriodRoutes.length; i++) {
                self.PeriodRoutes.push(ko.observable(serverModel.PeriodRoutes[i]));
            }

            //var _avCoaches = _(parent.AvailableCoaches());
            self.Coaches.removeAll();
            for (var i = 0; i < serverModel.CoachIds.length; i++) {
                /*
                var cId = serverModel.CoachIds[i];
                var coach = _avCoaches.find(function (elem) { return elem.Id==cId});
                if (coach) {
                    self.Coaches.push(coach);
                }
                */
                self.Coaches.push(serverModel.CoachIds[i]);
            }

            self.MailAnalRequests(serverModel.MailAnalRequests);
            self.CompetitionAdded(serverModel.CompetitionAdded);
            self.CompetitionAddedId(serverModel.CompetitionAddedId);
            self.CompetitionPlace(serverModel.CompetitionPlace);
            self.CompetitionOpenPlace(serverModel.CompetitionOpenPlace);
            self.CompetitionResult(serverModel.CompetitionResult);
            self.EndDateIsToday(serverModel.EndDateIsToday);

            self.PeriodPlanModel.initByModel(serverModel.PeriodPlanModel);

            self.TrainingPlanId(serverModel.TrainingPlanId);

            koArrayCopy(serverModel.ChildPeriodIds, self.ChildPeriodIds, false, false);
            self.IsParent(serverModel.IsParent);
            self.ParentId(serverModel.ParentId);
            self.ParentName(serverModel.ParentName);

            self.JoinInfoImgFileId(serverModel.JoinInfoImgFileId);

            self.IgnoreDates(serverModel.IgnoreDates);

            koArrayCopyComplex(serverModel.Targets, self.Targets, false, function (elem) {
                var target = new TargetModel(null, true);
                target.initByModel(elem);
                return target;
            });

            self.SponsorName(serverModel.SponsorName);
            self.SponsorLink(serverModel.SponsorLink);
            self.SponsorLogofileId(serverModel.SponsorLogofileId);

            self.NrOfLiveStreams(serverModel.NrOfLiveStreams);

            initTimePicker();

            self.EnsureEventManager();
        }
    }

    self.initById = function (id) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetPeriodModel",
            data: {
                id: id
            }
        })
         .done(function (result) {
             self.initByModel(result);
         });
    }

    self.cancel = function (id) {
        self.initById(self.Id());
    }

    self.getData = function () {
        var discIds = [];
        var types = self.TypeManager.SelectedSubTypes();
        for (var i = 0, len = types.length; i < len; i++) {
            discIds.push(types[i].Id);
        }

        var fIds = [];
        var files = self.FileIds();
        for (var i = 0, len = files.length; i < len; i++) {
            fIds.push(files[i]());
        }
        /*
        var coachIds = [];
        var coaches = self.Coaches();
        for (var i = 0, len = coaches.length; i < len; i++) {
            coachIds.push(coaches[i]);
        }
        */
        if (self.ParamsVisibility()) {
            if (self.ParamsVisibility().Value1Type == 'none') {
                self.CategoryValue1('');
            }
            if (self.ParamsVisibility().Value2Type == 'none') {
                self.CategoryValue2('');
            }
        }
        var data = {
            Id: self.Id(),
            Name: self.NameTransAble.getClearData(),
            HiddenNotes: self.HiddenNotes(),
            StartTime: self.StartTime(),
            StartDate: self.StartDate(),
            EndDate: self.EndDate(),
            GroupId: self.Group(),
            Deadline: self.Deadline(),
            Active: self.Active(),
            DocumentId: self.DocumentId(),
            DocumentIdTransAble: self.DocumentIdTransAble.getClearData(),
            LogoId: self.LogoId(),
            PartnersLogosFileId: self.PartnersLogosFileId(),
            AccessCode: self.AccessCode(),
            Type: self.Type(),
            Intensivity: self.Intensivity(),
            Organisers: GetDataFromKoArray(self.Organisers, function (elem) {
                elem.Name = ko.utils.unwrapObservable(elem.Name); return elem;
            }),
            PeriodRoutes: self.periodRoutesToSave(),
            Place: self.Place(),
            CategoryIds: discIds,
            DisciplineId: self.TypeManager.SelectedTypeId(),
            CategoryValue1: self.CategoryValue1(),
            CategoryValue2: self.CategoryValue2(),
            Description: self.DescriptionTransAble.getClearData(),
            IsSignUps: self.IsSignUps(),
            SimpleJoin: self.SimpleJoin(),
            RegisterQuestionareId: self.RegisterQuestionareId(),
            PlacesLimit: self.PlacesLimit(),
            WebAdress: self.WebAdressTransAble.getClearData(),
            Public: self.Public(),
            PublicAccepted: self.PublicAccepted(),
            OnlyForParticipants: self.OnlyForParticipants(),
            UseGrafik: self.UseGrafik(),
            GrafikDefaultEventType: self.DefaultEventType(),
            GrafikLengthType: self.GrafikLengthType(),
            GrafikDefaultLiveEvents: self.DefaultLiveEvents(),
            FileIds: fIds,
            LocationModel: self.LocationModel().getClearData(),
            ContactEmail: self.ContactEmailTransAble.getClearData(),
            CoachIds: self.Coaches(),
            MailAnalRequests: self.MailAnalRequests(),
            EmailFooter: self.EmailFooter(),
            GalleryLinks: self.GalleryLinks(),
            IsParent: self.IsParent(),
            ChildPeriodIds: self.ChildPeriodIds(),
            JoinInfoImgFileId: self.JoinInfoImgFileId(),
            CreatedBy: self.CreatedBy(),
            AuthorEmail: self.AuthorEmail(),
            IgnoreDates: self.IgnoreDates(),
            SponsorName: self.SponsorName(),
            SponsorLink: self.SponsorLink(),
            SponsorLogofileId: self.SponsorLogofileId(),
        };
        return data;
    }

    self.save = function (data, event, callback) {
        var strData = JSON.stringify(self.getData());
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/SetPeriodModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
         .done(function (result) {
             if (callback) callback();
             self.parent.ReloadGroupsList();
             $('#periodModal').modal('hide');
             ShowSavedAlert();
             //refreshTable(); //????
         });
    }
    self.saveAndAddNew = function (data, event) {
        self.save(data, event, function () {
            window.location = ContextPath + "Period/InitNewPeriod";
        });
    }
    self.copy = function (data, event) {
        var strData = JSON.stringify(self.getData());
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/CopyToNewPeriod",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
         .done(function (result) {
             if (result) {
                 window.location = ContextPath + "Period/ContestPage?periodId=" + result;
             }
         });
    }
    self.copyBinded = function (data, event) {
        var strData = JSON.stringify(self.getData());
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/CopyBinded",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
         .done(function (result) {
             if (result) {
                 window.location = ContextPath + "Period/ContestPage?periodId=" + result;
             }
         });
    }

    self.deletePeriod = function (elem) {
        $('#removePeriodModal').modal('show');
    }
    self.coreDeletePeriod = function () {
        $('#removePeriodModal').modal('hide');
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/Delete",
            data: { id: self.Id() }
        })
         .done(function (result) {
             if (result == "2") {
                 $('#removePeriodModal2').modal('show');
             } else {
                 window.location = ContextPath + "Period/InternalPeriods"
             }
         });
    }

    self.refresh = function () {
        self.initById(self.Id);
    }

    //#endregion

    self.removeDocument = function () {
        self.DocumentId(0);
    }
    self.removeLogo = function () {
        self.LogoId(0);
    }
    self.removePartnersLogo = function () {
        self.PartnersLogosFileId(0);
    }

    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    }

    self.openRankingsModal = function () {
        afterSaveAction = function () {
            self.refresh();
        }
        openGroupRankingExlusionModal(self.Group());
    }


    self.loadAvailableRankings = function () {
        if (self.RankingsModel() == null) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Groups/GetGroupRankingsForSign",
                data: {
                    groupId: self.Group()
                }
            })
             .done(function (result) {
                 var m = new RankingsPreferencesModel();
                 m.init(result);
                 self.RankingsModel(m);
             });
        }
    }

    //#region singnIn
    self.QuestionareModel = ko.observable(null);
    self.openSignIn = function () {
        self.loadAvailableRankings();
        self.parent.Parent.SelectdPeriod(self);

        if (self.ClubInfo) {
            self.ClubInfo().LoadClubDetailsModel();
        }

        var regId = self.RegisterQuestionareId();
        if (regId && regId != '0') {
            var questMgr = initQuestionnaireManager(true);
            questMgr.LoadRegisterForm(regId, function (regModel) { self.QuestionareModel(regModel); });
        }

        var mod = $('#signForEventModal');
        mod.one('shown', function () {
            JsRequestLog('period.js', 'openSignIn opened', 'groupId=' + self.Group() + '&periodId=' + self.Id());
        });
        mod.one('hidden', function () {
            JsRequestLog('period.js', 'openSignIn hidden', 'groupId=' + self.Group() + '&periodId=' + self.Id());
        });
        mod.modal('show');
        JsRequestLog('period.js', 'openSignIn', 'groupId=' + self.Group() + '&periodId=' + self.Id());
    }
    self.closeSignIn = function () {
        $('#signForEventModal').modal('hide');
        JsRequestLog('period.js', 'closeSignIn', 'groupId=' + self.Group() + '&periodId=' + self.Id());
    }

    self.singIn = function () {
        var regId = self.RegisterQuestionareId();
        if (regId && regId != '0') {
            var questModel = self.QuestionareModel();
            if (questModel) {
                questModel.Save(null, null, true);
                if (questModel.RequiredWarning()) {
                    ShowEmptyMsg(translations.QuestRequiredWarningMessage, translations["Warning"]);
                    return;
                }
            }
        }

        if (self.NotInClub && self.NotInClub()) {
            if (!self.ClubInfo().ClubModel().ValidDataAccess()) {
                return;
            }
        }
        if (self.RequireAccessCode() && !self.SignAccessCode()) {
            self.InvalidCode(true);
            return;
        }

        var ranks = self.RankingsModel();
        if (ranks) {
            ranks = ranks.ClerModel();
        }
        var data = {
            personId: 0,
            groupId: self.Group(),
            status: "wants",
            accessCode: self.SignAccessCode(),
            rankingsModel: ranks,
            ClubCanAccessData: self.ClubInfo().ClubModel().UserDataAcces()
        };

        $.ajax({
            type: "POST",
            url: ContextPath + "Groups/SetPersonInGroup",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(data)
        })
        .done(function (result) {
            if (result == "invalidCode") {
                self.InvalidCode(true);
            } else {
                self.InvalidCode(false);

                self.Saved(true);
                self.CanJoin(false);
                self.UserStatus('Wants');

                setTimeout(function () {
                    $('#signForEventModal').modal('hide');
                    self.Saved(false);
                }, 3000);

            }
        });
    }

    self.UnSign = function () {
        var data = {
            personId: 0,
            groupId: self.Group(),
            status: "none",
            accessCode: '',
            rankingsModel: null,
            ClubCanAccessData: false
        };
        modalAskModel.Show(translations.UnsignFromPeriod, translations.UnsignFromPeriodMsg, function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "Groups/SetPersonInGroup",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: JSON.stringify(data)
            })
            .done(function (result) {
                self.CanJoin(true);
                self.UserStatus('none');
                userCallendar.coreRefresh();
            });
        },
        translations.Delete);
    }

    self.signUpsClick = function () {
        self.IsSignUps(!self.IsSignUps());
    }


    self.ShowNotloggedJoinInfo = function () {
        var container = $('#joiningInffoContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: '#joiningInfoModal',
            ajaxPath: 'Period/GetJoiningInfoModal',
            bindData: self,
            callback: function () {
                container.find('#joiningInfoModal').modal('show');
            }
        });
    }
    //#endregion

    self.addOrganiser = function () {
        var elem = new Object({
            Name: ko.observable("")
        });
        self.Organisers.push(elem);
    }

    self.removeOrganiser = function (index) {
        if (self.Organisers().length > 1) {
            self.Organisers.splice(index, 1);
        }
    }

    //#region routes

    self.addPeriodRoute = function () {
        var elem = new Object({
            SourceFilename: '',
            Distance: '',
            TrainingTypeName: '',
        });
        self.PeriodRoutes.push(ko.observable(elem));
    }

    self.PeriodTrackModel = null;

    self.createUpdateDrawnTrackCallback = function (resultTrack) {
        return function (points, specialPoints, editablePoints, dist, elevationPoints, trainingType) {
            var currentTrackIndex = self.PeriodRouteIndex();
            var currentTrack = self.PeriodRoutes()[currentTrackIndex]();
            currentTrack.Points = resultTrack.Points;
            currentTrack.Distance = resultTrack.Distance;
            currentTrack.Type = trainingType != null ? trainingType.Id : resultTrack.Type;
            currentTrack.TrainingTypeName = trainingType != null ? trainingType.Name : resultTrack.TrainingTypeName;
            currentTrack.SpecialPoints = resultTrack.SpecialPoints;
            currentTrack.SourceFilename = resultTrack.SourceFilename;

            currentTrack.EditablePoints = points;
            currentTrack.ElevationPoints = elevationPoints;
            self.PeriodRoutes()[currentTrackIndex](currentTrack);

            var mapParent = $('#DrawTrackMap');
            var contSelector = ".routeMap";
            var routeMap = $(contSelector, mapParent);

            if (routeMap.length > 0) {
                /*
                var timeout = 500;
                setTimeout(function () {

                    hideMapControls(routeMap[0], true);

                    html2canvas(routeMap, {
                        "proxy": "/Image/LoadExternalImageAsDataURL",
                        background: self.Bg ? self.Bg : '#ffffff',
                        onrendered: function (canvas) {
                            var modeal = $('#DrawTrackModal');
                            hideMapControls(routeMap[0], false);
                            modeal.modal('hide');
                            var dataURL = canvas.toDataURL();
                            currentTrack.DataURL = dataURL;
                            self.PeriodRoutes()[currentTrackIndex](currentTrack);
                        },
                        error: function (data) {
                            var modeal = $('#DrawTrackModal');
                            hideMapControls(routeMap[0], false);
                            modeal.modal('hide');
                        },
                    });
                }, timeout);
                */
                var modeal = $('#DrawTrackModal');
                modeal.modal('hide');
            }
        };
    }

    self.createPeriodTrackModel = function (resultTrack) {
        if (self.PeriodTrackModel == null) {
            self.PeriodTrackModel = new TrackModel(self);
            ko.applyBindings(self.PeriodTrackModel, modeal = $('#DrawTrackModal')[0]);
        }
    }


    self.clearPeriodTrackModel = function () {
        self.PeriodTrackModel = null;
    }

    self.removePeriodRoute = function (index) {
        if (self.PeriodRoutes().length > 0) {
            self.PeriodRoutes.splice(index, 1);

            if (self.PeriodRoutes().length == 0) {
                self.addPeriodRoute();
            }
        }
    }

    self.showPeriodRouteMap = function (index) {
        if (self.PeriodRoutes().length > 0) {
            var currentRoute = self.PeriodRoutes()[index]();
            self.PeriodRouteIndex(index);
            if (currentRoute.Points == null && currentRoute.SpecialPoints == null) {
                self.getAndShowRoutePoints(currentRoute);
            } else {
                self.showPeriodRouteMapEditor(currentRoute);
            }
        }
    }

    self.showPeriodRouteMapEditor = function (currentRoute) {
        if (self.PeriodTrackModel == null) {
            self.createPeriodTrackModel(currentRoute);
        }
        self.PeriodTrackModel.updateDrawnTrackCallback = self.createUpdateDrawnTrackCallback(currentRoute);
        self.PeriodTrackModel.initNew();
        self.PeriodTrackModel.Distance = currentRoute.Distance;

        self.PeriodTrackModel.TypeManager.SelectValue(currentRoute.Type, 0, 0);

        self.showTrackEditor(false, self.PeriodTrackModel, currentRoute.Points, currentRoute.SpecialPoints);
    }

    self.getAndShowRoutePoints = function (periodRoute) {
        var ident = periodRoute.RoutePointsIdentifier;
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetPeriodRoutePoints",
            data: {
                identifier: periodRoute.RoutePointsIdentifier
            }
        })
      .done(function (result) {
          if (result != null) {
              periodRoute.Points = result.Points;
              periodRoute.SpecialPoints = result.SpecialPoints;
              self.showPeriodRouteMapEditor(periodRoute);
          }
      });
    }


    self.periodRoutesToSave = function () {
        var list = [];

        for (var i = 0, len = self.PeriodRoutes().length; i < len; i++) {
            var periodRoute = self.PeriodRoutes()[i]();
            var toSave = new Object({
                Id: periodRoute.Id,
                RoutePointsIdentifier: periodRoute.RoutePointsIdentifier,
                PointsText: periodRoute.Points != null ? JSON.stringify(periodRoute.Points) : null,
                Distance: periodRoute.Distanece,
                DataURL: periodRoute.DataURL != null ? periodRoute.DataURL.replace(/^data[:]image\/(png|jpg|jpeg)[;]base64,/i, "") : null,
                Type: periodRoute.Type,
                SpecialPointsText: periodRoute.SpecialPoints != null ? JSON.stringify(periodRoute.SpecialPoints) : null,
                ElevationPointsText: periodRoute.ElevationPoints != null ? JSON.stringify(periodRoute.ElevationPoints) : null,
                SourceFilename: periodRoute.SourceFilename
            });
            list.push(toSave);
        }


        return list;
    }

    self.showTrackEditor = function (newTrack, CurrentTrack, Points, SpecialPoints) {
        var modeal = $('#DrawTrackModal');

        if ($('#DrawTrackMap', '#DrawTrackMapBody').length == 0) {
            $('#DrawTrackMap').appendTo('#DrawTrackMapBody');
            $("#map-canvas", "#DrawTrackMap").css({ "height": "400px" });

            var resizeButton = $(".resizeContainer", "#DrawTrackMap");
            resizeButton.removeClass('resizeContainer-active');
            jQuery.data(resizeButton[0], "fullscreen", false);
        }

        CurrentTrack.initMap(null, null);
        modeal.modal({
            backdrop: 'static'
        });

        modeal.modal('show');
        CurrentTrack.refreshMap();

        var mapParent = $('#DrawTrackMap');

        MapInitialize(mapParent);

        clearMapPoints(mapParent, true);
        clearAllMarkers(mapParent);
        $("#clear-track", "#DrawTrackModal").css({ "display": "none" });
        $("#track-elevation", "#DrawTrackModal").css({ "display": "inline-block" });
        $("#DrawTrackModalParams", "#DrawTrackModal").css({ "display": "none" });


        addMapPoints(mapParent, Points, null, null, SpecialPoints);

    }

    //#endregion


    //#region plans
    self.AddPlanToCallendar = function () {
        //*
        self.PeriodPlanModel.OpenEdit();
        /*/
        if (!self.ShowPlanOnUserCal() && self.UserStatus() != 'IsIn') {
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/AddPlanToCallendar",
                data: {
                    id: self.Id()
                }
            })
            .done(function (result) {
                self.ShowPlanOnUserCal(true);
            });
        } else {
            self.DeleteUserPlan();
        }
       // */
    }

    self.CanDeleteUserPlan = ko.computed(function () {
        return self.Source() == 'plan' || self.Source() == 'signIn';
    });
    self.DeleteUserPlan = function () {
        if (self.Source() == 'plan' || self.ShowPlanOnUserCal()) {
            self.RemovePlanToCallendar();
        } else if (self.Source() == 'signIn' || self.UserStatus() == 'IsIn') {
            self.UnSign();
        }
    }
    self.RemovePlanToCallendar = function () {
        modalAskModel.Show(translations.RemovePlanToCallendar, translations.RemovePlanToCallendarMsg, function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/RemovePlanToCallendar",
                data: {
                    id: self.PeriodPlanModel.Id(),
                    periodId: self.Id()
                }
            })
            .done(function (result) {
                self.ShowPlanOnUserCal(false);
                userCallendar.coreRefresh();
            });
        },
        translations.Delete
            );
    }
    //#endregion

    self.ToggleCollapse = function (data, event) {
        self.Collapsed(!self.Collapsed());
        if (event.target) {
            periodExplorerDzyndzolekClick($(event.target).closest('.eventExploreContent'));
        }
    }
    self.ToggleCollapseDesc = function (data, event) {
        self.CollapsedTargetDesc(!self.CollapsedTargetDesc());
    }

    if (self.ClubInfo) {
        self.NotInClub = ko.pureComputed(function () {
            return !self.ClubInfo().ClubModel().IsIn() && self.ClubInfo().ClubModel().Status() != "Wants";
        });
        self.SignInTermsText = ko.pureComputed(function () {
            var rulesTxt = '';
            if (self.NotInClub()) {
                if (self.HasDocument()) {
                    rulesTxt = translations.SignForEventRulesAcceptanceInfoWithClub;
                } else {
                    rulesTxt = translations.SignForEventRulesAcceptanceInfoOnlyClub;
                }
            } else {
                if (self.HasDocument()) {
                    rulesTxt = translations.SignForEventRulesAcceptanceInfo;
                }
            }
            rulesTxt = rulesTxt.replace('{0}', (ContextPath + "Document/GetImage?id=" + self.DocumentId()));
            rulesTxt = rulesTxt.replace('{1}', (ContextPath + "Document/GetImage?id=" + self.ClubInfo().ClubModel().RulesFileId));
            return rulesTxt;
        });
    }

    //#region comunication    
    self.OpenComunicationControl = function () {
        GeneralModel.MailControl.SendCallback = function (emails) {
            var emailsAsText = '';
            $(emails).each(function (index, element) {
                emailsAsText = emailsAsText + element + ";";
            });
            // self.MailAdresses(emailsAsText);
            $('#emailsListTextArea').val(emailsAsText);
        };
        GeneralModel.MailControl.GetDataFnc = function (callback) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Participation/GetMailControlData",
                data: {
                    periodId: self.Id()
                }
            })
            .done(function (result) {
                callback(result);
            });
        };
        GeneralModel.MailControl.Show();
    }

    self.MailAttachements = ko.observableArray([]);
    self.ButtonsDisable = ko.observable(false);

    self.MailTitle = ko.observable("");
    self.MailContent = ko.observable("");
    self.MailAdresses = ko.observable("");


    // use: self.SendSimpleMail
    self.coreSendSimpleMail = function () {

        var attachementIds = "";
        $.each(self.MailAttachements(), function (index, attachement) {
            attachementIds += attachement.url + ",";
        });

        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "Comunication/SendSimpleMail",
            data: {
                periodId: self.Id(),
                title: self.MailTitle(),
                content: self.MailContent(),
                emails: self.MailAdresses(),
                attachements: attachementIds
            }
        })
         .done(function (result) {
             $('body').css('cursor', 'default');
             if (result.ok) {
                 ShowEmptyMsg(translations.MsgHasBeenSent, translations.Info);
                 self.MailTitle("");
                 self.MailContent("");
                 self.MailAdresses("");
                 self.MailAttachements.removeAll();
             } else {
                 ShowEmptyMsg(translations.MsgSendError + '<br />' + result.error, translations.Error);
             }
         });

    }
    self.SendSimpleMail = function () {

        if (self.MailTitle() == '') {
            ShowEmptyMsg(translations.TitleIsRequired, translations.Info);
            return;
        }
        if (self.MailContent() == '') {
            ShowEmptyMsg(translations.ContentIsRequired, translations.Info);
            return;
        }
        if (self.MailAdresses().trim() == '') {
            modalAskModel.Show(
                translations.MsgSendWarning,
                translations.MsgSendWarningAll,
                self.coreSendSimpleMail,
                translations.Send);
            return;
        } else {
            self.coreSendSimpleMail();
        }
    }

    self.RestoreMailAdresses = function () {
        self.MailAdresses($('#hiddenMailAdresses').val());
    }
    self.RestoreContextMailAdresses = function () {
        self.MailAdresses($('#hiddenContextMailAdresses').val());
    }
    self.LoadUserEmail = function (userId) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Comunication/UserEmail",
            data: {
                userId: userId
            }
        })
        .done(function (result) {
            self.MailAdresses(result);
        });

    }

    self.parseEmails = function parseEmails(mailAddresses, toLowerCase) {
        if (mailAddresses && mailAddresses.length > 0) {
            var parts = [];
            $.each(mailAddresses.split(/[ ,;\t\r\n]/), function (index, email) {
                if (email.length > 0) {
                    if (toLowerCase) {
                        email = email.toLowerCase();
                    }
                    parts.push(email);

                }
            });
            return parts;
        } else {
            return [];
        }
    }

    self.CleanEmails = function () {
        var parts = self.parseEmails(self.MailAdresses());
        if (parts && parts.length > 0) {
            var validEmails = [];
            var invalidEmails = [];
            $.each(parts, function (index, part) {
                if (part) {
                    var len = part.length;
                    if (len > 0) {
                        var index = part.indexOf("@");
                        if (index > 0 && index < len - 1) {
                            validEmails.push(part);
                        } else {
                            invalidEmails.push(part);
                        }
                    }
                }
            });
            // valid
            validEmails.sort();
            var mailAddresses = '';
            var distincTable = [];
            $.each(validEmails, function (index, email) {
                if ($.inArray(email, distincTable) < 0) {
                    mailAddresses += email + ",";
                    distincTable.push(email);
                }                
            });

            // invalid
            invalidEmails.sort();
            var mailAddressesInvalid = '';
            $.each(invalidEmails, function (index, email) {
                mailAddressesInvalid += email + ", ";
            });
            if (mailAddressesInvalid && mailAddressesInvalid.length > 0) {
                modalAskModel.Show(
                   translations.Warning,
                   translations.InvalidAddressMsg + " " + mailAddressesInvalid,
                   function () {
                       self.MailAdresses(mailAddresses);
                   },
                   translations.Delete);
            } else {
                self.MailAdresses(mailAddresses);
                var modal = $('#emptyMsgModal');
                modal.find('#emptyMsgModalLabel').html(translations.Info);
                modal.find('.askMsg').html(translations.NoInvalidEmails);
                modal.modal("show");
            }
        }
    }

    self.ExcludeEmails = ko.observable("");

    self.ShowFilterEmails = function () {
        loadAndBindTemplate({
            simpleContainer: 'filterModelContainer',
            existingElem: $('#filterModal'),
            ajaxPath: 'Comunication/FilterModal',
            bindData: self,
            callback: function () {
                $('#filterModal').modal('show');
            }
        });
    }
    self.FilterEmails = function () {
        $('#filterModal').modal('hide');

        var parts = self.parseEmails(self.MailAdresses());
        var partsToExclude = self.parseEmails(self.ExcludeEmails(), true);

        var removed = 0;
        if (partsToExclude.length > 0) {
            var mailAddresses = '';
            $.each(parts, function (index, email) {
                // case insensitive
                if ($.inArray(email.toLowerCase(), partsToExclude) < 0) {                    
                    mailAddresses += email + ",";
                } else {
                    removed++;
                }
            });
            self.MailAdresses(mailAddresses);
        }

        var modal = $('#emptyMsgModal');
        modal.find('#emptyMsgModalLabel').html(translations.Info);
        modal.find('.askMsg').html(translations.RemovedEmails + removed);
        modal.modal("show");

        self.ExcludeEmails("");
    }

    //#endregion

    //#region hierarchy
    self.PeriodsLoaded = false;
    self.loadAvailableChildPeriods = function () {
        if (!self.PeriodsLoaded) {
            self.PeriodsLoaded = true;
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/GetAvailableChildPeriods",
                data: { periodId: self.Id() }
            })
                .done(function (serverModel) {
                    if (serverModel) {
                        koArrayCopy(serverModel, self.AvailablePeriods, false, false);/* function (elem) {
                            elem.Selected = ko.pureComputed({
                                read: function () {
                                    return _.contains(self.ChildPeriodIds(), elem.Id);
                                },
                                write: function (value) {
                                    if (value) {
                                        if (!elem.Selected()) {
                                            self.ChildPeriodIds.push(elem.Id);
                                        }
                                    } else {
                                        if (elem.Selected()) {
                                            self.ChildPeriodIds.remove(elem.Id);
                                        }
                                    }
                                }
                            });
                        });*/
                    }
                });
        }
    }
    self.BindPeriodsLoaded = false;
    self.loadAvailableBindPeriods = function () {
        if (!self.BindPeriodsLoaded) {
            self.BindPeriodsLoaded = true;
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/GetAvailableBindPeriods",
                data: { periodId: self.Id() }
            })
                .done(function (serverModel) {
                    if (serverModel) {
                        koArrayCopy(serverModel, self.AvailableBindPeriods, false, false);
                    }
                });
        }
    }
    //self.AvailablePeriods
    self.FilteredAvailableBindPeriods = ko.pureComputed(function () {
        var pattern = self.AvailablePeriodsPattern();
        if (!pattern) return self.AvailableBindPeriods();

        pattern = pattern.toLowerCase();

        return _.filter(self.AvailableBindPeriods(), function (elem) {
            return elem.Name.toLowerCase().indexOf(pattern) >= 0;
        });
    });
    self.SelectedPeriodToBind = ko.observable(null);

    self.OpenBindToCycle = function () {
        $('body').css('cursor', 'wait');
        self.loadAvailableBindPeriods();
        loadAndBindTemplate({
            simpleContainer: 'bindToCycleModalContainer',
            existingElem: '#bindToCycleModal',
            ajaxPath: 'period/BindToCycleModal',
            bindData: null,
            callback: function () {
                $('#bindToCycleModal').modal('show');
                $('body').css('cursor', 'default');
            }
        });
    }

    self.BindToCycle = function () {
        var periodTobind = self.SelectedPeriodToBind();
        if (periodTobind) {
                $.ajax({
                    type: "POST",
                    url: ContextPath + "Period/BindToCycle",
                    data: { periodId: self.Id(), parentId: periodTobind.Id }
                })
               .done(function (result) {
                   if (result) {
                       self.initByModel(result);
                       $('#bindToCycleModal').modal('hide');
                   }
               });
        } else {
            //alert("no period to bind");
        }
    }

    //#endregion

    //#region changeClub

    self.ChangedClubId = ko.observable(0);
    self.ChangeClub = function () {
        loadAndBindTemplate({
            simpleContainer: 'changeClubModalContainer',
            existingElem: '#changeClubModal',
            ajaxPath: 'period/ChangeClubModal',
            bindData: null,
            callback: function () {
                $('#changeClubModal').modal('show');
            }
        });
    }
    self.coreChangeClub = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/ChangeClub",
            data: { periodId: self.Id(), clubId: self.ChangedClubId() }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                $('#changeClubModal').modal('hide');
                alert('Klub został zmieniony');
            } else {
                alert(result);
            }
        });
    }

    //#endregion

    self.GoToUserEdit = function () {        
        window.location = ContextPath + "Period/UserContestPage?periodId=" + self.Id();
    }
}

function PeriodsModel() {

    var self = this;

    self.AvailableCoaches = ko.observableArray([]);

    self.TypeManager = new TrainingTypeControl({
        mainId: 0,
        subId: 0,
        typeChangeCallback: null,
        clubId: null,
        multi: false,
        baseCtrl: null,
        disciplines: true,
        multiSubs: true,
        allInstedNone: true
    });

    self.SelectdPeriod = ko.observable(new PeriodModel(self));

    self.Groups = ko.observableArray();

    self.clearPeriod = function () {
        var em = new PeriodModel(self);
        self.SelectdPeriod(em);
    };

    self.initNewPeriod = function () {

        var em = new PeriodModel(self);
        self.SelectdPeriod(em);
        $('#periodModal').modal('show');
        periodInintUpload();
    }

    self.editPeriod = function (id) {
        //self.LoadCoaches();
        self.SelectdPeriod(new PeriodModel(self));
        self.SelectdPeriod().initById(id);
        $('#periodModal').modal('show');
        periodInintUpload();
    }

    self.SelectPeriod = function (id) {
        if (!self.SelectdPeriod() || self.SelectdPeriod().Id() != id) {
            self.LoadCoaches();
            self.SelectdPeriod(new PeriodModel(self));
            self.SelectdPeriod().initById(id);
        }
    }

    self.ReloadGroupsList = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetGroupsList"
        })
        .done(function (result) {
            for (var i = 0, len = result.length; i < len; i++) {
                self.Groups.push(result[i]);
            }
        });
    }

    self.PeriodsExplore = new PeriodsExploreModel(self);

    self.coachesLoaded = false;
    self.LoadCoaches = function () {
        if (!self.coachesLoaded) {
            self.coachesLoaded = true;
            $.ajax({
                type: "POST",
                url: ContextPath + "Coaches/GetRealCoachesSimple"
            })
            .done(function (serverModel) {
                self.AvailableCoaches.removeAll();
                for (var i = 0; i < serverModel.length; i++) {
                    var c = serverModel[i];
                    c.Text = c.FirstName + ' ' + c.LastName + ' ' + c.Nick;
                    self.AvailableCoaches.push(c);
                }
            });
        }
    }
}


function editPeriod(elem) {

    var tr = $(elem).parents('tr');
    var id = tr.data("id");

    periodsModel.editPeriod(id);
}

var periodsModel = new PeriodsModel();
GeneralModel.addProperty("PeriodsModel", periodsModel);



function periodInintUpload() {
    $('#fileupload').fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            if (data.result[0].url) {
                periodsModel.SelectdPeriod().DocumentIdTransAble.currentValue(data.result[0].url);
            }
            $('#fileupload .progress .bar').css(
                'width',
                0 + '%'
            );
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            $('#fileupload .progress .bar').css(
                'width',
                progress + '%'
            );
        }
    });

    $('#logo_fileupload').fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            if (data.result[0].url) periodsModel.SelectdPeriod().LogoId(data.result[0].url);
            $('#logo_fileupload .progress .bar').css(
                'width',
                0 + '%'
            );
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            $('#logo_fileupload .progress .bar').css(
                'width',
                progress + '%'
            );
        }
    });
    generalUploadInit('#partnerslogo_fileupload', periodsModel.SelectdPeriod().PartnersLogosFileId);
    generalUploadInit('#sponsor_fileupload', periodsModel.SelectdPeriod().SponsorLogofileId);

    $("#eventFileupload").fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            if (data.result[0].url) {
                var selP = periodsModel.SelectdPeriod();
                selP.FileIds()[selP.FileIndex()](data.result[0].url);
            }
        }
    });


    generalUploadInit('#join_fileupload', periodsModel.SelectdPeriod().JoinInfoImgFileId);


    var elemId = '#periodTrack_fileupload';
    var progresElem = $(elemId + ' .progress .bar');
    $(elemId).fileupload({
        dataType: 'json',
        add: function (e, data) {
            data.submit();
        },
        done: function (e, data) {
            var resultTrack = data.result;
            if (resultTrack != null) {
                var selP = periodsModel.SelectdPeriod();
                var currentTrack = selP.PeriodRoutes()[selP.PeriodRouteIndex()]();
                currentTrack.Points = resultTrack.Points;
                currentTrack.Distance = resultTrack.Distance;
                currentTrack.Type = resultTrack.Type;
                currentTrack.SpecialPoints = resultTrack.SpecialPoints;
                currentTrack.SourceFilename = resultTrack.SourceFilename;
                currentTrack.TrainingTypeName = resultTrack.TrainingTypeName;

                if (selP.PeriodTrackModel == null) {
                    selP.createPeriodTrackModel(resultTrack);
                }

                selP.PeriodTrackModel.updateDrawnTrackCallback = selP.createUpdateDrawnTrackCallback(resultTrack);

                selP.PeriodTrackModel.initNew();
                selP.PeriodTrackModel.Distance = resultTrack.Distance;

                selP.PeriodTrackModel.TypeManager.SelectValue(resultTrack.Type, 0, 0);

                selP.showTrackEditor(false, selP.PeriodTrackModel, resultTrack.Points, resultTrack.SpecialPoints);
            }
        }
    });
    if (periodsModel != null && periodsModel.SelectdPeriod() != null) {
        periodsModel.SelectdPeriod().clearPeriodTrackModel();
    }
}



function PeriodsExploreModel(parent) {
    var self = this;
    self.Parent = parent;

    self.OnlyClub = false;

    self.initState = true;
    self.setActualPage = true;

    self.MapExplorerControl = null;

    self.TypeManager =
    new TrainingTypeControl({
        mainId: null,
        subId: null,
        typeChangeCallback: null,
        clubId: null,
        multi: false,
        baseCtrl: null,
        disciplines: true,
        multiSubs: false,
        allInstedNone: true,
        dummy: null,
        selectNoneIfNoChoise: true,
        addNoneSubType: true
    });
    self.DiscId = 0;
    self.CatId = 0;

    self.TypeManagerSelector =
    new TrainingTypeControl({
        mainId: null,
        subId: null,
        typeChangeCallback: null,
        clubId: null,
        multi: false,
        baseCtrl: null,
        disciplines: false,
        multiSubs: false,
        allInstedNone: true,
        dummy: null,
        selectNoneIfNoChoise: true,
        addNoneSubType: true
    });
    self.TrainingTypeId = 0;
    self.TrainingSubTypeId = 0;

    self.LimitDisciplines = function () {
        var type = self.TypeManagerSelector.SelectedType();

        if (!type || !type.Id || !type.ConectedDisciplines) {
            self.TypeManager.AvailableIds = null;
            self.Search();
        } else {
            if (!self.TypeManager.AvailableIds) {
                self.TypeManager.AvailableIds = ko.observableArray([]);
            }
            self.TypeManager.AvailableIds.removeAll();
            var currDiscId = self.TypeManager.SelectedTypeId();
            var resetDisc = true;
            _.each(type.ConectedDisciplines, function (dId) {
                self.TypeManager.AvailableIds.push(dId);
                if (dId == currDiscId) resetDisc = false;
            });
            if (!currDiscId) {
                self.Search();
            } else if (resetDisc) {
                self.TypeManager.SelectValue(0, 0, 0, null, true);
            }

        }
    }
    self.TypeManagerSelector.SetChangeCallback(self.LimitDisciplines);

    self.ClubId = ko.observable(0);
    self.EntriesPerPage = ko.observable(0);
    self.PageNumber = ko.observable(0);
    self.TotalResuts = ko.observable(0);
    self.PeriodType = ko.observable('');
    self.SearchPattern = ko.observable('');
    self.DisciplineIds = ko.observableArray([]);
    self.List = ko.observableArray([]);
    self.PeriodTypes = ko.observableArray([]);

    self.NextDate = ko.observable('');
    self.PreviousDate = ko.observable('');
    self.ParamStart = ko.observable('');
    self.CurrentDate = ko.observable('');
    self.LengthType = ko.observable(0);

    //self.ToolbarStartDate = ko.observable('');
    //self.ToolbarEndDate = ko.observable('');

    self.ShowMap = ko.observable(false);
    self.SelectedPeriod = ko.observable(null);

    self.ProgramType = ko.observable('');
    self.ProgramLevel = ko.observable('');
    self.ProgramLength = ko.observable('');

    self.NoResults = ko.pureComputed(function () {
        return self.TotalResuts() == 0;
    });

    self.ShowPagingPages = ko.pureComputed(function () {
        return self.TotalResuts() > self.EntriesPerPage();
    });
    self.ShowPaging = ko.pureComputed(function () {
        return self.TotalResuts() > 10;
    });

    self.ShowCategories = ko.pureComputed(function () {
        return self.TypeManager.ShowCategories();
    });

    self.NrOfPages = ko.pureComputed(function () {
        var epp = self.EntriesPerPage();
        if (epp == 0) epp = 1;
        return Math.ceil(1.0 * self.TotalResuts() / epp);
    });

    self.Pages = ko.pureComputed(function () {
        var nrOfPages = self.NrOfPages();
        var currentPage = self.PageNumber();
        var startPage = currentPage - 2;
        if (startPage < 0) startPage = 0;
        var endPage = startPage + 5;
        if (endPage > (nrOfPages - 1)) {
            endPage = nrOfPages - 1;
            startPage = endPage - 5;
        }

        var result = [];
        var dots = false;
        for (var i = 0; i < nrOfPages; i++) {
            if (i == 0 || (i == nrOfPages - 1) || (i >= startPage && i <= endPage)) {
                result.push(i + 1);
                dots = false;
            } else {
                if (!dots) {
                    result.push("...");
                    dots = true;
                }
            }
        }
        return result;
    });


    self.CalendarDate = ko.observable();
    self.CalendarDate.subscribe(function (newValue) {
        if (!self.initState) {
            var date = self.CalendarDate();
            self.ParamStart(date);
            self.resetPageNumber();

            self.Search();
        }
    });
    self.clickDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.yearBox').find('.datepicker');
            if (dp) {
                dp.datepicker('show');
            }
        }
    };
    self.RegisterDataPicker = function () {

        var dp = $('.periodsExplorer .yearBox .datepicker');
        if (dp) {
            dp.datepicker().on('show', function (ev) {
                var mode = 0;
                var newValue = self.LengthType();
                if (newValue == 30) {
                    mode = 1
                }
                else if (newValue == 365) {
                    mode = 2
                }
                if (mode - 1) {
                    dp.datepicker('showMode', mode - 1);
                }
            });
        }
    }


    self.init = function (periodType, lengthType) {

        var discIds = [];
        var priDisc = null;
        if (!self.OnlyClub) {
            priDisc = CurrentUser.PrimaryDisciplineId();
            if (priDisc) {
                discIds.push(priDisc)
            }
        }
        var data = {
            DisciplineIds: discIds,
            onlyClub: self.OnlyClub,
            lengthType: lengthType || self.OnlyClub ? 365 : 30
        };
        if (periodType) {
            data.periodType = periodType;
        }

        var strData = JSON.stringify(data);

        if (periodType == 'Program') {
            self.SetProgramLevels();
        }

        piorityBusy.Increase();
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetExplorePeriodsModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
            .done(function (result) {
                self.initByModel(result);
                self.RegisterDataPicker();
                if (priDisc) {
                    self.TypeManager.SelectTypeById(priDisc);
                    self.DiscId = priDisc;
                }
                piorityBusy.Decrease();
            });
    };


    self.initSingle = function (periodId, callJoin) {

        piorityBusy.Increase();
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetSinglePeriodsModel",
            data: {
                periodId: periodId
            }
        })
            .done(function (result) {
                if (result) {
                    self.SelectedPeriod(new PeriodModel(self, true));
                    var sp = self.SelectedPeriod();
                    sp.initByModel(result);
                    sp.SinglePeriodExplore(true);
                    sp.Collapsed(false);
                    piorityBusy.Decrease();
                    if (callJoin && sp.CanJoin() && sp.IsSignUps()) {
                        sp.openSignIn();
                    }

                    var targets = $('.bigTargetContainer .simpleFoot .dzyndzolek');
                    if (targets.length == 1) {
                        if ($(targets[0]).hasClass('collapsed')) {
                            $(targets[0]).click();
                        }
                    }
                }
            });
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.initState = true;

            self.ClubId(serverModel.ClubId);
            self.EntriesPerPage(serverModel.EntriesPerPage);
            self.PageNumber(serverModel.PageNumber);
            self.TotalResuts(serverModel.TotalResuts);

            self.NextDate(serverModel.NextDate);
            self.PreviousDate(serverModel.PreviousDate);
            self.ParamStart(serverModel.ParamStart);
            self.CurrentDate(serverModel.CurrentDate);
            self.LengthType(serverModel.LengthType);

            self.PeriodType(serverModel.PeriodType);

            self.CalendarDate(self.ParamStart());

            self.TypeManager.initState = true;
            self.DiscId = 0;
            self.CatId = 0;

            if (serverModel.DisciplineIds != null && serverModel.DisciplineIds.length > 0) {
                self.TypeManager.DeselectAll();
                for (var i = 0, len = serverModel.DisciplineIds.length; i < len; i++) {
                    self.TypeManager.SelectTypeById(serverModel.DisciplineIds[i]);
                }
                self.DiscId = serverModel.DisciplineIds[0];
            }

            if (serverModel.CategoryIds != null && serverModel.CategoryIds.length > 0) {
                for (var i = 0, len = serverModel.CategoryIds.length; i < len; i++) {
                    self.TypeManager.SelectSubTypeById(serverModel.CategoryIds[i], self.TypeManager.SelectedTypeId());
                }
                self.CatId = serverModel.CategoryIds[0];
            }

            self.TypeManager.initState = false;


            self.List.removeAll();
            if (serverModel.List) {
                for (var i = 0, len = serverModel.List.length; i < len; i++) {
                    var elem = new PeriodModel(self, true);
                    elem.initByModel(serverModel.List[i]);
                    self.List.push(elem);
                }
            }

            if (self.MapExplorerControl && serverModel.MarkersList) {
                var markers = [];
                for (var i = 0, len = serverModel.MarkersList.length; i < len; i++) {
                    var data = serverModel.MarkersList[i];
                    if (data.LocationModel) {
                        var elem = self.CreatePeriodMarker(data);
                        markers.push(elem);
                    }
                }
                self.MapExplorerControl.ClearMarkers();
                self.MapExplorerControl.AddMarkers(markers);
            }

            koArrayCopy(serverModel.PeriodTypes, self.PeriodTypes, false, false);

            self.initState = false;
            self.setActualPage = false;
        }
    };

    self.CreatePeriodMarker = function (markerData) {
        var marker = new google.maps.Marker({
            map: self.MapExplorerControl.Map,
            position: new google.maps.LatLng(markerData.LocationModel.Latitude, markerData.LocationModel.Longitude),
            label: ('' + markerData.Ids.length),
            count: markerData.Ids.length
        });

        google.maps.event.addListener(marker, 'click', function () {

            self.MapExplorerControl.ShowInfoWindow(markerData.Name, marker, function () {
                self.SelectedPeriod(null);
            });
            self.LoadSelectedPeriods(markerData.Ids);
        });

        return marker;
    }

    self.LoadSelectedPeriod = function (id) {
        if (self.SelectedPeriod() == null || self.SelectedPeriod().Id != id) {
            self.SelectedPeriod(null);
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/GetPeriodInfoModel",
                data: {
                    id: id
                }
            })
               .done(function (result) {
                   var elem = new PeriodModel(self, true);
                   elem.initByModel(result);

                   self.SelectedPeriod(elem);
               });
        }
    }
    self.LoadSelectedPeriods = function (ids) {
        var strData = JSON.stringify(ids);
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetPeriodsInfoModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
           .done(function (serverModel) {
               self.List.removeAll();
               if (serverModel.List) {
                   for (var i = 0, len = serverModel.List.length; i < len; i++) {
                       var elem = new PeriodModel(self, true);
                       elem.initByModel(serverModel.List[i]);
                       self.List.push(elem);
                   }
               }
           });

    }

    self.Serialise = function () {
        var discIds = [];
        var catIds = [];
        var tTypesIds = [];

        //zmiana z możliwości wyboru wielu dyscyplin na wybór jednej dyscpliny
        //var types = self.TypeManager.SelectedTypes();
        //for (var i = 0, len = types.length; i < len; i++) {
        //    discIds.push(types[i].Id);
        //}

        var st = self.TypeManager.SelectedType();
        if (st && st.Id) {
            discIds.push(st.Id);
            var sc = self.TypeManager.SelectedSubType();
            if (sc && sc.Id) catIds.push(sc.Id);
        }
        var lst = self.TypeManagerSelector.SelectedType();
        if (lst && lst.Id) {
            tTypesIds.push(lst.Id);
        }


        //if (self.TypeManager.SelectedType() && self.TypeManager.SelectedType().Id) {
        //    discIds.push(self.TypeManager.SelectedType().Id);
        //    /*
        //    var subTypes = self.TypeManager.SelectedSubTypes();
        //    for (var i = 0, len = subTypes.length; i < len; i++) {
        //        if (subTypes[i].Id != 0) {
        //            catIds.push(subTypes[i].Id);
        //        }
        //    }*/
        //    var cI = self.TypeManager.SelectedSubTypeId();
        //    if (cI)                catIds.push(cI);
        //}

        var data = {
            ClubId: self.ClubId(),
            EntriesPerPage: self.EntriesPerPage(),
            PageNumber: self.PageNumber(),
            SearchPattern: self.SearchPattern(),
            periodType: self.PeriodType(),
            DisciplineIds: discIds,
            CategoryIds: catIds,
            TrainingTypeIds: tTypesIds,
            //NextDate     : self.NextDate     (),
            //PreviousDate : self.PreviousDate (),
            startDate: self.ParamStart(),
            endDate: null,
            LengthType: self.LengthType(),
            setActualPage: self.setActualPage,
            onlyClub: self.OnlyClub,
            forMap: self.ShowMap(),
            programType: self.ProgramType(),
            programLevel: self.ProgramLevel(),
            programLength: self.ProgramLength(),
        };

        return JSON.stringify(data);
    }

    self.Search = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetExplorePeriodsModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: self.Serialise()
        })
           .done(function (result) {
               self.initByModel(result);
               $('body').css('cursor', 'default');
           });
    }

    self.SearchBtnClick = function () {
        self.setActualPage = true;
        self.Search();
    }

    self.TypeManager.SetChangeCallback(function () {
        var st = self.TypeManager.SelectedType();
        var discId = st ? st.Id : 0;
        var sc = self.TypeManager.SelectedSubType();
        var catId = sc ? sc.Id : 0;
        if (!self.initState && (self.DiscId != discId || self.CatId != catId)) {
            self.resetPageNumber();

            self.DiscId = discId;
            self.CatId = catId;

            self.Search();
        }
    });

    self.resetPageNumber = function () {
        self.initState = true;
        self.PageNumber(0);
        self.initState = false;
        self.setActualPage = true;
    }

    self.subscribeReload = function (newValue) {
        if (!self.initState && newValue) {
            self.resetPageNumber();
            self.Search();
        }
    };
    self.subscribeReloadAllowFalse = function (newValue) {
        if (!self.initState) {
            self.resetPageNumber();
            self.Search();
        }
    };

    self.SearchPattern.subscribe(function (newValue) {
        if (!self.initState && ((newValue && newValue.length >= 3) || newValue == '')) {
            self.resetPageNumber();
            self.Search();
        }
    });
    self.EntriesPerPage.subscribe(self.subscribeReload);
    self.PeriodType.subscribe(self.subscribeReloadAllowFalse);

    self.GoToPage = function (page) {
        if (page != "...") {
            page = parseInt(page) - 1;
            self.PageNumber(page);
            self.Search();
            $.scrollTo($(".calHeader"), {
                duration: 1000, easing: 'swing'
            });
        }
    }
    self.GoToNextPage = function (page) {
        self.PageNumber(self.PageNumber() + 1);
        self.Search();
        $.scrollTo($(".calHeader"), {
            duration: 1000, easing: 'swing'
        });
    }
    self.GoToPreviousPage = function (page) {
        self.PageNumber(self.PageNumber() - 1);
        self.Search();
        $.scrollTo($(".calHeader"), {
            duration: 1000, easing: 'swing'
        });
    }

    self.GoToNextDate = function () {
        self.ParamStart(self.NextDate());
        self.resetPageNumber();
        self.Search();
    }
    self.GoToPreviousDate = function () {
        self.ParamStart(self.PreviousDate());
        self.resetPageNumber();
        self.Search();
    }
    self.LengthType.subscribe(self.subscribeReload);
    /*
        self.getCallendarIcon = function (length) {
            var ico = translations.CallendarDayIcon;
            switch (length) {
                case 1: ico = translations.CallendarDayIcon; break;
                case 7: ico = translations.CallendarWeekIcon; break;
                case 30: ico = translations.CallendarMonthIcon; break;
                case 365: ico = translations.CallendarYearIcon; break;
                case -1: ico = translations.CallendarPeriodIcon; break;
            }
            return ContextPath + 'Content/images/icons/' + ico + (self.LengthType() == length ? 'cze' : '') + '.png';
        }
        
        self.DayIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(1); });
        self.WeekIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(7); });
        self.MonthIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(30); });
        self.YearIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(365); });
        self.ListIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(-1); });
        self.CurrentIconSrc = ko.pureComputed(function () { return self.getCallendarIcon(self.LengthType()); });
    */
    self.PeriodLengthLabel = ko.pureComputed(function () {
        switch (self.LengthType()) {
            case 1: return translations.Day; break;
            case 7: return translations.Week; break;
            case 30: return translations.Month; break;
            case 365: return translations.Year; break;
            case -1: return translations.Period; break;
        }
    });

    self.PeriodTypeLabel = ko.pureComputed(function () {
        switch (self.PeriodType()) {
            case 'Contest': return translations.Contests; break;
            case 'Competitions': return translations.CompetitionsChalenges; break;
            case 'Program': return translations.Programs; break;
            case 'Schools': return translations.Schools; break;
            case 'League': return translations.League; break;
            case 'Action': return translations.Actions; break;
            case 'Group': return translations.Group; break;
            case 'Other': return translations.OtherPeriodTypes; break;
            case '': return translations.All; break;
        }
        return translations.All;
    });

    self.getPeriodTypeIcon = function (type) {
        var ico = 'allPeriodTypes';
        switch (type) {
            case 'Contest': ico = 'zawody'; break;
            case 'Competitions': ico = 'wyniki'; break;
            case 'Program': ico = 'programy'; break;
            case 'Schools': ico = 'schools'; break;
            case 'League': ico = 'league'; break;
            case 'Action': ico = 'action'; break;
            case 'Group': ico = 'group'; break;
            case 'Other': ico = 'otherPeriodTypes'; break;
        }
        return ico + '_39';// + (self.PeriodType() == type ? ' selected' : '');
    }

    self.displayPeriodType = function (type) {
        var _periodTypes = _(self.PeriodTypes());

        return _periodTypes.contains(type);
    }

    self.CurrentTypeIconClass = ko.computed(function () {
        return self.getPeriodTypeIcon(self.PeriodType());
    });

    self.ProgramType.subscribe(self.subscribeReloadAllowFalse);
    self.ProgramLevel.subscribe(self.subscribeReloadAllowFalse);
    self.ProgramLength.subscribe(self.subscribeReloadAllowFalse);

    self.SelectedProgramType = ko.pureComputed(function () {
        switch (self.ProgramType()) {
            case "": return translations.ProgramTypes_All; break;
            case "self": return translations.ProgramTypes_self; break;
            case "individ": return translations.ProgramTypes_individual; break;
            case "group": return translations.ProgramTypes_group; break;
        }
        return translations.ProgramTypes_All;
    });
    self.SelectedProgramLevelLabel = ko.pureComputed(function () {
        var val = self.ProgramLevel();

        if (!val || val == '') return translations.AllProgramLevels;

        var sel = _.findWhere(self.AvailableProgramLevels(), {
            value: val
        });
        if (sel) return sel.text;

        return translations.AllProgramLevels;
    });
    self.AvailableProgramLevels = ko.observableArray([]);
    self.SetProgramLevels = function () {
        var profile = TrainingPlanLib.LevelGroups;
        koArrayCopy(profile, self.AvailableProgramLevels);
    }

    self.ToggleMap = function () {
        var initMap = !self.ShowMap();
        self.ShowMap(!self.ShowMap());

        if (initMap) {
            if (!self.MapExplorerControl) {
                var container = $('.exploreMap')[0];
                setTimeout(function () {
                    var mcOptions = {
                        gridSize: 50, maxZoom: 15
                    };
                    self.MapExplorerControl = new MapExplorerControl(container, self.Search, mcOptions);
                    self.MapExplorerControl.initMap();
                }, 100);
            } else {
                self.MapExplorerControl.initMap();
            }
        } else {
            self.resetPageNumber();
            self.Search();
        }
    }
}

function periodExplorerDzyndzolekClick(eventExploreContent) {
    var signInCollapsed = eventExploreContent.find('.signInCollapsed');
    var fluidDashedSeparator = eventExploreContent.find('.fluidDashedSeparator');
    var eventExpandedContentLeft = eventExploreContent.find('.eventExpandedContentLeft');
    var dateBox = eventExploreContent.find('.dateBox');
    var tabContainer = eventExploreContent.find('#tabContainer');
    var dzyndzolekCollapsed = eventExploreContent.find('.dzyndzolek.collapsed');
    var scrollTopPosition = eventExploreContent.find('.scrollTopPosition');
    var top = window.pageYOffset || document.documentElement.scrollTop
    var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

    var height = $(eventExpandedContentLeft).height() + 39;

    if (dzyndzolekCollapsed.length == 0) {
        $(signInCollapsed).addClass('displayNone');
        if (is_firefox) {
            $.scrollTo(scrollTopPosition[0].innerText, 0, {
                duration: 1000, easing: 'swing'
            });
        }
        else {
            $.scrollTo(scrollTopPosition[0].innerHTML, 0, {
                duration: 1000, easing: 'swing'
            });
        }
        //        $("html, body").animate({scrollTop:scrollTopPosition[0].innerHTML},"slow");            
        //         $.scrollTo(scrollTopPosition[0].innerHTML, 0, { duration: 1000, easing: 'swing' });
        //window.scrollTo(0, scrollTopPosition[0].innerHTML);
        //         $(tabContainer).css('padding-top', '0px');                
    } else {
        $(signInCollapsed).removeClass('displayNone');
        scrollTopPosition[0].innerText = top;
        //         $(tabContainer).css('padding-top', '25px');  

    }

    var fluidDashedSeparatorHeight = $(fluidDashedSeparator).height();

    if (fluidDashedSeparatorHeight == 160) {
        //$(dateBox).css('border-left', 'none');        
        $(fluidDashedSeparator).css('height', height + 'px');
    } else {
        //$(dateBox).css('border-left', '2px dashed #E24631');        
        $(fluidDashedSeparator).css('height', '160px');
    }

    var targets = eventExploreContent.find('.bigTargetContainer .simpleFoot .dzyndzolek');
    if (targets.length == 1) {
        if ($(targets[0]).hasClass('collapsed')) {
            $(targets[0]).click();
        }
    }
}

function periodExplorerResizeInit(eventExploreContent) {

    /*var logger = document.id('eventExpandedContentLeft');
    var box = document.id('example-3-box');
    document.id('startStop3').addEvent('click', function(){
      if (box.hasClass('example-3-box-start')) {
        box.removeClass('example-3-box-start');
      } else {
        box.addClass('example-3-box-start');
      }
    });
    new ResizeSensor(box, function(el){
      logger.set('html', 'Changed to ' + box.getSize().x+'px width.');
    });*/

    var accordionBody = $('.accordion-group');
    var fluidDashedSeparator = $('.fluidDashedSeparator');
    var eventExpandedContentLeft = $('.eventExpandedContentLeft');
    //document.id('startStop3').addEvent('click', function () {
    //    if (box.hasClass('example-3-box-start')) {
    //        box.removeClass('example-3-box-start');
    //    } else {
    //        box.addClass('example-3-box-start');
    //    }
    //});
    new ResizeSensor(accordionBody, function (el) {
        $(fluidDashedSeparator).css('height', eventExpandedContentLeft.height() + 'px');
    });
}

function uploadBtnClick(fileIndex) {
    periodsModel.SelectdPeriod().FileIndex(fileIndex);
    $('#eventFileupload input').click();
}

function uploadTrackBtnClick(fileIndex) {
    periodsModel.SelectdPeriod().PeriodRouteIndex(fileIndex);
    $('#periodTrack_fileupload input').click();
}



function removeFile(id) {
    if (periodsModel.SelectdPeriod().FileIds().length > 1) {
        removeKoItem(periodsModel.SelectdPeriod().FileIds, id);
    }
    else {
        periodsModel.SelectdPeriod().FileIds()[0](0);
    }
}

function addFile() {
    periodsModel.SelectdPeriod().FileIds.push(ko.observable(0));
}

function adjustEventsNameFont() {
    $(".eventExploreContent .name .nameCore").fitText({
        minFontSize: '10px', maxFontSize: '24px', padding: '40'
    });
    $(".eventExploreContent .periodCategoryLabel").fitText({
        minFontSize: '10px', maxFontSize: '16px', padding: '60'
    });
}


function PeriodsAdminNavigator() {
    var self = this;

    self.initiated = false;

    self.GoToContests = function () {
        self.Scroll('#contestsPreiodSection');
    }
    self.GoToCompetitions = function () {
        self.Scroll('#competitionsPreiodSection');
    }
    self.GoToPrograms = function () {
        self.Scroll('#programsPreiodSection');
    }
    self.GoToSchools = function () {
        self.Scroll('#schoolsPreiodSection');
    }
    self.GoToActions = function () {
        self.Scroll('#actionsPreiodSection');
    }
    self.GoToLigue = function () {
        self.Scroll('#liguePreiodSection');
    }
    self.GoToGroups = function () {
        self.Scroll('#groupsPreiodSection');
    }
    self.GoToOther = function () {
        self.Scroll('#otherPreiodSection');
    }

    self.Scroll = function (selector) {
        $.scrollTo($(selector), {
            duration: 600, easing: 'swing'
        });
    }

    self.SelectedYear = ko.observable((new Date()).getFullYear());
    self.SelectedYear.subscribe(function (newValue) {
        if (newValue && self.initiated) {
            $('body').css('cursor', 'wait');
            $.ajax({
                type: "POST",
                url: ContextPath + "Period/GetAdminList",
                data: {
                    year: newValue
                }
            })
               .done(function (result) {
                   $('#PeriodsAdminTable_container').html(result);
                   $('body').css('cursor', 'default');
               });
        }
    });


    self.initNaviBelt = function () {
        self.initiated = true;
        var naviBelt = $('#naviBelt');
        if (naviBelt.length > 0) {
            var width = 0;
            var top = naviBelt.offset().top - parseFloat(naviBelt.css('marginTop').replace(/auto/, 0));

            $(window).scroll(function (event) {
                // what the y position of the scroll is
                var y = $(this).scrollTop();

                var fixed = naviBelt.data("fixed");

                // whether that's below the form
                if (y >= top) {
                    if (!fixed) {
                        width = naviBelt.width();
                        naviBelt.data("fixed", true);
                        // if so, ad the fixed class
                        naviBelt.addClass('fixed');
                        naviBelt.width(width);

                    }
                } else {
                    // otherwise remove it
                    if (fixed) {
                        naviBelt.data("fixed", false);
                        naviBelt.removeClass('fixed');
                        naviBelt.width('auto');
                    }
                }
            });
        }
    }
}


function PeriodUserPlan(parent) {
    var self = this;
    self.Parent = parent;

    //self.AddedToList = true;

    self.Disciplines = ko.observableArray([]);
    self.RegistrationStart = ko.observable(null);
    self.Distance = ko.observable(0);
    self.Ended = ko.observable(false);
    self.PeriodPlan = ko.observable(null);

    self.initByModel = function (serverModel) {

        if (serverModel.Disciplines) {
            for (var i = 0, len = serverModel.Disciplines.length; i < len; i++) {
                var elem = new DisciplineModel(self);
                elem.initByModel(serverModel.Disciplines[i]);
                self.Disciplines.push(elem);
            }
        }

        var period;
        if (serverModel.PeriodPlan.PeriodModel) {
            period = new PeriodsModel().SelectdPeriod();
        } else {
            period = new PeriodPlanModelPeriodSubstitute();
        }
        var periodPlanModel = new PeriodPlanModel(period);
        periodPlanModel.initByModel(serverModel.PeriodPlan);
        periodPlanModel.DeleteFdbk = self.DeleteFdbk;
        self.PeriodPlan(periodPlanModel);
        self.RegistrationStart(serverModel.RegistrationStart);
        self.Distance(serverModel.Distance);
        self.Ended(serverModel.Ended);
    };
    self.initNew = function () {
        self.PeriodPlan().DeleteFdbk = self.DeleteFdbk;
    }

    self.DeleteFdbk = function () {
        self.Parent.periodUserPlans.remove(self);
    }

    self.LogoFileSrc = ko.computed(function () {
        if (self.PeriodPlan() != null) {
            return '../Image/GetImage?width=25&height=25&id=' + self.PeriodPlan().PeriodModel().LogoId();
        } else {
            return '';
        }
    }, self);

    self.HasDistance = ko.computed(function () {
        return typeof self.Distance() === 'string' && self.Distance().length > 0;
    }, self);

    self.HasLogoFileId = ko.computed(function () {
        if (self.PeriodPlan() != null && self.PeriodPlan().PeriodModel() != null) {
            return self.PeriodPlan().PeriodModel().LogoId() > 0;
        } else {
            return false;
        }
    }, self);

    self.ExpandRowVisible = ko.computed(function () {
        if (self.PeriodPlan() != null) {
            return self.HasLogoFileId() || self.HasDistance() || self.PeriodPlan().HasTargetDescription() || self.PeriodPlan().HasPlace()
        } else {
            return false;
        }
    }, self);

    self.OpenEdit = function (saveCallBack) {
        self.PeriodPlan().OpenEdit(saveCallBack);
    };

    self.CompetitionLink = ko.computed(function () {
        if (self.PeriodPlan() != null && self.PeriodPlan().PeriodModel() != null) {
            return "/Contest/UserAdd?competitionsResultId=" + self.PeriodPlan().PeriodModel().CompetitionAddedId();
        } else {
            return "#";
        }
    }, self);

    self.TitleMode = ko.computed(function () {
        if (!self.Ended()) {
            return 'before';
        } else {
            if (self.PeriodPlan() != null && self.PeriodPlan().PeriodModel().CompetitionAdded()) {
                return 'completed';
            } else {
                return 'uncompleted';
            }
        }
    }, self);

    self.HasResult = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (periodPlan.PeriodModel().CompetitionAdded()) {
                var competitionResult = periodPlan.PeriodModel().CompetitionResult();
                return typeof competitionResult == 'string' && competitionResult.length > 0;
            } else {
                return periodPlan.HasResult();
            }
        }
    }, self);

    self.Result = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (periodPlan.PeriodModel().CompetitionAdded()) {
                return periodPlan.PeriodModel().CompetitionResult();
            } else {
                return periodPlan.Result();
            }
        }
        return '';
    }, self);

    self.HasOpenPlace = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (periodPlan.PeriodModel().CompetitionAdded()) {
                return periodPlan.PeriodModel().CompetitionOpenPlace() > 0;
            } else {
                return periodPlan.HasPlace();
            }
        }
    }, self);

    self.OpenPlace = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (periodPlan.PeriodModel().CompetitionAdded()) {
                return periodPlan.PeriodModel().CompetitionOpenPlace();
            } else {
                return periodPlan.Place();
            }
        }
        return '';
    }, self);

    self.TimeIcon = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null
            && periodPlan.PeriodModel() != null
            && periodPlan.PeriodModel().CompetitionAdded()) {
            return ContextPath + 'Content/images/icons/czas_zrealiz_33.png';
        } else {
            return ContextPath + 'Content/images/icons/czas_plan_33.png';
        }
        return '';
    }, self);

    self.TimeIconTitle = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null
            && periodPlan.PeriodModel() != null
            && periodPlan.PeriodModel().CompetitionAdded()) {
            return translations.TimeCompleted;
        } else {
            return translations.TimePlanned;
        }
        return '';
    }, self);

    self.PlaceIcon = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null
            && periodPlan.PeriodModel() != null
            && periodPlan.PeriodModel().CompetitionAdded()) {
            return ContextPath + 'Content/images/icons/miejsce_zrealiz_32.png';
        } else {
            return ContextPath + 'Content/images/icons/miejsce_plan_32.png';
        }
        return '';
    }, self);

    self.PlaceIconTitle = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null
            && periodPlan.PeriodModel() != null
            && periodPlan.PeriodModel().CompetitionAdded()) {
            return translations.OpenPlaceCompleted;
        } else {
            return translations.OpenPlacePlanned;
        }
        return '';
    }, self);


    self.StatusIcon = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (self.Ended() || periodPlan.PeriodModel().CompetitionAdded()) {
                if (periodPlan.PeriodModel().CompetitionAdded()) {
                    return ContextPath + 'Content/images/icons/zawody_udzial_29.png';
                } else {
                    return ContextPath + 'Content/images/icons/zawody_nie-udzial_29.png';
                }
            } else {
                if (periodPlan.StatusIcon()) {
                    return periodPlan.StatusIcon();
                }
            }
        }
        return '';
    }, self);

    self.StatusTitle = ko.computed(function () {
        var periodPlan = self.PeriodPlan();
        if (periodPlan != null && periodPlan.PeriodModel() != null) {
            if (self.Ended() || periodPlan.PeriodModel().CompetitionAdded()) {
                if (periodPlan.PeriodModel().CompetitionAdded()) {
                    return translations.CompetitionComplete;
                } else {
                    return translations.CompetitionUncomplete;
                }
            } else {
                if (periodPlan.StatusTitle) {
                    return periodPlan.StatusTitle;
                }
            }
        }
        return '';
    }, self);
};var QuestTypes = [
     { value: "TextBox", name: translations.QuestTypes_TextBox, minmax: false, availableValues: false }
    , { value: "TextBoxMulti", name: translations.QuestTypes_TextBoxMulti, minmax: false, availableValues: false }
    , { value: "EnumSingle", name: translations.QuestTypes_EnumSingle, minmax: false, availableValues: true }
    , { value: "EnumMulti", name: translations.QuestTypes_EnumMulti, minmax: false, availableValues: true }
    , { value: "ComboSingle", name: translations.QuestTypes_ComboSingle, minmax: false, availableValues: true }
    , { value: "ComboMulti", name: translations.QuestTypes_ComboMulti, minmax: false, availableValues: true }
   // , { value: "Bool", name: translations.QuestTypes_Bool, minmax: false, availableValues: false }
    , { value: "Int", name: translations.QuestTypes_Int, minmax: true, availableValues: false }
    , { value: "Float", name: translations.QuestTypes_Float, minmax: true, availableValues: false }
];


function ChildQuestElement(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Index = ko.observable(0);
    self.Name = ko.observable('');

    self.AvailableValues = ko.observableArray([]);

    self.AvgValue = ko.observable(null);
    self.StDevValue = ko.observable(null);
    self.OtherCounter = ko.observable(null);
    self.ValuesCounter = ko.observable(0);

    self.TextesToShow = ko.observableArray([]);

    self.TextBr = function (text) {
        if (text) {
            return text.replace(/\n/g, '<br />')
        } else {
            return '';
        }
    };
    self.NameBr = ko.computed(function () {
        return self.TextBr(self.Name());
    });

    self.ValueType = ko.computed(function () {
        return self.Parent.ValueType();
    });

    self.init = function () {
        self.Id(0);
        self.Index(0);
        self.Name('');

        self.AvailableValues.removeAll();

        self.AvgValue(null);
        self.StDevValue(null);
        self.OtherCounter(null);
        self.ValuesCounter(0);
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.Index(serverModel.Index);
            self.Name(serverModel.Name);

            self.AvgValue(serverModel.AvgValue);
            self.StDevValue(serverModel.StDevValue);
            self.OtherCounter(serverModel.OtherCounter);
            self.ValuesCounter(serverModel.ValuesCounter);

            self.AvailableValues.removeAll();
            if (serverModel.AvailableValues) {
                for (var i = 0, len = serverModel.AvailableValues.length; i < len; i++) {
                    var elem = serverModel.AvailableValues[i];
                    self.AvailableValues.push({
                        Name: ko.observable(elem.Name),
                        Value: i,
                        AnswerCounter: elem.AnswerCounter,
                        OtherTextLable: ko.observable(elem.OtherTextLable)
                    });
                }
            }
        }
    };

    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            Index: self.Index(),
            Name: self.Name(),
        }
        return data;
    };


    self.OpenTextes = function () {
        self.Parent.Parent.Parent.SelectedQuestElement(self);
        self.LoadTextes(-1);
        $('#statQuestTextesModal').modal('show');
    }

    self.OpenOtherTextes = function (data, event) {
        self.Parent.Parent.Parent.SelectedQuestElement(self);
        self.TextesToShow.removeAll();
        self.LoadTextes(data.Value + 1);
        $('#statQuestTextesModal').modal('show');
    }
    self.LoadTextes = function (answerValue) {
        if (!self.TextesToShow().length) {
            $('body').css('cursor', 'wait');
            $.ajax({
                type: "POST",
                url: ContextPath + "Questionnaire/GetQuestInstTexts",
                data: { elementId: self.Id(), answerValue: answerValue }
            })
               .done(function (result) {
                   if (result) {
                       var bred = _(result).map(self.TextBr);
                       koArrayCopy(bred, self.TextesToShow);
                   }
                   $('body').css('cursor', 'default');
               });
        }
    }
}

function QuestionnaireElementModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Index = ko.observable(0);
    self.MaxValue = ko.observable(0);
    self.MinValue = ko.observable(0);
    self.ValueType = ko.observable('');
    self.Required = ko.observable(false);
    self.Name = ko.observable('');
    self.Description = ko.observable('');
    self.AvailableValues = ko.observableArray([]);
    self.ChildElements = ko.observableArray([]);

    self.AvgValue = ko.observable(null);
    self.StDevValue = ko.observable(null);
    self.OtherCounter = ko.observable(null);
    self.ValuesCounter = ko.observable(0);

    self.TextBr = function (text) {
        if (text) {
            return text.replace(/\n/g, '<br />')
        } else {
            return '';
        }
    };
    self.NameBr = ko.computed(function () {
        return self.TextBr(self.Name());
    });
    self.DescriptionBr = ko.computed(function () {
        return self.TextBr(self.Description());
    });

    self.TextesToShow = ko.observableArray([]);

    self.init = function () {
        self.Id(0);
        self.Index(0);
        self.MaxValue(0);
        self.MinValue(0);
        self.ValueType('');
        self.Required(false);
        self.Name('');
        self.Description('');
        self.AvailableValues.removeAll();
        self.ChildElements.removeAll();

        self.AvgValue(null);
        self.StDevValue(null);
        self.OtherCounter(null);
        self.ValuesCounter(0);
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.Index(serverModel.Index);
            self.MaxValue(serverModel.MaxValue);
            self.MinValue(serverModel.MinValue);
            self.ValueType(serverModel.ValueType);
            self.Required(serverModel.Required);
            self.Name(serverModel.Name);
            self.Description(serverModel.Description);

            self.AvgValue(serverModel.AvgValue);
            self.StDevValue(serverModel.StDevValue);
            self.OtherCounter(serverModel.OtherCounter);
            self.ValuesCounter(serverModel.ValuesCounter);

            self.AvailableValues.removeAll();
            if (serverModel.AvailableValues) {
                for (var i = 0, len = serverModel.AvailableValues.length; i < len; i++) {
                    var elem = serverModel.AvailableValues[i];
                    self.AvailableValues.push({
                        Name: ko.observable(elem.Name),
                        Value: i,
                        AnswerCounter: elem.AnswerCounter,
                        OtherTextLable: ko.observable(elem.OtherTextLable)
                    });
                }
            }

            self.ChildElements.removeAll();
            if (serverModel.ChildElements) {
                for (var i = 0, len = serverModel.ChildElements.length; i < len; i++) {
                    var elem = new ChildQuestElement(self);
                    elem.initByModel(serverModel.ChildElements[i]);
                    self.ChildElements.push(elem);
                }
            }
        }
    };

    self.initByTemplate = function (template) {
        if (template) {
            self.BaseObj = template.BaseObj;
            self.initByModel(template.BaseObj);
            self.Id(0);
        }
    }

    self.getClearData = function () {
        var _AvailableValues = [];
        var _AvailableValues_Iter = self.AvailableValues();
        for (var i = 0, len = _AvailableValues_Iter.length; i < len; i++) {
            _AvailableValues.push({ Value: i, Name: _AvailableValues_Iter[i].Name(), OtherTextLable: _AvailableValues_Iter[i].OtherTextLable() });
        }

        var _ChildElements = [];
        var _ChildElements_Iter = self.ChildElements();
        for (var i = 0, len = _ChildElements_Iter.length; i < len; i++) {
            var elem = _ChildElements_Iter[i].getClearData();
            elem.Index = i;
            _ChildElements.push(elem);
        }

        var data = {
            Id: self.Id(),
            Index: self.Index(),
            MaxValue: self.MaxValue(),
            MinValue: self.MinValue(),
            ValueType: self.ValueType(),
            Required: self.Required(),
            AvailableValues: _AvailableValues,
            ChildElements: _ChildElements,
            Name: self.Name(),
            Description: self.Description()
        }
        return data;
    };

    self.GetAvailabelTypes = function () {
        return QuestTypes;
    }
    self._QuestTypes = _(QuestTypes);
    self.ValueTypeObj = ko.computed(function () {
        var v = self.ValueType();
        var obj = self._QuestTypes.find(function (elem) { return elem.value == v; });
        if (!obj) obj = QuestTypes[0];
        return obj;
    });

    self.AddValue = function () {
        self.AvailableValues.push({ Name: ko.observable(''), Value: self.AvailableValues().length, OtherTextLable: ko.observable('') });
    }
    self.RemoveValue = function (data, event) {
        self.AvailableValues.remove(data);
    }

    self.AddSubQuest = function () {
        var elem = new ChildQuestElement(self);
        elem.Index(self.ChildElements().length);
        self.ChildElements.push(elem);
    }
    self.RemoveSubQuest = function (data, event) {
        self.ChildElements.remove(data);
    }

    self.RemoveElement = function (data, event) {
        self.Parent.Elements.remove(self);
    }

    self.OpenTextes = function () {
        self.Parent.Parent.SelectedQuestElement(self);
        self.LoadTextes(-1);
        $('#statQuestTextesModal').modal('show');
    }

    self.OpenOtherTextes = function (data, event) {
        self.Parent.Parent.SelectedQuestElement(self);
        self.TextesToShow.removeAll();
        self.LoadTextes(data.Value + 1);
        $('#statQuestTextesModal').modal('show');
    }

    self.LoadTextes = function (answerValue) {
        if (!self.TextesToShow().length) {
            $('body').css('cursor', 'wait');
            $.ajax({
                type: "POST",
                url: ContextPath + "Questionnaire/GetQuestInstTexts",
                data: { elementId: self.Id(), answerValue: answerValue }
            })
               .done(function (result) {
                   if (result) {
                       var bred = _(result).map(self.TextBr);
                       koArrayCopy(bred, self.TextesToShow);
                   }
                   $('body').css('cursor', 'default');
               });
        }
    }
}

function QuestionnaireModel(parent) {
    var self = this;
    self.Parent = parent;

    self.InitMode = true;

    self.Id = ko.observable(0);
    self.PeriodId = ko.observable(0);
    self.Name = ko.observable('');
    self.Status = ko.observable('');
    self.Active = ko.observable(false);
    self.Template = ko.observable(false);
    self.Elements = ko.observableArray([]);
    self.Error = ko.observable('');
    self.AllEntries = ko.observable(0);
    self.FilledEntries = ko.observable(0);
    self.PeriodName = ko.observable('');

    self.NewParticipsEmails = ko.observable(0);
    self.AllParticipsEmails = ko.observable(0);
    self.UnfillParticipsEmails = ko.observable(0);
    self.ResultsEmails = ko.observable(0);
    self.ResultsEmailsSent = ko.observable(0);

    self.MailContent = ko.observable('');
    self.MailTitle = ko.observable('');
    self.Header = ko.observable('');
    self.Footer = ko.observable('');

    self.HasStats = ko.observable(false);

    self.LoadingDefinition = ko.observable(false);

    self.SinglePage = ko.observable(false);

    self.TextBr = function (text) {
        if (text) {
            return text.replace(/\n/g, '<br />')
        } else {
            return '';
        }
    };
    self.HeaderBr = ko.computed(function () {
        return self.TextBr(self.Header());
    });
    self.FooterBr = ko.computed(function () {
        return self.TextBr(self.Footer());
    });

    self.InformAboutFinished = ko.observable(false);
    self.IsContactEmailSet  = ko.observable(false);

    self.ParticipantIdToSend = ko.observable(0);

    self.SaveBasicTimeOut = null;
    self.SaveBasicSubscriberText = null;
    self.SaveBasicSubscriber = ko.computed(function () {
        var text = '' + self.Id() + self.Name() + self.Status() + self.Active() + self.MailContent() + self.MailTitle() + self.Header() + self.Footer() + self.InformAboutFinished() + self.SinglePage();
        if (!self.InitMode && self.SaveBasicSubscriberText && self.SaveBasicSubscriberText != text) {
            if (self.SaveBasicTimeOut) clearTimeout(self.SaveBasicTimeOut);
            self.SaveBasicTimeOut = setTimeout(self.SaveBasic, 300);
        }
        self.SaveBasicSubscriberText = text;
    });

    self.TranslatedStatus = ko.computed(function () {
        if (!self.Status() || self.Status() == 'none') {
            return '';
        } else {
            return translations['QuestionnaireStatus_' + self.Status()];
        }
    });
    self.StatusClick = function () {
        if (self.Error()) {
            ShowEmptyMsg(self.Error(), translations.Info);
        }
    }

    self.init = function () {
        self.InitMode = true;

        self.Id(0);
        self.PeriodId(0);
        self.Name('');
        self.Status('');
        self.Active(false);
        self.Template(false);
        self.Error('');
        self.Elements.removeAll();
        self.AllEntries(0);
        self.FilledEntries(0);
        self.MailContent('');
        self.MailTitle('');
        self.Header('');
        self.Footer('');
        self.NewParticipsEmails(0);
        self.AllParticipsEmails(0);
        self.UnfillParticipsEmails(0);
        self.HasStats(false);
        self.PeriodName('');
        self.SinglePage(false);

        self.InitMode = false;
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel, noElements) {
        if (serverModel) {
            self.InitMode = true;

            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.PeriodId(serverModel.PeriodId);
            self.PeriodName(serverModel.PeriodName);
            self.Name(serverModel.Name);
            self.Status(serverModel.Status);
            self.Active(serverModel.Active);
            self.Template(serverModel.Template);
            self.Error(serverModel.Error);
            self.AllEntries(serverModel.AllEntries);
            self.FilledEntries(serverModel.FilledEntries);

            self.NewParticipsEmails(serverModel.NewParticipsEmails);
            self.AllParticipsEmails(serverModel.AllParticipsEmails);
            self.UnfillParticipsEmails(serverModel.UnfillParticipsEmails);
            self.ResultsEmails(serverModel.ResultsEmails);
            self.ResultsEmailsSent(serverModel.ResultsEmailsSent);

            self.MailContent(serverModel.MailContent);
            self.MailTitle(serverModel.MailTitle);
            self.Header(serverModel.Header);
            self.Footer(serverModel.Footer);

            self.InformAboutFinished(serverModel.InformAboutFinished);
            self.IsContactEmailSet(serverModel.IsContactEmailSet);

            self.SinglePage(serverModel.SinglePage);

            if (!noElements) {
                self.HasStats(serverModel.HasStats);
                self.Elements.removeAll();
                if (serverModel.Elements) {
                    for (var i = 0, len = serverModel.Elements.length; i < len; i++) {
                        var elem = new QuestionnaireElementModel(self);
                        elem.initByModel(serverModel.Elements[i]);
                        elem.Index(i + 1);
                        self.Elements.push(elem);
                    }
                }
            }

            self.InitMode = false;
        }
    };



    self.getClearData = function (full) {
        var _Elements = [];
        if (full) {
            var _Elements_Iter = self.Elements();
            for (var i = 0, len = _Elements_Iter.length; i < len; i++) {
                _Elements_Iter[i].Index(i);
                _Elements.push(_Elements_Iter[i].getClearData());
            }
        }
        var data = {
            Id: self.Id(),
            PeriodId: self.PeriodId(),
            Name: self.Name(),
            Status: self.Status(),
            Active: self.Active(),
            Template: self.Template(),
            Elements: _Elements,
            MailContent: self.MailContent(),
            MailTitle: self.MailTitle(),
            Header: self.Header(),
            Footer: self.Footer(),
            InformAboutFinished: self.InformAboutFinished(),
            SendToParticipId: self.ParticipantIdToSend(),
            SinglePage: self.SinglePage()
        }
        return data;
    };

    self.Save = function (data, event) {
        var isNew = self.Id() <= 0;
        var strData = JSON.stringify(self.getClearData(true));
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/SetQuestionnaire",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            if (result) {
                self.initByModel(result);
                if (isNew) {
                    self.Parent.List.push(self);
                }
                ShowSavedAlert();
                $('#elementsConfiguratorModal').modal('hide');
            } else {
                alert("Cannot save.");
            }
        });
    }
    self.SaveBasic = function (data, event) {
        var isNew = self.Id() <= 0;
        var strData = JSON.stringify(self.getClearData(false));
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/SetQuestionnaireBasic",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            if (result) {
                self.initByModel(result, true);
                if (isNew) {
                    self.Parent.List.push(self);
                }
            }
        });

    }

    self.Edit = function () {
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "Questionnaire/ContestAddPage"
        }, function (result) {
            var container = setPeriodAdminContent(result, true);

            GeneralModel.CheckIfApply(container.children('div')[0]);

            self.Select();

            // resetTiny(container);
            initTiny(container);

        },
            3600000 //60min
         );
    }
    self.Select = function (doNotLoadDef) {
        self.Parent.SelectdQuest(self);

        if (self.Id() > 0 && !doNotLoadDef) {
            self.LoadDefinition();
        }
    }
    self.IsSelected = ko.computed(function () {
        return self.Parent && self.Parent.SelectdQuest() && self.Parent.SelectdQuest() == self;
    });
    self.LoadDefinition = function (getStats, callback) {
        self.LoadingDefinition(true);
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetQuestionnaire",
            data: { questId: self.Id(), getStats: true }
        })
       .done(function (result) {
           if (result) {
               self.initByModel(result);
           }
           self.LoadingDefinition(false);
           if (callback) callback();
       });
    }

    self.EditFields = function () {
        if (!self.Elements().length) {
            self.AddElement();
        }

        $('#elementsConfiguratorModal').modal('show');
    }

    self.Delete = function () {
        $('#removeSelectedQuestModal').modal('show');
    }
    self.DeleteEntries = function () {
        $('#removeSelectedQuestEntriesModal').modal('show');
    }

    self.coreDelete = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/Delete",
            data: { questId: self.Id() }
        })
        .done(function (result) {
            if (result) {
                self.Parent.coreLoadList(result);
                loadQuestionnaire(true);
                $('#removeSelectedQuestModal').modal('hide');
            } else {
                alert("Cannot delete.");
            }
        });
    }
    self.coreDeleteEntries = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/DeleteEntries",
            data: { questId: self.Id() }
        })
        .done(function (result) {
            if (result) {
                self.initByModel(result);
                $('#removeSelectedQuestEntriesModal').modal('hide');
            } else {
                alert("Cannot delete.");
            }
        });
    }

    self.AddElement = function () {
        var element = new QuestionnaireElementModel(self);
        var elems = self.Elements();
        if (!elems || !elems.length) {
            element.Index(1);
        } else {
            element.Index(_(elems).max(function (el) { return el.Index(); }).Index() + 1);
        }

        self.Elements.push(element);
    }

    self.OpenPreview = function () {
        self.Parent.InstanceModel().SampleQuestionnaire(self);
        $('#userQuestPreviewModal').modal('show');
    }


    self.OpenSend = function (participantId) {
        if (!participantId || isNaN(participantId)) participantId = 0;
        self.ParticipantIdToSend ( participantId);
        if (!self.Active()) {
            ShowEmptyMsg(translations.CannotSendInactiveQuest, translations.Info);
        } else {
            $('#questSendModal').modal('show');
        }
    }

    self.SendAll = function () {

        var msg = translations.SendAllMsg1 + ' ' + self.AllParticipsEmails() + ' ' + translations.SendAllMsg2 + '<br />';
        var again = self.AllParticipsEmails() - self.NewParticipsEmails();
        if (again) {
            msg += translations.SendAgainMsg1 + ' ' + again + ' ' + translations.SendAgainMsg2;
        } else {
            msg += translations.SendNotAgainMsg;
        }

        modalAskModel.Show(
            translations.SendAll,
            msg,
             function () { self.Send("SendAll"); },
            translations.Send
        );
    }
    self.SendNew = function () {
        var msg = translations.SendAllMsg1 + ' ' + self.NewParticipsEmails() + ' ' + translations.SendAllMsg2 + '<br />' + translations.SendNotAgainMsg;

        modalAskModel.Show(
            translations.SendNew,
            msg,
             function () { self.Send("SendNew"); },
            translations.Send
        );
    }
    self.SendResults = function () {
        var msg = translations.SendAllMsg1 + ' ' + self.ResultsEmails() + ' ' + translations.SendAllMsg2 + '<br />';
        var again = self.ResultsEmailsSent();
        if (again) {
            msg += translations.SendAgainMsg1 + ' ' + again + ' ' + translations.SendAgainMsg2;
        } else {
            msg += translations.SendNotAgainMsg;
        }

        modalAskModel.Show(
            translations.SendResults,
            msg,
             function () { self.Send("SendResults"); },
            translations.Send
        );
    }
    self.SendNotFilled = function () {
        var msg = translations.SendAllMsg1 + ' ' + self.UnfillParticipsEmails() + ' ' + translations.SendAllMsg2 + '<br />';
        var again = self.UnfillParticipsEmails() - self.NewParticipsEmails();
        if (again) {
            msg += translations.SendAgainMsg1 + ' ' + again + ' ' + translations.SendAgainMsg2;
        } else {
            msg += translations.SendNotAgainMsg;
        }

        modalAskModel.Show(
            translations.SendNotFilled,
            msg,
             function () { self.Send("SendNotFilled"); },
            translations.Send
        );
    }
    self.SendSingle = function () {
        self.Send("SendSingle");
    }


    self.ToggleActive = function () {
        var curr = self.Active();
        self.Active(!curr);
        if (!curr) {
            modalAskModel.Show(translations.OnlyCreateQuests, translations.OnlyCreateQuestsMsg, self.CreateQuests, translations.OnlyCreateQuests)
        }
    }

    self.CreateQuests = function () {
        self.Send("CreateQuests");
    }

    self.Send = function (methodName) {
        var isNew = self.Id() <= 0;
        var strData = JSON.stringify(self.getClearData(true));
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/" + methodName,
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            if (result) {
                if (!result.Error) {
                    self.initByModel(result, true);
                    if (isNew) {
                        self.Parent.List.push(self);
                    }
                    self.CheckSendingStatus();
                    if (self.InstancesDataTable) self.InstancesDataTable.draw();
                    $('#questSendModal').modal('hide');
                    if (methodName == "CreateQuests") {
                        ShowEmptyMsg(translations.Initiated, translations["Message"]);
                    }
                } else {
                    ShowEmptyMsg(translations[result.Error], translations.Error);
                    self.Status('error');
                    self.Error(translations[result.Error]);
                }
            } else {
                ShowEmptyMsg(translations.CannotSendQuests, translations.Error);
                self.Status('error');
            }
        });
    }

    
   


    self.CheckSendingStatus = function () {
        if (self.Status() == 'sending') {
            $.ajax({
                type: "POST",
                url: ContextPath + "Questionnaire/GetSendingStatus",
                data: { questId: self.Id() }
            })
            .done(function (result) {
                if (result) {

                    self.Status(result.Status);
                    self.Error(translations[result.Error]);

                    if (result.Status && result.Status != 'none' && result.Status != 'sending') {
                        //if (loadEntries) self.LoadEntries();
                        self.Reload();
                    } else {
                        setTimeout(function () { self.CheckSendingStatus(); }, 3000);
                    }

                } else {
                    ShowEmptyMsg(translations.CannotSendQuests, translations.Error);
                    self.Status('error');
                }
            });
        }
    }
    self.Reload = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetQuestionnaire",
            data: { questId: self.Id() }
        })
         .done(function (serverModel) {
             if (serverModel) {
                 self.initByModel(serverModel);
             }
         });
        if (self.InstancesDataTable) self.InstancesDataTable.draw();
    }

    self.OpenStats = function () {
        if (!self.HasStats()) {
            self.LoadDefinition(true, function () {
                self.Parent.InstanceModel().SampleQuestionnaire(self);
                self.Parent.InstanceModel().ShowFirsfQuestion();
            });
        } else {
            self.Parent.InstanceModel().SampleQuestionnaire(self);
            self.Parent.InstanceModel().ShowFirsfQuestion();
        }
        $('#statQuestModal').modal('show');
    }

    self.LoadExcel = function () {
        window.open(
           ContextPath + "Questionnaire/GetQuestSummaryFile?questId=" + self.Id(),
          '_blank' // <- This is what makes it open in a new window.
        );
    }

    self.SelectTemplate = function () {
        self.Parent.SelectdQuestTemplate(self);
    }
    self.IsSelectedTemplate = ko.computed(function () {
        return self.Parent && self.Parent.SelectdQuestTemplate() && self.Parent.SelectdQuestTemplate() == self;
    });
    self.PreviewTemplate = function () {
        self.LoadDefinition(false, self.OpenPreview);
    }

    self.LoadTemplate = function (template, loadAll) {
        if (template) {

            modalAskModel.Show(
                translations.CopyQuests,
                translations.CopyQuestsMsg,
                 function () {

                     var elems = template.Elements();
                     if (!elems.length) {
                         template.LoadDefinition(false, function () {
                             self.coreLoadTemplate(template, loadAll);
                         });
                     } else {
                         self.coreLoadTemplate(template, loadAll);
                     }

                 },
                translations.Copy
            );            
        }
    }
    self.coreLoadTemplate = function (template, loadAll) {
        if (template) {

            self.InitMode = true;

            if (loadAll) {
                if (!self.Name()) self.Name(template.Name());
                if (!self.MailContent()) self.MailContent(template.MailContent());
                if (!self.MailTitle()) self.MailTitle(template.MailTitle());
                if (!self.Header()) {
                    self.Header(template.Header());
                    $('#tinyHeader').val(self.Header());
                }
                if (!self.Footer()) {
                    self.Footer(template.Footer());
                    $('#tinyFooter').val(self.Footer());
                }
                initTiny();               
            }

            var elems = template.Elements();
            for (var i = 0, len = elems.length; i < len; i++) {
                var elem = new QuestionnaireElementModel(self);
                elem.initByTemplate(elems[i]);
                elem.Index(self.Elements().length);
                self.Elements.push(elem);
            }

            self.Save();

            self.InitMode = false;
        }
    }


    self.Instances = ko.observableArray([]);
    self.OpenInstances = function () {

        $('#instancesQuestModal').modal('show');
        self.inintInstancesDataTables();
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetInstancesInfo",
            data: {questId: self.Id() }
        })
        .done(function (result) {
            if (result) {
                self.Instances.removeAll();
                for (var i = 0, len = result.length; i < len; i++) {
                    self.Instances.push(result[i]);
                }
                if (self.InstancesDataTable) self.InstancesDataTable.draw();
            }
        });
    }
    self.InstancesDataTable = null;
    self.inintInstancesDataTables = function (elem) {
        if (!self.InstancesDataTable) {
            self.InstancesDataTable = $("#questInstances").DataTable({
                "oLanguage": dataTableLanguageOptions,
                columns: [
                    { data: 'Id', "visible": false },
                    { data: 'Email' },
                    { data: 'FirstName' },
                    { data: 'LastName' },
                    { data: 'Nick' },
                    {
                        data: 'Status', "render": function (data, type, row) {
                            var queststat = translations['questInstStatus_' + data];
                            return queststat?queststat:'';
                            //none, initiated, sent, filled, closed, invalidEmail, error
                        }
                    },
                    {
                        data: 'Id', "render": function (data, type, row) {
                            var btns = '<a class="btn btn-mini" href="#" onclick="questionnaireManager.Send('  + row.ParticipantId + '); return false;">' + translations.Send + '</a> ';
                            if (row.Status == 'filled' || row.Status == 'closed') {
                                btns += '<a class="btn btn-mini" href="#" onclick="questionnaireManager.Open(' + data + '); return false;">' + translations.Open + '</a> ';
                            }
                            return btns;
                        }
                    }
                ],
                "order": [[2, "desc"]],
                "scrollY": "550px",
                "scrollCollapse": true,
                "scrollX": true,
                "paging": false,
                "deferRender": true,
                "createdRow": function (row, data, index) {
                    /*
                    if (data.Id == self.SelectedContestId()) {
                        $(row).addClass('selected');
                    }
                    $(row).click(function () {
                        var tRow = $(this);
                        tRow.parents('table').find('tr').removeClass('selected');
                        tRow.addClass('selected');

                        self.SelectedContest(data);
                    });
                    */
                }
            });

            self.Instances.subscribeArrayChanged(
                function (addedItem) {
                    self.InstancesDataTable.row.add(addedItem);//.draw();
                },
                function (deletedItem) {
                    var rowIdx = self.InstancesDataTable.column(0).data().indexOf(deletedItem.Id);
                    self.InstancesDataTable.row(rowIdx).remove();//.draw();
                },
                function () { //clearCallback
                    self.InstancesDataTable.clear();
                }
            );
        }
    }


    self.ShowChart = function () {
        var modal = $('#fetchChartModal');
        modal.modal("show");
        var placeholder = $('.chart', modal);

        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetFetchChartData",
            data: { questId: self.Id() }
        })
        .done(function (result) {
            if (result.series) {
                var options = {
                    xaxis: { show: true, possition: "bottom", mode: "time", monthNames: monthNames, dayNames: dayNames, minTickSize: [1, "day"] },
                    yaxis: { show: true, possition: "right", minTickSize: 1, tickDecimals: 0 },
                    grid: {
                        backgroundColor: '#ffffff',
                        borderWidth: 0,
                        margin: 10
                    },
                    legend: {
                        show: false //noColumns: result.zoneData.length, position: "se"
                    },
                    series: {
                        shadowSize: 0
                    }
                };
                var d = result.series;

                var singleTraindChart = $.plot(placeholder, d, options);
            }
        });
    }

    self.InitMode = false;
}


function QuestionnaireElementValueModel(parent) {
    var self = this;
    self.Parent = parent;

    self.ElementId = ko.observable(0);
    self.FloatValue = ko.observable(null);
    self.Id = ko.observable(0);
    self.IntValue = ko.observable(null);
    self.StrValue = ko.observable('');
    self.BoolValue = ko.observable(false);
    self.AvailableValues = ko.observableArray([]);
    self.OtherString = ko.observable('');

    self.ChildValues = ko.observableArray([]);

    self.OtherStrValueEnable = ko.observable(false);

    self.Element = ko.observable(new QuestionnaireElementModel(self.Parent.Questionnaire()));
    self.SubElement = ko.observable(new ChildQuestElement(self.Element()));

    self.HtmlName = ko.computed(function () {
        var el = self.Element();
        var sel = self.SubElement();
        return el.ValueType() + '_' + el.Index() + '_' + sel ? sel.Index() : '0';
    });

    self.RequiredWarning = ko.pureComputed(function () {
        var e = self.Element();
        var data = self.getClearData();
        var hasVal = data.IntValue || data.StrValue || data.FloatValue;
        return e && e.Required() && !hasVal;
    });

    self.LoadElement = function (elementModel, subElement) {
        self.Element(elementModel);
        self.ElementId(elementModel.Id());

        if (elementModel.ValueType() == 'Bool') {
            self.IntValue.subscribe(function (newValue) {
                self.BoolValue(self.IntValue() == 1);
            });
            self.IntValue(0);
            self.BoolValue(self.IntValue() == 1);
        }
        self.AvailableValues.removeAll();
        var availVals = elementModel.AvailableValues();
        if (availVals && availVals.length) {
            for (var i = 0, len = availVals.length; i < len; i++) {
                self.AvailableValues.push({
                    Value: (i + 1),
                    Name: availVals[i].Name(),
                    selected: ko.observable(false),
                    OtherTextLable: availVals[i].OtherTextLable()
                });//, AnswerCounter: availVals[i].AnswerCounter });
            }
        }

        if (elementModel.ValueType() == 'EnumSingle') {
            self.IntValue.subscribe(function (newValue) {
                self.OtherString('');
            });
        }

        if (elementModel.ValueType() == 'ComboSingle' || elementModel.ValueType() == 'ComboMulti') {
            var elem = { Value: -1, Name: translations.Other, selected: ko.observable(false) };

            self.AvailableValues.push(elem);

            if (elementModel.ValueType() == 'ComboSingle') {
                self.IntValue.subscribe(function (newValue) {
                    self.OtherStrValueEnable(newValue == elem.Value);
                });
            } else {
                elem.selected.subscribe(function (newValue) {
                    self.OtherStrValueEnable(newValue);
                });
            }
        }

        if (elementModel.ValueType() == 'Int' || elementModel.ValueType() == 'Float') {
            //self.IntValue(elementModel.MinValue());
            //self.FloatValue(elementModel.MinValue());
            //self.StrValue(elementModel.MinValue());
            if (elementModel.MinValue() != elementModel.MaxValue()) {
                self.StrValue.subscribe(function (newValue) {

                    if (elementModel.ValueType() == 'Int') {
                        if (newValue) {
                            if (!isNaN(newValue)) {
                                var intVal = parseInt(newValue);
                                if (intVal < elementModel.MinValue()) intVal = elementModel.MinValue();
                                if (intVal > elementModel.MaxValue()) intVal = elementModel.MaxValue();
                                self.IntValue(intVal);
                            }
                        }
                        self.StrValue(self.IntValue());
                    } else {
                        if (newValue) {
                            if (!isNaN(newValue)) {
                                var floatVal = parseFloat((newValue + '').replace(',', '.'));
                                if (floatVal < elementModel.MinValue()) floatVal = elementModel.MinValue();
                                if (floatVal > elementModel.MaxValue()) floatVal = elementModel.MaxValue();
                                self.FloatValue(floatVal);
                            }
                        }
                        self.StrValue(self.FloatValue());
                    }
                });
            }
        }

        if (!subElement) {
            self.ChildValues.removeAll();
            if (elementModel.ChildElements().length) {
                var elems = elementModel.ChildElements();
                for (var i = 0, len = elems.length; i < len; i++) {
                    var elem = elems[i];
                    var val = new QuestionnaireElementValueModel(self.Parent);
                    val.LoadElement(elementModel, elem);

                    self.ChildValues.push(val);
                }
            }
        } else {
            self.SubElement(subElement);
            self.ElementId(subElement.Id());
        }
    }

    self.init = function () {
        self.ElementId(0);
        self.FloatValue(null);
        self.Id(0);
        self.IntValue(null);
        self.StrValue('');
        self.OtherString('');
        self.ChildValues.removeAll();
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.ElementId(serverModel.ElementId);
            self.FloatValue(serverModel.FloatValue);
            self.Id(serverModel.Id);
            self.IntValue(serverModel.IntValue);
            self.StrValue(serverModel.StrValue);
            self.OtherString(serverModel.OtherString);

            var elemType = self.Element().ValueType();
            if (elemType == 'ComboMulti' || elemType == 'EnumMulti') {
                var availVals = self.AvailableValues();
                if (availVals) {
                    var selVals = serverModel.StrValue ? serverModel.StrValue.split(";") : [];
                    if (selVals.length) {
                        _availVals = _(availVals);
                        _(selVals).each(function (sVal) {
                            if (sVal) {
                                var intVal = parseInt(sVal);
                                var selVal = _availVals.find(function (elem) { return elem.Value == intVal; });
                                if (selVal) selVal.selected(true);
                            }
                        });
                    }
                }
            }
        }
    };

    self.getClearData = function () {

        var elemType = self.Element().ValueType();

        if (elemType == 'Bool') {
            self.IntValue(self.BoolValue() ? 1 : 0);
        } else
            if (elemType == 'ComboMulti' || elemType == 'EnumMulti') {
                var availVals = self.AvailableValues();
                if (availVals) {
                    var result = '';
                    _.chain(availVals).filter(function (aVal) { return aVal.selected(); }).each(function (sVal) { result += (';' + sVal.Value); });
                    self.StrValue(result);
                }
            } else
                if (elemType == 'Float') {
                    if (self.StrValue()) {
                        self.FloatValue(parseFloat((self.StrValue() + '').replace(',', '.')));
                        self.StrValue(self.FloatValue());
                    } else {
                        self.FloatValue(null)
                    }
                } else
                    if (elemType == 'Int') {
                        if (self.StrValue()) {
                            self.IntValue(parseInt(self.StrValue()));
                            self.StrValue(self.IntValue());
                        } else {
                            self.IntValue(null)
                        }
                    }


        var data = {
            ElementId: self.SubElement() && self.SubElement().Id() ? self.SubElement().Id() : self.ElementId(),
            FloatValue: self.FloatValue(),
            Id: self.Id(),
            IntValue: self.IntValue(),
            StrValue: self.StrValue(),
            OtherString: self.OtherString()
        }
        return data;
    };
}

function QuestionnaireInstanceModel(parent) {
    var self = this;
    self.Parent = parent;

    self.InitMode = true;

    self.Id = ko.observable(0);
    self.QuestionnaireId = ko.observable(0);
    self.UserId = ko.observable(0);
    self.Email = ko.observable('');
    self.FillDate = ko.observable('');
    self.Guid = ko.observable('');
    self.SendDate = ko.observable('');
    self.Status = ko.observable('');
    self.Values = ko.observableArray([]);
    self.Error = ko.observable('');

    self.TranslatedStatus = ko.computed(function () {
        if (!self.Status() || self.Status() == 'none') {
            return '';
        } else {
            return translations['QuestionnaireStatus_' + self.Status()];
        }
    });
    self.StatusClick = function () {
        if (self.Error()) {
            ShowEmptyMsg(self.Error(), translations.Info);
        }
    }


    self.Questionnaire = ko.observable(new QuestionnaireModel(self.Parent));

    self.SampleQuestionnaire = function (questionnaireModel) {

        self.Questionnaire(questionnaireModel);

        self.Values.removeAll();

        var elems = questionnaireModel.Elements();
        if (elems) {
            for (var i = 0, len = elems.length; i < len; i++) {
                var val = new QuestionnaireElementValueModel(self);
                val.LoadElement(elems[i]);
                self.Values.push(val);
            }
        }
    }

    self.init = function () {
        self.InitMode = true;
        self.Id(0);
        self.QuestionnaireId(0);
        self.UserId(0);
        self.Email('');
        self.FillDate('');
        self.Guid('');
        self.SendDate('');
        self.Status('');
        self.Error('');
        self.Values.removeAll();
        self.InitMode = false;
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel, questionnaireModel) {
        if (serverModel) {
            self.InitMode = true;

            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.QuestionnaireId(serverModel.QuestionnaireId);
            self.UserId(serverModel.UserId);
            self.Email(serverModel.Email);
            self.FillDate(serverModel.FillDate);
            self.Guid(serverModel.Guid);
            self.SendDate(serverModel.SendDate);
            self.Status(serverModel.Status);

            self.Questionnaire(questionnaireModel);

            self.Values.removeAll();

            var elems = questionnaireModel.Elements();
            var _vals = _(serverModel.Values);
            if (elems) {
                for (var i = 0, len = elems.length; i < len; i++) {
                    var elem = elems[i];
                    var val = new QuestionnaireElementValueModel(self);
                    val.LoadElement(elem);

                    var d = _vals.find(function (vElem) { return vElem.ElementId == elem.Id() });
                    if (d) {
                        val.initByModel(d);
                    }

                    var chVals = val.ChildValues()
                    if (chVals.length) {
                        for (var j = 0, len2 = chVals.length; j < len2; j++) {
                            var subVal = chVals[j];
                            var sd = _vals.find(function (vElem) { return vElem.ElementId == subVal.SubElement().Id() });
                            if (sd) {
                                subVal.initByModel(sd);
                            }
                        }
                    }

                    self.Values.push(val);
                }
            }
            /*
            if (serverModel.Values) {
                for (var i = 0, len = serverModel.Values.length; i < len; i++) {
                    var elem = new QuestionnaireElementValueModel(self);
                    elem.initByModel(serverModel.Values[i]);
                    self.Values.push(elem);
                }
            }
            */
            if (!self.Questionnaire().Header()) {
                self.CurrentPage(self.Values()[0]);
            }
            self.InitMode = false;
        }
    };

    self.getClearData = function () {
        var _Values = [];
        var _Values_Iter = self.Values();
        for (var i = 0, len = _Values_Iter.length; i < len; i++) {
            var v = _Values_Iter[i];
            _Values.push(v.getClearData());

            var chVals = v.ChildValues();
            if (chVals.length) {
                for (var j = 0, len2 = chVals.length; j < len2; j++) {
                    var subVal = chVals[j];
                    _Values.push(subVal.getClearData());
                }
            }
        }

        var data = {
            Id: self.Id(),
            QuestionnaireId: self.QuestionnaireId(),
            UserId: self.UserId(),
            Email: self.Email(),
            FillDate: self.FillDate(),
            Guid: self.Guid(),
            SendDate: self.SendDate(),
            Status: self.Status(),
            Values: _Values,
            LastPage: self.DisplayFooter()
        }
        return data;
    };

    self.Select = function () {
        self.Parent.InstanceModel(self);
    }


    self.Save = function (data, event, donotReload) {
        var strData = JSON.stringify(self.getClearData());
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/SetQuestionnaireInstance",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            if (result) {
                if (!donotReload) {
                    self.Parent.SelectdQuest().initByModel(result.Quest);

                    self.initByModel(result.Instance, self.Parent.SelectdQuest());

                    ShowSavedAlert();
                }
            } else {
                alert("Cannot save.");
            }
        });
    }

    self.CurrentPage = ko.observable(null);
    self.DisplayHeader = ko.observable(true);
    self.DisplayFooter = ko.observable(false);

    self.CurrentPage.subscribe(function (newValue) {
        if (!self.InitMode && self.Id()) {
            setTimeout(function () { self.Save(null, null, true); }, 100);
        }
    });

    self.RequiredWarning = ko.pureComputed(function () {
        var vals = self.Values();
        return _.find(vals, function (elem) { return elem.RequiredWarning(); })?true:false;
    });

    self.ShowFirsfQuestion = function () {
        if (self.Values().length) {
            self.ShowPage(self.Values()[0]);
        }
    }
    self.ShowFirsfPage = function () {
        self.CurrentPage(null);
        self.DisplayHeader(true);
        self.DisplayFooter(false);
    }
    self.ShowLastPage = function () {
        self.CurrentPage(null);
        self.DisplayHeader(false);
        self.DisplayFooter(true);
    }
    self.ShowPage = function (data, event) {
        self.CurrentPage(data);
        self.DisplayHeader(false);
        self.DisplayFooter(false);
    }

    self.NextPage = function (data, event) {
        if (self.DisplayHeader()) {
            self.ShowPage(self.Values()[0]);
        } else if (self.DisplayFooter()) { }
        else {
            var vals = self.Values();
            var currIndex = vals.indexOf(self.CurrentPage());
            if ((currIndex + 1) >= vals.length) {
                self.ShowLastPage();
            } else {
                self.ShowPage(self.Values()[currIndex + 1]);
            }
        }
    }
    self.PreviousPage = function (data, event) {
        if (self.DisplayFooter()) {
            self.ShowPage(self.Values()[self.Values().length - 1]);
        } else if (self.DisplayHeader()) { }
        else {
            var vals = self.Values();
            var currIndex = vals.indexOf(self.CurrentPage());
            if ((currIndex - 1) < 0) {
                self.ShowFirsfPage();
            } else {
                self.ShowPage(self.Values()[currIndex - 1]);
            }
        }
    }

    self.InitMode = false;
}


function QuestionnaireManager() {
    var self = this;

    self.List = ko.observableArray([]);

    self.List.extend({ rateLimit: 50 });
    self.List.subscribe(function (newValue) {
        if (typeof (periodsModel) != 'undefined' && periodsModel) {
            periodsModel.SelectdPeriod().QuestsNr(self.List().length);

            var selectedRegQuest = periodsModel.SelectdPeriod().RegisterQuestionareId();
            koArrayCopyComplex(self.List(), periodsModel.SelectdPeriod().AvailableQuests,  false, function (elem) {               
                return {Text: elem.Name, Value: elem.Id};
            });
            periodsModel.SelectdPeriod().AvailableQuests.unshift({ Text: '', Value: '0' });
            periodsModel.SelectdPeriod().RegisterQuestionareId(selectedRegQuest);
        }
    });

    self.SelectdQuest = ko.observable(null);
    self.SelectdQuestTemplate = ko.observable(null);
    self.SelectdQuest(new QuestionnaireModel(self));
    self.SelectdQuestTemplate(new QuestionnaireModel(self));

    self.TemplatesList = ko.observableArray([]);

    self.InstanceModel = ko.observable(new QuestionnaireInstanceModel());

    self.SelectedQuestElement = ko.observable(new QuestionnaireElementModel(self));

    self.PeriodId = 0;

    self.initNewQuest = function () {
        var em = new QuestionnaireModel(self);
        em.init();
        em.Id(-1);
        em.PeriodId(self.PeriodId);
        em.Edit();
    }

    self.LoadList = function (periodId, force) {
        if (!self.List().length || force) {
            if (!periodId) {
                periodId = 0;
            }
            self.PeriodId = periodId;
            $.ajax({
                type: "POST",
                url: ContextPath + "Questionnaire/GetPeriodQuestionnaires",
                data: { periodId: periodId }
            })
             .done(function (serverModel) {
                 self.coreLoadList(serverModel);
             });
        }
    }
    self.coreLoadList = function (serverModel) {
        if (serverModel) {
            self.List.removeAll();

            for (var i = 0, len = serverModel.length; i < len; i++) {
                var elem = new QuestionnaireModel(self);
                elem.initByModel(serverModel[i]);
                self.List.push(elem);
            }
        }
    }

    self.LoadQuestInstance = function (guid) {

        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetQuestionnaireInstance",
            data: { guid: guid }
        })
        .done(function (result) {
            if (result && !result.GuidError) {

                var em = new QuestionnaireModel(self);
                em.initByModel(result.Quest);
                em.Select(true);

                var inst = new QuestionnaireInstanceModel(self);
                inst.initByModel(result.Instance, em);
                inst.Select();
            } else {
                if (result.GuidError) {
                    ShowEmptyMsg(result.GuidError, translations.Error);
                } else {
                    alert("Cannot load questionnaire.");
                }
            }
        });


    }

    self.LoadRegisterForm = function (questId, callback) {

        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetRegisterFormInstance",
            data: { questId: questId }
        })
        .done(function (result) {
            if (result && !result.Error) {

                var em = new QuestionnaireModel(self);
                em.initByModel(result.Quest);
                em.Select(true);

                var inst = new QuestionnaireInstanceModel(self);
                inst.initByModel(result.Instance, em);
                inst.Select();
                if (callback) callback(inst);
            } else {                
                    alert("Cannot load questionnaire.");
            }
        });


    }

    self.LoadTemplatesList = function () {
        if (!self.TemplatesList().length) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Questionnaire/GetQuestTemplates"
            })
             .done(function (serverModel) {
                 if (serverModel) {
                     self.TemplatesList.removeAll();
                     for (var i = 0, len = serverModel.length; i < len; i++) {
                         var elem = new QuestionnaireModel(self);
                         elem.initByModel(serverModel[i]);
                         self.TemplatesList.push(elem);
                     }
                 }
             });
        }
    }
    self.OpenTemplates = function () {
        self.LoadTemplatesList();
        $('#questTemplatesModal').modal('show');
    }
    self.LoadSelectedTemplate = function () {
        self.SelectdQuest().LoadTemplate(self.SelectdQuestTemplate(), true);
        $('#questTemplatesModal').modal('hide');
    }
    self.LoadQuestsFromSelectedTemplate = function () {
        self.SelectdQuest().LoadTemplate(self.SelectdQuestTemplate(), false);
        $('#questTemplatesModal').modal('hide');
    }

    self.Open = function (instanceId) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Questionnaire/GetQuestInstance",
            data: { instanceId: instanceId }
        })
        .done(function (result) {
            if (result ) {
                var inst = new QuestionnaireInstanceModel(self);
                inst.initByModel(result, self.SelectdQuest());
                inst.Select();
                $('#userQuestPreviewModal').modal('show');
            } else {
                    alert("Cannot load questionnaire.");
            }
        });
    }
    self.Send = function (paricipId) {
        self.SelectdQuest().OpenSend(paricipId);
    }
}

var questionnaireManager;
function initQuestionnaireManager(silent) {
    if (!questionnaireManager) {
        questionnaireManager = new QuestionnaireManager();
        GeneralModel.addProperty("QuestionnaireManager", questionnaireManager, silent);
    }
    return questionnaireManager;
};var usersInGroupTable = null;
var searchedUsersForGroupsTable = null;
var usersInGroupTableApi = null;
var searchedUsersForGroupsTableApi = null;
var selectGroupsTableApi = null;
var selectGroupsModalTableSelected = [];
var observableUsersTable = null;
var observableUsersTableApi = null;
var onMouseUpPressedHandler = null;
var onMouseDownPressedHandler = null;
var onMouseDownPressedTimeoutHandler = null;
var communityDisableDetailsTimeOut = null; 
var communityAvatarCloudMouseOver = false;
var communityAvatarMouseEnter = false;

var wantsObserveObservingTable = null;
var wantsObserveObservingTableApi = null;
var discipineBackgroundPosition = '';
var footerInfoModalShowed = false;
var loadNextPackageToCommunityTrainingsModalBusy;
var loadCommunityWall = null;
var sideTileCommunityUpDownButtonClicked = false;
var sideTileCommunityUpDownButtonClickedFirst = false;
var communityModalSelectedTab = '', communityModalShowTab = '';
var loadNextPackageToCommunityTrainingsBusy;
var communityOpenTrainingBusy;
var canLoadCommunityWall = true;
var communityTrainingsModalAllLoaded = false;

//var wantsObserveObservingTableCurrentPage=null;
//var wantsObserveObservingTableFirstElemIndex = null;
//var shouldGoToCommunitySection = false;
//var communityActionedUser = new CommunityUserModel(0,'');

function correctDisplayAvatarList()
{
    $('.communitySearchedUsersContainer.horizontalList .userAvatarCommunityMainContainer').each(function (index) {
        var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', this);
        var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', this);
        var width = ($(this).offset().left + $(this).width()) - communityAvatarNick.offset().left;
        communityAvatarNick.css('width', width + 'px');
        communityAvatarName.css('width', width + 'px');
                
        if (communityAvatarNick.height() > 16) {
            var topCorrection = (communityAvatarNick.height() - 16);
            var top = 78 - topCorrection;
            communityAvatarNick.css('top', top + 'px');
        }else
        {
            communityAvatarNick.css('top', 78 + 'px');
        }
    });    

    $('.communitySearchedUsersContainer.verticalList .userAvatarCommunityMainContainer').each(function (index) {
        var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', this);
        var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', this);
        var width = ($(this).offset().left + $(this).width()) - communityAvatarNick.offset().left;
        communityAvatarNick.css('width', width + 'px');
        communityAvatarName.css('width', width + 'px');
                
        if (communityAvatarNick.height() > 16) {
            var topCorrection = (communityAvatarNick.height() - 16);
            var top = 30 - topCorrection;
            communityAvatarNick.css('top', top + 'px');
        }else
        {
            communityAvatarNick.css('top', 30 + 'px');
        }
    });      
}

function CommunityModelSimple(parent, typeOfSearch, type, append) {
    var self = this;

    self.parent = parent;
    self.pageCount = 10;
    self.WantsObservedObserving = ko.observableArray([]);
    self.EntriesPerPage = ko.observable(0);            
    self.PageNumber = ko.observable(0);
    self.TotalResuts = ko.observable(0);
    self.TypeOfSearch = typeOfSearch;
    self.Type = type;
    self.Append = append;
    self.SearchListParentId = null;
    self.DontSearch = false;

    if (!typeOfSearch)
    {
        self.SearchPatternResourceString = translations['SearchForPersonGroup'];
    }
    else
        if (typeOfSearch)
        {
            self.SearchPatternResourceString = translations[typeOfSearch];
        }
    self.SearchPattern = ko.observable(self.SearchPatternResourceString);             
    self.DataLoaded = ko.observable(false);

    self.SearchPattern.subscribe(function (newValue) {
        if (newValue && newValue.length >= 0) {
            if (!self.DontSearch) {
                self.resetPageNumber();
                self.Search();
            }
        }
    });

    self.SearchBtnClick = function () {
        if (self.SearchPattern() != self.SearchPatternResourceString)
        {
            self.Search();
        }
    }  
    
    self.SearchFieldLostFocus = function (item, event) {
        if (event.target.value == '')
        {
            event.target.value = self.SearchPatternResourceString;
            self.SearchPattern(event.target.value);
        }        
    }  
    
    self.SearchFieldClick = function (item, event) {
        if (event.target.value == self.SearchPatternResourceString)
        {
            event.target.value = '';
            self.SearchPattern('');
        }
    }      

    self.ShowPagingPages = ko.computed(function () { 
        return self.TotalResuts() > self.EntriesPerPage(); 
    });
    
    self.NrOfPages = ko.computed(function () {
        var epp = self.EntriesPerPage();
        if (!epp) epp = 1;
        return Math.ceil(1.0 * self.TotalResuts() / epp);
    });    
    
    self.DisabledPrevious = ko.computed(function () {
        return !(self.PageNumber() > 0);
    });    

    self.DisabledNext = ko.computed(function () {
        return !(self.PageNumber() < (self.NrOfPages() - 1));
    });    
    
    self.Pages = ko.computed(function () {
        var nrOfPages = self.NrOfPages();
        var currentPage = self.PageNumber();
        var startPage = currentPage - 2;
        if (startPage < 0) startPage = 0;
        var endPage = startPage + 5;
        if (endPage > (nrOfPages - 1)) {
            endPage = nrOfPages - 1;
            startPage = endPage - 5;
        }

        var result = [];
        var dots = false;
        for (var i = 0; i < nrOfPages; i++) {
            if (i == 0 || (i == nrOfPages - 1) || (i >= startPage && i <= endPage)) {
                result.push(i + 1);
                dots = false;
            } else {
                if (!dots) {
                    result.push("...");
                    dots = true;
                }
            }
        }
        return result;
    }); 
        
    self.resetPageNumber = function () {
        self.PageNumber(0);
    }       
    
    self.GoToPage = function (page) {
        if (page != "...") {
            page = parseInt(page) - 1;
            self.PageNumber(page);
            self.Search();
        }
    }
    self.GoToNextPage = function (page) {
        if (self.PageNumber() + 1 < self.NrOfPages())
        {
            self.PageNumber(self.PageNumber() + 1);
            
            if (self.SearchListParentId)
                self.Search(self.SearchListParentId);
            else
                self.Search();
        }
    }
    self.GoToPreviousPage = function (page) {
        if (self.PageNumber() > 0)
        {
            self.PageNumber(self.PageNumber() - 1);
            self.Search();            
        }
    } 

    self.Search = function (elementIdParam, callBack) {
        var searchValue = null;
        var pageCount = 12;
        var controllerMethod = self.Type;
        var elementId = 0;

        if (self.SearchPattern() != self.SearchPatternResourceString)
            searchValue = self.SearchPattern();

        if (self.Type == 'TrainingInvitation') {
            pageCount = 6;
            controllerMethod = 'SearchForAcceptedTrainingInvitations';
        }
        if (self.Type == 'CommunityFanModel') {
            pageCount = 6;
            controllerMethod = 'SearchForFans';
        }
        if (self.Type == 'CommunityInvitedModel') {
            pageCount = 6;
            controllerMethod = 'SearchInvited';
        }

        if (elementIdParam)
            elementId = elementIdParam;
        else
            elementId = self.parent.Id();

        self.EntriesPerPage(pageCount);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/" + controllerMethod,
            data: { searchPattern: searchValue, elementId: elementId, pageCount: pageCount, pageIndex: self.PageNumber() }
        })
       .done(function (serverModel) {
           self.setList(serverModel, callBack);
       });
    }

    self.setList = function (serverModel, callBack)
    {
        if (serverModel) {
            self.DataLoaded(true);
            self.TotalResuts(serverModel.TotalResuts);

            if (self.WantsObservedObserving) {
                if (self.Append != true) self.WantsObservedObserving.removeAll();
                if (serverModel.WantsObservedObserving)
                {
                    for (var i = 0, len = serverModel.WantsObservedObserving.length; i < len; i++) {
                        var elem = new CommunityUserModel(null, null, self);
                        elem.initByModel(serverModel.WantsObservedObserving[i]);
                        self.WantsObservedObserving.push(elem);
                    }
                }

                correctDisplayAvatarList();                  
            }

            if (callBack) callBack();
        }       
    }

    self.SetTextsParameters = function() {        
        $('.userAvatarCommunityMainContainer').each(function () {
            var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', this);
            var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', this);
            var width = ($(this).offset().left + $(this).width()) - communityAvatarNick.offset().left;
            communityAvatarNick.css('width', width + 'px');
            communityAvatarName.css('width', width + 'px');

            if (communityAvatarNick.height() > 16) {
                var topCorrection = (communityAvatarNick.height() - 16);
                var top = 30 - topCorrection;
                communityAvatarNick.css('top', top + 'px');
            }else
            {
                communityAvatarNick.css('top', 30 + 'px');
            }                 
        });   
    }              
}

function CommunityInviteModel(parent, searchMethodPrefix) {
    var self = this;

    self.Parent = parent;
    self.binded = false;

    self.Id = function () {
        return self.Parent.NewId();
    }

    self.initValues = function () {
        self.CommunitySelectionComboboxItems = ko.observableArray([]);
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 0, translations['AllCummunityGroupLabel'], 'All', 0, true)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 1, translations['Joined'], 'Joined')));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 2, translations['Invited'], 'Invited')));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 3, translations['NotJoined'], 'NotJoined')));
        self.CommunitySelectionComboboxItemIndex = ko.observable(0);
        self.Users = new CommunityModelSimple(self, 'EnterNameOrNick', searchMethodPrefix + 'All');
        self.AllInvited = ko.observable(true);
        self.Public = ko.observable(false);
        self.AllInvitedOld = true;
        self.SearchPattern = ko.observable('');
    }

    self.initValues();

    self.SelectedCommunityItemText = ko.computed(function () {
        return self.CommunitySelectionComboboxItems()[self.CommunitySelectionComboboxItemIndex()]().Text();
    });

    self.Search = function (callBack, firstOpening) {
        if (firstOpening) {
            if (self.Parent.UsersWhichWasInvitedCount() > 0) {
                self.CommunitySelectionComboboxItemIndex(2);
            }
            else 
                if (self.Parent.UsersWhichWasInvitedCount() == 0) {
                    self.CommunitySelectionComboboxItemIndex(0);
                }
        }

        var selectedItem = self.CommunitySelectionComboboxItems()[self.CommunitySelectionComboboxItemIndex()]();
        var searchType = selectedItem.Name();

        self.Users.Type = searchMethodPrefix + searchType;
        self.Users.DontSearch = true;
        self.Users.SearchPattern(self.SearchPattern());
        self.Users.DontSearch = false;
        self.Users.Search(self.Parent.NewId(), callBack);
    }

    self.resetPageNumber = function () {
        self.Users.resetPageNumber();
    }

    self.SearchtFieldKeyPressed = function (item, event) {
        if (event && event.keyCode === 13) {
            if (!event.srcElement) {
                event.srcElement = event.target;
            }

            self.SearchPattern($(event.srcElement).val());
            self.resetPageNumber();
            //self.Users.TotalResuts(0);
            self.Search();
            return false;
        }
        return true;
    }

    self.SearchBtnClick = function () {
        self.resetPageNumber();
        self.Search();
    }

    self.SearchFieldLostFocus = function (item, event) {
        if (event.target.value == '') {
            self.SearchPattern('');
        }
    }

    self.loadView = function () {
        self.init();

        if ($("body #communityInviteUserModal").length == 0) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetInviteUserModelView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);

                     if (!self.AllInvited()) {
                         self.Search(self.show, true);
                     }
                     else {
                         self.show();
                     }

                     $('#communityInviteUserModal').on('hidden', function () {
                         self.Parent.communityInviteUserModalHidden();
                     })
                 }
             });
        }
        else {
            if (!self.AllInvited()) {
                self.Search(self.show, true);
            }
            else {
                self.Users.WantsObservedObserving.removeAll();
                self.resetPageNumber();
                self.Users.TotalResuts(0);
                self.show();
            }
        }
    }

    self.init = function () {
        self.AllInvited(self.Parent.AllInvited());
    }

    self.show = function () {
        $('#communityInviteUserModal').modal("show");

        if (!self.binded) {
            ko.applyBindings(GeneralModel, $("body #communityInviteUserModal")[0]);
            self.binded = true;
        }

        correctDisplayAvatarList();
    }

    self.setAllInvited = function (allInvited) {
        self.AllInvited(allInvited);

        if (self.Parent.Id()) {
            trainingInvitationsExplorerModel.SelectedForExplorer().AllInvited(allInvited);
        }
        else
        {
            communityExplorerModel.SelectedTrainingInvitation().AllInvited(allInvited);
        }

        //var isNew = communityExplorerModel.SelectedTrainingInvitation().IsNew();
        //var invitedUserIdsModel = communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvitedIdsInitState;

        var data = { 
            messageId: self.Parent.NewId(),
            allInvited: allInvited
            //isNew: isNew, 
            //invitedUserIdsModel: invitedUserIdsModel 
        };

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetTrainingInvitationsAllInvited",
            contentType: "application/json; charset=utf-8",
            data: strData 
        })
        .done(function (result) {
            if (result) {
                if (result != "Done") {
                    //if ($.isArray(result)) {
                    //    trainingInvitationsExplorerModel.SelectedForExplorer().UsersWhichWasInvitedIdsInitState = result;
                    //    communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvitedIdsInitState = result;
                    //}
                    //else
                    //{
                    self.Parent.NewId(parseInt(result));
                    self.Parent.NewMessageWasCreated(true);
                    //}
                }
            }
        });

        if (!self.AllInvited()) {
            self.CommunitySelectionComboboxItemIndex(0);
            self.resetPageNumber();
            self.Search();
        }
        else
        {
            self.Users.WantsObservedObserving.removeAll();
            self.Users.TotalResuts(0);
            self.resetPageNumber();
        }
    }
}

function CommunityModel(typeOfSearch) {
    var self = this;

    self.SelectedGroupId = function (groupId) {
        for (var i = 0, len = self.Groups().length; i < len; i++) {
            if (self.Groups()[i].Id() == groupId)
            {              
                self.SelectedGroup = self.Groups()[i];
                break;
            }
        }
    }

    self.initValues = function () {
        //         self.Groups = ko.observableArray([]);
        //         self.UsersInGroup = ko.observableArray([]);
        self.pageCount = 10;
        //         self.SelectedGroup = ko.observable("");
        //         self.PreviousSelectedGroup = ko.observable("");
        self.ActionedGroup = ko.observable(new CommunityGroupModel(self));
        //         self.SearchedUsers = ko.observableArray([]);
        self.ActionedUser = ko.observable(new CommunityUserModel(0,''));
        self.AlreadyDisplayedTrainingIds = [];
        self.LastUploadTime = '';
        self.LastUploadNotViewedTime = '';
        self.LastUploadNotReadCommentTime = '';
        //self.LastUploadViewedCommentedTime = '';
        self.RunNewQuery = true;        
        self.AllLoaded = false;        
        //        self.TimeoutOccured = false;        
        //         self.MissingLastUploadTime = '';
        //         self.MissingLastUploadNotViewedTime = '';
        self.MissingFirstUploadTime = '';
        self.MissingFirstUploadNotViewedTime = '';
        self.MissingFirstUploadTimeNotReadCommentTime = '';
        //         self.MissingRunNewQuery = true;        
        //         self.MissingAllLoaded = false;   
        self.NumberOfTimeouts = 0;
        self.LoadTrainingsTimeoutHandler = null;
        self.LoadNextTrainingsTimeoutHandler = null;
        self.LoadMissingTrainingsTimeoutHandler = null;
        self.WantsObservedObserving = ko.observableArray([]); 
        self.LoadForMissingTrainingsTimeoutHandler = null;
        self.ObservedCount = ko.observable(0);
        self.ObservingCount =  ko.observable(0);
        self.CommunitySelectionComboboxItems = ko.observableArray([]);                 
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 0, translations['AllCummunityGroupLabel'], 'All', 40, true)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 1, translations['LastAdded'], 'LastAdded', 40)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 2, translations['Observers'], 'Observers', 40)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 3, translations['Observeds'], 'Observeds', 40)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 4, translations['Invited'], 'Invited', 40)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 5, translations['Rejecteds'], 'Rejecteds', 40)));
        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 6, translations['Waitings'], 'Waitings', 8)));
        //        self.CommunitySelectionComboboxItems.push(new ko.observable(new CommunitySelectionComboboxItem(self, 6, translations['NoStatus'], 'NoStatus', 12)));
        self.CommunitySelectionComboboxItemIndex = ko.observable(0);
        self.EntriesPerPage = ko.observable(0);            
        self.PageNumber = ko.observable(0);
        self.TotalResuts = ko.observable(0);
        self.TypeOfSearch = typeOfSearch;
        if (!typeOfSearch)
        {
            self.SearchPatternResourceString = translations['SearchForPersonGroup'];
        }
        else
            if (typeOfSearch)
            {
                self.SearchPatternResourceString = translations[typeOfSearch];
            }
        self.SearchPattern = ko.observable(self.SearchPatternResourceString);             
        self.FirstLoaded = false;
        self.addCommunityUserModalTableSelected = ko.observableArray([]); 
        if (typeOfSearch != 'Waitings')
            self.CommunityModelForWaitings = new ko.observable(new CommunityModel('Waitings'));
        self.CommunityModelForWaitingsSearchIsExecuting = true;
        self.IsAnyNotSpamRelation = ko.observable(false);
        self.OnlyForCurrentClub = false;
        self.AreSomeStatusNotifications = false;
        self.DataLoaded = ko.observable(false);
        self.IsFacebook = ko.observable(false);
        self.IsGoogle = ko.observable(false);
        self.SearchComplete = ko.observable(false);
        self.SelectedDiscId = ko.observable(0);
        self.DisciplineManager = new TrainingTypeControl({
            mainId: null,
            subId: null,
            typeChangeCallback: null,
            clubId: null,
            multi: false,
            baseCtrl: null,
            disciplines: true,
            multiSubs: false,
            allInstedNone: true
        });
        self.AddFriendSelectedCommunityItemText = ko.observable('');
        self.AddFriendCommunitySelectionComboboxItemId = ko.observable(0);
        self.AddFriendCommunitySelectionComboboxItems = ko.observableArray([]);
        self.AddFriendCommunitySelectionComboboxActive = ko.observable(false);
        self.AddFriendCommunitySelectionComboboxItemIndex = ko.observable(0);
        self.DontSearch = false;
    }

    self.initValues();

    self.initByModel = function (serverModel) {
        self.ObservedCount(serverModel.ObservedCount);  
        self.ObservingCount(serverModel.ObservingCount);  
        self.IsAnyNotSpamRelation(serverModel.IsAnyNotSpamRelation);  
        self.DataLoaded(true);
        
        if (serverModel) {
            //             if (self.WantsObservedObserving) {
            //                 self.WantsObservedObserving.removeAll();
            //                 for (var i = 0, len = serverModel.WantsObservedObserving.length; i < len; i++) {
            //                     var elem = new CommunityUserModel();
            //                     elem.initByModel(serverModel.WantsObservedObserving[i]);
            //                     self.WantsObservedObserving.push(elem);
            //                 }
            //             }            
            
            //             if (self.Groups) {
            //                 self.Groups.removeAll();
            //                 for (var i = 0, len = serverModel.Groups.length; i < len; i++) {
            //                     var elem = new CommunityGroupModel(self);
            //                     elem.initByModel(serverModel.Groups[i]);
            //                     self.Groups.push(elem);
            //                 }
            //             }

            //            self.setUsersInGroup(serverModel);
        }
    }

    //     self.communitySelectionComboboxButton = function () {
    //        for(var i = 0; i< self.CommunitySelectionComboboxItems().length; i++) {
    //             self.CommunitySelectionComboboxItems()[i]().Visible(true);
    //         }         
    //     }

    self.SearchPattern.subscribe(function (newValue) {
        //         if (newValue && newValue.length >= 0) {
        //             self.resetPageNumber();
        //             self.Search();
        //         }
    });

    self.IsGoogle.subscribe(function (newValue) {        
        self.resetPageNumber();
        
        if (!newValue) {
            if (self.IsFacebook()) {
                self.Search();    
            } else
            {
                self.WantsObservedObserving.removeAll();
            }
        } else {          
            self.SearchPattern('');
            self.addFriendSelectedCommunityItemIndex(-1);
            self.Search();
        }
    });

    self.IsFacebook.subscribe(function (newValue) {
        self.resetPageNumber();

        if (!newValue) {
            if (self.IsGoogle()) {
                self.Search();    
            } else
            {
                self.WantsObservedObserving.removeAll();
            }
        } else {            
            self.SearchPattern('');
            self.addFriendSelectedCommunityItemIndex(-1);
            self.Search();
        }                
    });

    self.SearchBtnClick = function () {
        //         if (self.SearchPattern() != self.SearchPatternResourceString)
        {
            self.Search();
        }
    }  
    
    self.SearchFieldLostFocus = function (item, event) {
        if (event.target.value == '')
        {
            event.target.value = self.SearchPatternResourceString;
            self.SearchPattern(event.target.value);
        }        
    }  
    
    self.SearchFieldClick = function (item, event) {
        if (event.target.value == self.SearchPatternResourceString)
        {
            event.target.value = '';
            self.SearchPattern('');
        }
    }
    self.ResultSearchText = ko.computed(function () {
        if (!self.SearchComplete()) return '';

        if (self.IsFacebook() && self.IsGoogle()) {
            if (self.TotalResuts() > 0) {
                return translations.FaceAndGmailResults.replace('{0}', 'facebook/gmail');
            }
            else {
                return translations.FaceAndGmaiNolResults.replace('{0}', 'facebook/gmail');
            }
        } else
            if (self.IsFacebook()) {
                if (self.TotalResuts() > 0) {
                    return translations.FaceAndGmailResults.replace('{0}', 'facebook');
                }
                else {
                    return translations.FaceAndGmaiNolResults.replace('{0}', 'facebook');
                }
            } else
                if (self.IsGoogle()) {
                    if (self.TotalResuts() > 0) {
                        return translations.FaceAndGmailResults.replace('{0}', 'gmail');
                    }
                    else {
                        return translations.FaceAndGmaiNolResults.replace('{0}', 'gmail');
                    }
                }

        return '';
    });

    self.ShowPagingPages = ko.computed(function () { 
        return self.TotalResuts() > self.EntriesPerPage(); 
    });
    //     self.ShowPaging = ko.computed(function () { 
    //         return self.TotalResuts() > 10; 
    //     });
    
    self.NrOfPages = ko.computed(function () {
        var epp = self.EntriesPerPage();
        if (!epp) epp = 1;
        return Math.ceil(1.0 * self.TotalResuts() / epp);
    });    
    
    self.DisabledPrevious = ko.computed(function () {
        return !(self.PageNumber() > 0);
    });    

    self.DisabledNext = ko.computed(function () {
        return !(self.PageNumber() < (self.NrOfPages() - 1));
    });    
    
    self.Pages = ko.computed(function () {
        var nrOfPages = self.NrOfPages();
        var currentPage = self.PageNumber();
        var startPage = currentPage - 2;
        if (startPage < 0) startPage = 0;
        var endPage = startPage + 5;
        if (endPage > (nrOfPages - 1)) {
            endPage = nrOfPages - 1;
            startPage = endPage - 5;
        }

        var result = [];
        var dots = false;
        for (var i = 0; i < nrOfPages; i++) {
            if (i == 0 || (i == nrOfPages - 1) || (i >= startPage && i <= endPage)) {
                result.push(i + 1);
                dots = false;
            } else {
                if (!dots) {
                    result.push("...");
                    dots = true;
                }
            }
        }
        return result;
    }); 
        
    self.resetPageNumber = function () {
        self.PageNumber(0);
    }       
    
    self.GoToPage = function (page) {
        if (page != "...") {
            page = parseInt(page) - 1;
            self.PageNumber(page);
            self.Search();
        }
    }
    self.GoToNextPage = function (page) {
        if (self.PageNumber() + 1 < self.NrOfPages())
        {
            self.PageNumber(self.PageNumber() + 1);
            self.Search();
        }
    }
    self.GoToPreviousPage = function (page) {
        if (self.PageNumber() > 0)
        {
            self.PageNumber(self.PageNumber() - 1);
            self.Search();            
        }
    }
    
    self.LoadObservedList = function () {
        self.CommunitySelectionComboboxItemIndex(3);
        self.resetPageNumber();
        self.Search();
        if ($('.communityObservedAndObserversExpanderContainer .dzyndzolek').hasClass('collapsed'))
        {
            $('.communityObservedAndObserversExpanderContainer .dzyndzolek').click();            
        }
    }
    self.LoadObserversList = function () {
        self.CommunitySelectionComboboxItemIndex(2);
        self.resetPageNumber();
        self.Search();        
        if ($('.communityObservedAndObserversExpanderContainer .dzyndzolek').hasClass('collapsed'))
        {
            $('.communityObservedAndObserversExpanderContainer .dzyndzolek').click();
        }
    }    

    self.Search = function (isError, forceSearchType) {
        if (self.DontSearch) return;

        self.SearchComplete(false);
        if (self.IsFacebook() && !facebookIds) {
            if (isError) {
                self.IsFacebook(false);
            } else {
                fbGetEmails(self.Search);
            }
        } else
            if (self.IsGoogle() && !googleMails) {
                if (isError) {
                    self.IsGoogle(false);
                } else {
                    googleGetEmails(self.Search);
                }
            } else {
                self.SearchExecute(forceSearchType);
            }
    }

    self.RandomProposes = function () {
        self.Search(false, 'FriendOfFriends');
    }

    self.SearchtFieldKeyPressed = function (item, event) {
        if (event && event.keyCode === 13) {
            if (!event.srcElement)
            {
                event.srcElement = event.target;
            }     
            
            //            self.searchPattern($($('#addCommunityUserModal input')[0]).val());
            self.SearchPattern($(event.srcElement).val());
            self.resetPageNumber();
            self.Search();
            return false;
        }
        return true;
    }

    self.DisciplineManager.TypeChangeCallback = function (discId) {
        //if (!self.initState) {
        //    self.initState = true;
        self.SelectedDiscId(discId);
        self.resetPageNumber();
        //self.initState = false;
        //self.Start = true;
        self.Search();

        //        }
    }

    self.FriendOfFriendsExecution = ko.observable(false);
    self.SearchExecute = function (forceSearchType) {
        self.FirstLoaded = true;
        var searchValue = null;
        var pageCount = null;
        var emailListFilter = [];
        var facebookIdListFilter = [];
        var disciplinesIds = [];

        if (self.SelectedDiscId()) {
            disciplinesIds.push(self.SelectedDiscId());
        }        

        if (self.SearchPattern() != self.SearchPatternResourceString)
            searchValue = self.SearchPattern();

        if (self.TypeOfSearch == 'Waitings') {
            pageCount = self.EntriesPerPage();
            searchType = 'Waitings';
        }
        else if (self.TypeOfSearch == 'FriendOfFriends' || forceSearchType == 'FriendOfFriends') {
            searchType = 'FriendOfFriends';
            pageCount = 12;
        }
        else
            if (self.TypeOfSearch == 'EnterNameOrNick') {
                pageCount = 12;
                searchType = 'Free';
            }
            else {
                var selectedItem = self.CommunitySelectionComboboxItems()[self.CommunitySelectionComboboxItemIndex()]();
                searchType = selectedItem.Name();
                pageCount = 8;//selectedItem.EntriesPerPage();
            }

        if (searchType == 'Waitings' && self.AreSomeStatusNotifications) {
            self.PageNumber(0);
        }

        self.EntriesPerPage(pageCount);

        if (self.IsGoogle() && googleMails) {
            emailListFilter = emailListFilter.concat(googleMails);

            if (emailListFilter.length == 0) {
                emailListFilter.push(0);                
            }
        }

        if (self.IsFacebook() && facebookIds) {
            facebookIdListFilter = facebookIdListFilter.concat(facebookIds);

            if (facebookIdListFilter.length == 0) {
                facebookIdListFilter.push(0);                
            }            
        }        

        var data = {
            searchType: searchType, 
            searchPattern: searchValue, 
            pageCount: pageCount, 
            pageIndex: self.PageNumber(), 
            onlyForCurrentClub: self.OnlyForCurrentClub, 
            emailListFilter: emailListFilter,
            facebookIdListFilter: facebookIdListFilter,                              
            communityId: self.AddFriendCommunitySelectionComboboxItemId(),
            disciplineIds: disciplinesIds,
        };

        var strData = JSON.stringify(data);        

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SearchUsers",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (serverModel) {
           if (serverModel) {
               communityModel.ObservedCount(serverModel.ObservedCount);
               communityModel.ObservingCount(serverModel.ObservingCount);
               communityModel.IsAnyNotSpamRelation(serverModel.IsAnyNotSpamRelation);
               communityModel.DataLoaded(true);
               self.DataLoaded(true);
               self.SearchComplete(true);
               communityExplorerModel.setInvitationsCount(serverModel.InvitationsCount);
               //communityExplorerModel.setNewTrainingInvitationsCount(serverModel.NewTrainingInvitationsCount);
               self.OnlyForCurrentClub = false;

               if (searchType == 'FriendOfFriends') {
                   self.FriendOfFriendsExecution(true);
                   self.TotalResuts(0);
               } else {
                   self.TotalResuts(serverModel.TotalResuts);
                   self.FriendOfFriendsExecution(false);
               }

               if (self.WantsObservedObserving) {
                   self.WantsObservedObserving.removeAll();
                   if (serverModel.WantsObservedObserving) {
                       var sawApprovalStateCount = 0;
                       for (var i = 0, len = serverModel.WantsObservedObserving.length; i < len; i++) {
                           var elem = new CommunityUserModel(null, null, self);
                           elem.initByModel(serverModel.WantsObservedObserving[i]);
                           self.WantsObservedObserving.push(elem);
                           if (elem.ObserverSawApprovalState() || elem.ObservedSawApprovalState()) {
                               sawApprovalStateCount++;
                           }
                       }

                       self.AreSomeStatusNotifications = sawApprovalStateCount > 0;
                   }
                   $('.userAvatarCommunityMainContainer').each(function () {
                       var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', this);
                       var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', this);
                       var width = ($(this).offset().left + $(this).width()) - communityAvatarNick.offset().left;
                       communityAvatarNick.css('width', width + 'px');
                       communityAvatarName.css('width', width + 'px');

                       if (communityAvatarNick.height() > 16) {
                           var topCorrection = (communityAvatarNick.height() - 16);
                           var top = 78 - topCorrection;
                           communityAvatarNick.css('top', top + 'px');
                       } else {
                           communityAvatarNick.css('top', 78 + 'px');
                       }
                   });
               }

               //             if (self.Groups) {
               //                 self.Groups.removeAll();
               //                 for (var i = 0, len = serverModel.Groups.length; i < len; i++) {
               //                     var elem = new CommunityGroupModel(self);
               //                     elem.initByModel(serverModel.Groups[i]);
               //                     self.Groups.push(elem);
               //                 }
               //             }

               //            self.setUsersInGroup(serverModel);
           }

           setTimeout(function () {
               communityModel.CommunityModelForWaitingsSearchIsExecuting = false;
           }, 1000);           
       });
    }
    
    self.SelectedCommunityItemText = ko.computed(function () {        
        return self.CommunitySelectionComboboxItems()[self.CommunitySelectionComboboxItemIndex()]().Text();
    });    

    self.SelectedCommunityItemName = ko.computed(function () {        
        return self.CommunitySelectionComboboxItems()[self.CommunitySelectionComboboxItemIndex()]().Name();
    });

    self.addFriendSelectedCommunityItemIndex = function (index) {  
        index++;
      
        self.AddFriendCommunitySelectionComboboxItemIndex(index);

        self.resetPageNumber();

        if (index) {
            self.AddFriendCommunitySelectionComboboxItemId(self.AddFriendCommunitySelectionComboboxItems()[self.AddFriendCommunitySelectionComboboxItemIndex()].Id());
            self.AddFriendSelectedCommunityItemText(self.AddFriendCommunitySelectionComboboxItems()[self.AddFriendCommunitySelectionComboboxItemIndex()].ClubName());
            self.DontSearch = true;
            self.IsFacebook(false);
            self.IsGoogle(false);
            self.SearchPattern('');
            self.DontSearch = false;
            self.Search();
        }
        else {
            if (self.AddFriendCommunitySelectionComboboxItemId() > 0)
            {
                self.WantsObservedObserving.removeAll();
            }
            self.AddFriendSelectedCommunityItemText('');                        
            self.AddFriendCommunitySelectionComboboxItemId(0);
        }
    }

    self.setUsersInGroup = function (serverModel) {
        self.UsersInGroup.removeAll();
        if (serverModel.UsersInGroup) {
            for (var i = 0, len = serverModel.UsersInGroup.length; i < len; i++) {
                var elem = new CommunityUserModel();
                elem.initByModel(serverModel.UsersInGroup[i]);
                self.UsersInGroup.push(elem);
            }
        }
    }

    self.init = function (refresh) {
        var pageLength = getNotificationsPageLength($('.row-fluid.box.communityIndexPageTile'));

        self.CommunityModelForWaitings().EntriesPerPage(pageLength);
        self.CommunityModelForWaitingsSearchIsExecuting = true;
        self.CommunityModelForWaitings().Search();
        //self.CommunityModelForWaitings = new ko.observable(new CommunityModel('Waitings'));
        //         self.EntriesPerPage(3);
        //         self.CommunityModelForWaitings.Search();
        
        //         $.ajax({
        //             type: "POST",
        //             url: ContextPath + "Community/GetCommunityModelJson",
        //             data: { modelType: "Main", pageCount: self.pageCount }
        //         })
        //          .done(function (result) {
        //              self.initByModel(result);
        //              //self.SelectedGroupId(0);
        //              //registerCommunityUsersDatatable();
        //              //self.Groups()[0].Selected('active');
        //              //communityModel.PreviousSelectedGroup = self.Groups()[0];                    
        //              if (!refresh) initTables();
        //          });
    }
    
    self.addCommunityGroup = function () {           
        $('#addCommunityGroupModal').modal('show');
    }

    self.openAddUserDialog = function () {
        //         $.ajax({
        //             type: "POST",
        //             url: ContextPath + "Community/AddUsersToGroups",
        //             dataType: "json",
        //             contentType: "application/json; charset=utf-8",
        //             data: strData
        //         })
        //          .done(function (serverModel) {
        //          });
         
        //communityModelForUsers.SearchPattern(communityModelForUsers.SearchPatternResourceString);
        //communityModelForUsers.resetPageNumber();
        //communityModelForUsers.Search();

        //self.TypeOfSearch = 'FriendOfFriends';
        self.SearchPattern('');
        self.addFriendSelectedCommunityItemIndex(-1);
        self.IsFacebook(false);
        self.IsGoogle(false);
        self.WantsObservedObserving.removeAll();
        self.TotalResuts(0);

        self.AddFriendSelectedCommunityItemText('');
        self.AddFriendCommunitySelectionComboboxItems.removeAll();
        self.AddFriendCommunitySelectionComboboxActive(userBarModel.ShowSomeClubs());
        self.AddFriendCommunitySelectionComboboxItemIndex(0);

        koArrayCopyComplex(userBarModel.UserClubs(), self.AddFriendCommunitySelectionComboboxItems, false,
            function (e) {
                var elem = new ClubSimpleModel(self);
                elem.initByModel(e.ServerModel);
                return elem;
            });

        if (self.AddFriendCommunitySelectionComboboxItems().length > 0)
        {
            var firstClubSimpleModel = new ClubSimpleModel(self);
            self.AddFriendCommunitySelectionComboboxItems().unshift(firstClubSimpleModel);
        }

        var found = false;
        for (i = 0; i < self.AddFriendCommunitySelectionComboboxItems().length; i++) {
            if (self.AddFriendCommunitySelectionComboboxItems()[i].Id() == self.AddFriendCommunitySelectionComboboxItemId()) {
                self.AddFriendCommunitySelectionComboboxItemIndex(i);
                self.AddFriendSelectedCommunityItemText(self.AddFriendCommunitySelectionComboboxItems()[i].ClubName()); 
                found = true;
                break;
            }
        }

        if (!found) {
            self.AddFriendCommunitySelectionComboboxItemId(0);
        }

        self.RandomProposes();

        $('#addCommunityUserModal').modal('show');
    }
    
    //     self.addCommunityUserBtn2 = function () {
    //         if (communityModelForUsers.addCommunityUserModalTableSelected().length == 0)
    //         {
    //             ShowEmptyMsg('Musisz wybrać osobę', translations.Info);                    
    //         } else
    //         {
    //             if (self.SelectedGroup.Id() == 0 )
    //             {
    //                 selectGroupsModalTableSelected = [];
    //                 $('#selectGroupsModal table.dataTable tr').each(function () {
    //                     $(this).removeClass('selected');
    //                 });
    //                 $($('#selectGroupsModal input')[0]).val("");  
    //                 $('#selectGroupsModal').modal('show');                        
    //             }
    //             else
    //             {
    //                 selectGroupsModalTableSelected = [self.SelectedGroup.Id()];
    //                 self.addCommunityUsers();
    //             }
    //         }
    //     }   

    self.addCommunityUsers = function () {
        var data = { 
            userIds: addCommunityUserModalTableSelected
        };            

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/AddUsersToGroups",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
         .done(function (serverModel) {
             self.SelectedGroup.showCommunityUsers();                              
             $('#selectGroupsModal').modal('hide');                         
             $('#addCommunityUserModal').modal('hide');
         });                           
    } 

    self.searchInputBoxMouseOver = function (model, event) {
        if (!event.srcElement)
        {
            event.srcElement = event.target;
        }
        
        var p = GetScreenCordinatesToFirstObjName(event.srcElement, 'communityObservedAndObservers');
        var x = p.x + 112;
        var y = p.y + 26;

        $('.publishInfoCloudContainer.friendsSearch').css( { left: x, top: y } ) 
        $('.publishInfoCloudContainer.friendsSearch').show();
    }

    self.searchInputBoxMouseOut = function (model, event) {
        $('.publishInfoCloudContainer.friendsSearch').hide();        
    }

    //     self.addCommunityUsers = function () {
    //         if (selectGroupsModalTableSelected.length == 0)
    //         {
    //             ShowEmptyMsg('Musisz wybrać grupę', translations.Info);                    
    //         } else
    //         {
    //             var data = { 
    //                 groupIds: selectGroupsModalTableSelected,
    //                 userIds: addCommunityUserModalTableSelected
    //             };            
            
    //             var strData = JSON.stringify(data);
                        
    //             $.ajax({
    //                 type: "POST",
    //                 url: ContextPath + "Community/AddUsersToGroups",
    //                 dataType: "json",
    //                 contentType: "application/json; charset=utf-8",
    //                 data: strData
    //             })
    //              .done(function (serverModel) {
    //                 self.SelectedGroup.showCommunityUsers();                              
    //                 $('#selectGroupsModal').modal('hide');                         
    //                 $('#addCommunityUserModal').modal('hide');
    //              });                           
    //         }                
    //     }    
    
    //     self.search = function () {
    //         var searchString = $($('#addCommunityUserModal input')[0]).val();
    //         searchedUsersForGroupsTable.fnFilter(searchString);
        
    // //         $.ajax({
    // //             type: "POST",
    // //             url: ContextPath + "Community/SearchUsers",
    // //             data: { sSearch: self.searchString, pageCount: self.pageCount }
    // //         })
    // //          .done(function (serverModel) {
    // //              communityModel.setUsersInGroup(serverModel);
    // //              communityModel.SelectedGroupId(self.Id);
    // //          });        
    //     } 
    
    //     self.deleteUser = function (userId, nick) {
    //         self.ActionedUser(new CommunityUserModel(userId,nick));
        
    //         $('#deleteCommunityUserModal').modal('show');                
    //     }
    
    //     self.groupsForSelect = function () {
    //         var result = self.Groups().slice(0);
        
    //         return result.splice(1);
    //     }
    
    //     self.ObserveUser = function (elem, userId) {
    //         $.ajax({
    //             type: "POST",
    //             url: ContextPath + "Community/ObserveUser",
    //             data: { userId: userId}
    //         })
    //          .done(function (serverModel) {
    //             $(elem).toggleClass('none');
    //          });         
    //     }    
    
    //     self.Groups.subscribeArrayChanged(
    //         function (addedItem) {
    //             self.DataTable.row.add(addedItem);//.draw();
    //         },
    //         function (deletedItem) {
    //             var rowIdx = self.DataTable.column(0).data().indexOf(deletedItem.Id);
    //             self.DataTable.row(rowIdx).remove();//.draw();
    //         },
    //         function () { //clearCallback
    //             self.DataTable.clear();
    //         }
    //     );    
}

function CommunityGroupModel(parent) {
    var self = this;
    self.Parent = parent;

    self.initValues = function () {
        self.Id = ko.observable(0);
        self.Name = ko.observable("");
        self.Selected = ko.observable("");
    }

    self.initValues();

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
        }
    }

    self.showCommunityUsers = function () {
        communityModel.SelectedGroupId(self.Id());
        if (communityModel.PreviousSelectedGroup != null)
        {
            communityModel.PreviousSelectedGroup.Selected('');
        }               
        self.Selected('active');
        communityModel.PreviousSelectedGroup = self;       
        usersInGroupTableApi.ajax.reload();
        //         $.ajax({
        //             type: "POST",
        //             url: ContextPath + "Community/GetCommunityModelJson",
        //             data: { modelType: "UsersInGroup", groupId: self.Id(), pageCount: communityModel.pageCount }
        //         })
        //          .done(function (serverModel) {
        //              communityModel.setUsersInGroup(serverModel);
        //              communityModel.SelectedGroupId(self.Id());
        //              if (communityModel.PreviousSelectedGroup != null)
        //              {
        //                 communityModel.PreviousSelectedGroup.Selected('');
        //              }               
        //              self.Selected('active');
        //              communityModel.PreviousSelectedGroup = self;
        //          });
    }

    self.removeCommunityGroup = function () {
        communityModel.ActionedGroup(self);
        $('#deleteCommunityGroupModal').modal('show');
    }

    self.editCommunityGroup = function () {
        communityModel.ActionedGroup(self);
        $($('#editCommunityGroupModal input')[0]).val(self.Name());
        $('#editCommunityGroupModal').modal('show');
    }
   
    self.save = function () {      
        self.Name($($('#editCommunityGroupModal input')[0]).val());
    
        var data = {
            Id: self.Id(),
            Name: self.Name(),
        };
        var strData = JSON.stringify(data);
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SaveGroup",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
         .done(function (result) {
             var rowIdx = selectGroupsTableApi.column(0).data().indexOf(communityModel.ActionedGroup().Id());
             var d = selectGroupsTableApi.row(rowIdx).data();
             d.Name(self.Name());
             selectGroupsTableApi.row(rowIdx).data(d);
             selectGroupsTable.fnDraw();                 
         });
        $('#editCommunityGroupModal').modal('hide');
    }
}

function CommunityUserModel(userId, nick, parent) {
    var self = this;
    self.Parent = parent;
    
    self.initValues = function () {
        self.Id = ko.observable(0);
        self.AvatarId = ko.observable(0);
        self.Nick = ko.observable("");
        self.MaleSex = ko.observable("");
        self.AvatarImg = ko.observable("");
        self.Name = ko.observable("");
        self.IsObserved = ko.observable(false);
        self.IsObserving = ko.observable(false);
        self.UserJoinId = ko.observable(0);        
        self.WantsToBeObserved      = ko.observable(false);        
        self.WantsToObserve         = ko.observable(false);        
        self.DontWantsToBeObserved  = ko.observable(false);        
        self.DontWantsToObserve      = ko.observable(false);        
        //         self.UserIdObservationAccepted = ko.observable(false);        
        //         self.UserIdObservingAccepted = ko.observable(false);        
        self.AskedToBeObserved      = ko.observable(false);        
        self.AskedToObserve         = ko.observable(false);        
        //         self.ObservationAccepted    = ko.observable(false);        
        //         self.ObservingAccepted    = ko.observable(false);        
        self.RejectedToBeObserved   = ko.observable(false);        
        self.RejectedToObserve      = ko.observable(false);        
        self.ObservingStatus      = ko.observable(0);        
        self.ObservedStatus      = ko.observable(0);        
        self.ObserverSawApprovalState = ko.observable(0);
        self.ObservedSawApprovalState = ko.observable(0);
        self.IsObserving = ko.observable(0);
        self.IsObserved      = ko.observable(0);        
        self.Visible      = ko.observable(false);   
        self.VisibleSecondUser      = ko.observable(false);   
        self.CloudTextTop      = ko.observable("");           
        self.CloudTextBottom      = ko.observable("");           
        self.ObservingUserId      = ko.observable(0);           
        self.ObservedUserId      = ko.observable(0);           
        self.RemoveUserFromObserveListText = ko.observable("");
        self.RemoveUserFromObservedListText= ko.observable("");
        self.YouSentObservingInvitationText= ko.observable("");
        self.SendObservingInvitationText   = ko.observable("");
        self.SendObservedInvitationText    = ko.observable("");
        self.YouSentObservedInvitationText = ko.observable("");        
        self.ReturnStatusObservingText   = ko.observable("");
        self.ReturnStatusObservedText    = ko.observable("");
        self.RemoveUserText = ko.observable("");        
        self.UnRemoveUserText = ko.observable("");        
        self.SendObservingAndObservedInvitationText = ko.observable("");        
        self.RemoveObservingInvitationText   = ko.observable("");
        self.RemoveObservedInvitationText = ko.observable("");
        self.IsFromMailFilter = ko.observable(false);
        self.IsFromFacebookFilter = ko.observable(false);
        self.IsForTrainingInvitation = ko.observable(false);
        self.Order = ko.observable(0);
        self.YouSentTrainingInvitationText = ko.observable("");
        self.SentTrainingInvitationText = ko.observable("");
    }

    self.initValues();
    
    if (userId) self.Id(userId);
    if (nick) self.Nick(nick);
    
    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.AvatarId(serverModel.AvatarId);
            self.Nick(serverModel.Nick);
            self.MaleSex(serverModel.MaleSex);  
            self.AvatarImg(self.avatarImg());
            self.Name(serverModel.Name);  
            self.IsObserved(serverModel.IsObserved);  
            self.IsObserving(serverModel.IsObserving);  
            self.UserJoinId(serverModel.UserJoinId);  
            self.WantsToBeObserved               (serverModel.WantsToBeObserved      );  
            self.WantsToObserve                  (serverModel.WantsToObserve         );  
            self.DontWantsToBeObserved           (serverModel.DontWantsToBeObserved  );  
            self.DontWantsToObserve              (serverModel.DontWantsToObserve     );  
            //             self.UserIdObservationAccepted         (serverModel.UserIdObservationAccepted);  
            //             self.UserIdObservingAccepted         (serverModel.UserIdObservingAccepted);  
            self.AskedToBeObserved               (serverModel.AskedToBeObserved      );  
            self.AskedToObserve                  (serverModel.AskedToObserve         );  
            //             self.ObservationAccepted             (serverModel.ObservationAccepted    );  
            //             self.ObservingAccepted             (serverModel.ObservingAccepted    );  
            self.RejectedToBeObserved            (serverModel.RejectedToBeObserved   );  
            self.RejectedToObserve               (serverModel.RejectedToObserve      );  
            self.ObservingStatus            (serverModel.ObservingStatus   );  
            self.ObservedStatus               (serverModel.ObservedStatus      );  
            self.ObserverSawApprovalState(serverModel.ObserverSawApprovalState);
            self.ObservedSawApprovalState(serverModel.ObservedSawApprovalState);
            self.IsObserving(serverModel.IsObserving);
            self.IsObserved               (serverModel.IsObserved      );  
            self.Visible               (serverModel.Visible      );  
            self.VisibleSecondUser               (serverModel.VisibleSecondUser      );  
            self.CloudTextTop               (serverModel.CloudTextTop      );  
            self.CloudTextBottom               (serverModel.CloudTextBottom      );  
            self.ObservingUserId               (serverModel.ObservingUserId      );  
            self.ObservedUserId               (serverModel.ObservedUserId      );  
            self.RemoveUserFromObserveListText               (serverModel.RemoveUserFromObserveListText      );  
            self.RemoveUserFromObservedListText               (serverModel.RemoveUserFromObservedListText      );  
            self.YouSentObservingInvitationText               (serverModel.YouSentObservingInvitationText      );  
            self.SendObservingInvitationText               (serverModel.SendObservingInvitationText      );  
            self.SendObservedInvitationText               (serverModel.SendObservedInvitationText      );  
            self.YouSentObservedInvitationText               (serverModel.YouSentObservedInvitationText      );  
            self.ReturnStatusObservingText               (serverModel.ReturnStatusObservingText      );  
            self.ReturnStatusObservedText               (serverModel.ReturnStatusObservedText      );  
            self.RemoveUserText               (serverModel.RemoveUserText      );  
            self.UnRemoveUserText               (serverModel.UnRemoveUserText      );  
            self.SendObservingAndObservedInvitationText               (serverModel.SendObservingAndObservedInvitationText      );  
            self.RemoveObservingInvitationText               (serverModel.RemoveObservingInvitationText      );  
            self.RemoveObservedInvitationText               (serverModel.RemoveObservedInvitationText      );  
            self.IsFromMailFilter(serverModel.IsFromMailFilter);
            self.IsFromFacebookFilter(serverModel.IsFromFacebookFilter);
            self.IsForTrainingInvitation(serverModel.IsForTrainingInvitation);
            self.Order(serverModel.Order);
            self.YouSentTrainingInvitationText(serverModel.YouSentTrainingInvitationText);
            self.SentTrainingInvitationText(serverModel.SentTrainingInvitationText);
        }
    }    
    
    self.GetParent = ko.computed(function () { 
        return self.Parent; 
    });

    self.AcceptBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() != 4)
        {
            return true;
        }
        if ( self.ObservedStatus() == 4 && self.ObservingStatus() != 1)
        {
            return true;
        }        
                
        return false; 
    });   

    
    self.AcceptObservingBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() != 4)
        {
            return true;
        }
        
        //if ( self.IsObserving() && self.IsObserved()) return false;
        //         if ( self.AskedToBeObserved() && !self.IsObserved() && !self.DontWantsToBeObserved()) return true;
        //         if ( self.AskedToObserve() && !self.IsObserving() && !self.DontWantsToObserve()) return true;
        
        return false; 
    });   
    
    self.AcceptObservedBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        if ( self.ObservedStatus() == 4 && self.ObservingStatus() != 1)
        {
            return true;
        }
                
        return false; 
    });

    self.RejectBtnVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 1 || self.ObservedStatus() == 4)
        {
            return true;
        }    
        
        return false; 
    });    
    
    self.RejectObservingBtnVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() != 4)
        {
            return true;
        }    
        //        if ( self.IsObserving() && self.IsObserved()) return false;
        //         if ( self.AskedToBeObserved() && !self.IsObserved() && !self.DontWantsToBeObserved()) return true;
        //         if ( self.AskedToObserve() && !self.IsObserving() && !self.DontWantsToObserve()) return true;
        
        return false; 
    });     
    
    self.RejectObservedBtnVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if ( self.ObservedStatus() == 4 && self.ObservingStatus() != 1)
        {
            return true;
        }    
        
        return false; 
    });     
    
    self.WantToObserveBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if (!self.Visible() || !self.VisibleSecondUser()) return false;
        //         if ( (self.ObservingStatus() == 1 || self.ObservedStatus() == 4) && !(self.IsObserving() || self.IsObserved()))
        //         {
        //             return false;
        //         }         
        
        if ( (self.ObservingStatus() == 13 || self.ObservingStatus() == 9 || 
             self.ObservingStatus() == 0) && (self.ObservedStatus() != 4 || !self.Visible())) return true;        
        //if ( (!self.WantsToObserve() && !self.IsObserving() && !self.AskedToObserve())) return true;
        //         if ( self.IsObserving()) return false;
        //         if ( !self.IsObserving() && self.AskedToObserve() ) return false;        
        //         if ( !self.IsObserved() && self.AskedToBeObserved() ) return false;        
        
        return false; 
    });    

    self.WantToBeObservedBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if (!self.Visible() || !self.VisibleSecondUser()) return false;
        //         if ( (self.ObservingStatus() == 1 || self.ObservedStatus() == 4) && !(self.IsObserving() || self.IsObserved()))
        //         {
        //             return false;
        //         }         
        
        if ( (self.ObservedStatus() == 6 || self.ObservedStatus() == 7 || 
             self.ObservedStatus() == 0) && (self.ObservingStatus() != 1 || !self.Visible())) return true;        
        //         if ( self.ObservedStatus() == 7 || self.ObservedStatus() == 6 || 
        //              (self.ObservingStatus() == 5 && self.ObservedStatus() == 0)
        //              ) return true;        
        
        //        if ( (!self.WantsToBeObserved() && !self.IsObserved() && !self.AskedToBeObserved())) return true;
        //         if ( (self.WantsToObserve() && !self.IsObserving()) || (self.WantsToBeObserved() && !self.IsObserved()) ) return false;
        //         if ( self.IsObserved()) return false;
        //         if ( !self.IsObserved() && self.AskedToBeObserved() ) return false;        
        //         if ( !self.IsObserving() && self.AskedToObserve() ) return false;        
        
        return false; 
    });   
    
    self.WantOnlyBeObservedBtnVisible = ko.computed(function () {        
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() == 4 )
        {
            return true;
        }           
        
        return false; 
    });     
        
    self.SaveInfoButtonsBtnVisible = ko.computed(function () {        
        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 1 || self.ObservedStatus() == 4 )
        {
            return true;
        }           
        
        return false; 
    });    
    
    self.WantToObserveAndBeObservedBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if (!self.Visible()) return false;
        if ((self.ObservingStatus() == 0 && self.ObservedStatus() == 0)) return true;
        //if (!self.Visible() || (self.ObservingStatus() == 0 && self.ObservedStatus() == 0)) return true;
                
        return false; 
    });    

    self.AlsoWantToObserveBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        //         if ( (self.ObservingStatus() == 1 && self.ObservedStatus() == 4))
        //         {
        //             return false;
        //         }        
        if ( (self.ObservedStatus() == 4) && 
            !(self.IsObserving() || self.IsObserved()) &&
            !(self.ObservedStatus() == 6 || self.ObservingStatus() == 6) &&
            !(self.ObservedStatus() == 9 || self.ObservingStatus() == 9))
        {
            return true;
        }        
        
        //         if ( self.WantsToObserve() || self.WantsToBeObserved()) return false;
        //         if ( self.IsObserving() || self.IsObserved()) return false;
        
        return false; 
    });    
    
    self.AlsoWantToBeObservedBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;

        //if ( self.ObservingStatus() == 0 && self.ObservedStatus() == 0) return true;
        //         if ( (self.ObservingStatus() == 1 && self.ObservedStatus() == 4))
        //         {
        //             return false;
        //         }        
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() == 4 )
        {
            return true;
        }        
                
        return false; 
    });    
    

    self.RejectTrashBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if ( self.ObservingStatus() == 0 && self.ObservedStatus() == 0) return false;
        
        return self.Visible(); 
    });  
        
    self.UnTrashBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        return !self.Visible();
    });  

    self.DontWantsToObserveBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if (!self.Visible()) return false;
        if ( self.ObservingStatus() == 5 || self.ObservingStatus() == 4) return true;
        //if ( self.IsObserving()) return true;
        
        return false; 
    });    

    self.DontWantsToBeObservedBtnVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation()) return false;

        if (!self.Visible()) return false;
        if ( self.ObservedStatus() == 5 || self.ObservedStatus() == 1) return true;
        
        //if ( self.IsObserved()) return true;
        
        return false; 
    });

    self.InviteFriendBtnVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        
        if (self.IsForTrainingInvitation()) {
            if (self.Order() == 0 || self.Order() == 4) return true;
        }

        return false;
    });
    
    self.CommunityAvatarCloudContainerClass = ko.computed(function () {
        if (self.AskedToObserve() && !self.IsObserving()) return 'bottom';         
        if (self.AskedToBeObserved() && !self.IsObserved()) return 'top';         
        
        return ''; 
    });
    
    self.CommunityAvatarCloudText = ko.computed(function () {
        if (self.AskedToObserve() && !self.IsObserving()) return translations['ObserveMe'];         
        if (self.AskedToBeObserved() && !self.IsObserved()) return translations['IWantToObserveYou'];         
        
        return ''; 
    });    
    
    
    self.wantOnlyBeObserved = function () {
        setJoinStatus(null,  9, 'PUT', 1, 'OR', true, null, null, null, self.UserJoinId());                               
    }    
    
    self.accept = function () {
        if (self.ObservingStatus() == 1)
        {
            setJoinStatus(null, 4, 'OR', null, null, null, true, null, null, self.UserJoinId());                               
        }
        if (self.ObservedStatus() == 4) 
        {
            setJoinStatus(null, null, null, 1, 'OR', true, null, null, null, self.UserJoinId());                               
        }
    }    

    self.acceptObserving = function () {
        if (self.AskedToObserve() && !self.IsObserving())
        {
            setJoinStatus(null, 4, 'OR', null, null, null, true, null, null, self.UserJoinId());                               
        }
    }
    
    self.acceptObserved = function () {
        if (self.AskedToBeObserved() && !self.IsObserved()) 
        {
            setJoinStatus(null, null, null, 1, 'OR', true, null, null, null, self.UserJoinId());                               
        }
    } 
    
    
    self.reject = function () {
        if ( self.ObservingStatus() == 1 && self.ObservedStatus() == 4) 
        {
            setJoinStatus(null, 9, 'PUT', 6, 'PUT', false, false, null, null, self.UserJoinId());                         
        }
        
        if (self.ObservingStatus() == 1)
        {
            setJoinStatus(null, 9, 'PUT', null, null, null, false, null, null, self.UserJoinId());               
        }
        if (self.ObservedStatus() == 4) 
        {
            setJoinStatus(null, null, null, 6, 'PUT', false, null, null, null, self.UserJoinId());             
        }        
    }    
    
    self.rejectObserving = function () {
        if (self.AskedToObserve() && !self.IsObserving())
        {
            setJoinStatus(null, 8, 'OR', null, null, null, null, null, null, self.UserJoinId());                               
        }
    }    

    self.rejectObserved = function () {
        if (self.AskedToBeObserved() && !self.IsObserved()) 
        {
            setJoinStatus(null, null, null, 2, 'OR', null, null, null, null, self.UserJoinId());                               
        }
    }

    self.wantToObserve = function () {
        if (self.ObservingStatus() == 0) {
            modalAskModel.Show(translations.InvitationMessage, '<textarea class="friendsInvitationText" maxlength="64"></textarea>', function () {
                modalAskModel.parent.wantToObserveCore($('#GenericAskModal .friendsInvitationText').val());
            }, translations.Send, 'friendsInvitationTextContainer', this, true);
            $('#GenericAskModal').width('430');
            $('#GenericAskModal .friendsInvitationText').val('');
        }
        else
        {
            self.wantToObserveCore();
        }
    }

    self.wantToObserveCore = function (invitationText) {  
        if (self.AskedToObserve())
        {
            if (self.DontWantsToObserve())
            {
                if (self.Visible())
                {
                    setJoinStatus(null, 5, 'PUT', null, null, null, true, true, null, self.UserJoinId());                                                                   
                }
                else
                {
                    setJoinStatus(null, 5, 'PUT', null, null, null, true, true, null, self.UserJoinId(), 1);                                               
                }
            }
            else
            {
                setJoinStatus(null, 4, 'OR', null, null, null, true, null, null, self.UserJoinId());                           
            }            
        }    
        else
        {
            setJoinStatus(null, 4, 'OR', null, null, null, null, null, null, self.UserJoinId(), self.ObservedUserId(), invitationText);                                       
        }
    }

    self.wantToBeObserved = function () {
        if (self.ObservedStatus() == 0) {
            modalAskModel.Show(translations.InvitationMessage, '<textarea class="friendsInvitationText" maxlength="64"></textarea>', function () {
                modalAskModel.parent.wantToBeObservedCore($('#GenericAskModal .friendsInvitationText').val());
            }, translations.Send, 'friendsInvitationTextContainer', this, true);
            $('#GenericAskModal').width('430');
            $('#GenericAskModal .friendsInvitationText').val('');
        }
        else {
            self.wantToBeObservedCore();
        }
    }
    
    self.wantToBeObservedCore = function (invitationText) {
        if (self.AskedToBeObserved())
        {    
            if (self.DontWantsToBeObserved())
            {
                if (self.Visible())
                {
                    setJoinStatus(null, null, null, 5, 'PUT', true, null, true, null, self.UserJoinId());             
                }
                else
                {
                    setJoinStatus(null, null, null, 5, 'PUT', true, null, true, null, self.UserJoinId(), 1);             
                }                
            }
            else
            {
                setJoinStatus(null, null, null, 1, 'OR', true, null, null, null, self.UserJoinId());             
            }    
        }
        else
        {
            setJoinStatus(null, null, null, 1, 'OR', null, null, null, null, self.UserJoinId(), self.ObservedUserId(), null, invitationText);
        }
    }
    
    self.dontWantsToObserve = function () {  
        if (self.ObservingStatus() == 5)
        {
            setJoinStatus(null, 9, 'PUT', null, null, null, false, null, null, self.UserJoinId());                           
        }
        else
        {
            setJoinStatus(null, 0, 'PUT', null, null, null, false, null, null, self.UserJoinId());                           
        }      
    }
    
    self.dontWantsToBeObserved = function () {    
        if (self.ObservedStatus() == 5)
        {    
            setJoinStatus(null, null, null, 6, 'PUT', false, null, null, null, self.UserJoinId());             
        }
        else
        {
            setJoinStatus(null, null, null, 0, 'PUT', false, null, null, null, self.UserJoinId());             
        }
    }

    self.wantToObserveAndBeObserved = function () {
        if (self.ObservingStatus() == 0 || self.ObservedStatus() == 0) {
            modalAskModel.Show(translations.InvitationMessage, '<textarea class="friendsInvitationText" maxlength="64"></textarea>', function () {
                modalAskModel.parent.wantToObserveAndBeObservedCore($('#GenericAskModal .friendsInvitationText').val());
            }, translations.Send, 'friendsInvitationTextContainer', this, true);
            $('#GenericAskModal').width('430');
            $('#GenericAskModal .friendsInvitationText').val('');
        }
        else {
            self.wantToObserveAndBeObservedCore();
        }
    }
    
    self.wantToObserveAndBeObservedCore = function (invitationText) {
        setJoinStatus(null, 4, 'OR', 1, 'OR', null, null, null, null, self.UserJoinId(), self.ObservedUserId(), invitationText);             
    
        //         if (self.AskedToBeObserved() && !self.IsObserved()) 
        //         {
        //             setJoinStatus(null, 4, 'OR', 1, 'OR', true, null, null, null, self.UserJoinId());               
        //         }else
        //         if (self.AskedToObserve() && !self.IsObserving())
        //         {
        //             setJoinStatus(null, 4, 'OR', 1, 'OR', null, true, null, null, self.UserJoinId());             
        //         }        
        //         else
        //         {
        //             setJoinStatus(null, 4, 'OR', 1, 'OR', null, null, null, null, self.UserJoinId(), self.ObservedUserId());             
        //         }
    }
    
    self.rejectTrashStep1 = function () {
        communityExplorerModel.ActionedUser(self);
        
        $('#deleteCommunityUserModal').modal('show');
    }
    
    self.rejectTrashStep2 = function () {        
        //         if ((self.AskedToBeObserved() && self.isObserved()) && 
        //             (self.AskedToObserve() && self.IsObserving())) 
        //             {
        //                 setJoinStatus(null, 8, 'OR', 2, 'OR', false, false, null, null, self.UserJoinId());                                               
        //             }else
        //         if ((self.AskedToBeObserved() && !self.isObserved())) 
        //             {
        //                 setJoinStatus(null, null, null, 2, 'OR', null, null, null, null, self.UserJoinId());                                               
        //             }else
        //         if ((self.AskedToObserve() && !self.IsObserving())) 
        //             {
        //                 setJoinStatus(null, 8, 'OR', null, null, null, null, null, null, self.UserJoinId());                                               
        //             }else
        //         if ((self.IsObserving())) 
        //             {
        //                 setJoinStatus(null, 8, 'OR', null, null, null, null, null, null, self.UserJoinId());                                               
        //             }        

        //         if ((self.WantsToBeObserved() && !self.isObserved()) || 
        //             (self.WantsToObserve() && !self.IsObserving()) 
        //             {
        //                 setJoinStatus(null, null, null, null, null, null, null, false, null, userJoinIdP);               
        //             }
        //             else
        //             {
        setJoinStatus(null,  9, 'PUT',  6, 'PUT', false, false, false, null, self.UserJoinId());                               
        //             }
        $('#deleteCommunityUserModal').modal('hide');
    }
    
    self.unTrash = function () {
        setJoinStatus(null, null, null, null, null, null, null, true, null, self.UserJoinId(), 1);                               
    }

    self.inviteFriend = function () {
        var messageId = communityExplorerModel.SelectedTrainingInvitation().NewId();
        //var isNew = communityExplorerModel.SelectedTrainingInvitation().IsNew();

        var data = {
            messageId: messageId,
            receiverId: self.Id()
            //isNew: isNew
        };

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/InviteFriendToTraining",
            contentType: "application/json; charset=utf-8",
            data: strData 
        })
        .done(function (result) {
            if (result) {
                communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvited.Search();

                //if ($.isArray(result)) {
                //    trainingInvitationsExplorerModel.SelectedForExplorer().UsersWhichWasInvitedIdsInitState = result;
                //    communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvitedIdsInitState = result;
                //}
            }
        });
    }
    
    self.BlackListed = ko.observable(false);
    self.doNotPropose=function(){
        //FriendProposeBlackListAdd(int friendId)
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/FriendProposeBlackListAdd",
            data: { friendId: self.Id() }
        })
           .done(function (serverModel) {
               self.BlackListed(true);
               self.GetParent().WantsObservedObserving.remove(self);
               if (!self.GetParent().WantsObservedObserving().length) {
                   self.GetParent().RandomProposes();
               }
           });
    }

    //     self.CloudText = ko.computed(function () { 
    //         if ( self.AskedToBeObserved() && !self.IsObserved()) 
    //         return '';        
        
    //         return 'none'; 
    //     });        
    
    self.Type = ko.computed(function () { 
        //         if ( (self.ObservingAccepted() || self.UserIdObservationAccepted()) && 
        //              (self.ObservationAccepted() || self.UserIdObservingAccepted())) return 'observingAndObserved';
        if ( self.IsObserving() && self.IsObserved()) return 'observingAndObserved';
        //         if (self.ObservedStatus() == 4) return 'waiting';
        //         if (self.ObservingStatus() == 1) return 'waiting';
        if (self.IsObserving()) return 'observing';
        if (self.IsObserved()) return 'observed';
        if ((self.DontWantsToBeObserved() || self.DontWantsToObserve()) ||
            (self.RejectedToBeObserved() || self.RejectedToObserve())) return 'rejected';
        if (self.WantsToBeObserved() || self.WantsToObserve()) return 'invited';
        
        return 'none'; 
    });    
    
    self.AvatarColor = ko.computed(function () { 
        if (!self.Visible()) return 'black';
        if (self.Type() == 'observingAndObserved') return 'two_colors';
        if (self.Type() == 'observed') return 'mod';
        if (self.Type() == 'observing') return 'red';
        if (self.Type() == 'rejected') return 'gray';
        if (self.Type() == 'invited') return 'gray';
        if (self.ObservingStatus() == 1) return 'gray';
        if (self.ObservedStatus() == 4) return 'gray';
       
        return 'gray'; 
    });

    self.AvatarTopCloudText = ko.computed(function () {
        if (self.IsForTrainingInvitation()) {
            if (self.Order() == 2) return translations['ImJoiningHappyFace'];
            if (self.Order() == 3) return translations['ImNotJoiningSadFace'];

            return "";
        }            

        if (self.ObservingStatus() == 1) return translations['ObserveMe'];
        else
            if (self.ObserverSawApprovalState() == 4) { 
                if (self.ObservingStatus() == 5) return translations['YouObserveMe'];
                if (self.ObservingStatus() == 6) return translations['IDontWantToBeObserved'];
            }
    });

    self.AvatarBottomCloudText = ko.computed(function () {
        if (self.ObservedStatus() == 4) return translations['IWantToObserveYou'];
        else
            if (self.ObservedSawApprovalState() == 1) {
                if (self.ObservedStatus() == 5) return translations['IObserveYou'];
                if (self.ObservedStatus() == 9) return translations['IDontWantToObserveYou'];
            }
    });

    self.AvatarTopCloudColor = ko.computed(function () {
        if (self.IsForTrainingInvitation()) {
            if (self.Order() == 2) return 'red';
            if (self.Order() == 3) return 'gray';
        }

        if (self.ObserverSawApprovalState() == 4) { 
            if (self.ObservingStatus() == 6) return 'black';
        }
        
        return 'red';
    });

    self.AvatarBottomCloudColor = ko.computed(function () {
        if (self.ObservedSawApprovalState() == 1) {
            if (self.ObservedStatus() == 9) return 'black';
        }

        return 'mod';
    });    

    //     self.AvatarIcon = ko.computed(function () { 
    //         if (self.Type() == 'observingAndObserved') return 'observerAndObserved_30';
    //         if (self.Type() == 'observed') return 'oko_20';
    //         if (self.Type() == 'observing') return 'lupa_28';
    //         if (self.Type() == 'rejected') return 'zamknij_22';
    //         if (self.ObservingStatus() == 4) return 'arrowRight_22';
    //         if (self.ObservedStatus() == 1) return 'arrowLeft_22';
        
    //         return 'none';  
    //     });   
    
    self.AvatarIconObservingVisible = ko.computed(function () { 
        if (self.ObservingStatus() == 5 && self.Visible()) return true;
        //         if (self.Type() == 'observingAndObserved' || self.IsObserving() ) return true;
        return false;  
    });    

    self.AvatarIconObservedVisible = ko.computed(function () { 
        if (self.ObservedStatus() == 5 && self.Visible()) return true;
        //         if (self.Type() == 'observingAndObserved' || self.IsObserved()) return true;
    
        return false;  
    });    

    self.AvatarIconRejectedObservingVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if (self.DontWantsToObserve() || self.RejectedToObserve()) return true;
    
        return false;  
    });    

    self.AvatarIconRejectedObservedVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if (self.DontWantsToBeObserved() || self.RejectedToBeObserved()) return true;
    
        return false;  
    });    

    self.AvatarIconInvitedObservingVisible = ko.computed(function () { 
        if (!self.Visible()) return false;
        if (self.ObservingStatus() == 4) return true;
    
        return false;  
    });

    self.AvatarIconInvitedVisible = ko.computed(function () {
        if (self.IsForTrainingInvitation() && self.Order() == 1) return true;

        return false;
    });

    self.AvatarIconInvitedObservedVisible = ko.computed(function () {
        if (!self.Visible()) return false;
        if (self.ObservedStatus() == 1) return true;
    
        return false;  
    });    
    
    self.avatarImg = function () {
        var resutl = '<img src=';
        if (self.AvatarId() != null)
        {
            resutl = resutl + '/Image/GetImage?width=156&height=156&id=' + self.AvatarId();
        }            
        else
        {
            resutl = resutl + ContextPath + 'Content/images/icons/'
            if (self.MaleSex()=='M')
            {
                resutl = resutl + 'mezczysna_36.png';                                        
            }else
            {
                resutl = resutl + 'kobieta_36_mod.png';                                        
            }            
        }
    }    



    self.removeUserFromCommunity = function () {
    }   
}

function loadCommunityUsersInGroupTable() {
    //personsTable = $('#usersInGroupTable').dataTable({
    //    "oLanguage": dataTableLanguageOptions,
    //    "aoColumns": [
    //      null, null, null, null, { "sWidth": "100px" }, { "sWidth": "50px" }, { "sWidth": "50px" }, null
    //    ],
    //    "bProcessing": true,
    //    "bServerSide": true,
    //    "sAjaxSource": ContextPath + "PersonsManagement/GetClubPersonsListJson",
    //    "bServerSide": true,
    //    "fnServerData": function (sSource, aoData, fnCallback, oSettings) {
    //        aoData.push({ name: "Mode", value: PersonManagementMode });
    //        oSettings.jqXHR = $.ajax({
    //            "dataType": 'json',
    //            "type": "POST",
    //            "url": sSource,
    //            "data": aoData,
    //            "success": fnCallback
    //        });
    //    },
    //    "sServerMethod": "POST",
    //    "bStateSave": true
    //});


}


if (typeof GeneralModel !== 'undefined' && GeneralModel) {
    var communityModel = new CommunityModel();
    GeneralModel.addProperty("CommunityModel", communityModel);
    var communityModelForUsers = new CommunityModel('EnterNameOrNick');
    GeneralModel.addProperty("CommunityModelForUsers", communityModelForUsers);
    var communityExplorerModel = new CommunityExplorerModel();
    GeneralModel.addProperty("CommunityExplorerModel", communityExplorerModel);
    var trainingInvitationsExplorerModel = new TrainingInvitationModelList();
    GeneralModel.addProperty("TrainingInvitationsExplorerModel", trainingInvitationsExplorerModel);
    var clubTableExplorerModel = new ClubTableExplorerModel();
    GeneralModel.addProperty("ClubTableExplorerModel", clubTableExplorerModel);
    var communityUserProfileExplorerModel = new CommunityUserProfileExplorerModel();
    GeneralModel.addProperty("CommunityUserProfileExplorerModel", communityUserProfileExplorerModel);
}

function deleteCommunityGroup() {
    $.ajax({
        type: "POST",
        url: ContextPath + "Community/RemoveGroup",
        data: { groupId: communityModel.ActionedGroup().Id() }
    })
     .done(function (result) {
         removeKoNoFunctionItem(communityModel.Groups, communityModel.ActionedGroup());
         var rowIdx = selectGroupsTableApi.column(0).data().indexOf(communityModel.ActionedGroup().Id());
         selectGroupsTableApi.row(rowIdx).remove();        
         selectGroupsTable.fnDraw();
         
         if (communityModel.ActionedGroup().Selected() == 'active')
         {
             communityModel.SelectedGroupId(0);
         }         
         communityModel.SelectedGroup.showCommunityUsers();             
     });
     
    $('#deleteCommunityGroupModal').modal('hide');
}

function deleteCommunityUser() {
    $.ajax({
        type: "POST",
        url: ContextPath + "Community/RemoveUserFromGroup",
        data: { groupId: communityModel.SelectedGroup.Id(), userId: communityModel.ActionedUser().Id() }
    })
     .done(function (serverModel) {
         usersInGroupTableApi.ajax.reload();             
     });      
    $('#deleteCommunityUserModal').modal('hide');
}


function addCommunityGroupModal() {
    var groupName = $($('#addCommunityGroupModal input')[0]).val();

    $.ajax({
        type: "POST",
        url: ContextPath + "Community/AddGroup",
        data: { groupName: groupName }
    })
     .done(function (serverModel) {
         var elem = new CommunityGroupModel();
         elem.initByModel(serverModel);
         communityModel.Groups.push(elem);
         communityModel.SelectedGroupId(elem.Id());
         communityModel.SelectedGroup.showCommunityUsers();
         selectGroupsTableApi.row.add(elem);         
         selectGroupsTable.fnDraw();
     });
    $('#addCommunityGroupModal').modal('hide');
    $($('#addCommunityGroupModal input')[0]).val("");
}

// function cancelCommunityGroupModal()
// {
//     $($('#addCommunityGroupModal input')[0]).val("");
// }

function cancelAddUserModal()
{
}


function refreshMainLists()
{
    if ($('.row-fluid.box.communityObservedAndObservers').is(':Visible')) {
        communityModel.CommunityModelForWaitings().Search();
        
        if ($('#collapseCommunityObservedAndObservers').hasClass('in')) {
            communityModel.Search();    
        }
    }
}

// function cancelSelectGroupsModal()
// {
//     selectGroupsModalTableSelected = [];
// }

function registerCommunityUsersDatatable() {
    //    usersInGroupTable = $('#usersInGroupTable').dataTable();

    //     usersInGroupTable = $('#usersInGroupTable').dataTable({
    //         "oLanguage": dataTableLanguageOptions,
    //         "columnDefs": [
    //           { "visible": false, "targets": 0 }, {"orderable": false, "targets": [0,1,2,3]  }
    //         ],    
    //         "scrollY": "200px",
    //     });

    //     usersInGroupTable = $('#usersInGroupTable').dataTable({
    //         "oLanguage": dataTableLanguageOptions,
    //         "columnDefs": [
    //           { "visible": false, "targets": 0 }, {"orderable": false, "targets": [0,1,2,3]  }
    //         ],    
    // //        "aoColumns": [
    // //             null, null,null,null,
    // // //           { "sWidth": "10px" }, { "sWidth": "10px" }, { "sWidth": "10px" }, { "sWidth": "10px" }
    // //         ],        
    //         "scrollY": "200px",
    // //         "scrollCollapse": true,  
    //         "bServerSide": true,
    //         "sAjaxSource": ContextPath + "Community/SearchUsers",
    //         "fnServerData": function (sSource, aoData, fnCallback, oSettings) {
    //            aoData.push({ name: "sGroupId", value: communityModel.SelectedGroup.Id() });                
    //            oSettings.jqXHR = $.ajax({
    //                "dataType": 'json',
    //                "type": "POST",
    //                "url": sSource,
    //                "data": aoData,
    //                "success": function(result){
    //                     //communityModel.setUsersInGroup(result);                    
    //                     fnCallback(result); 
    //                }
    //            });
    //         },
    //         "sServerMethod": "POST",
    //         "bStateSave": true,
    //         "aoColumns": [
    //              { "mData": "Id" },
    //              { "mData": "AvatarImg" },
    //              { "mData": "Nick" },
    //              { "mData": "Minusik" }
    //          ],
    //          //"sAjaxDataProp": "",
    //     });
    //     usersInGroupTableApi = usersInGroupTable.api();
   
    //    usersInGroupTable._fnLengthChange(communityModel.pageCount);
    
    //     $('#usersInGroupTable').on( 'page.dt', function () {
    //         var info = usersInGroupTableApi.page.info();
    //     } );

    //     searchedUsersForGroupsTable = $('#addCommunityUserModalTable').dataTable({
    //         "oLanguage": dataTableLanguageOptions,
    //         "columnDefs": [
    //           { "visible": false, "targets": 0 }, {"orderable": false, "targets": [0,1,2]  }
    //         ],    
    //         "scrollY": "200px",
    // //         "scrollCollapse": true,  
    //         "bServerSide": true,
    //         "sAjaxSource": ContextPath + "Community/SearchUsers",
    //         "fnServerData": function (sSource, aoData, fnCallback, oSettings) {
    //            oSettings.jqXHR = $.ajax({
    //                "dataType": 'json',
    //                "type": "POST",
    //                "url": sSource,
    //                "data": aoData,
    //                "success": function(result){
    //                     fnCallback(result); 
    //                }
    //            });
    //         },
    //         "sServerMethod": "POST",
    //         "bStateSave": true,
    //         "aoColumns": [
    //              { "mData": "Id" },
    //              { "mData": "AvatarImg" },
    //              { "mData": "Nick" }
    //          ],    
    //         "rowCallback": function( row, data, displayIndex ) {
    //             if ( $.inArray(data.DT_RowId, addCommunityUserModalTableSelected) !== -1 ) {
    //                 $(row).addClass('selected');
    //             }  
    //         }
    //          });             

    //     searchedUsersForGroupsTableApi = searchedUsersForGroupsTable.api();
    
    //     $('#addCommunityUserModalTable tbody').on('click', 'tr', function () {
    //         var sData = searchedUsersForGroupsTable.fnGetData( this );        
    //         var id = sData.Id;
    //         var index = $.inArray(id, addCommunityUserModalTableSelected);
 
    //         if ( index === -1 ) {
    //             addCommunityUserModalTableSelected.push( id );
    //         } else {
    //             addCommunityUserModalTableSelected.splice( index, 1 );
    //         }
 
    //         $(this).toggleClass('selected');
    //     } );  
    
    
    //     selectGroupsTable = $('#selectGroupsModalTable').dataTable({
    //         "data": communityModel.groupsForSelect(),
    //         "oLanguage": dataTableLanguageOptions,
    //         "columnDefs": [
    //           { "visible": false, "targets": 0 }, {"orderable": false, "targets": [0,1]  }
    //         ],    
    //         "scrollY": "200px",
    // //         "scrollCollapse": true,  
    //         "aoColumns": [
    //              { "mData": "Id" },
    //              { "mData": "Name" }
    //          ],    
    //         "rowCallback": function( row, data, displayIndex ) {
    //             if ( $.inArray(data.DT_RowId, addCommunityUserModalTableSelected) !== -1 ) {
    //                 $(row).addClass('selected');
    //             }  
    //         }
    //          });     

    //     selectGroupsTableApi = selectGroupsTable.api();         
         
    //     $('#selectGroupsModalTable tbody').on('click', 'tr', function () {
    //         var sData = selectGroupsTable.fnGetData( this );        
    //         var id = sData.Id();
    //         var index = $.inArray(id, selectGroupsModalTableSelected);
 
    //         if ( index === -1 ) {
    //             selectGroupsModalTableSelected.push( id );
    //         } else {
    //             selectGroupsModalTableSelected.splice( index, 1 );
    //         }
 
    //         $(this).toggleClass('selected');
    //     } ); 
    
    //initObservableUsersTable();
}

// function initObservableUsersTable()
// {
//     observableUsersTable = $('#observableUsersTable').dataTable({
//         "oLanguage": dataTableLanguageOptions,
//         "columnDefs": [
//           { "visible": false, "targets": 0 }, {"orderable": false, "targets": [0,1,2,3]  }
//         ],    
// //        "aoColumns": [
// //             null, null,null,null,
// // //           { "sWidth": "10px" }, { "sWidth": "10px" }, { "sWidth": "10px" }, { "sWidth": "10px" }
// //         ],        
//         "scrollY": "200px",
//         //"scrollCollapse": true,  
//         "bServerSide": true,
//         "sAjaxSource": ContextPath + "Community/SearchObservedUsers",
//         "fnServerData": function (sSource, aoData, fnCallback, oSettings) {
//            oSettings.jqXHR = $.ajax({
//                "dataType": 'json',
//                "type": "POST",
//                "url": sSource,
//                "data": aoData,
//                "success": function(result){
//                     //communityModel.setUsersInGroup(result);                    
//                     fnCallback(result); 
//                }
//            });
//         },
//         "sServerMethod": "POST",
//         //"bStateSave": true,
//         "aoColumns": [
//              { "mData": "Id" },
//              { "mData": "AvatarImg" },
//              { "mData": "Nick" },
//              { "mData": "ObserveUser" }
//          ],
//          //"sAjaxDataProp": "",
//     });    
    
//     observableUsersTableApi = selectGroupsTable.api();       
// }


// function initTables()
// {    
//     initWantsObserveObservingTable();
// }

// function initWantsObserveObservingTable()
// {
//     wantsObserveObservingTable = $('#wantsObserveObservingTable').dataTable({
//         "oLanguage": dataTableLanguageOptions,
// //         "columnDefs": [
// //           { "visible": false, "targets": [0,1,2,3,4]  }, {"orderable": false, "targets": [0,1,2,3]  }
// //         ],   
//         "bServerSide": true,
//         "sAjaxSource": ContextPath + "Community/SearchWants",
//         "fnServerData": function (sSource, aoData, fnCallback, oSettings) {
//            oSettings.jqXHR = $.ajax({
//                "dataType": 'json',
//                "type": "POST",
//                "url": sSource,
//                "data": aoData,
//                "success": function(result){
//                     fnCallback(result); 
//                     communityShowNotifications(result);
//                }
//            });
//         },
//         "sServerMethod": "POST",
//         "iDisplayLength"    : 3,
//         //"bStateSave": true,
//         "aoColumns": [
//              { "mData": "Id" },
//              { "mData": "AvatarImg" },
//              { "mData": "Nick" },
//              { "mData": "AskedToBeObserved" },//obseruj mnie 
//              { "mData": "AskedToObserve" }//chcę cię obserwować 
//          ],
//     });                
    
//     wantsObserveObservingTableApi = wantsObserveObservingTable.api();   
    
//     wantsObserveObservingTable._fnLengthChange(3);  
// }

function callBackFromExplorerModelInit()
{
    if (communityExplorerModel.SelectedTab() == 'Trainings') 
    {
        communityExplorerModel.TabTrainingsCount_old = communityExplorerModel.TabTrainingsCount();
        addToCache('TabTrainingsCount_old', communityExplorerModel.TabTrainingsCount_old, 86400000);//3600000);
    }
    else
        if (communityExplorerModel.SelectedTab() == 'TrainingInvitations')
        {
            communityExplorerModel.TabTrainingInvitationsCount_old = communityExplorerModel.TabTrainingInvitationsCount();
            addToCache('TabTrainingInvitationsCount_old', communityExplorerModel.TaTrainingInvitationsCount_old, 86400000);//3600000);
        }
        else
            if (communityExplorerModel.SelectedTab() == 'Competitions') 
            {
                communityExplorerModel.TabCompetitionsCount_old = communityExplorerModel.TabCompetitionsCount();
                addToCache('TabCompetitionsCount_old', communityExplorerModel.TabCompetitionsCount_old, 86400000);//3600000);
            }        
            else
                if (communityExplorerModel.SelectedTab() == 'Targets') 
                {
                    communityExplorerModel.TabTargetsCount_old = communityExplorerModel.TabTargetsCount();
                    addToCache('TabTargetsCount_old', communityExplorerModel.TabTargetsCount_old, 86400000);//3600000);        
                }
                else
                    if (communityExplorerModel.SelectedTab() == 'LiveStreams') 
                    {
                        communityExplorerModel.TabLiveStreamsCount_old = communityExplorerModel.TabLiveStreamsCount();
                        addToCache('TabLiveStreamsCount_old', communityExplorerModel.TabLiveStreamsCount_old, 86400000);//3600000);        
                    }
                    else
                        if (communityExplorerModel.SelectedTab() == 'Publications') 
                        {
                            communityExplorerModel.TabPublicationsCount_old = communityExplorerModel.TabPublicationsCount();
                            addToCache('TabPublicationsCount_old', communityExplorerModel.TabPublicationsCount_old, 86400000);//3600000);        
                        }
}

function communityRefreshTrainings(withoutLoadingData)
{
    $('#communitySideTile').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButtons').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButton.refresh_20').css('cursor', 'wait');
   
    clearTimeout(communityModel.LoadTrainingsTimeoutHandler);
    communityModel.LoadTrainingsTimeoutHandler = null;        
    
    clearTimeout(communityModel.LoadNextTrainingsTimeoutHandler);
    communityModel.LoadNextTrainingsTimeoutHandler = null;        
    
    clearTimeout(communityModel.LoadMissingTrainingsTimeoutHandler);
    communityModel.LoadMissingTrainingsTimeoutHandler = null;        
    
    communityModel.NumberOfTimeouts = 0;
   
    //var prefix = 'communityTrainings_' + communityExplorerModel.UrlEnd();
    //var keys = getCacheKeys(prefix);    
    
    //for (var i = 0, len = keys.length; i < len; i++) {
    //    var keyWithParams = keys[i].substring(prefix.length);
    //    deleteFromCache(keyWithParams);
    //    deleteFromCache(keys[i]);
    //}              

    communityExplorerModel.clearAllCachedTabs();
    
    //deleteFromCache('actualScrollPosition_' + communityExplorerModel.UrlEnd());
    addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), 0, 86400000);//3600000);
    //$("#communityTrainings").scrollTop(0);
    $(".sideTileCommunity #communityTrainings").mCustomScrollbar('scrollTo', 0);
    
    clearTimeout(communityExplorerModel.LoadModelTimeoutHandler);
    communityExplorerModel.LoadModelTimeoutHandler = null;
    communityExplorerModel.NumberOfTimeouts = 0;

    if (!withoutLoadingData) {
        communityExplorerModel.init(true, callBackFromExplorerModelInit);
        communityLoadFirstTrainings(true);
    }
    
    //communityCheckScroll();
    
    //     $.ajax({
    //         type: "POST",
    //         url: ContextPath + "Community/Trainings"
    //     })
    //     .done(function (result) {
    //         var container = $('#communityTrainings');
        
    //         container.html(result);
                            
    //         communityCheckScroll();        
        
    //         var alreadyDisplayedTrainingIds = $('.displayedTrainingIds', container).data().alreadydisplayed

    //         for (var i = 0, len = alreadyDisplayedTrainingIds.length; i < len; i++) {
    //             communityModel.AlreadyDisplayedTrainingIds.push(alreadyDisplayedTrainingIds[i]);
    //         }          
    //     });  
}

function communityWallSetTargetValue(container)
{
    var targetValueDiv = 
        $('.training.target .targetName', container);
    
    if (targetValueDiv.length > 0)
    {
        targetValueDiv.each(function() {
            //            $(this).data().trainingid);
            var data =  $(this).data();        
            var value = data.value;
            var valueType = data.valuetype;
            var valueUnit = data.valueunit;        
            var valueFirstText = data.firsttext;        
            var valueLastText = data.lasttext;   
            var resultValue = '';     
            var resultValueTitle = '';     
            
            if (valueType != 'none')
            {
                var formattedValue = formatedTargetValueWithUnit(value, valueType, valueUnit);

                if (valueLastText)
                {
                    resultValue = valueFirstText + ' <span>' + formattedValue + '</span> ' + valueLastText;
                    resultValueTitle = valueFirstText + ' ' + formattedValue + ' ' + valueLastText;
                }
                else
                {
                    resultValue = valueFirstText + ' <span>' + formattedValue + '</span>';
                    resultValueTitle = valueFirstText + ' ' + formattedValue;
                }


                $(this).html(resultValue);
                $(this).attr('title', resultValueTitle);
            }
            else
            {
                if (valueLastText)
                {
                    resultValue = valueFirstText + ' <span>' + value + '</span> ' + valueLastText;
                    resultValueTitle = valueFirstText + ' ' + value + ' ' + valueLastText;
                }
                else
                {
                    resultValue = valueFirstText + ' <span>' + value + '</span>';
                    resultValueTitle = valueFirstText + ' ' + value;
                }

                $(this).html(resultValue);
                $(this).attr('title', resultValueTitle);
            }
        });               
    }
}

function communitySetPackageData(container)
{
    if ($('.data', container).length > 0)
    {
        var data = $('.data', container).data();

        communityModel.LastUploadNotViewedTime = data.lastuploadnotviewedtime;
        communityModel.LastUploadNotReadCommentTime = data.lastuploadtimenotreadcomment;
        //communityModel.LastUploadViewedCommentedTime = data.lastuploadviewedcommentedtime;
        communityModel.RunNewQuery = data.runnewquery;
        communityModel.AllLoaded = data.allloaded == "True";
        //        communityModel.TimeoutOccured = data.timeoutoccured == "True";
    } 

    if ($('.lastUploadTime', container).length > 0)
    {
        var LastUploadTime = $('.lastUploadTime', container).data().lastuploadtime;

        communityModel.LastUploadTime = LastUploadTime;
    }    
}

function communityIsTimeout(container)
{
    if ($('.data', container).length > 0)
    {
        var data = $('.data', container).data();

        return data.timeoutoccured == "True";
    } 

    return false;
}

function setCommunityWallWaitCursor() {
    $('#communitySideTile').css('cursor', 'wait');
    $('#communityTrainings .training').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButton.down').css('cursor', 'wait');
    $('#communitySideTile .communityTopBoxButtonParent').css('cursor', 'wait');
    $('#communitySideTile .communityTopBoxButton').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButton.gray').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButton').css('cursor', 'wait');
    $('#communitySideTile .communityUserBarTileButton a').css('cursor', 'wait');   
    $('#communitySideTile .sideTileCommunityExpanderContainer a').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButtons').css('cursor', 'wait');
}
function setCommunityWallNormalCursor() {
    $('#communitySideTile').css('cursor', 'auto');
    $('#communitySideTile .communityUsersButtons').css('cursor', 'auto');
    $('#communitySideTile .communityUsersButton.refresh_20').css('cursor', 'pointer');
    $('#communityTrainings .training').css('cursor', 'pointer');
    $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'pointer');
    $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'pointer');
    $('#communitySideTile .communityUsersButton.down').css('cursor', 'pointer');
    $('#communitySideTile .communityTopBoxButtonParent').css('cursor', 'pointer');
    $('#communitySideTile .communityTopBoxButton').css('cursor', 'pointer');
    $('#communitySideTile .communityUsersButton.gray').css('cursor', 'auto');
    $('#communitySideTile .communityUsersButton').css('cursor', 'pointer');
    $('#communitySideTile .communityUserBarTileButton a').css('cursor', 'pointer');
    $('#communitySideTile .sideTileCommunityExpanderContainer a').css('cursor', 'pointer');
    $('#communitySideTile .communityUsersButtons').css('cursor', 'auto');
}

function addNoElementsTile(containerDiv) {
    var noElementsTileDiv = $(document.createElement('div'));
    var noElementsTileTextDiv = $(document.createElement('div'));
    var noElemensText = null;

    if (communityExplorerModel.SelectedTab() == 'Trainings') {
        noElemensText = translations.NoTrainings;
    }
    if (communityExplorerModel.SelectedTab() == 'TrainingInvitations') {
        noElemensText = translations.NoTrainingInvitations;
    }
    if (communityExplorerModel.SelectedTab() == 'Competitions') {
        noElemensText = translations.NoCompetitions;
    }
    if (communityExplorerModel.SelectedTab() == 'Targets') {
        noElemensText = translations.NoTargets;
    }
    if (communityExplorerModel.SelectedTab() == 'LiveStreams') {
        noElemensText = translations.NoTransmissions;
    }
    if (communityExplorerModel.SelectedTab() == 'Publications') {
        noElemensText = translations.NoPublications;
    }

    noElementsTileDiv.addClass('communityNoElementsTile displayNone');
    //noElementsTileTextDiv.addClass('communityNoElementsTileText');

    //noElementsTileTextDiv.append(noElemensText);
    noElementsTileDiv.append('<div class="communityNoElementsTileText">' + noElemensText + '</div>');

    containerDiv.append(noElementsTileDiv);
}

function communityLoadFirstTrainings(refresh, doneCalback)
{
    $('#communitySideTile').css('cursor', 'wait');
    $('#communityTrainings .training').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'wait');

    var callParams = {
        type: "POST",
        url: ContextPath + "Community/" + communityExplorerModel.UrlEnd()
    }    

    var key = 'communityTrainings_' + communityExplorerModel.UrlEnd() + getCacheKeyFromCallParams(callParams);
    
    cachedAjaxCall(callParams, function (result) {
        var containerDiv = $(document.createElement('div'));
        containerDiv.append(result);
            
        addNoElementsTile(containerDiv);

        containerDiv.append('<div class="clearfix"></div>');

        var container = $('#communityTrainings');
        var isTimeout = communityIsTimeout(containerDiv);

        if (communityModel.LoadTrainingsTimeoutHandler) {
            clearTimeout(communityModel.LoadTrainingsTimeoutHandler);
            communityModel.LoadTrainingsTimeoutHandler = null;        
        }

        if (isTimeout) 
        {
            var keyToDelete = getCacheKeyFromCallParams(callParams);

            deleteFromCache(keyToDelete);

            container.html('');
            communityCheckScroll(refresh, true);   
            communityExplorerModel.ShowDownArrow(false);   

            communityModel.NumberOfTimeouts++;
            communityModel.LoadTrainingsTimeoutHandler = 
            setTimeout(function() {   
                communityLoadFirstTrainings(refresh, doneCalback);
            }, 500 * communityModel.NumberOfTimeouts);                                                                          
        } else
        {
            var packageType = $('.data', containerDiv).data().packagetype;
                
            if (packageType == communityExplorerModel.SelectedTab()) 
            {
                communityModel.NumberOfTimeouts = 0;

                $(".sideTileCommunity #communityTrainings").mCustomScrollbar('destroy');

                container.html(containerDiv);

                $(".sideTileCommunity #communityTrainings").mCustomScrollbar({
                    callbacks: {
                        whileScrolling: function () {
                            var clientHeight = $('.sideTileCommunity #communityTrainings.mCustomScrollbar').height();
                            var scrollHeight = $(".sideTileCommunity #communityTrainings").find('.mCSB_container').height();
                            var scrollTop = getActualScrollPosition($('.sideTileCommunity #communityTrainings'));

                            if ((scrollHeight - (scrollTop + clientHeight)) <= (20 * 55))
                            {
                                communityLoadNextPageTrainings();                     
                            } 
                        },
                        onTotalScrollOffset: 20 * 55,
                        //mouseWheelPixels: 55
                        onScroll:function(){
                            var actualScrollPosition = getActualScrollPosition($('.sideTileCommunity #communityTrainings'));

                            addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), actualScrollPosition, 86400000);//3600000);                               
                        },                        
                    },
                    scrollbarPosition: "outside",
                    autoHideScrollbar: true
                });

                adjustTrainingPackageCountFont();
                communityCheckScroll(refresh);        

                var alreadyDisplayedTrainingIds = $('.displayedTrainingIds', container).data().alreadydisplayed;

                communitySetPackageData(container);           

                communityWallSetTargetValue(containerDiv);

                var trainingsCount = $('.training', container).length;

                if (trainingsCount == 0) {
                    var communityNoElementsTile = $('.communityNoElementsTile');
                    communityNoElementsTile.removeClass('displayNone');
                }

                showHideScrolls(trainingsCount, refresh == null); 

                addToCache(key, "", 86400000);//3600000);                
            }
        }         

        $('#communitySideTile').css('cursor', 'auto');
        $('#communitySideTile .communityUsersButtons').css('cursor', 'auto');  
        $('#communitySideTile .communityUsersButton.refresh_20').css('cursor', 'pointer');          
        $('#communityTrainings .training').css('cursor', 'pointer');
        $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'pointer');
        $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'pointer');

        if (doneCalback && !isTimeout) doneCalback();            
    }, 86400000//24h //3600000 //1h
    );     
}

function communityLoadNextPageTrainings()
{
    if (loadNextPackageToCommunityTrainingsBusy || communityModel.AllLoaded || communityModel.LoadTrainingsTimeoutHandler) return;    

    $('#communitySideTile').css('cursor', 'wait');
    $('#communityTrainings .training').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'wait');
    $('#communitySideTile .communityUsersButton.down').css('cursor', 'wait');

    loadNextPackageToCommunityTrainingsBusy = true;

    var alreadyDisplayed = [];
        
    //     $( "#communityTrainings .training" ).each(function() {
    //         alreadyDisplayed.push($(this).data().trainingid);
    //     });       
    
    var data = {
        //alreadyDisplayed: communityModel.AlreadyDisplayedTrainingIds
        lastUpdateTimeAsString: communityModel.LastUploadTime,
        lastUpdateTimeNotViewedAsString: communityModel.LastUploadNotViewedTime,
        lastUpdateTimeNotReadCommentAsString: communityModel.LastUploadNotReadCommentTime,
        //lastUpdateTimeViewedCommentedAsString: communityModel.LastUploadViewedCommentedTime
        runNewQuery: communityModel.RunNewQuery,
    };
    
    var strData = JSON.stringify(data);
    
    var callParams = {
        type: "POST",
        url: ContextPath + "Community/" + communityExplorerModel.UrlEnd(),
        contentType: "application/json; charset=utf-8",
        data: strData        
    }
    
    var key = 'communityTrainings_' + communityExplorerModel.UrlEnd() + getCacheKeyFromCallParams(callParams);
        
    cachedAjaxCall(callParams, function (result) {
        var communityTrainings = $('#communityTrainings .mCSB_container');
            
        var container = $(document.createElement('div'));
        container.append(result);
        container.append('<div class="clearfix"></div>');

        if (communityModel.LoadNextTrainingsTimeoutHandler) {
            clearTimeout(communityModel.LoadNextTrainingsTimeoutHandler);
            communityModel.LoadNextTrainingsTimeoutHandler = null;        
        }            

        if (communityIsTimeout(container)) 
        {
            var keyToDelete = getCacheKeyFromCallParams(callParams);

            deleteFromCache(keyToDelete);

            communityModel.NumberOfTimeouts++;
            communityModel.LoadNextTrainingsTimeoutHandler = 
            setTimeout(function() { communityLoadNextPageTrainingsTimeout(communityModel.NumberOfTimeouts) }, 500 * communityModel.NumberOfTimeouts); 
        } else
        {
            var packageType = $('.data', container).data().packagetype;
                
            if (packageType == communityExplorerModel.SelectedTab()) 
            {
                communityModel.NumberOfTimeouts = 0;
                var alreadyDisplayedTrainingIds = $('.displayedTrainingIds', container).data().alreadydisplayed

                communitySetPackageData(container); 

                communityWallSetTargetValue(container);

                var trainingsCount = $('.training', container).length;

                if (trainingsCount > 0)
                {
                    communityTrainings.append(container);
                    adjustTrainingPackageCountFont();
                }
                showHideScrolls(trainingsCount);            

                addToCache(key, callParams, 86400000);//3600000);
            }
        }            
        $('#communitySideTile').css('cursor', 'auto');
        $('#communityTrainings .training').css('cursor', 'pointer');
        $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'pointer');
        $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'pointer');
        $('#communitySideTile .communityUsersButton.down').css('cursor', 'pointer');

        loadNextPackageToCommunityTrainingsBusy = false;
    }, 86400000//24h //3600000 //1h
    );

    function communityLoadNextPageTrainingsTimeout(count) {   
        clearTimeout(communityModel.LoadNextTrainingsTimeoutHandler);
        communityModel.LoadNextTrainingsTimeoutHandler = null;   
        communityLoadNextPageTrainings();
    }
            
        
    //     $.ajax(callParams)
    //     .done(function (result) {
    //         var communityTrainings = $('#communityTrainings');
        
    //         var container = $(document.createElement('div'));
    //         container.append(result);

    //         var alreadyDisplayedTrainingIds = $('.displayedTrainingIds', container).data().alreadydisplayed

    //         for (var i = 0, len = alreadyDisplayedTrainingIds.length; i < len; i++) {
    //             communityModel.AlreadyDisplayedTrainingIds.push(alreadyDisplayedTrainingIds[i]);
    //         }               

    //         communityTrainings.append(container);
    //     });    
}

function communitySetPackageDataForMissing(container)
{
    if ($('.data', container).length > 0)
    {
        var data = $('.data', container).data();

        communityModel.MissingFirstUploadNotViewedTime = data.firstuploadnotviewedtime;
        communityModel.MissingFirstUploadTimeNotReadCommentTime = data.firstuploadtimenotreadcomment;
        //communityModel.MissingLastUploadNotViewedTime = data.lastuploadnotviewedtime;
        //communityModel.MissingRunNewQuery = data.runnewquery;
        //communityModel.MissingAllLoaded = data.allloaded == "True";
    }     

    if ($('.firstUploadTime', container).length > 0)
    {
        var FirstUploadTime = $('.firstUploadTime', container).data().firstuploadtime;
        //var LastUploadTime = $('.lastUploadTime', container).data().lastuploadtime;

        //         if (communityModel.LastUploadTime && LastUploadTime)
        //         {
        //             var originalLastUploadTime = new Date(communityModel.LastUploadTime);        
        //             var serverMissingLastUploadTime = new Date(LastUploadTime); 

        //             if (+serverMissingLastUploadTime <= +originalLastUploadTime)
        //                 communityModel.MissingAllLoaded = true;    
        //         }
        
        communityModel.MissingFirstUploadTime = FirstUploadTime;
    }    
}

function communityUpdatePackageDataByMissing(container)
{
    var communityTrainings = $('#communityTrainings .mCSB_container > div')[0];

    if ($('.data', container).length > 0)
    {
        var data = $('.data', container).data();
        var topData = $('.data', communityTrainings).data();

        topData.firstuploadnotviewedtime = data.firstuploadnotviewedtime;
        topData.firstuploadtimenotreadcomment = data.firstuploadtimenotreadcomment;
    }     

    if ($('.firstUploadTime', container).length > 0)
    {
        $('.firstUploadTime', communityTrainings).data().firstuploadtime = 
        $('.firstUploadTime', container).data().firstuploadtime;
    }    
}

function communityLoadMissingTrainings(first)
{
    //if (communityModel.MissingAllLoaded) return;    

    $('#communitySideTile').css('cursor', 'wait');
    $('#communityTrainings .training').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'wait');
    $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'wait');

    var data, callParams; 

    if (first)
    {
        data = {
            firstUpdateTimeAsString: communityModel.MissingFirstUploadTime,
            firstUpdateTimeNotViewedAsString: communityModel.MissingFirstUploadNotViewedTime,
            firstUpdateTimeNotReadCommentdAsString: communityModel.MissingFirstUploadTimeNotReadCommentTime,
        };

        var strData = JSON.stringify(data);

        callParams = {
            type: "POST",
            url: ContextPath + "Community/" + communityExplorerModel.UrlEndForMissing(),
            contentType: "application/json; charset=utf-8",
            data: strData        
        }          
    }
    //     else    
    //     {
    //         data = {
    //             lastUpdateTimeAsString: communityModel.MissingLastUploadTime,
    //             lastUpdateTimeNotViewedAsString: communityModel.MissingLastUploadNotViewedTime,
    //             runNewQuery: communityModel.MissingRunNewQuery,
    //         };

    //         var strData = JSON.stringify(data);

    //         callParams = {
    //             type: "POST",
    //             url: ContextPath + "Community/" + communityExplorerModel.UrlEndForMissing(),
    //             contentType: "application/json; charset=utf-8",
    //             data: strData        
    //         }
    //     }

    $.ajax(callParams)
     .done(function (result) {
         var containerDiv = $(document.createElement('div'));
         containerDiv.append(result);
         containerDiv.append('<div class="clearfix"></div>');

         if (communityModel.LoadMissingTrainingsTimeoutHandler) {
             clearTimeout(communityModel.LoadMissingTrainingsTimeoutHandler);
             communityModel.LoadMissingTrainingsTimeoutHandler = null;        
         }  

         if (communityIsTimeout(containerDiv)) 
         {
             communityModel.NumberOfTimeouts++;
             communityModel.LoadMissingTrainingsTimeoutHandler = 
             setTimeout(function() {   
                 communityLoadMissingTrainings(first);
             }, 500 * communityModel.NumberOfTimeouts);                                                                                          
         }
         else
         {
             var packageType = $('.data', containerDiv).data().packagetype;

             if (packageType == communityExplorerModel.SelectedTab()) 
             {
                 communityWallSetTargetValue(containerDiv);           

                 var trainingsCount = $('.training', containerDiv).length;

                 if (trainingsCount > 0)
                 {
                     communityAddMissingElements(containerDiv);
                     adjustTrainingPackageCountFont();
                 }

                 communityRemoveNotExistingAndOlderElements(containerDiv);

                 callBackFromExplorerModelInit();                    

                 communityCheckScroll(true, null, true);
             }
         }

         $('#communitySideTile').css('cursor', 'auto');
         $('#communitySideTile .communityUsersButtons').css('cursor', 'auto');  
         $('#communitySideTile .communityUsersButton.refresh_20').css('cursor', 'pointer');          
         $('#communityTrainings .training').css('cursor', 'pointer');
         $('#communitySideTile .communityTrainingsUpDiv').css('cursor', 'pointer');
         $('#communitySideTile .communityTrainingsDownDiv').css('cursor', 'pointer');                                    
     });    
}

function communityCheckForMissingTrainings() {
    if (communityAreMissingTrainings())
    {
        var communityTrainings = $('#communityTrainings .mCSB_container > div');

        communitySetPackageDataForMissing(communityTrainings[0]);

        //         communityModel.MissingFirstUploadTime = '';
        //         communityModel.MissingFirstUploadNotViewedTime = '';
        //     communityModel.MissingLastUploadTime = '';
        //     communityModel.MissingLastUploadNotViewedTime = '';
        //     communityModel.MissingRunNewQuery =  true;
        //     communityModel.MissingAllLoaded =  false;
        communityLoadMissingTrainings(true);
    }
}

function communityAreMissingTrainings() {
    if (communityExplorerModel.TabTargetsCount() != communityExplorerModel.TabTargetsCount_old || 
        communityExplorerModel.TabCompetitionsCount() != communityExplorerModel.TabCompetitionsCount_old ||
        communityExplorerModel.TabTrainingsCount() != communityExplorerModel.TabTrainingsCount_old ||
        communityExplorerModel.TabTrainingInvitationsCount() != communityExplorerModel.TabTrainingInvitationsCount_old ||
        communityExplorerModel.TabLiveStreamsCount() != communityExplorerModel.TabLiveStreamsCount_old ||
        communityExplorerModel.TabPublicationsCount() != communityExplorerModel.TabPublicationsCount_old)
    {
        var newElementsCount = $('.sideTileCommunity #communityTrainings .training.noResponse').length;        
        var elementsWithNewComments = $('.sideTileCommunity #communityTrainings .training .newComments').length;        
        var elementsWithNewCommentsSeen = $('.sideTileCommunity #communityTrainings .training .newComments.displayNone').length;        
        var newComments = elementsWithNewComments - elementsWithNewCommentsSeen;        
        var allCount = newElementsCount + newComments;
        
        if (communityExplorerModel.SelectedTab() == 'Trainings') 
            return (communityExplorerModel.TabTrainingsCount() - allCount) > 0;
        else
            if (communityExplorerModel.SelectedTab() == 'TrainingInvitations') 
                return (communityExplorerModel.TabTrainingInvitationsCount() - allCount) > 0;
            else
                if (communityExplorerModel.SelectedTab() == 'Competitions') 
                    return (communityExplorerModel.TabCompetitionsCount() - allCount) > 0;
                else
                    if (communityExplorerModel.SelectedTab() == 'Targets') 
                        return (communityExplorerModel.TabTargetsCount() - allCount) > 0;
                    else
                        if (communityExplorerModel.SelectedTab() == 'LiveStreams') 
                            return true;
                        else
                            if (communityExplorerModel.SelectedTab() == 'Publications') 
                                return (communityExplorerModel.TabPublicationsCount() - allCount) > 0;
    }   
    else return false;
}

function communityClearCacheForActualTab(selectedTab)
{
    var clear = false;

    if (communityExplorerModel.TargetWasPressed && 
        ((communityExplorerModel.NewTargetsCount() > 0 && selectedTab != 'Targets') 
        )
        )
    {
        //       communityExplorerModel.SetAllTargetsToRead();
        clear = true;
    }
    if (communityExplorerModel.CompetitionsWasPressed && 
        ((communityExplorerModel.NewCompetitionsCount() > 0 && selectedTab != 'Competitions') 
        )
        )
    {
        //       communityExplorerModel.SetAllCompetitionsToRead();
        clear = true;
    }
    if (communityExplorerModel.TrainingsWasPressed && 
        ((communityExplorerModel.NewTrainingsCount() > 0 && selectedTab != 'Trainings') 
        )
        )
    {
        //       communityExplorerModel.SetAllTrainingsToRead();
        clear = true;
    }
    if (communityExplorerModel.LiveStreamsWasPressed &&
        ((communityExplorerModel.NewLiveStreamsCount() > 0 && selectedTab != 'LiveStreams')
        )
        )
    {
        clear = true;
    }
    if (communityExplorerModel.TrainingInvitationsWasPressed &&
        ((communityExplorerModel.NewTrainingInvitationsCount() > 0 && selectedTab != 'TrainingInvitations')
        )
        )
    {
        clear = true;
    }

    if (communityExplorerModel.PublicationsWasPressed &&
        ((communityExplorerModel.NewPublicationsCount() > 0 && selectedTab != 'Publications')
        )
        ) {
        //       communityExplorerModel.SetAllTargetsToRead();
        clear = true;
    }

    if (clear)
    {
        var prefix = 'communityTrainings_' + communityExplorerModel.UrlEnd();
        var keys = getCacheKeys(prefix);    

        for (var i = 0, len = keys.length; i < len; i++) {
            var keyWithParams = keys[i].substring(prefix.length);
            deleteFromCache(keyWithParams);
            deleteFromCache(keys[i]);
        }
    }                  
}

function communityRemoveNotExistingAndOlderElements(containerDivFromServer) {
    if (communityExplorerModel.SelectedTab() != 'LiveStreams') return;

    var allWallIds = $('.data', containerDivFromServer).data().allwallids;

    if (!allWallIds) return;

    var callParams = {
        type: "POST",
        url: ContextPath + "Community/" + communityExplorerModel.UrlEnd()
    }

    var key = JSON.stringify(callParams);
    var resultFromCache = $.jStorage.get(key);
    var ttl = $.jStorage.getTTL(key);

    var containerDivFromCache = $(document.createElement('div'));
    var divTmpContainer = $(document.createElement('div'));
    containerDivFromCache.append(resultFromCache);

    var communityTrainings = $('.sideTileCommunity #communityTrainings .mCSB_container > :first');

    $('.training', '.sideTileCommunity #communityTrainings').each(function () {
        var elemFromWall = $(this);
        var elemFromWallId = parseInt(elemFromWall.data('messageid'));

        var theSameElem = $('.training.messageId_' + elemFromWallId, communityTrainings);
        var theSameElemInCache = $('.training.messageId_' + elemFromWallId, containerDivFromCache);

        var elementIds = $.grep(allWallIds, function (e) { return e == elemFromWallId; });

        if (elementIds.length == 0) {
            theSameElem.remove();
            theSameElemInCache.remove();
        }
    });

    addToCache(key, containerDivFromCache[0].innerHTML, ttl);

    diplayOrHideEmptyElementsTile();
}

function diplayOrHideEmptyElementsTile() {
    var communityNoElementsTile = $('.communityNoElementsTile');

    if ($('.training', '.sideTileCommunity #communityTrainings').length > 0) {
        if (!communityNoElementsTile.hasClass('displayNone')) {
            $('.communityNoElementsTile').addClass('displayNone');
        }
    }
    else {
        if (communityNoElementsTile.hasClass('displayNone')) {
            $('.communityNoElementsTile').removeClass('displayNone');
        }
    }
}

function communityAddMissingElements(containerDivFromServer) {
    var callParams = {
        type: "POST",
        url: ContextPath + "Community/" + communityExplorerModel.UrlEnd()
    }    

    var key = JSON.stringify(callParams);
    var resultFromCache = $.jStorage.get(key);    
    var ttl = $.jStorage.getTTL(key);

    var containerDivFromCache = $(document.createElement('div'));
    var divTmpContainer = $(document.createElement('div'));
    containerDivFromCache.append(resultFromCache);

    var communityTrainings = $('.sideTileCommunity #communityTrainings .mCSB_container > :first');
    //     var communityFirstTraining = null;//$('#communityTrainings > :first > .training :first');
    //     var communityFirstTrainingInCache = $('.training :first', containerDivFromCache);    
    //     var communityFirstTrainingId = parseInt(communityFirstTraining.data('messageid'));

    $('.training', containerDivFromServer).each(function () { 
        var elemFromServer = $(this);
        var elemFromServerId = parseInt(elemFromServer.data('messageid'));
           
        var theSameElem = $('.training.messageId_' + elemFromServerId, communityTrainings);
        var theSameElemInCache = $('.training.messageId_' + elemFromServerId, containerDivFromCache);
        
        if (!('.circleContainer', theSameElem).length == 0) 
        {
            theSameElem.remove();
            theSameElemInCache.remove();                  
        }

        divTmpContainer.append(elemFromServer.clone());
    });

    communityTrainings.prepend(divTmpContainer[0].innerHTML);            
    containerDivFromCache.prepend(divTmpContainer[0].innerHTML);            

    //     $('.training', containerDivFromServer).each(function () {
    //         var elemFromServer = $(this);
    //         var elemFromServerId = parseInt(elemFromServer.data('messageid'));

    //         var theSameElem = $('.sideTileCommunity #communityTrainings .training.messageId_' + elemFromServerId);
    //         var theSameElemInCache = $('.training.messageId_' + elemFromServerId, containerDivFromCache);

    //         theSameElem.remove();
    //         theSameElemInCache.remove();

    //         if (communityFirstTraining.length == 0 )
    //         {
    //             communityTrainings.append(elemFromServer.clone());
    //             containerDivFromCache.append(elemFromServer.clone());
    //         }
    //         else
    //         {
    //             elemFromServer.clone().insertBefore(communityFirstTraining);
    //             elemFromServer.clone().insertBefore(communityFirstTrainingInCache);            
    //         }
    //     });

    communityUpdatePackageDataByMissing(containerDivFromServer);

    //    callBackFromExplorerModelInit(); przeniesione wyżej do communityLoadMissingTrainings(first)

    //$('div', containerDivFromCache).prepend(containerDivFromServer); 

    //     var lastPackageDate =  $('#communityTrainings > div').each(function () {
    //         var packageDiv = $(this);

    //         $('', packageDiv)
    //     });

    //communityTrainings.prepend(containerDivFromServer);    

    //     var wasSomeChange = false;
    //     $('.training', containerDivFromServer).each(function () {
    //         var elemFromServer = $(this);
    //         var messageIdFromServer = elemFromServer.data('messageid');   
    //         var lastUploadDateFromServer = new Date(elemFromServer.data('lastuploaddate'));   
    //         var isNewFromServer = elemFromServer.hasClass('noResponse');
    //         var isInCache = false;
                          
    //         $('.training', containerDivFromCache).each(function () {
    //             var elemFromCache = $(this);
    //             var messageIdFromCache = elemFromCache.data('messageid');   
    //             var lastUploadDateFromCache = new Date(elemFromCache.data('lastuploaddate'));   
    //             var isNewFromCache = elemFromCache.hasClass('noResponse');
    //             var mainElem = $('.sideTileCommunity #communityTrainings .training.messageId_' + messageIdFromCache);
            
    //             if (isNewFromServer == isNewFromCache)
    //             {
    //                 if (+lastUploadDateFromServer >= +lastUploadDateFromCache)
    //                 {
    //                     communityTrainings.insertBefore(elemFromServer.clone(), mainElem);     
    //                     containerDivFromCache.insertBefore(elemFromServer.clone(), elemFromCache);     
    //                 }
    //             }
    //             else
    //             {
    //                 if (+lastUploadDateFromServer >= +lastUploadDateFromCache)
    //                 {
    //                     communityTrainings.insertBefore(elemFromServer.clone(), mainElem);                               
    //                     containerDivFromCache.insertBefore(elemFromServer.clone(), elemFromCache);     
    //                 }                
    //             }

    //             if (messageIdFromServer == messageIdFromCache)
    //             {
    //                 mainElem.remove();

    //                 elemFromCache.remove();

    //                 isInCache = true;
    //                 return false;
    //             } 
    //         });
    //     });

    addToCache(key, containerDivFromCache[0].innerHTML, ttl);
} 

function adjustTrainingPackageCountFont() {
    $("#collapseSideTileCommunity .circle.fi_45.circ_0").fitText({ minFontSize: '10px', maxFontSize: '19px', padding: '25' });        
}

function communityLoadCachedTrainings()
{
    var prefix = 'communityTrainings_' + communityExplorerModel.UrlEnd();
    var keys = getCacheKeys(prefix);    
    
    for (var i = 0, len = keys.length; i < len; i++) {
        var key = keys[i];
        var callParams = $.jStorage.get(key);
        
        communityLoadTrainingsByParams(callParams, (i == (len -1)));
        if (callParams == "" && (i == (len - 1)))
        {
            communityCheckForMissingTrainings();
        }
    }
}

function communityLoadTrainingsByParams(callParams, lastOne)
{
    if (callParams == "") return;
            
    //    var key = 'communityTrainings_' + communityExplorerModel.UrlEnd() + getCacheKeyFromCallParams(callParams);
        
    cachedAjaxCall(callParams, function (result) {
        var communityTrainings = $('#communityTrainings .mCSB_container');

        var container = $(document.createElement('div'));
        container.append(result);

        var alreadyDisplayedTrainingIds = $('.displayedTrainingIds', container).data().alreadydisplayed;

        //             for (var i = 0, len = alreadyDisplayedTrainingIds.length; i < len; i++) {
        //                 communityModel.AlreadyDisplayedTrainingIds.push(alreadyDisplayedTrainingIds[i]);
        //             }     

        communitySetPackageData(container);             

        communityWallSetTargetValue(container);

        var trainingsCount = $('.training', container).length;

        if (trainingsCount > 0)
        {
            communityTrainings.append(container);
            adjustTrainingPackageCountFont();
        }

        if (lastOne) communityCheckForMissingTrainings();            

        //             else
        //             {
        //                 var actualScrollPosition = $.jStorage.get('actualScrollPosition');
        //                 if (actualScrollPosition != null)
        //                     $( "#communityTrainings" ).scrollTop(actualScrollPosition);                        
        //             }

        //             showHideScrolls(trainingsCount);                        
    }, 86400000//24h //3600000 //1h
    );    
}

$(document).ready(function () {
    var communityTrainings = $('#communityTrainings');
    if (communityTrainings.length == 0) {
        getCurrentUser();

        return;
    }

    //     var communityTrainings = $('#communityTrainings');
        
    //     if (communityTrainings.length == 0) return;

    //     var selectedTab = $.jStorage.get('SelectedTab');
    //     if (selectedTab != null && selectedTab != 'Trainings')
    //     {
    //         communityRefreshTrainings();

    // //         if (selectedTab == 'TrainingInvitations') 
    // //             communityExplorerModel.SetAllTrainingInvitationsToRead();
    //     }
    //     else
    //     {
    //         communityLoadFirstTrainings();
    //         communityLoadCachedTrainings();        
    //     }

    //     communityExplorerModel.setSelectedTab('Trainings');

    //communityCheckScroll();
    
    //     var alreadyDisplayedTrainingIdsData = $('.displayedTrainingIds', communityTrainings).data();

    //     if (alreadyDisplayedTrainingIdsData)
    //     {
    //         var alreadyDisplayedTrainingIds = alreadyDisplayedTrainingIdsData.alreadydisplayed;

    //         for (var i = 0, len = alreadyDisplayedTrainingIds.length; i < len; i++) {
    //             communityModel.AlreadyDisplayedTrainingIds.push(alreadyDisplayedTrainingIds[i]);
    //         }                       
    //     }

    // zmiana scrolla
    //     $( "#communityTrainings" ).scroll(function() {
    //         if (($(this)[0].scrollHeight - ($(this).scrollTop()+$(this)[0].clientHeight)) <= (20 * 55))
    //         //if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
    //         {
    //             communityLoadNextPageTrainings();                     
    //         } 
    //     });    

    $( ".communityScrollUserList" ).scroll(function() {
        if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
        {
            var usersWhichAcceptedInvitation = communityExplorerModel.SelectedTrainingInvitation().UsersWhichAcceptedInvitation;
            
            usersWhichAcceptedInvitation.GoToNextPage();   
        } 
    });

    $(window).scroll(function(event) {
        var clubTableExplorerTileHeader = $('.clubTableExplorerTile');
        if (clubTableExplorerTileHeader.length > 0)
        {
            if ((window.scrollY > 0) && ((document.body.scrollHeight - (window.scrollY+window.innerHeight)) <= 1680))
            {
                clubTableExplorerModel.SearchForElements();
            } 
        } 
    });                  
         
    $('#addCommunityUserModal').on('hidden', function () {
        refreshMainLists();

        clubTableExplorerModel.SelectedTab(clubTableExplorerModel.LastSelectedTab);
    })         

    showHideCommunityTile(true);
    
    $('#footerInfoModal').on('shown', function(event) {
        communityModalSetCommunityTrainingsHeight(event.target);
        
        //         var trainingSelected = $('#footerInfoModal .communityTrainingsModalLeft #communityTrainings .training.selected');
        //         if (trainingSelected.length > 0)
        //         if (!footerInfoModalShowed && $('#footerInfoModalBody .communityTrainingsModalRight .mediumTrainingTileBody .hrChart').length == 0)
        //         {
        //             var trainingid = trainingSelected.data().trainingid;
        //             var basicinfo = $('#footerInfoModalBody .communityTrainingsModalRight #basicinfo_' + trainingid);
        //             if (!basicinfo.hasClass('in'))
        //             {
        //                 $('#footerInfoModalBody .communityTrainingsModalRight .icon25.basicinfo').click();
        //             }
        //         }

        $('#footerInfoModal .communityCommentsLine').each(function () {
            var elem = $(this);
            var commentIndex = $(this).data().index;

            if (typeof commentIndex !== 'undefined')
            {
                var communityComments = communityExplorerModel.SelectedCommentsModel;
                var comment = communityComments.List()[commentIndex];

                var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                if (invisibleHeight > 72)  comment.IsCommentLinkNeeded(true);
                else
                    comment.IsCommentLinkNeeded(false);                                
            }
        });

        correctAvatarToolTipPosition($('#footerInfoModal'));

        var communityTrainingsAvatarParent = $('.communityTrainingsAvatarParent .avatar', '#footerInfoModal');
        var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', communityTrainingsAvatarParent);
        var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', communityTrainingsAvatarParent);
        var top = communityAvatarNick.offset().top - $('#footerInfoModalBody', '#footerInfoModal').offset().top;

        if (communityAvatarNick.height() > 16) {
            var topCorrection = (communityAvatarNick.height() - 16);
            var top = top - topCorrection;
            communityAvatarNick.css('top', top + 'px');
        }

        footerInfoModalShowed = true;
    })  

    $('#footerInfoModal').on('hidden', function (e) {
        var communityComments = communityExplorerModel.SelectedWallTrainingCommentsModel();
        
        if (communityComments)
        {
            communityComments.SetCommentsToRead();                                               
        }

        if (e.target.className == "modal hide fade")
        {
            $('#communityCommentsContainer', this).remove();        
        }
    })     

    $('#myTrainingInvitationModal').on('hidden', function () {
        if (!communityExplorerModel.SelectedTrainingInvitation().IsSender()) 
            communityExplorerModel.SelectedTrainingInvitation().save();        
    })
           
    
    //     if ($('.span12.communityNotifications .tileBody').length > 0)
    //     {
    //         resize();        
    //     }
    
    //     $('.communityNotificationPaginateButton.next').click(function() {
    //        // $('div#wantsObserveObservingTable_wrapper .paginate_button.next').click();
    //     });
    //     $('.communityNotificationPaginateButton.previous').click(function() {
    //        // $('div#wantsObserveObservingTable_wrapper .paginate_button.previous').click();
    //     }); 

    //         loadCommunityWall = setInterval(function() {            
    //             var communityTrainings = $( "#communityTrainings" )
                        
    //             if (communityTrainings[0].clientHeight > 0)
    //             {
    //                 clearInterval(loadCommunityWall);          
    //                 loadCommunityWall = null;
    //                 var actualScrollPosition = $.jStorage.get('actualScrollPosition');
    //                 if (actualScrollPosition != null)
    //                     $( "#communityTrainings" ).scrollTop(actualScrollPosition);                                        
    //                 var container = $('#communityTrainings');
    //                 var trainingsCount = $('.training', container).length;
                    
    //                 showHideScrolls(trainingsCount);
    //             }
    //         }, 100);        
});

function correctAvatarToolTipPosition(container) {
    var cloud = $('.avatarPublishInfoCloudContainer .publishInfoCloudContainer', container);

    cloud.css('display', 'block');

    var cloudRight = $('.avatarPublishInfoCloudContainer .publishInfoCloudContainer .hrCloud.right', container);
    var cloudRightWidth = cloudRight.width();
    cloudRight.css('right', '');
    var rightOriginal = parseInt(cloudRight.css('right').replace('px', ''));

    if (cloudRightWidth > 140) {
        var resulRightPosition = rightOriginal - Math.abs((140 - cloudRightWidth) + 40);
        cloudRight.css('right', resulRightPosition);
    }    

    cloud.css('display', '');
}


function showHideCommunityTile(refreshp)
{
    var callParams = {
        refresh: refreshp
    }
        
    getCurrentUser(showHideCommunityTileGetCurrentUserCallBack, callParams);                
}

function showHideCommunityTileGetCurrentUserCallBack(currentUser, callParams)
{
    var selectedTab = $.jStorage.get('SelectedTab');
    if (selectedTab != null)
    {
        communityExplorerModel.setSelectedTab(selectedTab);          

        if (selectedTab == 'Competitions') communityExplorerModel.CompetitionsWasPressed = true;
        if (selectedTab == 'Targets') communityExplorerModel.TargetWasPressed = true;
        if (selectedTab == 'Trainings') communityExplorerModel.TrainingsWasPressed = true;        
        if (selectedTab == 'LiveStreams') communityExplorerModel.LiveStreamsWasPressed = true;
        if (selectedTab == 'TrainingInvitations') communityExplorerModel.TrainingInvitationsWasPressed = true;
        if (selectedTab == 'Publications') communityExplorerModel.PublicationsWasPressed = true;
    }
    else
    {
        communityExplorerModel.setSelectedTab('Trainings');  
        communityExplorerModel.TrainingsWasPressed = true;                               
    } 

    if ($.jStorage.get('TabTrainingsCount_old') != null) 
        communityExplorerModel.TabTrainingsCount_old = $.jStorage.get('TabTrainingsCount_old');
        
    if ($.jStorage.get('TabCompetitionsCount_old') != null) 
        communityExplorerModel.TabCompetitionsCount_old = $.jStorage.get('TabCompetitionsCount_old');

    if ($.jStorage.get('TabTargetsCount_old') != null) 
        communityExplorerModel.TabTargetsCount_old = $.jStorage.get('TabTargetsCount_old');

    if ($.jStorage.get('TabLiveStreamsCount_old') != null)
        communityExplorerModel.TabLiveStreamsCount_old = $.jStorage.get('TabLiveStreamsCount_old');

    if ($.jStorage.get('TabTrainingInvitationsCount_old') != null)
        communityExplorerModel.TabTrainingInvitationsCount_old = $.jStorage.get('TabTrainingInvitationsCount_old');

    if ($.jStorage.get('TabPublicationsCount_old') != null)
        communityExplorerModel.TabPublicationsCount_old = $.jStorage.get('TabPublicationsCount_old');

    if (currentUser.ComTileVisible())
    {
        $('#communitySideTile').css('display', 'block');  
        
        setTimeout(function () {

            initFriendsWall(callParams);
        
        }, 350);              
    }
    else
        $('#communitySideTile').css('display', 'none'); 
}

function initFriendsWall(callParams)
{
    var prefix = 'communityTrainings_' + communityExplorerModel.UrlEnd();
    var keys = getCacheKeys(prefix);    
    
    if (!canLoadCommunityWall && keys.length == 0) {
        setTimeout(function () { initFriendsWall(callParams); }, 100);
        return;
    }

    if (callParams.refresh) {
        clearTimeout(communityExplorerModel.LoadModelTimeoutHandler);
        communityExplorerModel.LoadModelTimeoutHandler = null;
        communityExplorerModel.NumberOfTimeouts = 0;
        communityExplorerModel.init(false, communityLoadFirstTrainings, null, communityLoadCachedTrainings);
    }
    else {
        communityRefreshTrainings();
    }

    loadCommunityWall = setInterval(function () {
        var communityTrainings = $("#communityTrainings")

        if ((communityTrainings[0].clientHeight > 0) && communityModel.LastUploadTime != "" ||
             communityModel.LastUploadNotViewedTime != "" || communityModel.LastUploadNotReadCommentTime != "") {
            clearInterval(loadCommunityWall);
            loadCommunityWall = null;

            var actualScrollPosition = $.jStorage.get('actualScrollPosition_' + communityExplorerModel.UrlEnd());
            if (actualScrollPosition != null)
                $(".sideTileCommunity #communityTrainings").mCustomScrollbar('scrollTo', actualScrollPosition);
                //$("#communityTrainings").scrollTop(actualScrollPosition);
            else
                $(".sideTileCommunity #communityTrainings").mCustomScrollbar('scrollTo', 0);
            var container = $('#communityTrainings');
            var trainingsCount = $('.training', container).length;

            showHideScrolls(trainingsCount);
        }
    }, 100);
}

// function communityOpenTrainingInvitation(elem, trainingId, userId, isSender)
// {
//     $('#communitySideTile').css('cursor', 'wait');
//     $('#communityTrainings .training').css('cursor', 'wait');
 
//     var modal = $('#addTrainingInvitationModal');   

//     $.ajax({
//         type: "POST",
//         url: ContextPath + "Community/GetTrainingInvitation",
//         data: {  }
//     })
//     .done(function (result) {
//         $('#communitySideTile').css('cursor', 'auto');
//         $('#communityTrainings .training').css('cursor', 'pointer');


//     });    
// }

function communityOpenTraining(elem, trainingId, firstUploadDate, lastUploadDate, userId) {
    if (communityExplorerModel.CommentsBallPressed) { 
        communityExplorerModel.CommentsBallPressed = false; 
        return;
    }

    if (communityOpenTrainingBusy) return;    

    var isModalShowed = $('#footerInfoModal.modal.hide.fade.in').length == 1    
    var manyTrainings = elem.data().subtrainingids.length > 1;
    $('#communitySideTile').css('cursor', 'wait');
    $('#communityTrainings .training').css('cursor', 'wait');

    communityOpenTrainingBusy = true;

    if (isModalShowed)
    {
        loadCommunityTrainingToModal(elem, trainingId);
    }
    else
    {
        communityTrainingsModalAllLoaded = false;


        //         var height = $(window).height() - 200;

        //         $('#footerInfoModalBody').html('<div style="height: ' + height + 'px"></div>');

        var modeal = $('#footerInfoModal');

        var data = {
            mainTrainingId: trainingId,
            //subTrainings: elem.data().subtrainingids
            firstUploadDate: firstUploadDate,
            lastUploadDate: lastUploadDate,
            userId: userId,
            start: true,
            subTrainingsExluded: elem.data().subtrainingexludedids
        };

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/TrainingsShow",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            if (result == 'NoVisibleTrainings')
            {
                if (manyTrainings)
                    ShowEmptyMsg(translations.TrainingsWasHidden, translations.Info);
                else
                    ShowEmptyMsg(translations.TrainingWasHidden, translations.Info);

                $('#communitySideTile').css('cursor', 'auto');
                $('#communityTrainings .training').css('cursor', 'pointer');
                
                communityOpenTrainingBusy = false;

                return;
            }
            if (result == 'NoTrainingsError')
            {
                if (manyTrainings)
                    ShowEmptyMsg(translations.TrainingsWasDeleted, translations.Info);
                else
                    ShowEmptyMsg(translations.TrainingWasDeleted, translations.Info);

                $('#communitySideTile').css('cursor', 'auto');
                $('#communityTrainings .training').css('cursor', 'pointer');

                communityOpenTrainingBusy = false;
                
                return;
            }
        
            var containerDiv = $(document.createElement('div'));
            containerDiv.append(result);

            var cloned = $('#communityCommentsContainer', containerDiv).clone();
            $('#communityCommentsContainer', containerDiv).remove();       

            $('#footerInfoModalBody').html(containerDiv.html());
            $('#footerInfoModal').append(cloned);

            $('#footerInfoModalBody div.line.exeParams.dispMode').each(function (idx, elem) {
                ko.cleanNode(elem);
                ko.applyBindings(GeneralModel, elem);
            });

            loadSerialisedJson([['communityTrainingModel', 'communityCommentsContainer', function (serverModel) {
                communityExplorerModel.CommunityTraining().initByModel(serverModel);
                communityExplorerModel.SelectedCommentsModel = communityExplorerModel.CommunityTraining().Comments();            
                communityExplorerModel.SelectedWallTrainingCommentsModel().initByModel(serverModel.CommentsModel);
                communityExplorerModel.SelectedCommentsWindowName = '#footerInfoModal';

                return communityExplorerModel.CommunityTraining();
            }]]);

            $( "#footerInfoModal .communityComments" ).scroll(function() {
                if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
                {
                    var communityComments = communityExplorerModel.SelectedCommentsModel;

                    if (communityComments)
                    {
                        communityComments.GoToNextPage();   
                    }
                } 
            });                             
            
            var data = $('#footerInfoModal .communityTrainingsModalLeft #data');

            var trainingsTotalCount = parseInt(data.data().trainingstotalcount);            

            if (trainingsTotalCount > 1)
                $('#footerInfoModalLabel').html(translations.Activities + ': ' + elem.data().subtrainingids.length);
            else
                $('#footerInfoModalLabel').html(translations.Activity);

            var timeAndCategory = data.data().timeandcategory;
            var trainingName = data.data().trainingname;

            var tripleStripe = $('.modal-header .tripleStripeParent', modeal);
            var grayStripe = $('.grayStripe', tripleStripe);
            grayStripe.html(timeAndCategory);
            tripleStripe.css('margin-left', '155px');
            tripleStripe.css('display', 'block');

            var stripeExtraInfo = $('.stripeExtraInfo .content', tripleStripe);
            stripeExtraInfo.html(trainingName + '&nbsp;');

            communityModalSelectedTab = '';
            communityOpenTrainingCallBack();

            $(".communityTrainingsModalLeft #communityTrainings").mCustomScrollbar({
                callbacks: {
                    whileScrolling: function () {
                        var clientHeight = $('.communityTrainingsModalLeft #communityTrainings.mCustomScrollbar').height();
                        var scrollHeight = $(".communityTrainingsModalLeft #communityTrainings").find('.mCSB_container').height();
                        var scrollTop = getActualScrollPosition($(".communityTrainingsModalLeft #communityTrainings"));

                        if (scrollTop + clientHeight > (scrollHeight - (20*55))) {
                            loadNextPackageToCommunityTrainingsModal();
                        }
                    },
                },
                //scrollInertia: 0,
                scrollbarPosition: "outside",
                autoHideScrollbar: true,
                mouseWheelPixels: 170,
                //autoDraggerLength:false                 
            });

                                    
            //$(".communityTrainingsModalLeft #communityTrainings" ).scroll(function() {
            //    if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
            //    {
            //        loadNextPackageToCommunityTrainingsModal();                     
            //    } 
            //});            
            //             var trainingZero = $('#footerInfoModal #communityTrainings .training')[0];
            
            //             loadCommunityTrainingToModal(trainingZero, trainingId);

            footerInfoModalShowed = false;
            modeal.modal('show');
        });  
    }
}

function loadNextPackageToCommunityTrainingsModal(checkNextPage)
{
    if (loadNextPackageToCommunityTrainingsModalBusy || communityTrainingsModalAllLoaded) return;

    $('#communityTrainings .training', $('#footerInfoModal')).css('cursor', 'wait');
    $('.communityUsersButtons .communityTrainingsUpDiv', $('#footerInfoModal')).css('cursor', 'wait');
    $('.communityUsersButtons .communityTrainingsDownDiv', $('#footerInfoModal')).css('cursor', 'wait');
    $('.communityUsersButtons .communityUsersButton.down', $('#footerInfoModal')).css('cursor', 'wait');

    loadNextPackageToCommunityTrainingsModalBusy = true;
    var firstUploadDate, lastUploadDate, skipCount, userId, subTrainingsExludedIds;
    var data = $('#footerInfoModal .communityTrainingsModalLeft #data');
        
    if (data.length > 0)
    {
        firstUploadDate = data.data().firstuploaddate;
        lastUploadDate = data.data().lastuploaddate;
        userId = data.data().userid;
        skipCount = $('#footerInfoModal .communityTrainingsModalLeft #communityTrainings .training').length;        
        subTrainingsExludedIds = data.data().subtrainingexludedids;
    }    
    
    var data = {
        firstUploadDate: firstUploadDate,
        lastUploadDate: lastUploadDate,
        skipCount: skipCount,
        userId: userId,
        subTrainingsExluded: subTrainingsExludedIds
    };

    var strData = JSON.stringify(data);    
    
    $.ajax({
        type: "POST",
        url: ContextPath + "Community/TrainingsShow",
        contentType: "application/json; charset=utf-8",
        data: strData
    })
    .done(function (result) {
        var communityTrainings = $('#footerInfoModalBody #communityTrainings .mCSB_container');

        var container = $(document.createElement('div'));
        container.append(result);
                
        if ($('.training', container).length > 0)
        {                
            communityTrainings.append(container);        
        }
        else {
            communityTrainingsModalAllLoaded = true;
        }

        loadNextPackageToCommunityTrainingsModalBusy = false;

        $('#communityTrainings .training', $('#footerInfoModal')).css('cursor', 'pointer');
        $('.communityUsersButtons .communityTrainingsUpDiv', $('#footerInfoModal')).css('cursor', 'pointer');
        $('.communityUsersButtons .communityTrainingsDownDiv', $('#footerInfoModal')).css('cursor', 'pointer');
        $('.communityUsersButtons .communityUsersButton.down', $('#footerInfoModal')).css('cursor', 'pointer');        

        if (checkNextPage) communityModalLoadNextPageIfNeeded();
    });      
}

function loadCommunityTrainingToModal(elem, trainingId) {
    $.each($('.communityTrainingsModalLeft .training'), function (index, elem) {
        var element = $(elem);
        
        element.removeClass('selected');
    });

    elem.addClass('selected');
    
    $.ajax({
        type: "POST",
        url: ContextPath + "Community/TrainingsShow",
        data: { mainTrainingId: trainingId}
    })
    .done(function (result) {
        if (result == 'NoTrainingError')
        {
            ShowEmptyMsg(translations.TrainingWasDeleted, translations.Info);

            $('#communitySideTile').css('cursor', 'auto');
            $('#communityTrainings .training').css('cursor', 'pointer');

            communityOpenTrainingBusy = false;

            return;
        }   
        
        var containerDiv = $(document.createElement('div'));
        containerDiv.append(result);        
        
        var selector = '.detailsMenu';
        var selector2 = '.mediumTrainingTileBody';
        var tab = '', postfix = '_none';
        communityModalShowTab = '';

        //ukierunkowane na wykresy
        //if (communityModalSelectedTab.indexOf('basicinfo') != -1) tab = '#basicinfo_' + trainingId + postfix;
        //if (communityModalSelectedTab.indexOf('heartRate') != -1 && $(selector + ' .hrChart', containerDiv).length > 0) tab = '#hrChart_' + trainingId + postfix;          
        //if (communityModalSelectedTab.indexOf('lapsAuto') != -1 && $(selector +  ' #trainingLapsAutoFixed', containerDiv).length > 0) tab = '#auto_laps_' + trainingId + postfix;            
        //else
        //if (communityModalSelectedTab.indexOf('.icon25.laps') != -1 && $(selector + ' #trainingLapsFixed', containerDiv).length > 0) tab = '#manual_laps_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('speed') != -1 || communityModalSelectedTab.indexOf('pace') != -1 && $(selector + ' .speedChart', containerDiv).length > 0) tab = '#speed_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('cadence') != -1 && $(selector + ' .cadenceChart', containerDiv).length > 0) tab = '#cadence_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('route') != -1 && $(selector + ' .routeMap', containerDiv).length > 0) tab = '#route_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('power') != -1 && $(selector + ' .powerChart', containerDiv).length > 0) tab = '#power_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('altitude') != -1 && $(selector + ' .altitudeChart', containerDiv).length > 0) tab = '#altitude_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('exercises') != -1 && $(selector + ' .exercise', containerDiv).length > 0) tab = '#exercises_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('feading') != -1 && $(selector + ' .collapsed.feading', containerDiv).length > 0) tab = '#feading_' + trainingId + postfix;            
        //if (communityModalSelectedTab.indexOf('description') != -1 && $(selector + ' .collapsed.description', containerDiv).length > 0) tab = '#description_' + trainingId + postfix;            

        if (communityModalSelectedTab.indexOf('basicinfo') != -1 && $(selector + ' .basicinfo', containerDiv).data().active == "True") tab = '#basicinfo_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('exercises') != -1 && $(selector + ' .inactive.exercises', containerDiv).length == 0) tab = '#exercises_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('heartRate') != -1 && $(selector + ' .inactive.heartRate', containerDiv).length == 0) tab = '#hrChart_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('route') != -1 && $(selector + ' .inactive.route', containerDiv).length == 0) tab = '#route_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('altitude') != -1 && $(selector + ' .inactive.altitude', containerDiv).length == 0) tab = '#altitude_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('speed') != -1 && $(selector + ' .inactive.speed', containerDiv).length == 0) tab = '#speed_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('pace') != -1 && $(selector + ' .inactive.pace', containerDiv).length == 0) tab = '#speed_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('cadence') != -1 && $(selector + ' .inactive.cadence', containerDiv).length == 0) tab = '#cadence_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('power') != -1 && $(selector + ' .inactive.power', containerDiv).length == 0) tab = '#power_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('lapsAuto') != -1 && $(selector + ' .inactive.lapsAuto', containerDiv).length == 0) tab = '#auto_laps_' + trainingId + postfix;
        else
            if (communityModalSelectedTab.indexOf('.icon25.laps') != -1 && $(selector + ' .inactive.laps', containerDiv).length == 0) tab = '#manual_laps_' + trainingId + postfix;
        if (communityModalSelectedTab.indexOf('feading') != -1 && $(selector + ' .inactive.feading', containerDiv).length == 0) tab = '#feading_' + trainingId + postfix;

        if (tab != '' && $(selector2 + ' ' + tab, containerDiv).length > 0)
        {
            $('.trainingDetailsContainer .' + communityModalSelectedTab, containerDiv).addClass('collapsed');
            $('.mediumTrainingTileBody .collapse.in', containerDiv).removeClass('in');
            //$(selector + ' ' +  tab, containerDiv).addClass('in');
            communityModalShowTab = tab;
        }
        
        $('#footerInfoModalBody .communityTrainingModal').html(containerDiv.html());

        var modeal = $('#footerInfoModal');

        var data = $('#footerInfoModal .communityTrainingsModalRight #dataLoadedTraining');
        var timeAndCategory = data.data().timeandcategory;
        var trainingName = data.data().trainingname;

        var tripleStripe = $('.modal-header .tripleStripeParent', modeal);
        var grayStripe = $('.grayStripe', tripleStripe);
        grayStripe.html(timeAndCategory);

        var stripeExtraInfo = $('.stripeExtraInfo .content', tripleStripe);
        stripeExtraInfo.html(trainingName + '&nbsp;');

        var strMdl = $('#' + 'communitySingleTrainingModel').val();
        var model = JSON.parse(strMdl);
        communityExplorerModel.CommunityTraining().initByModel(model);             

        $('#footerInfoModalBody .communityTrainingModal div.line.exeParams.dispMode').each(function (idx, elem) {
            ko.cleanNode(elem);
            ko.applyBindings(GeneralModel, elem);
        });

        communityOpenTrainingCallBack();
    });  
}

function communityOpenTrainingCallBack() {
    $('#communitySideTile').css('cursor', 'auto');
    $('#communityTrainings .training').css('cursor', 'pointer');
    $('#footerInfoModalBody .calStatsContainer  .tileFooterButtons > .down_11').click();
    loadStatControlForElem($('#footerInfoModalBody'));
    $('#footerInfoModalBody #communityTrainings').css('max-height', '440px'); 
    var dummyStatHeartSpace = $('.communityTrainingsModalRight .simpleTraining .dummyStatHeartSpace');
    if (dummyStatHeartSpace.length > 0)
    {
        dummyStatHeartSpace.css('display', 'none');
        //$('.communityTrainingsModalRight .header .statControl.longerStat').css('width', 'auto');
    }
    
    var isModalShowed = $('#footerInfoModal.modal.hide.fade.in').length == 1      
    if (isModalShowed)      
    {
        if (communityModalShowTab != '') 
        {
            var communityModalSelectedTabTmp = communityModalSelectedTab;
            var tabExists = $('#footerInfoModalBody .communityTrainingModal .' + communityModalSelectedTab).length > 0;

            if (!tabExists)
            {
                if (communityModalSelectedTab.indexOf('speed') != -1)
                    communityModalSelectedTabTmp = communityModalSelectedTab.replace('speed', 'pace');
                if (communityModalSelectedTab.indexOf('pace') != -1)
                    communityModalSelectedTabTmp = communityModalSelectedTab.replace('pace', 'speed');
            }
                
            $('#footerInfoModalBody .communityTrainingModal .' + communityModalSelectedTabTmp).click();
        }

        communityModalSetCommunityTrainingsHeight($('#footerInfoModalBody'));

        $('#footerInfoModal .communityCommentsLine').each(function () {
            var elem = $(this);
            var commentIndex = $(this).data().index;

            if (typeof commentIndex !== 'undefined')
            {
                var communityComments = communityExplorerModel.SelectedCommentsModel;
                var comment = communityComments.List()[commentIndex];

                var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                if (invisibleHeight > 72)  comment.IsCommentLinkNeeded(true);
                else
                    comment.IsCommentLinkNeeded(false);                                
            }
        });        
    }

    //var classList = $(".communityTrainingsModalRight .disc_49")[0].classList;
    //var selector = getSelectorFromClassList(classList);
    //discipineBackgroundPosition = $(".communityTrainingsModalRight " + selector).css('background-position');     
     
    //$(".communityTrainingsModalRight .disc_49").hover(function(){
    //    $(".communityTrainingsModalRight .disc_49").css('background-position',discipineBackgroundPosition);
    //});       
    
    $(".communityTrainingsModalRight .detailsMenu").mouseup(function(elem){
        if ($('.communityTrainingsModalRight .mediumTrainingTileBody .photo').length > 0 && 
            elem.target.className.indexOf('basicinfo') != -1)
        {
            var wideStats = $('.communityTrainingsModalRight .wide.stats');            
            var wideStatsHeight = wideStats[0].offsetHeight + wideStats[0].offsetTop;            
            var clubNameHeight = $('.communityTrainingsModalRight .statControl.text.clubName').outerHeight();            
            var height = wideStatsHeight - clubNameHeight;
            var trainingToShowCount = Math.ceil(height / 55);
            if (trainingToShowCount < 8) trainingToShowCount = 8;
            var maxHeight = trainingToShowCount * 55;
            $('#footerInfoModalBody #communityTrainings').css('max-height', maxHeight + 'px');

            //             if ($('.communityTrainingsModalRight .mediumTrainingTileBody .intensivityToolbar.input-append').length > 0)
            //             {
            //                 var height = $('.communityTrainingsModalRight .simpleTraining .stats .photo img').height();
                
            //                 if (height <= 255) 
            //                     $('#footerInfoModalBody #communityTrainings').css('max-height', '432px');
            //                 else                
            //                     $('#footerInfoModalBody #communityTrainings').css('max-height', '546px');
            //             }
            //             else
            //             {
            //                 var height = $('.communityTrainingsModalRight .simpleTraining .stats .photo img').height();
                
            //                 if (height <= 323) 
            //                     $('#footerInfoModalBody #communityTrainings').css('max-height', '432px');
            //                 else                
            //                     $('#footerInfoModalBody #communityTrainings').css('max-height', '546px');
            //             }
        }
        else
        {
            $('#footerInfoModalBody #communityTrainings').css('max-height', '440px');
        }
        
        communityModalLoadNextPageIfNeeded($('#footerInfoModalBody'));

        communityModalSelectedTab = elem.target.className.trim().split(/\s{1,}/g).join('.').replace('.collapsed', '');        
    });    
    communityOpenTrainingBusy = false;
}

function communityModalSetCommunityTrainingsHeight(elem)
{
    setTimeout(function() {      
        var selected  = $('.mediumTrainingTileBody .collapse.in');

        if (selected.length > 0 
            && $('.communityTrainingsModalRight .mediumTrainingTileBody .photo').length > 0 
            && selected[0].id.indexOf('basicinfo') != -1)
        {
            var wideStats = $('.communityTrainingsModalRight .wide.stats');            
            var wideStatsHeight = wideStats[0].offsetHeight + wideStats[0].offsetTop;            
            var clubNameHeight = $('.communityTrainingsModalRight .statControl.text.clubName').outerHeight();            
            var height = wideStatsHeight - clubNameHeight;
            var trainingToShowCount = Math.ceil(height / 55);
            if (trainingToShowCount < 8) trainingToShowCount = 8;
            var maxHeight = trainingToShowCount * 55;
            $('#footerInfoModalBody #communityTrainings').css('max-height', maxHeight + 'px');
        }
        else
        {
            $('#footerInfoModalBody #communityTrainings').css('max-height', '440px');
        }

        communityModalLoadNextPageIfNeeded(elem);                  
    }, 100);        
}

function communityModalLoadNextPageIfNeeded(elem)
{
    var trainingsCount = $('.communityTrainingsModalLeft #communityTrainings .training', elem).length;
    if (trainingsCount > 0)
    {
        var trainingsHeight = trainingsCount * 55;
        var communityTrainingsHeight = $('.communityTrainingsModalLeft #communityTrainings').height();
        if (trainingsHeight <= communityTrainingsHeight) 
        {
            var data = $('#footerInfoModal .communityTrainingsModalLeft #data');

            var trainingsTotalCount = parseInt(data.data().trainingstotalcount);            

            if (trainingsCount < trainingsTotalCount) 
            {
                loadNextPackageToCommunityTrainingsModal(true);
            }
            else
            {
                communityShowHideNavigateButtons(elem);
            }
        
            $("#footerInfoModalBody #communityTrainings").css('height', (trainingsHeight + 2) + 'px');
        }
        else
        {
            communityShowHideNavigateButtons(elem);
        }         
    }
}

function communityShowHideNavigateButtons(elem)
{
    var trainingsCount = $('.communityTrainingsModalLeft #communityTrainings .training', elem).length;
    if (trainingsCount > 0)
    {
        var trainingsHeight = trainingsCount * 55;
        var communityTrainingsHeight = $('.communityTrainingsModalLeft #communityTrainings').height();
        if (trainingsHeight > communityTrainingsHeight) 
        {
            $('.communityTrainingsModalLeft .communityUsersButton').each(function () {
                var elem = $(this);

                elem.css('display', 'block');
            });
        }
        else
        {
            $('.communityTrainingsModalLeft .communityUsersButton').each(function () {
                var elem = $(this);

                elem.css('display', 'none');
            });         
        }    
    }
}

function communityCheckScroll(refresh, noTrainings, noTimeDeletion) {
    if (!noTimeDeletion)
    {
        communityModel.AlreadyDisplayedTrainingIds = [];
        communityModel.LastUploadTime = '';
        communityModel.LastUploadNotViewedTime = '';
        communityModel.LastUploadNotReadCommentTime = '';
        communityModel.RunNewQuery = true;
        communityModel.AllLoaded =  false;
    }
    //    communityModel.TimeoutOccured =  false;
    
    //     $.liveClickHold(".up_17.communityUsersButton.up", sideTileCommunityUpButtonClick);
    //     $.liveClickHold(".down_17.communityUsersButton.down", sideTileCommunityDownButtonClick);
    
    var trainingsCount = 0;
    
    if (!noTrainings)
        //trainingsCount = parseInt($("#communityTrainings .trainingsCount").html()); 
        trainingsCount = $('.training', '.sideTileCommunity #communityTrainings').length;

    if (trainingsCount != 0)
    {
        var communityTrainingsHeight = trainingsCount * 55;

        if (communityTrainingsHeight > 275) communityTrainingsHeight = 275;
        
        $("#communityTrainings").css('height', communityTrainingsHeight + 'px');        
        
        //         var sideTileCommunityExpanderState = $.jStorage.get('sideTileCommunityExpanderState');
        //         if (sideTileCommunityExpanderState == 'expanded')
        if ($('.sideTileCommunity a.dzyndzolek').hasClass('collapsed'))
        {
            if (($(window).width() > 768) || refresh)
            {             
                $('.sideTileCommunity a.dzyndzolek').click();
            }
        }
        else
        {           
            var autoHeight = $("#collapseSideTileCommunity").css('height', 'auto').height();            
            
            $("#collapseSideTileCommunity").animate({height: autoHeight}, 500);
        }       
    }
    else
    {
        $("#communityTrainings").css('height', '105px');                

        if (!refresh)
        {
            if (!$('.sideTileCommunity a.dzyndzolek').hasClass('collapsed'))
            {
                if ($(window).width() > 768)
                {
                    $('.sideTileCommunity a.dzyndzolek').click();
                }
            }
            else
            {
                $("#collapseSideTileCommunity").css('height', '0px');                 
            }
        }
        else
        {
            if ($('.sideTileCommunity a.dzyndzolek').hasClass('collapsed'))
            {
                $('.sideTileCommunity a.dzyndzolek').click();
            }
            else
            {
                $("#collapseSideTileCommunity").css('height', '112px');
            }
        }
    }
}

//function sideTileCommunityUpStartButtonClick(communityTrainings, notAddToCache) {
//    if (!communityExplorerModel.ShowUpToStartArrow()) return;

//    communityTrainings.scrollTop(0);  
//    showHideScrolls();
    
//    if (!notAddToCache)
//    {
//        var actualScrollPosition =  $( "#communityTrainings" ).scrollTop();

//        addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), actualScrollPosition, 86400000);//3600000);
//    }
//}

//function communityDialogUpStartButtonClick(communityTrainings) {
//    communityTrainings.scrollTop(0);  
//}


//function sideTileCommunityUpButtonMouseOver(communityTrainings) {
//    if (!communityExplorerModel.ShowUpArrow()) return;

//    if (onMouseDownPressedTimeoutHandler == null)
//    {
//        onMouseDownPressedTimeoutHandler = setTimeout(function () {     
//            if (onMouseUpPressedHandler == null)
//            {    
//                onMouseUpPressedHandler = setInterval(function() {
//                    var actualScrollPosition =  communityTrainings.scrollTop();

//                    communityTrainings.scrollTop(actualScrollPosition - 55);        
//                    showHideScrolls();
//                }, 100);        
//            }
//        }, 1000);
//    }    
//}

//function communityDialogUpButtonMouseOver(communityTrainings) {
//    if (onMouseDownPressedTimeoutHandler == null)
//    {
//        onMouseDownPressedTimeoutHandler = setTimeout(function () {     
//            if (onMouseUpPressedHandler == null)
//            {    
//                onMouseUpPressedHandler = setInterval(function() {
//                    var actualScrollPosition =  communityTrainings.scrollTop();

//                    communityTrainings.scrollTop(actualScrollPosition - 55);        
//                }, 100);        
//            }
//        }, 1000);
//    }    
//}

//function sideTileCommunityUpButtonClick(communityTrainings) {
//    if (!communityExplorerModel.ShowUpArrow()) return;

//    var actualScrollPosition = $("#communityTrainings").find(".mCSB_dragger").position().top;
//    //var actualScrollPosition = communityTrainings.scrollTop();
//    $("#communityTrainings").mCustomScrollbar('scrollTo', actualScrollPosition - 55);
//    showHideScrolls();

//    sideTileCommunityUpDownButtonClicked = true;
//    sideTileCommunityUpDownButtonClickedFirst = true;   

//    if (onMouseUpPressedHandler == null)
//    {    
//        onMouseUpPressedHandler = setInterval(function() {
//            if (sideTileCommunityUpDownButtonClicked &&
//                !sideTileCommunityUpDownButtonClickedFirst)
//            {
//                var actualScrollPosition = $("#communityTrainings").find(".mCSB_dragger").position().top;
//                //var actualScrollPosition = communityTrainings.scrollTop();

//                $("#communityTrainings").mCustomScrollbar('scrollTo', actualScrollPosition - 55);
//                showHideScrolls();
//            }
//            sideTileCommunityUpDownButtonClickedFirst = false;            
//        }, 100);        
//    }            
//}

//function communityDialogUpButtonClick(communityTrainings) {
//    var actualScrollPosition =  communityTrainings.scrollTop();
//    communityTrainings.scrollTop(actualScrollPosition - 55);

//    sideTileCommunityUpDownButtonClicked = true;
//    sideTileCommunityUpDownButtonClickedFirst = true;   

//    if (onMouseUpPressedHandler == null)
//    {    
//        onMouseUpPressedHandler = setInterval(function() {
//            if (sideTileCommunityUpDownButtonClicked &&
//                !sideTileCommunityUpDownButtonClickedFirst)
//            {
//                var actualScrollPosition =  communityTrainings.scrollTop();

//                communityTrainings.scrollTop(actualScrollPosition - 55);        
//            }
//            sideTileCommunityUpDownButtonClickedFirst = false;            
//        }, 100);        
//    }            
//}

//function sideTileCommunityUpButtonRelease(notAddToCache) {
//    clearTimeout(onMouseDownPressedTimeoutHandler);
//    clearInterval(onMouseUpPressedHandler);          
    
//    if (!notAddToCache)
//    {            
//        var actualScrollPosition = $("#communityTrainings").find(".mCSB_dragger").position().top;
//        //var actualScrollPosition = $("#communityTrainings").scrollTop();

//        addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), actualScrollPosition, 86400000);//3600000);
//    }
    
//    onMouseDownPressedTimeoutHandler = null;
//    onMouseUpPressedHandler = null;

//    sideTileCommunityUpDownButtonClicked = false;
//    sideTileCommunityUpDownButtonClickedFirst = false;            
//}

//function sideTileCommunityDownButtonMouseOver(communityTrainings) {
//    if (!communityExplorerModel.ShowDownArrow()) return;

//    if (onMouseDownPressedTimeoutHandler == null)
//    {
//        onMouseDownPressedTimeoutHandler = setTimeout(function () { 
//            if (onMouseDownPressedHandler == null)
//            {
//                    onMouseDownPressedHandler = setInterval(function() {
//                            var actualScrollPosition =  communityTrainings.scrollTop();

//                            communityTrainings.scrollTop(actualScrollPosition + 55);
//                            showHideScrolls();
//                    }, 100);        
//            }
//        }, 1000);
//    }
//}

//function communityDialogDownButtonMouseOver(communityTrainings) {
//    if (onMouseDownPressedTimeoutHandler == null)
//    {
//        onMouseDownPressedTimeoutHandler = setTimeout(function () { 
//            if (onMouseDownPressedHandler == null)
//            {
//                    onMouseDownPressedHandler = setInterval(function() {
//                            var actualScrollPosition =  communityTrainings.scrollTop();

//                            communityTrainings.scrollTop(actualScrollPosition + 55);
//                    }, 100);        
//            }
//        }, 1000);
//    }
//}

function getActualScrollPosition(component) {
    var $scrollerOuter  = component;
    var $dragger        = $scrollerOuter.find( '.mCSB_dragger' );
    var scrollHeight    = $scrollerOuter.find( '.mCSB_container' ).height();
    var draggerTop      = $dragger.position().top;

    var scrollTop = draggerTop / ($scrollerOuter.height() - $dragger.height()) * (scrollHeight - $scrollerOuter.height());

    return scrollTop;
}

//function sideTileCommunityDownButtonClick(communityTrainings) {
//    if (!communityExplorerModel.ShowDownArrow()) return;

//    var actualScrollPosition = getActualScrollPosition($('.sideTileCommunity #communityTrainings'));
//    //var actualScrollPosition = communityTrainings.scrollTop();
    
//    $("#communityTrainings").mCustomScrollbar('scrollTo', actualScrollPosition + 55);
//    showHideScrolls();

//    sideTileCommunityUpDownButtonClicked = true;
//    sideTileCommunityUpDownButtonClickedFirst = true;   

//    if (onMouseDownPressedHandler == null)
//    {    
//        onMouseDownPressedHandler = setInterval(function() {
//            if (sideTileCommunityUpDownButtonClicked &&
//                !sideTileCommunityUpDownButtonClickedFirst)
//            {            
//                var actualScrollPosition = getActualScrollPosition();
//                //var actualScrollPosition = communityTrainings.scrollTop();

//                $("#communityTrainings").mCustomScrollbar('scrollTo', actualScrollPosition + 55);
//                showHideScrolls();
//            }
//            sideTileCommunityUpDownButtonClickedFirst = false;            
//        }, 100);         
//    }            
//}

//function communityDialogDownButtonClick(communityTrainings) {
//    var actualScrollPosition =  communityTrainings.scrollTop();
//    communityTrainings.scrollTop(actualScrollPosition + 55);

//    sideTileCommunityUpDownButtonClicked = true;
//    sideTileCommunityUpDownButtonClickedFirst = true;   

//    if (onMouseDownPressedHandler == null)
//    {    
//        onMouseDownPressedHandler = setInterval(function() {
//            if (sideTileCommunityUpDownButtonClicked &&
//                !sideTileCommunityUpDownButtonClickedFirst)
//            {            
//                var actualScrollPosition =  communityTrainings.scrollTop();

//                communityTrainings.scrollTop(actualScrollPosition + 55);
//            }
//            sideTileCommunityUpDownButtonClickedFirst = false;            
//        }, 100);         
//    }            
//}

//function sideTileCommunityDownButtonRelease(notAddToCache) {
//    clearTimeout(onMouseDownPressedTimeoutHandler);
//    clearInterval(onMouseDownPressedHandler);
    
//    if (!notAddToCache)
//    {
//        var actualScrollPosition = $("#communityTrainings").find(".mCSB_dragger").position().top;
//        //var actualScrollPosition = $("#communityTrainings").scrollTop();

//        addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), actualScrollPosition, 86400000);//3600000);    
//    }

//    onMouseDownPressedTimeoutHandler = null;
//    onMouseDownPressedHandler = null;
//    sideTileCommunityUpDownButtonClicked = false;
//    sideTileCommunityUpDownButtonClickedFirst = false;        
//}

function showHideScrolls(packageTrainingsCount, notExecute)
{
    //    var communityTrainings = $( "#communityTrainings" )
    //    var trainingsCount = $('.training', communityTrainings).length; 
        
    //    if (notExecute) return;
        
    //    if (packageTrainingsCount != null)
    //    {
    //        communityExplorerModel.NoMorePackages(packageTrainingsCount == 0 || packageTrainingsCount <= 5);
    //    }         

    //    var clientHeight = $('#communityTrainings.mCustomScrollbar').height();
    //    var scrollHeight = $("#communityTrainings").find('.mCSB_container').height();
    //    var scrollTop = getActualScrollPosition();

    //    if ((scrollTop + clientHeight) == scrollHeight)
    ////    if ((communityTrainings.scrollTop()+communityTrainings[0].clientHeight)==communityTrainings[0].scrollHeight)
    //    {
    //        if (communityExplorerModel.NoMorePackages())
    //        {
    //            sideTileCommunityDownButtonRelease();
    //            communityExplorerModel.ShowDownArrow(false); 
    //        }

    //        if (trainingsCount > 5) 
    //        {
    //            communityExplorerModel.ShowUpArrow(true);                 
    //            communityExplorerModel.ShowUpToStartArrow(true);
    //        }
    //        else 
    //        {
    //            communityExplorerModel.ShowUpArrow(false);                 
    //            communityExplorerModel.ShowUpToStartArrow(false);
    //        }                        
    //    } else
    //    if (scrollTop == 0)
    //    {
    //        sideTileCommunityUpButtonRelease();
    //        communityExplorerModel.ShowUpArrow(false);                 
    //        communityExplorerModel.ShowUpToStartArrow(false);
        
    //        if (trainingsCount > 5) 
    //        {
    //            communityExplorerModel.ShowDownArrow(true);            
    //        }
    //        else 
    //        {
    //            communityExplorerModel.ShowDownArrow(false);                            
    //        }
    //    }
    //    else
    //    {
    //        communityExplorerModel.ShowUpArrow(true);                 
    //        communityExplorerModel.ShowDownArrow(true);                 
    //        communityExplorerModel.ShowUpToStartArrow(true);                 
    //    }
}

function sideTileCommunityExpanderClick() {
    $(".communityUserBarEditCommunity").toggleClass('displayNone');
    $(".communityUserBarTileText").toggleClass('displayNone');
    $(".communityUserBarTile").toggleClass('paddings');
    //$("#collapseSideTileCommunity").toggleClass('collapsed');
    $('.sideTileCommunity a.dzyndzolek').toggleClass('collapsed');
    
    if ($('.sideTileCommunity a.dzyndzolek').hasClass('collapsed'))
    {
        //addToCache('sideTileCommunityExpanderState', 'collapsed', 3600000);
        $('#collapseSideTileCommunity').animate({ 
            height: '0' + 'px'
        }, 500);               
    }
    else
    {
        //addToCache('sideTileCommunityExpanderState', 'expanded', 3600000);
        
        var height = 105;
        
        if ($('#communityTrainings').css('height') != '0px')
        {
            var communityTrainingsHeight = parseInt($('#communityTrainings').css('height'));
            height = communityTrainingsHeight + 7;
        }
                        
        $('#collapseSideTileCommunity').animate({ 
            height: height + 'px'
        }, 500);               
    }    
}


// function communityShowNotifications(result)
// {
//     if (result)
//     {
//         $('.span12.communityNotifications .communityNotificationsContainer .userAvatar.communityMain').remove();                                 

//         for (var i = 0, len = result.aaData.length; i < len; i++) {
//             $('.span12.communityNotifications .communityNotificationsContainer').append(result.aaData[i].AvatarImg);             
//         }
        
//         if (result.aaData.length > 0)
//         {
//             $('.row-fluid.box.communityNotifications').css('display', 'block');                      
// //             $('#wantsObserveObservingTable_wrapper .paginate_button').html('');
// //             $('#wantsObserveObservingTable_wrapper .paginate_button').css('display', 'inline-block');               
//         }
//         else
//         {
//             $('.row-fluid.box.communityNotifications').css('display', 'none');                                  
//         }
        
//         var previousDisabled = $('div#wantsObserveObservingTable_wrapper .paginate_button.previous').hasClass('disabled');
//         var nextDisabled = $('div#wantsObserveObservingTable_wrapper .paginate_button.next').hasClass('disabled');        
        
//         if (previousDisabled) 
//         {
//             $('.communityNotificationPaginateButton.previous').addClass('disabled');                   
//         }
//         else
//         {
//             $('.communityNotificationPaginateButton.previous').removeClass('disabled');            
//         }
//         if (nextDisabled) 
//         {
//             $('.communityNotificationPaginateButton.next').addClass('disabled');                   
//         }
//         else
//         {
//             $('.communityNotificationPaginateButton.next').removeClass('disabled');            
//         }
//         if (nextDisabled && previousDisabled)
//         {
//              $('.communityNotificationPaginateButton.previous').css('display', 'none');
//              $('.communityNotificationPaginateButton.next').css('display', 'none');
//         }else
//         {
//             $('.communityNotificationPaginateButton.previous').css('display', 'block');
//             $('.communityNotificationPaginateButton.next').css('display', 'block');
//         }
//     }
// }


$( window ).resize(function() {
    if ($('.span12.communityNotifications .tileBody').length > 0)
    {
        communityResize();        
    }

    correctDisplayAvatarList();
    correctActivityTypeInfoText('.clubTableExplorerTile', 60);
    correctActivityTypeInfoText('.sideBarTargetTile', 30);
});

function communityResize()
{
    var pageLength = getNotificationsPageLength($('.span12.communityNotifications .tileBody'));

    if (pageLength) {
        changePageLength(pageLength);
    }
   
    //var tileBodyWidth = $('.span12.communityNotifications .tileBody').width();
    
    //var pageLength = communityModel.CommunityModelForWaitings().EntriesPerPage();//wantsObserveObservingTableApi.page.len();
    
    //if (tileBodyWidth == 0) return;
    
    //if (tileBodyWidth > 900 && pageLength != 3)
    //{
    //    changePageLength(3);
    //}else
    //if (tileBodyWidth < 900 && tileBodyWidth > 700 && pageLength != 2)
    //{        
    //    changePageLength(2);
    //}else
    //if (tileBodyWidth < 700 && pageLength != 1)
    //{   
    //    changePageLength(1);
    //}    
}

function getNotificationsPageLength(checkedElement)
{
    //if (wantsObserveObservingTableApi == null) return;
    
    var tileBodyWidth = checkedElement.width();
    
    var pageLength = communityModel.CommunityModelForWaitings().EntriesPerPage();//wantsObserveObservingTableApi.page.len();
    //wantsObserveObservingTableCurrentPage = communityModel.CommunityModelForWaitings.PageNumber(); //wantsObserveObservingTableApi.page(); 
    //    wantsObserveObservingTableFirstElemIndex = (wantsObserveObservingTableCurrentPage) * pageLength;
    //var elemCount = $('.span12.communityNotifications .communityNotificationsContainer .userAvatar.communityMain').length;    
    
    if (tileBodyWidth == 0) return 0;
    
    if (tileBodyWidth > 900 && pageLength != 3)
    {
        return 3;
    }else
        if (tileBodyWidth < 900 && tileBodyWidth > 700 && pageLength != 2)
        {        
            return 2;
        }else
            if (tileBodyWidth < 700 && pageLength != 1)
            {   
                return 1;
            }

    return 0;
}

function changePageLength(pageLength)
{
    if (!communityModel.CommunityModelForWaitingsSearchIsExecuting) {
        communityModel.CommunityModelForWaitings().EntriesPerPage(pageLength);
        communityModel.CommunityModelForWaitings().Search();
    }

    //      wantsObserveObservingTable._fnLengthChange(pageLength);        
    //      wantsObserveObservingTableApi.ajax.reload(); 



    //$('.span12.communityNotifications .communityNotificationsContainer .userAvatar.communityMain').remove();                                 
    //     wantsObserveObservingTableApi.page(wantsObserveObservingTableCurrentPage);        

    //     var oSettings = wantsObserveObservingTable.fnSettings();

    //     oSettings._iDisplayStart = wantsObserveObservingTableFirstElemIndex; //for example is 3rd page if page size is 5
    //     wantsObserveObservingTable.fnDraw(true);
}

// function communityAvatarMinusClick(container, askedToObserve, userJoinIdP)
// {
//     if (askedToObserve == "True" || askedToObserve == "true")
//     {
//         setJoinStatus(container, 8, 'OR', null, null, null, null, userJoinIdP);
//     }
//     else
//     {
//         setJoinStatus(container, null, null, 2,'OR', null, null, userJoinIdP);
//     }
// }

// function communityAvatarPlusMouseOver(container)
// {
//     $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer').hide();

//     communityAvatarCloudMouseOver = false;
//     clearTimeout(communityDisableDetailsTimeOut);
    
//     var cloud = $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer', container);

//     cloud.css('display', 'block');        
// }

function fansCounterContainerMouseOver(container, event)
{
    communityHideCloud();
    communityAvatarCloudMouseOver = false;
    clearTimeout(communityDisableDetailsTimeOut);
    
    var cloud = null;
    
    if (event) cloud = $('.publishInfoCloudContainer', event.currentTarget);
    else cloud = $('.publishInfoCloudContainer', container);
            
    cloud.css('display', 'block');
        
    communityAvatarMouseEnter = true;
}

function communityAvatarMouseOver(container, event)
{
    //$('.userAvatar.communityMain .publishInfoCloudContainer').hide();        
    communityHideCloud();
    communityAvatarCloudMouseOver = false;
    clearTimeout(communityDisableDetailsTimeOut);
    
    var cloud = null;
    
    if (event) cloud = $('.publishInfoCloudContainer', event.currentTarget);
    else cloud = $('.publishInfoCloudContainer', container);
    
    $('#addCommunityUserModal .modal-body').css('overflow', 'visible');
    $('#communityInviteUserModal .modal-body').css('overflow', 'visible');
    $('#collapseCommunityObservedAndObservers').css('overflow', 'visible');
    var scrollWidthBefore = 0;
    
    if ($('#collapseCommunityObservedAndObservers').length > 0)
    {
        scrollWidthBefore = $('#collapseCommunityObservedAndObservers')[0].scrollWidth;    
    }
    
    if ($('#addCommunityUserModal').is(':Visible')) {
        scrollWidthBefore = scrollWidthBefore + $('#addCommunityUserModal .modal-body')[0].scrollWidth;    
    }

    if ($('#communityInviteUserModal').is(':Visible')) {
        scrollWidthBefore = scrollWidthBefore + $('#communityInviteUserModal .modal-body')[0].scrollWidth;
    }

    cloud.css('display', 'block');
    
    if (!communityAvatarMouseEnter)
    {
        var scrollWidthAfter = 0;
        
        if ($('#collapseCommunityObservedAndObservers').length > 0)
        {
            scrollWidthAfter = $('#collapseCommunityObservedAndObservers')[0].scrollWidth;    
        }
        
        if ($('#addCommunityUserModal').is(':Visible')) {        
            scrollWidthAfter = scrollWidthAfter + $('#addCommunityUserModal .modal-body')[0].scrollWidth;    
        }

        if ($('#communityInviteUserModal').is(':Visible')) {
            scrollWidthAfter = scrollWidthAfter + $('#communityInviteUserModal .modal-body')[0].scrollWidth;    
        }
        
        var hrCloudFilled = $('.hrCloud.filled', cloud);

        if (scrollWidthBefore != scrollWidthAfter)
        {        
            if (hrCloudFilled.hasClass('right'))
            {
                hrCloudFilled.removeClass('right');
                hrCloudFilled.addClass('left');    
                cloud.removeClass('right');
                cloud.addClass('left');    
            }
        }
    }
    $('#addCommunityUserModal .modal-body').css('overflow', 'hidden');
    $('#communityInviteUserModal .modal-body').css('overflow', 'hidden');
    $('#collapseCommunityObservedAndObservers').css('overflow', 'hidden');
    
    communityAvatarMouseEnter = true;
}

function communityAvatarMouseOut()
{
    communityDisableDetailsTimeOut = setTimeout(function () { communityHideCloud(); }, 100);   
    communityAvatarMouseEnter = false;
}

function communityHideCloud()
{
    if (!communityAvatarCloudMouseOver) 
    {
        $('.userAvatar.communityMain .publishInfoCloudContainer').hide();    
        $('#communityTargetModal .publishInfoCloudContainer').hide();    
        $('.clubTableExplorerTile.targetContainer .communityTargetModalFansCounterContainer .publishInfoCloudContainer').hide();

        var cloud = $('.userAvatar.communityMain .publishInfoCloudContainer');
        cloud.each(function () {
            var elem = $(this);
            if (elem.hasClass('left'))
            {
                cloud.removeClass('left');
                cloud.addClass('right');    
            }                     
        });

        var hrCloudFilled = $('.userAvatar.communityMain .publishInfoCloudContainer .hrCloud.filled');
        hrCloudFilled.each(function () {
            var elem = $(this);
            if (elem.hasClass('left'))
            {
                elem.removeClass('left');
                elem.addClass('right');            
            }                     
        });
    }
}

function communityAvatarPublishInfoCloudContainerMouseOver()
{
    communityAvatarCloudMouseOver = true;
    clearTimeout(communityDisableDetailsTimeOut);
}

function communityAvatarPublishInfoCloudContainerMouseOut()
{
    communityAvatarCloudMouseOver = false;
    communityHideCloud();
}


// $(document).on('click', function (e) {
//     if (e.target.className == "communityAvatarPlus plusik14") return;
//     if ($(e.target).closest('.communityNotifications.plusButtonInfo .publishInfoCloudContainer').length === 0) {
//         $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer').hide();
//     }
//     if (e.target.className == "communitySelectionCombobox" || e.target.className == "communitySelectionComboboxButton") return;
//     if ($(e.target).closest('.communitySelectionCombobox').length === 0) {
//        for(var i = 0; i< communityModel.CommunitySelectionComboboxItems().length; i++) {
//             communityModel.CommunitySelectionComboboxItems()[i]().Visible(true);
//         }         
//     }
// });

// function communityAvatarPlusMouseOver(container)
// {    
//     var cloud = $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer');

//     cloud.css('display', 'none');        
    
//     cloud = $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer', container);

//     cloud.css('display', 'block');        
// //     $('#collapseCommunityObservedAndObservers').css('overflow', 'visible');
// //     $('.row-fluid.box.communityObservedAndObservers .tileBody').css('overflow', 'visible');
// }

// function communityAvatarMinusMouseOver(container)
// {
//     $('.userAvatar.communityMain .publishInfoCloudContainer').hide();    
//     communityAvatarCloudMouseOver = false;
//     clearTimeout(communityDisableDetailsTimeOut);    
    
// //     $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer').hide();

//     var cloud = $('.communityNotifications.minusButtonInfo .publishInfoCloudContainer', container);

//     cloud.css('display', 'block'); 
            
// //      $('#collapseCommunityObservedAndObservers').css('overflow', 'visible')
// //      $('.row-fluid.box.communityObservedAndObservers .tileBody').css('overflow', 'visible');
// }

// function communityAvatarMinusMouseOut()
// {
//     var cloud = $('.communityNotifications.minusButtonInfo .publishInfoCloudContainer');

//      cloud.css('display', 'none');        
        
// //     $('#collapseCommunityObservedAndObservers').css('overflow', 'hidden')
// //     $('.row-fluid.box.communityObservedAndObservers .tileBody').css('overflow', 'hidden');
// }

// $(document).on('click', function (e) {
//     if (e.target.className == "communityAvatarPlus plusik14") return;
//     if ($(e.target).closest('.communityNotifications.plusButtonInfo .publishInfoCloudContainer').length === 0) {
        
//         var cloud = $('.communityNotifications.plusButtonInfo .publishInfoCloudContainer');

//         cloud.css('display', 'none');        
                
// //         $('#collapseCommunityObservedAndObservers').css('overflow', 'hidden')        
// //         $('.row-fluid.box.communityObservedAndObservers .tileBody').css('overflow', 'hidden');
//     }
// //     if (e.target.className == "communitySelectionCombobox" || e.target.className == "communitySelectionComboboxButton") return;
// //     if ($(e.target).closest('.communitySelectionCombobox').length === 0) {
// //        for(var i = 0; i< communityModel.CommunitySelectionComboboxItems().length; i++) {
// //             communityModel.CommunitySelectionComboboxItems()[i]().Visible(true);
// //         }         
// //     }
// });

// function askedToObserveIAgreeButtonClick(container, userJoinIdP)
// {
//     setJoinStatus(container, 4, 'OR', null, null, null, true, null, null, userJoinIdP);                   
// }

// function askedToObserveIAlsoWantYouTooObserveMeButtonClick(container, userJoinIdP)
// {
//     setJoinStatus(container, 4, 'OR', 1, 'OR', null, true, null, null, userJoinIdP);             
// }

// function askedToBeObservedIAgreeButtonClick(container, userJoinIdP)
// {
//     setJoinStatus(container, null, null, 1, 'OR', true, null, null, null, userJoinIdP);           
// }

// function askedToBeObservedIAlsoWantToObserveYouButtonClick(container, userJoinIdP)
// {
//     setJoinStatus(container, 4, 'OR', 1, 'OR', true, null, null, null, userJoinIdP);           
// }

function setJoinStatus(container, observerState, observerStateOperation, observedState, 
                       observedStateOperation, isObserved, isObserving, 
                       observerVisibility, observedVisibility, userJoinIdP, observedUserId,
                       observerInvitationText, observedInvitationText)
{
    var userJoinId = null;
    if (userJoinIdP || userJoinIdP == 0) 
    {
        userJoinId = userJoinIdP;
    }
    else
    {
        userJoinId = container.data().userjoinid;
    }
    
    $.ajax({
        type: "POST",
        url: ContextPath + "Community/SetJoinStatus",
        data: { 
            userJoinId: userJoinId, 
            observerState: observerState, 
            observerStateOperation: observerStateOperation,
            observedState: observedState, 
            observedStateOperation: observedStateOperation,
            isObserved: isObserved, 
            isObserving: isObserving, 
            observerVisibility: observerVisibility, 
            observedVisibility: observedVisibility,
            observedUserId: observedUserId,
            observerInvitationText: observerInvitationText,
            observedInvitationText: observedInvitationText
        }
    })
     .done(function (result) {
         if (result == 'done')
         {
             //             if (!userJoinIdP)
             //             {
             //                wantsObserveObservingTableApi.ajax.reload();
             //communityModel.init(true);
             //             }
             //             else
             //             {
             if (observedUserId)
             {
                 communityModelForUsers.Search();
             } else
             {
                 communityModelForUsers.Search();
                 communityModel.CommunityModelForWaitings().Search();
                 communityModel.Search();
             }
             //             }
         }
     });     
}

function communityObservedAndObserversExpanderClick()
{
    //     $('#collapseCommunityObservedAndObservers').toggleClass('overflowVisible');
    //     $('.row-fluid.box.communityObservedAndObservers .tileBody').toggleClass('overflowVisible');
    $('.communitySearchedUsersContainer', $('#collapseCommunityObservedAndObservers')).toggleClass('minHeight');
    if (!communityModel.FirstLoaded) communityModel.Search();
}

function CommunitySelectionComboboxItem(parent, index, text, name, entriesPerPage, selected)
{
    var self = this;   

    self.Parent = parent;
    self.Index = ko.observable(index);
    self.Text = ko.observable(text);
    self.Name = ko.observable(name);     
    self.Selected = ko.observable(selected);   
    self.Visible = ko.observable(selected);
    self.EntriesPerPage = ko.observable(entriesPerPage);    

    self.Select = function () {
        self.Parent.CommunitySelectionComboboxItemIndex(self.Index());
        self.Parent.resetPageNumber();
        self.Parent.Search();
    }     
}

function GotoCommunitySettings()
{
    //    shouldGoToCommunitySection = true;
    window.location = ContextPath + "Account/AccountDetails#comunityFeature";//?GoToCommunitySection=true";
}

function CommunityExplorerModel()
{
    var self = this;   

    self.ActionedUser = ko.observable(new CommunityUserModel(0,''));
    self.InvitationsCount = ko.observable(0);
    self.NewTrainingInvitationsCount = ko.observable(0);
    self.NewTargetsCount = ko.observable(0);
    self.NewCompetitionsCount = ko.observable(0);
    self.NewTrainingsCount = ko.observable(0);
    self.NewTargetCommentsCount = ko.observable(0);
    self.NewCompetitionsCommentsCount = ko.observable(0);
    self.NewTrainingsCommentsCount = ko.observable(0);
    self.NewTrainingInvitationsCommentsCount = ko.observable(0);
    self.NewLiveStreamsCount = ko.observable(0);
    self.LiveStreamsCount = ko.observable(0);
    self.ChangedTrainingInvitationsCount = ko.observable(0);
    self.NewPublicationsCount = ko.observable(0);
    self.NewPublicationsCommentsCount = ko.observable(0);
    self.ShowUpToStartArrow = ko.observable(false);
    self.ShowUpArrow = ko.observable(false);
    self.ShowDownArrow = ko.observable(false);
    self.FirstTrainingsCount = ko.observable(0);
    self.NoMorePackages = ko.observable(false);
    self.SelectedTab = ko.observable('Trainings');
    self.SelectedTrainingInvitation = ko.observable(new TrainingInvitationModel(self));
    self.DataLoaded = ko.observable(false);
    self.TrainingInvitationsWasPressed = false;
    self.IgnoreTrainingInvitationsWasPressed = false;
    self.CompetitionsWasPressed = false;
    self.TargetWasPressed = false;
    self.TrainingsWasPressed = false;
    self.UserTarget = ko.observable(new UserTargetModel());
    self.CommunityCompetition = ko.observable(new CommunityCompetitionModel());
    self.CommunityCompetitionPlan = ko.observable(new CommunityCompetitionPlanModel());
    self.CommunityTraining = ko.observable(new CommunityTrainingModel());
    self.CommunityUserTrainingPlan = ko.observable(new CommunityUserTrainingPlanModel());
    self.SelectedCommentsModel = null;
    self.SelectedCommentsWindowName = null;
    self.SelectedWallTrainingCommentsModel = ko.observable(new CommunityCommentsModel(self));;
    self.CommentsBallPressed = false;
    self.TabTrainingsCount_old = 0;
    self.TabCompetitionsCount_old = 0;
    self.TabTargetsCount_old = 0;
    self.TabLiveStreamsCount_old = 0;
    self.TabTrainingInvitationsCount_old = 0;
    self.TabPublicationsCount_old = 0;
    self.NumberOfTimeouts = 0;
    self.LoadModelTimeoutHandler = null;
    self.StartedLoadingCommunityExplorer = false;
    self.CommunityScrollUserList = ko.observable(new CommunityScrollUserListModel(self));
    self.LiveStreamLoaded = false;

    self.MainBtnsShow = ko.computed(function () { 
        return true;//self.SelectedTab() == 'All' || self.SelectedTab() == 'Trainings' || self.SelectedTab() == 'Competitions' || self.SelectedTab() == 'Targets';
    });

    self.TabTrainingInvitationsCount = ko.computed(function () {
        return self.NewTrainingInvitationsCount() + self.NewTrainingInvitationsCommentsCount();
    });

    self.TabTargetsCount = ko.computed(function () { 
        return self.NewTargetsCount() + self.NewTargetCommentsCount();
    }); 

    self.TabCompetitionsCount = ko.computed(function () { 
        return self.NewCompetitionsCount() + self.NewCompetitionsCommentsCount();
    }); 

    self.TabTrainingsCount = ko.computed(function () { 
        return self.NewTrainingsCount() + self.NewTrainingsCommentsCount();
    });

    self.TabLiveStreamsCount = ko.computed(function () {
        return self.NewLiveStreamsCount();// + self.LiveStreamsCount();
    });

    self.TabPublicationsCount = ko.computed(function () {
        return self.NewPublicationsCount() + self.NewPublicationsCommentsCount();
    });

    self.setInvitationsCount = function (count) {
        self.InvitationsCount(count);
        
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null)
        {             
            var data = JSON.parse(communityExplorerModelTmp);

            data.InvitationsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    } 

    self.setNewTrainingInvitationsCount = function (count) {
        self.NewTrainingInvitationsCount(count);
        
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null)
        {             
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTrainingInvitationsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewTrainingInvitationsCommentsCount = function (count) {
        self.NewTrainingInvitationsCommentsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTrainingInvitationsCommentsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewTrainingsCount = function (count) {
        self.NewTrainingsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTrainingsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewTrainingsCommentsCount = function (count) {
        self.NewTrainingsCommentsCount(count);
        
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null)
        {             
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTrainingsCommentsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewCompetitionsCount = function (count) {
        self.NewCompetitionsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewCompetitionsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewCompetitionsCommentsCount = function (count) {
        self.NewCompetitionsCommentsCount(count);
        
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null)
        {             
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewCompetitionsCommentsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }     

    self.setNewTargetsCount = function (count) {
        self.NewTargetsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTargetsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewTargetCommentsCount = function (count) {
        self.NewTargetCommentsCount(count);
        
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null)
        {             
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewTargetCommentsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewLiveStreamsCount = function (count) {
        self.NewLiveStreamsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewLiveStreamsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.setNewPublicationsCount = function (count) {
        self.NewPublicationsCount(count);

        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp != null) {
            var data = JSON.parse(communityExplorerModelTmp);

            data.NewPublicationsCount = count;

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000);
        }
    }

    self.serialize = function () {
        var data = {
            InvitationsCount: self.InvitationsCount(),
            NewTrainingInvitationsCount: self.NewTrainingInvitationsCount(),
            NewTrainingInvitationsCommentsCount: self.NewTrainingInvitationsCommentsCount(),
            ChangedTrainingInvitationsCount: self.ChangedTrainingInvitationsCount(),
            NewTargetsCount: self.NewTargetsCount(),
            NewCompetitionsCount: self.NewCompetitionsCount(),
            NewTargetCommentsCount: self.NewTargetCommentsCount(),
            NewCompetitionsCommentsCount: self.NewCompetitionsCommentsCount(),
            NewTrainingsCommentsCount: self.NewTrainingsCommentsCount(),
            NewTrainingsCount: self.NewTrainingsCount(),
            NewLiveStreamsCount: self.NewLiveStreamsCount(),
            LiveStreamsCount: self.LiveStreamsCount(),
            NewPublicationsCount: self.NewPublicationsCount(),
            NewPublicationsCommentsCount: self.NewPublicationsCommentsCount(),
        };
        var strData = JSON.stringify(data);

        return strData;
    }

    self.initfromCache = function (communityExplorerModelTmp, callBack, callParam1, callParam2) {
        var data = JSON.parse(communityExplorerModelTmp);

        self.initByModel(data, callBack, callParam1, callParam2);
        //         self.InvitationsCount(data.InvitationsCount);
        //         self.NewTrainingInvitationsCount(data.NewTrainingInvitationsCount);
        //         self.ChangedTrainingInvitationsCount(data.ChangedTrainingInvitationsCount);
        //         self.NewTargetsCount(data.NewTargetsCount);
        //         self.NewCompetitionsCount(data.NewCompetitionsCount);
        //         self.NewTargetCommentsCount(data.NewTargetCommentsCount);
        //         self.NewCompetitionsCommentsCount(data.NewCompetitionsCommentsCount);
        //         self.NewTrainingsCommentsCount(data.NewTrainingsCommentsCount);
        //         self.NewTrainingsCount(data.NewTrainingsCount);
        //         self.DataLoaded(true);   
    } 

    self.initByModel = function (serverModel, callBack, callParam1, callParam2) {
        if (serverModel) {
            self.InvitationsCount(serverModel.InvitationsCount);
            self.NewTrainingInvitationsCount(serverModel.NewTrainingInvitationsCount);
            self.NewTrainingInvitationsCommentsCount(serverModel.NewTrainingInvitationsCommentsCount);
            self.ChangedTrainingInvitationsCount(serverModel.ChangedTrainingInvitationsCount);
            self.NewTargetsCount(serverModel.NewTargetsCount);
            self.NewCompetitionsCount(serverModel.NewCompetitionsCount);
            self.NewTargetCommentsCount(serverModel.NewTargetCommentsCount);
            self.NewCompetitionsCommentsCount(serverModel.NewCompetitionsCommentsCount);
            self.NewTrainingsCommentsCount(serverModel.NewTrainingsCommentsCount);
            self.NewTrainingsCount(serverModel.NewTrainingsCount);
            self.NewLiveStreamsCount(serverModel.NewLiveStreamsCount);
            self.LiveStreamsCount(serverModel.LiveStreamsCount);
            self.NewPublicationsCount(serverModel.NewPublicationsCount);
            self.NewPublicationsCommentsCount(serverModel.NewPublicationsCommentsCount);
            self.DataLoaded(true);

            var strData = self.serialize();

            addToCache("CommunityExplorerModel", strData, 600000)//10min///3600000);

            if (callBack) callBack(callParam1, callParam2);
        }
    };          

    self.init = function (refresh, callBack, callParam1, callParam2) {
        var communityExplorerModelTmp = $.jStorage.get('CommunityExplorerModel');

        if (communityExplorerModelTmp == null || refresh) {
            setCommunityWallWaitCursor();
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetCommunityExplorerModel",
            })
           .done(function (serverModel) {
               if (serverModel) {
                   if (!serverModel.TimeoutOccured) {
                       self.TabLiveStreamsCount_old = 0;
                       self.initByModel(serverModel, callBack, callParam1, callParam2);
                       setCommunityWallNormalCursor();
                   } else {
                       self.NumberOfTimeouts++;
                       clearTimeout(self.LoadModelTimeoutHandler);
                       self.LoadModelTimeoutHandler =
                       setTimeout(function () {
                           self.init(refresh, callBack, callParam1, callParam2)
                       }, 500 * self.NumberOfTimeouts);
                   }
               }
           });
        }
        else {
            self.initfromCache(communityExplorerModelTmp, callBack, callParam1, callParam2);
        }
    }     
     
    self.UpToStartArrowClass = ko.computed(function () { 
        return self.ShowUpToStartArrow() ? '' : 'gray'; 
    }); 
    self.UpArrowClass = ko.computed(function () { 
        return self.ShowUpArrow() ? '' : 'gray'; 
    }); 
    self.DownArrowClass = ko.computed(function () { 
        return self.ShowDownArrow() ? '' : 'gray'; 
    });

    self.loadTrainingInvitations = function () {
        self.setSelectedTab('TrainingInvitations', true);
        self.TrainingInvitationsWasPressed = true; 

        if (self.TargetWasPressed && self.NewTargetCommentsCount() > 0)
        {
            self.SetAllTargetsToRead();
        }
        if (self.TrainingsWasPressed && self.NewTrainingsCount() > 0) {
            self.SetAllTrainingsToRead();
        }
        if (self.CompetitionsWasPressed && self.NewCompetitionsCount() > 0)
        {
            self.SetAllCompetitionsToRead();
        }
        if (self.LiveStreamsWasPressed && self.NewLiveStreamsCount() > 0) {
            self.SetAllLiveStreamsToRead();
        }
        if (self.PublicationsWasPressed && self.NewPublicationsCount() > 0) {
            self.SetAllPublicationsToRead();
        }

        self.TrainingsWasPressed = false;
        self.CompetitionsWasPressed = false;
        self.TargetWasPressed = false;
        self.LiveStreamsWasPressed = false;
        self.PublicationsWasPressed = false;
    }

    self.loadTrainings = function () {
        self.setSelectedTab('Trainings', true);
        self.TrainingsWasPressed = true;

        if (self.TrainingInvitationsWasPressed && self.NewTrainingInvitationsCount() > 0)
        {
            self.SetAllTrainingInvitationsToRead();
        }
        if (self.TargetWasPressed && self.NewTargetsCount() > 0)
        {
            self.SetAllTargetsToRead();
        }
        if (self.CompetitionsWasPressed && self.NewCompetitionsCount() > 0)
        {
            self.SetAllCompetitionsToRead();
        }                  
        if (self.LiveStreamsWasPressed && self.NewLiveStreamsCount() > 0) {
            self.SetAllLiveStreamsToRead();
        }
        if (self.PublicationsWasPressed && self.NewPublicationsCommentsCount() > 0) {
            self.SetAllPublicationsToRead();
        }

        self.TrainingInvitationsWasPressed = false;
        self.CompetitionsWasPressed = false;    
        self.TargetWasPressed = false;    
        self.LiveStreamsWasPressed = false;
        self.PublicationsWasPressed = false;
    }

    self.loadCompetitions = function () {
        self.setSelectedTab('Competitions', true);
        self.CompetitionsWasPressed = true;  

        if (self.TrainingInvitationsWasPressed && self.NewTrainingInvitationsCount() > 0) {
            self.SetAllTrainingInvitationsToRead();
        }
        if (self.TargetWasPressed && self.NewTargetsCount() > 0)
        {
            self.SetAllTargetsToRead();
        }
        if (self.TrainingsWasPressed && self.NewTrainingsCount() > 0)
        {
            self.SetAllTrainingsToRead();
        }         
        if (self.LiveStreamsWasPressed && self.NewLiveStreamsCount() > 0) {
            self.SetAllLiveStreamsToRead();
        }
        if (self.PublicationsWasPressed && self.NewPublicationsCommentsCount() > 0) {
            self.SetAllPublicationsToRead();
        }
        self.TrainingInvitationsWasPressed = false;
        self.TrainingsWasPressed = false;    
        self.TargetWasPressed = false;                                               
        self.LiveStreamsWasPressed = false;
        self.PublicationsWasPressed = false;
    }

    self.loadTargets = function () {
        self.setSelectedTab('Targets', true);
        self.TargetWasPressed = true;   

        if (self.TrainingInvitationsWasPressed && self.NewTrainingInvitationsCount() > 0) {
            self.SetAllTrainingInvitationsToRead();
        }
        if (self.CompetitionsWasPressed && self.NewCompetitionsCount() > 0)
        {
            self.SetAllCompetitionsToRead();
        }   
        if (self.TrainingsWasPressed && self.NewTrainingsCount() > 0)
        {
            self.SetAllTrainingsToRead();
        }
        if (self.LiveStreamsWasPressed && self.NewLiveStreamsCount() > 0) {
            self.SetAllLiveStreamsToRead();
        }
        if (self.PublicationsWasPressed && self.NewPublicationsCommentsCount() > 0) {
            self.SetAllPublicationsToRead();
        }

        self.TrainingInvitationsWasPressed = false;
        self.CompetitionsWasPressed = false;    
        self.TrainingsWasPressed = false;
        self.LiveStreamsWasPressed = false;
        self.PublicationsWasPressed = false;
    }

    self.loadLiveStreams = function () {
        self.setSelectedTab('LiveStreams', true);
        self.LiveStreamsWasPressed = true;

        if (self.TrainingInvitationsWasPressed && self.NewTrainingInvitationsCount() > 0) {
            self.SetAllTrainingInvitationsToRead();
        }
        if (self.CompetitionsWasPressed && self.NewCompetitionsCount() > 0) {
            self.SetAllCompetitionsToRead();
        }
        if (self.TrainingsWasPressed && self.NewTrainingsCount() > 0) {
            self.SetAllTrainingsToRead();
        }
        if (self.TargetWasPressed && self.NewTargetsCount() > 0) {
            self.SetAllTargetsToRead();
        }
        if (self.PublicationsWasPressed && self.NewPublicationsCommentsCount() > 0) {
            self.SetAllPublicationsToRead();
        }

        self.TrainingInvitationsWasPressed = false;
        self.CompetitionsWasPressed = false;
        self.TrainingsWasPressed = false;
        self.TargetWasPressed = false;
        self.PublicationsWasPressed = false;
    }

    self.loadPublications = function () {
        self.setSelectedTab('Publications', true);
        self.PublicationsWasPressed = true;

        if (self.TrainingInvitationsWasPressed && self.NewTrainingInvitationsCount() > 0) {
            self.SetAllTrainingInvitationsToRead();
        }
        if (self.CompetitionsWasPressed && self.NewCompetitionsCount() > 0) {
            self.SetAllCompetitionsToRead();
        }
        if (self.TrainingsWasPressed && self.NewTrainingsCount() > 0) {
            self.SetAllTrainingsToRead();
        }
        if (self.LiveStreamsWasPressed && self.NewLiveStreamsCount() > 0) {
            self.SetAllLiveStreamsToRead();
        }
        if (self.TargetWasPressed && self.NewTargetsCount() > 0) {
            self.SetAllTargetsToRead();
        }

        self.TrainingInvitationsWasPressed = false;
        self.CompetitionsWasPressed = false;
        self.TrainingsWasPressed = false;
        self.LiveStreamsWasPressed = false;
        self.TargetWasPressed = false;
    }
     
    self.SetAllTrainingInvitationsToRead = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllTrainingInvitationsToRead",
        })
       .done(function (result) {
           userBarModel.init(true);
           communityExplorerModel.setNewTrainingInvitationsCount(0);
           if (self.SelectedTab() != 'TrainingInvitations') {
               self.TrainingInvitationsWasPressed = false;
           }
           trainingInvitationsExplorerModel.NotReadCount(0);
           
           for (var i = 0, len = trainingInvitationsExplorerModel.List().length; i < len; i++) {
               trainingInvitationsExplorerModel.List()[i].IsRead(true);
           }

           self.TabTrainingInvitationsCount_old = self.TabTrainingInvitationsCount();
           addToCache('TabTrainingInvitationsCount_old', self.TabTrainingInvitationsCount_old, 86400000);//3600000);
       });          
    }

    self.SetAllTargetsToRead = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllTargetsToRead",
        })
       .done(function (result) {
           communityExplorerModel.setNewTargetsCount(0);
           if (self.SelectedTab() != 'Targets') {
               self.TargetWasPressed = false;
           }
           self.TabTargetsCount_old = self.TabTargetsCount();
           addToCache('TabTargetsCount_old', self.TabTargetsCount_old, 86400000);//3600000);

           //            $('#communitySideTile .training.target').each(function () {
           //                 var elem = $(this);

           //                 elem.removeClass('noResponse');
           //            });           
       });          
    }     

    self.SetAllCompetitionsToRead = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllCompetitionsToRead",
        })
       .done(function (result) {
           communityExplorerModel.setNewCompetitionsCount(0);

           if (self.SelectedTab() != 'Competitions') {
               self.CompetitionsWasPressed = false;
           }

           self.TabCompetitionsCount_old = self.TabCompetitionsCount();
           addToCache('TabCompetitionsCount_old', self.TabCompetitionsCount_old, 86400000);//3600000);

           //            $('#communitySideTile .training.competition').each(function () {
           //                 var elem = $(this);

           //                 elem.removeClass('noResponse');
           //            });                      
       });          
    } 

    self.SetAllTrainingsToRead = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllTrainingsToRead",
        })
       .done(function (result) {
           communityExplorerModel.setNewTrainingsCount(0);

           if (self.SelectedTab() != 'Trainings') {
               self.TrainingsWasPressed = false;
           }

           self.TabTrainingsCount_old = self.TabTrainingsCount();
           addToCache('TabTrainingsCount_old', self.TabTrainingsCount_old, 86400000);//3600000);           
       });          
    }

    self.SetAllLiveStreamsToRead = function () {
        var data = {
            messageEntityType: 'LiveStream',
            inboxMessageType: 9
        };

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllWallElementsToRead",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (result) {
           communityExplorerModel.setNewLiveStreamsCount(0);
           if (self.SelectedTab() != 'LiveStreams') {
               self.LiveStreamsWasPressed = false;
           }
           self.TabLiveStreamsCount_old = self.TabLiveStreamsCount();
           addToCache('TabLiveStreamsCount_old', self.TabLiveStreamsCount_old, 86400000);//3600000);
       });
    }

    self.SetAllPublicationsToRead = function () {
        var data = {
            messageEntityType: 'Publication',
            inboxMessageType: 11
        };

        var strData = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetAllWallElementsToRead",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (result) {
           communityExplorerModel.setNewPublicationsCount(0);
           if (self.SelectedTab() != 'Publications') {
               self.PublicationsWasPressed = false;
           }
           self.TabPublicationsCount_old = self.TabPublicationsCount();
           addToCache('TabPublicationsCount_old', self.TabPublicationsCount_old, 86400000);
       });
    }

    self.setSelectedTab = function (tab, refreshWall) {
        clearTimeout(communityModel.LoadTrainingsTimeoutHandler);
        communityModel.LoadTrainingsTimeoutHandler = null;        

        clearTimeout(communityModel.LoadNextTrainingsTimeoutHandler);
        communityModel.LoadNextTrainingsTimeoutHandler = null;                

        clearTimeout(communityModel.LoadMissingTrainingsTimeoutHandler);
        communityModel.LoadMissingTrainingsTimeoutHandler = null;                

        communityModel.RefreshWasPressed = true;
        communityModel.NumberOfTimeouts = 0;
        communityClearCacheForActualTab(tab);

        self.SelectedTab(tab);

        addToCache('SelectedTab', tab, 86400000);//3600000);

        if (refreshWall) 
        {
            //communityRefreshTrainings();         

            addToCache('actualScrollPosition_' + communityExplorerModel.UrlEnd(), 0, 86400000);//3600000);
            $(".sideTileCommunity #communityTrainings").mCustomScrollbar('scrollTo', 0);
            //$("#communityTrainings").scrollTop(0);
                        
            clearTimeout(communityExplorerModel.LoadModelTimeoutHandler);
            communityExplorerModel.LoadModelTimeoutHandler = null;
            communityExplorerModel.NumberOfTimeouts = 0;
            communityExplorerModel.init(true, communityLoadFirstTrainings, true, communityCheckForMissingTrainings);
        }
    }       

    self.UrlEnd = function () {
        var urlEnd = "Trainings";

        if (self.SelectedTab() == 'Trainings') 
            urlEnd = 'GetOnlyTrainings';
        else
            if (self.SelectedTab() == 'Competitions') 
                urlEnd = 'GetOnlyCompetitions';
            else
                if (self.SelectedTab() == 'Targets') 
                    urlEnd = 'GetOnlyTargets';
                else        
                    if (self.SelectedTab() == 'TrainingInvitations') 
                        urlEnd = 'GetOnlyTrainingInvitations';  
                    else        
                        if (self.SelectedTab() == 'LiveStreams') 
                            urlEnd = 'GetOnlyLiveStreams';
                        else
                            if (self.SelectedTab() == 'Publications') 
                                urlEnd = 'GetOnlyPublications';

        return urlEnd;
    }

    self.clearAllCachedTabs = function () {
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTrainings');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTrainingInvitations');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyCompetitions');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyTargets');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyLiveStreams');
        deleteFromCacheWithPrefix('communityTrainings_GetOnlyPublications');
        deleteFromCache('actualScrollPosition_GetOnlyTrainings');
        deleteFromCache('actualScrollPosition_GetOnlyTrainingInvitations');
        deleteFromCache('actualScrollPosition_GetOnlyCompetitions');
        deleteFromCache('actualScrollPosition_GetOnlyTargets');
        deleteFromCache('actualScrollPosition_GetOnlyLiveStreams');
        deleteFromCache('actualScrollPosition_GetOnlyPublications');
    }

    self.UrlEndForMissing = function () {
        var urlEnd = "";

        if (self.SelectedTab() == 'Trainings') 
            urlEnd = 'GetOnlyMissingTrainings';
        else
            if (self.SelectedTab() == 'TrainingInvitations')
                urlEnd = 'GetOnlyMissingTrainingInvitations';
            else
                if (self.SelectedTab() == 'Competitions') 
                    urlEnd = 'GetOnlyMissingCompetitions';
                else
                    if (self.SelectedTab() == 'Targets') 
                        urlEnd = 'GetOnlyMissingTargets';
                    else
                        if (self.SelectedTab() == 'LiveStreams') 
                            urlEnd = 'GetOnlyMissingLiveStreams';
                        else
                            if (self.SelectedTab() == 'Publications') 
                                urlEnd = 'GetOnlyMissingPublications';

        return urlEnd;
    }     

    self.RedBarText = ko.computed(function () { 
        if (self.SelectedTab() == 'Trainings')
            return translations['LastTrainings'];
        if (self.SelectedTab() == 'TrainingInvitations')
            return translations['TrainingInvitations'];
        if (self.SelectedTab() == 'Competitions')
            return translations['CompetitionsChalenges'];
    
        return translations['LastPublications']; 
    });  

    self.openAddTrainingInvitationDialog = function (goToPage) {
        self.openTrainingInvitationDialog(null, null, null, true, null, goToPage == true);    
    }    

    self.openAddTrainingInvitationDialogFromMainButton = function () {
        self.openTrainingInvitationDialog(null, null, null, true, null, false, true);    
    }

    self.openTrainingInvitationDialog = function (elem, id, userId, isSender, invitationType, goToPage, refresh) {
        //self.SelectedTrainingInvitation = ko.observable(new TrainingInvitationModel(self, isSender));
        //         $('#addTrainingInvitationModal input[type="text"]')[0].value = '';
        //$('#addTrainingInvitationModal .activity.trainingDetails.none').click();
        self.SelectedTrainingInvitation().init(isSender, invitationType);
        //self.SelectedTrainingInvitation().GoToExplorer = goToPage;
        self.SelectedTrainingInvitation().Refresh = refresh;                

        if (id)
        {           
            if (self.CommentsBallPressed) 
            {
                self.CommentsBallPressed = false;
                return;
            }
        
            self.SelectedCommentsModel = self.SelectedTrainingInvitation().Comments();
            self.SelectedCommentsWindowName = '#myTrainingInvitationModal';

            self.SelectedTrainingInvitation().loadView(id, userId, invitationType, isSender);
            //self.SelectedTrainingInvitation().initById(id, userId, invitationType, isSender);
        }
        else
        {
            self.SelectedTrainingInvitation().loadView();

            //             self.SelectedTrainingInvitation().setUserByCurrentUser();
            //             self.SelectedTrainingInvitation().UpdateVisibility(true);            

            //             $('#myTrainingInvitationModal').modal('show');        
        }
    } 

    self.openTargetDialog = function (targetId, userId) {
        if (self.CommentsBallPressed) 
        {
            self.CommentsBallPressed = false;
            return;
        }

        self.UserTarget().loadView(targetId, userId);
        self.SelectedCommentsModel = self.UserTarget().Comments();
        self.SelectedCommentsWindowName = '#communityTargetModal';
    }    

    self.openCompetitionDialog = function (elemId, userId, type) {
        if (self.CommentsBallPressed) 
        {
            self.CommentsBallPressed = false;
            return;
        }

        if (type == 'Plan')
        {
            self.CommunityCompetitionPlan().loadView(elemId, userId);
            self.SelectedCommentsModel = self.CommunityCompetitionPlan().Comments();
            self.SelectedCommentsWindowName = '#communityCompetitionPlanModal';
        }
        else
        {
            self.CommunityCompetition().loadView(elemId, userId);
            self.SelectedCommentsModel = self.CommunityCompetition().Comments();
            self.SelectedCommentsWindowName = '#communityCompetitionModal';
        }
    }

    self.openUserTrainingPlanDialog = function (userTrainingPlanId) {
        self.CommunityUserTrainingPlan().loadView(userTrainingPlanId);
        self.SelectedCommentsModel = self.CommunityUserTrainingPlan().Comments();
        self.SelectedCommentsWindowName = '#communityUserTrainingPlanModal';
    }

    self.openStreamView = function (guid) {
        if (typeof liveStreamManager !== 'undefined' && liveStreamManager) {
            liveStreamManager.LoadStream(guid);
        }
        else
        {
            window.location = ContextPath + "Community/CommunityStream?guid=" + guid;
        }
    }

    self.openEventStreamView = function (eventId) {
        window.location = ContextPath + "LiveStream/Event?eventId=" + eventId;
    }

    self.openPublicationView = function (messageId, guid) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetWallElementToRead",
            data: { conversationId: messageId }
        })
        .done(function (result) {
            if (result == '1') {
                if ((communityExplorerModel.NewPublicationsCount() - 1) >= 0) {
                    communityExplorerModel.setNewPublicationsCount(communityExplorerModel.NewPublicationsCount() - 1);
                }

                var elem = $('.sideTileCommunity #communityTrainings .training.publication.id_' + messageId + '.noResponse');
                if (elem.length > 0) {
                    communityRefreshTrainings(true);
                }
            }

            window.location = ContextPath + "ExternalPublication/Publication?guid=" + guid;
        });
    }

    self.SetCurrentCommentsToRead = function (type, subtype, messageId) {            
        if (type == 'training') self.SelectedCommentsModel = self.CommunityTraining().Comments();
        if (type == 'competition') 
        {
            if (subtype == 'Plan')
            {
                self.SelectedCommentsModel = self.CommunityCompetitionPlan().Comments();
            }
            else
            {
                self.SelectedCommentsModel = self.CommunityCompetition().Comments();
            }                    
        }
        if (type == 'target') self.SelectedCommentsModel = self.UserTarget().Comments();
        if (type == 'trainingInvitation') self.SelectedCommentsModel = self.SelectedTrainingInvitation().Comments();

        communityExplorerModel.SelectedCommentsModel.MessageId(messageId);
        communityExplorerModel.SelectedCommentsModel.SetCommentsToRead(true);     

        self.CommentsBallPressed = true;
    }

    self.openCommunityExplorer = function () {
        if (!self.StartedLoadingCommunityExplorer) {
            self.StartedLoadingCommunityExplorer = true;
            window.location = ContextPath + "Community";
        }
    }                                      
}

function checkIfElementIsOutOfBorder(parent, elem)
{
    if (parent.length > 0 && elem.length > 0)
    {
        var parentOverflow = parent.css('overflow');
        parent.css('overflow', 'visible');		

        var scrollWidthBefore = parent[0].scrollWidth; 

        cloud.css('display', 'block');   

        var scrollWidthAfter = parent[0].scrollWidth; 

        parent.css('overflow', parentOverflow);		

        if (scrollWidthBefore != scrollWidthAfter)
        {
            return true;
        }
    }

    return false;
}

function CommunityUserProfileExplorerModel() {
    var self = this;

    self.User = ko.observable(new UserSimpleModel());

    self.DisciplineManager = new TrainingTypeControl({
        mainId: null,
        subId: null,
        typeChangeCallback: null,
        clubId: null,
        multi: true,
        baseCtrl: null,
        disciplines: true,
        multiSubs: false,
        allInstedNone: true,
        selectNoneIfNoChoise: true
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            if (serverModel.User) {
                self.User().initByModel(serverModel.User);

                self.DisciplineManager.initState = true;
                self.DisciplineManager.SelectTypeById(serverModel.User.PrimaryDisciplineId);

                //if (serverModel.User.PrimaryDisciplineId) {
                //    self.DisciplineManager.SelectTypeById(serverModel.User.PrimaryDisciplineId);
                //}
                //else
                //    self.DisciplineManager.SelectTypeById(0);

                self.DisciplineManager.initState = false;
            }
        }
    };

    self.initByUserId = function (userId) {
        if (userId) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetUserProfileExplorerModel",
                data: { userId: userId }
            })
             .done(function (result) {
                 if (result == 'NoData') {
                     var modal = $('#emptyMsgModal');
                     modal.find('.askMsg').html(translations[result]);
                     modal.modal("show");
                 }
                 else {
                     self.initByModel(result);
                 }
             });
        }
    };
}

function TrainingInvitationModelList(pageCount) {

    var self = this;

    self.OnlyClub = ko.observable(false);

    self.pageCount = 12;

    if (pageCount != null) self.pageCount = pageCount;

    self.initState = true;

    self.ShowMap = ko.observable(false);
    self.EntriesPerPage = ko.observable(0);
    self.PageNumber = ko.observable(0);
    self.TotalResuts = ko.observable(0);
    self.NotReadCount = ko.observable(0);
    self.Start = false;
    self.DataLoaded = ko.observable(false);
    self.TypeManager = new TrainingTypeControl(0, 0, 0, null, true, null, false, false, true);
    //self.TypeManager = new TrainingTypeControl(0, 0, 0, null, true, null, true, false);
    self.SearchPattern = ko.observable('');
    self.SearchOnlyMyVariable = false;
    self.SelectedClubId = null;

    self.List = ko.observableArray([]);

    self.SelectedForExplorer = ko.observable(new TrainingInvitationModel(self, false, self.OnlyClub()));

    self.ShowPagingPages = ko.computed(function () { 
        return self.TotalResuts() > self.EntriesPerPage(); 
    });

    self.ShowPaging = ko.computed(function () { return self.TotalResuts() > 10; });    

    self.NrOfPages = ko.computed(function () {
        var epp = self.EntriesPerPage();
        if (!epp) epp = 1;
        return Math.ceil(1.0 * self.TotalResuts() / epp);
    });    
    
    self.DisabledPrevious = ko.computed(function () {
        return !(self.PageNumber() > 0);
    });    

    self.DisabledNext = ko.computed(function () {
        return !(self.PageNumber() < (self.NrOfPages() - 1));
    });  

    self.Pages = ko.computed(function () {
        var nrOfPages = self.NrOfPages();
        var currentPage = self.PageNumber();
        var startPage = currentPage - 2;
        if (startPage < 0) startPage = 0;
        var endPage = startPage + 5;
        if (endPage > (nrOfPages - 1)) {
            endPage = nrOfPages - 1;
            startPage = endPage - 5;
        }

        var result = [];
        var dots = false;
        for (var i = 0; i < nrOfPages; i++) {
            if (i == 0 || (i == nrOfPages - 1) || (i >= startPage && i <= endPage)) {
                result.push(i + 1);
                dots = false;
            } else {
                if (!dots) {
                    result.push("...");
                    dots = true;
                }
            }
        }
        return result;
    });   

    self.resetPageNumber = function () {
        self.PageNumber(0);
    }       
    
    self.GoToPage = function (page) {
        if (page != "...") {
            page = parseInt(page) - 1;
            self.PageNumber(page);
            self.Search();
        }

        $.scrollTo($(".span60p.trainingInvitationsTile.big .redStripe"), { duration: 1000, easing: 'swing' });        
    }
    self.GoToNextPage = function (page) {
        if (self.PageNumber() + 1 < self.NrOfPages())
        {
            self.PageNumber(self.PageNumber() + 1);
            self.Search();
        }

        $.scrollTo($(".span60p.trainingInvitationsTile.big .redStripe"), { duration: 1000, easing: 'swing' });        
    }
    self.GoToPreviousPage = function (page) {
        if (self.PageNumber() > 0)
        {
            self.PageNumber(self.PageNumber() - 1);
            self.Search();            
        }

        $.scrollTo($(".span60p.trainingInvitationsTile.big .redStripe"), { duration: 1000, easing: 'swing' });        
    }

    self.EntriesPerPage.subscribe(function (newValue) {
        if (!self.initState && newValue) {
            self.initState = true;
            self.PageNumber(0);
            self.initState = false;
            self.pageCount = newValue;

            self.Search();

            $.scrollTo($(".span60p.trainingInvitationsTile.big .redStripe"), { duration: 1000, easing: 'swing' });        
        }
    });    

    self.Serialise = function () {       
        var disciplinesIds = [];

        var types = self.TypeManager.SelectedTypes();
        for (var i = 0, len = types.length; i < len; i++) {
            disciplinesIds.push(types[i].Id);
        }            

        var searchPattern = self.SearchPattern();
    
        var data = {
            pageCount: self.pageCount,
            pageIndex: self.PageNumber(),
            start: self.Start,
            onlyClub: self.OnlyClub(),
            searchPattern: searchPattern,
            disciplineIds: disciplinesIds,
            onlyMy: self.SearchOnlyMyVariable,
            clubId: self.SelectedClubId,
            forMap: self.ShowMap()
        };

        return JSON.stringify(data);
    }

    self.Search = function () {         
        if (!$('body.pulsstory').hasClass('wait'))
        {
            $('body.pulsstory').addClass('wait');    
        }        
     
        //         $('body').css('cursor', 'wait');
        //         $('.mainColumn.span80p div').css('cursor', 'wait');

        var data_tmp = self.Serialise();

        self.SearchOnlyMyVariable = false;

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetTrainingInvitationsExplorerModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data_tmp
        })
        .done(function (serverModel) {
            if (serverModel) {
                self.initState = true;
                self.Start = false;
                self.TotalResuts(serverModel.TotalResuts);
                self.DataLoaded(true);
             
                if (self.MapExplorerControl && serverModel.MarkersList) {
                    var markers = [];
                    for (var i = 0, len = serverModel.MarkersList.length; i < len; i++) {
                        var data = serverModel.MarkersList[i];
                        if (data.LocationModel) {
                            var elem = self.CreateInvitMarker(data);
                            markers.push(elem);
                        }
                    }
                    self.MapExplorerControl.ClearMarkers();
                    self.MapExplorerControl.AddMarkers(markers);
                }

                if (serverModel.NotReadCount != null)
                    trainingInvitationsExplorerModel.NotReadCount(serverModel.NotReadCount);
             
                if (serverModel.NotReadForMenuCount != null) {
                    userBarModel.NotReadTrainingInvitationsCount(serverModel.NotReadForMenuCount);
                    communityExplorerModel.setNewTrainingInvitationsCount(serverModel.NotReadForMenuCount);
                }
             
                self.List.removeAll();
                if (serverModel.List) {
                    for (var i = 0, len = serverModel.List.length; i < len; i++) {
                        var elem = new TrainingInvitationModel(self);
                        elem.initByModel(serverModel.List[i]);
                        elem.Index = i;
                        self.List.push(elem);
                    }
                }
                        
                self.initState = false;
            }
        
            if ($('body.pulsstory').hasClass('wait'))
            {
                $('body.pulsstory').removeClass('wait');    
            }
            //          $('body').css('cursor', 'default');
            //          $('.mainColumn.span80p div').css('cursor', 'initial');
            //          $('.trainingInvitationsListItem div').css('cursor', 'pointer');        
        });        
    }

    self.SearchBtnClick = function () {
        self.initState = true;
        self.PageNumber(0);
        self.initState = false;        
        self.Start = true;
        self.Search();
    }    

    self.TypeManager.TypeChangeCallback = function () {
        if (!self.initState) {
            self.initState = true;
            self.PageNumber(0);
            self.initState = false;
            self.Start = true;
            
            self.Search();
        }
    }    

    self.SearchPattern.subscribe(function (newValue) {
        if (!self.initState && newValue && newValue.length > 3) {
            self.initState = true;
            self.PageNumber(0);
            self.initState = false;
            self.Start = true;
            
            self.Search();
        }
    }); 

    self.SearchOnlyMy = function () {
        self.SearchOnlyMyVariable = true;
        self.SearchBtnClick();
    }

    self.MapExplorerControl = null;

    self.ToggleMap = function () {
        var initMap = !self.ShowMap();
        self.ShowMap(!self.ShowMap());
        self.Start = true;

        if (initMap) {
            if (!self.MapExplorerControl) {
                var container = $('.exploreMap')[0];
                setTimeout(function () {
                    var mcOptions = { gridSize: 50, maxZoom: 15 };
                    self.MapExplorerControl = new MapExplorerControl(container, self.Search, mcOptions);
                    self.MapExplorerControl.initMap();
                }, 100);
            } else {
                self.MapExplorerControl.initMap();
            }
        } else {
            self.Search();
        }
    }
    self.CreateInvitMarker = function (markerData) {
        var marker = new google.maps.Marker({
            map: self.MapExplorerControl.Map,
            position: new google.maps.LatLng(markerData.LocationModel.Latitude, markerData.LocationModel.Longitude),
            label: ('' + markerData.Ids.length),
            count: markerData.Ids.length
        });

        google.maps.event.addListener(marker, 'click', function () {

            self.MapExplorerControl.ShowInfoWindow(markerData.Name, marker, null);
            self.LoadSelectedInvits(markerData.Ids);
        });

        return marker;
    }

    self.LoadSelectedInvits = function (ids) {
        var strData = JSON.stringify(ids);
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetTrainingInvitationsByIds",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (serverModel) {
           self.List.removeAll();
           for (var i = 0, len = serverModel.List.length; i < len; i++) {
               var elem = new TrainingInvitationModel(self);
               elem.initByModel(serverModel.List[i]);
               elem.Index = i;
               self.List.push(elem);
           }
       });
    }
}

function TrainingInvitationModel(parent, isSender) {
    var self = this;
    self.Parent=parent;
    self.ParentExplorer = null;

    self.Id = ko.observable(0);
    self.NewId = ko.observable(0);
    self.NewMessageWasCreated = ko.observable(false);
    self.CancelWasPressed = ko.observable(false);
    self.SaveWasPressed = ko.observable(false);
    self.Date = ko.observable(dateToString(new Date())).extend({ required: true });
    self.Description = ko.observable('');
    self.Distance = ko.observable(0);
    self.Duration = ko.observable('');//.extend({ required: true });
    self.Place = ko.observable('').extend({ required: true });
    self.Time = ko.observable('').extend({ required: true });;
    self.UsersWhichAcceptedInvitation = new CommunityModelSimple(self, 'EnterNameOrNick', 'TrainingInvitation', true);
    self.UsersWhichWasInvited = new CommunityInviteModel(self, 'SearchForTrainingInvitation');
    self.UsersWhichWasInvitedCount = ko.observable(0);
    self.UsersWhichWasInvitedSimpleList = ko.observable(new CommunitySimpleUserListModel(self));
    self.TypeManager = new TrainingTypeControl(0, 0, 0);
    self.ParamsVisibility = ko.observable(new ParamsVisibility(''));
    self.IsSender = ko.observable(isSender);
    self.Intensivity = ko.observable(0);
    self.IntensToolBarHovered = ko.observable(0);
    self.Pace = ko.observable(0);
    self.AvgSpeed = ko.observable(0);
    self.initState = true;
    self.User = ko.observable(new UserSimpleModel());
    self.Response = ko.observable('false');
    self.SomeResponseExists = ko.observable(false);
    self.ResponseFromServer = ko.observable(false);
    self.DisplayDate = ko.observable('');
    self.DisplayDateType = ko.observable('');
    self.DisplayDuration = ko.observable('');
    self.DisplayDistance = ko.observable('');
    self.DisplayPace = ko.observable('');
    self.DisplayAvgSpeed = ko.observable('');
    self.NameOfDay = ko.observable('');
    self.DayNr = ko.observable('');
    self.MonthName = ko.observable('');
    self.Year = ko.observable('');
    self.IsNew = ko.observable(true);
    self.IsCanceled = ko.observable(false);
    self.CallBackFunction = null;
    self.CallBackFunctionParams = null;
    self.SenderEventId = ko.observable(0);
    self.TrainingInvitationType = ko.observable('');
    self.ClubLogoId = ko.observable(0);
    self.SharingLogoId = ko.observable(0);
    self.ClubName = ko.observable('');
    self.ClubType = ko.observable('');
    self.IsRead = ko.observable(false);
    self.Collapsed = ko.observable(true);
    self.TopLabelText = ko.observable('');
    self.InstructorTrainingInvitation = ko.observable('');
    self.Clicked = ko.observable(false);
    self.CloudContentShowed = false;
    self.Deleted = ko.observable(false);
    self.StoredServerModel = null;
    self.GoToExplorer = false;
    self.Refresh = false;
    self.binded = false;
    self.NiceThatYouJoinedText = ko.observable('');
    self.TrainingPlanInstanceId = ko.observable(0);
    self.Comments = ko.observable(new CommunityCommentsModel(self));
    self.AreSomeNotViewedComments = ko.observable(false);
    self.AllInvited = ko.observable(true);
    self.Public =ko.observable(false);
    self.Context = ko.observable('TrainingInvitation');
    self.UsersWhichWasInvitedIdsInitState = [];
    self.AllInvitedOld = true;
    self.OpenedFromWall = ko.observable(false);

    self.IsReadComputed = ko.computed(function () {
        var isRead = self.IsRead() || self.OpenedFromWall();

        return isRead;
    });

    self.CollapsedComputed = ko.computed(function () {
        var result = self.Collapsed() && !self.OpenedFromWall();

        return result;
    });

    self.IsSenderComputed = ko.computed(function () {
        var result = self.IsSender() && !self.OpenedFromWall();

        return result;
    });

    self.LocationModelChanged = function (changedModel) {
        self.Place(changedModel.Name());
    }

    self.LocationModel = ko.observable(new LocationModel(self, self.LocationModelChanged));

    self.errors = ko.validation.group(self);

    self.InvitedText = ko.computed(function () {
        if (self.AllInvited()) {
            return translations['Invited'].toLowerCase() + ': ' + translations['InvitedAll'].toLowerCase();
        }
        else if (self.UsersWhichWasInvitedCount() > 1) 
        {
            return translations['Invited'].toLowerCase() + ': ' + self.UsersWhichWasInvitedCount();
        }
        else if (self.UsersWhichWasInvitedCount() == 1 && self.UsersWhichWasInvitedSimpleList().List().length == 1) 
        {
            var firstOnListName = self.UsersWhichWasInvitedSimpleList().List()[0].Name();
            return translations['Invited'].toLowerCase() + ': ' + firstOnListName;
        }
        else if (self.UsersWhichWasInvitedCount() == 0) {
            return translations['Invited'].toLowerCase() + ': ' + translations['InvitedNoOne'].toLowerCase();
        }

        return '';
    });

    self.JoinedText = ko.computed(function () {
        return translations['TrainingInvitationJoined'] + ' ' + self.UsersWhichAcceptedInvitation.TotalResuts();
    });

    var today = (new Date()).toISOString().substr(0, 10);
    self.Date.subscribe(function (newValue) {
        if (newValue && !self.initState) {
            if (newValue < today) {
                self.Date(today);
            }

        }
    });    

    self.CalculatePaceStringFromSpeed = function (speed) {
        var paceParams = self.ParamsVisibility().PaceParams;
        var unit = paceParams && paceParams.unit ? paceParams.unit : "min/km";
        var factor = paceParams && paceParams.factor ? paceParams.factor : 60;
        return calculatePaceString(speed, unit, factor, false, false);
    };
    self.CalculateSpeedFromPaceString = function (pace) {
        var paceParams = self.ParamsVisibility().PaceParams;
        var unit = paceParams && paceParams.unit ? paceParams.unit : "min/km";
        var factor = paceParams && paceParams.factor ? paceParams.factor : 60;
        return calculateSpeedFromPace(pace, unit, factor);
    }; 
    
    self.AvgSpeed.subscribe(function (newValue) {
        if (newValue && !self.initState) {
            //var val = parseFloat(newValue.replace(',', '.'));
            //var n = val * 1000 / 60;
            self.initState = true;
            
            var pace = self.CalculatePaceStringFromSpeed(newValue);
            self.Pace(pace);
            
            self.initState = false;
        }
    });

    self.Pace.subscribe(function (newValue) {
        if (newValue && !self.initState) {
            var n = self.CalculateSpeedFromPaceString(newValue);
            self.initState = true;
            self.AvgSpeed(n.toFixed(2).replace('.', ','));

            self.initState = false;
        }
    });

    self.PaceUnit = ko.computed(function () {
        var paceUnit = 'min/km';
        var params = self.ParamsVisibility();

        if (self.ParamsVisibility() && self.ParamsVisibility().PaceParams && self.ParamsVisibility().PaceParams.unit) {
            return self.ParamsVisibility().PaceParams.unit;
        }

        return paceUnit;
    });

    self.TimePaceUnit = ko.computed(function () {
        return isMinPaceUnit(self.PaceUnit());
    });

    self.PaceTimeUnit = ko.computed(function () {
        if (self.PaceUnit()) {
            var idx = self.PaceUnit().indexOf('/');
            if (idx >= 0)
            {
                return self.PaceUnit().substring(0, idx);
            }
        }

        return '';
    });

    self.PaceDistUnit = ko.computed(function () {
        if (self.PaceUnit()) {
            var idx = self.PaceUnit().indexOf('/');
            if (idx >= 0)
            {
                return self.PaceUnit().substring(idx +1);
            }
        }

        return '';
    });

    self.LocationModelOpenControl = function () {
        if (self.LocationModel().Name() != self.Place()) {
            self.LocationModel().init();
            localisationControl.RemoveMarker();
        }

        self.LocationModel().OpenControl(null, null, self.Place());
    }
      
    self.IntensToolBarMouseIn = function (value) {
        self.IntensToolBarHovered(value);
    }
    self.IntensToolBarMouseOut = function () {
        self.IntensToolBarHovered(0);
    }
    self.IntensToolBarMouseClick = function (value) {
        self.Intensivity(value);
    }    

    self.DisplayIntensivity = ko.computed(function () {
        var hIntens = self.IntensToolBarHovered();
        if (hIntens) {
            return hIntens;
        } else {
            return self.Intensivity();
        }
    });    

    self.IntensivityLabel = ko.computed(function () {
        return translations["Intensivity_" + self.DisplayIntensivity()];
    });
    self.IntensivitySubLabel = ko.computed(function () {
        return translations["IntensivitySub_" + self.DisplayIntensivity()];
    });
    self.IntensivityRPE = ko.computed(function () {
        return translations["IntensivityRPE_" + self.DisplayIntensivity()];
    });

    self.init = function(isSender, invitationType){
        self.initState = true;
		
        self.Id(0);
        self.NewId(0);
        self.NewMessageWasCreated(false);
        self.CancelWasPressed(false);
        self.SaveWasPressed(false);
        self.Date(dateToString(new Date()));
        self.Description('');
        self.Distance(0);
        self.Duration('');
        self.Place('');
        self.Time('');
        //        self.UsersWhichAcceptedInvitation.init();
        self.TypeManager.SelectValue(0, 0, 0);
        self.Intensivity(0);
        self.IntensToolBarHovered(0);
        self.AvgSpeed(0);
        self.Pace(self.CalculatePaceStringFromSpeed(0));
        self.User().init();
        self.Response('false');
        self.SomeResponseExists(false);
        self.ResponseFromServer(false);
        self.DisplayDate('');
        self.DisplayDateType('');
        self.DisplayDuration('');
        self.DisplayDistance('');
        self.DisplayPace('');
        self.DisplayAvgSpeed('');
        self.NameOfDay('');
        self.DayNr('');
        self.MonthName('');
        self.Year('');
        self.IsNew(true);
        self.IsSender(isSender);
        self.UsersWhichAcceptedInvitation.WantsObservedObserving.removeAll();                
        self.IsCanceled(false);
        self.SenderEventId(0);
        self.ClubType('');
        self.IsRead(false);
        self.TopLabelText('');
        self.InstructorTrainingInvitation('');
        self.Clicked(false);
        self.StoredServerModel = null;
        self.GoToExplorer = false;
        self.Refresh = false;
        self.ParentExplorer = null;
        self.TrainingPlanInstanceId(0);
        self.AreSomeNotViewedComments(false);
        self.UsersWhichWasInvitedCount(0);
        self.AllInvited(true);
        self.LocationModel().init();
        self.UsersWhichWasInvitedIdsInitState = [];
        self.AllInvitedOld = true;
        self.OpenedFromWall = ko.observable(false);

        if (invitationType == 'Club')
            self.TrainingInvitationType('Club');
        else
            self.TrainingInvitationType('User');

        self.initState = false;
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.initState = true;

            self.StoredServerModel = serverModel;
            self.Id(serverModel.Id);
            self.NewId(serverModel.Id);

            self.TypeManager.SelectValue(serverModel.TypeId, serverModel.SubTypeId, 0);

            self.Date(serverModel.Date);
            self.Description(serverModel.Description);
            self.Distance(serverModel.Distance);
            self.Duration(serverModel.Duration);
            self.Place(serverModel.Place);
            self.AvgSpeed(serverModel.AvgSpeed);
            self.Time(serverModel.Time);
            self.Intensivity(serverModel.Intensivity);
            self.Response(serverModel.Response);
            self.SomeResponseExists(serverModel.SomeResponseExists);
            self.ResponseFromServer(serverModel.Response);
            self.DisplayDate(serverModel.DisplayDate);
            self.DisplayDateType(serverModel.DisplayDateType);
            self.DisplayDuration(serverModel.DisplayDuration);
            self.DisplayDistance(serverModel.DisplayDistance);
            self.DisplayPace(serverModel.DisplayPace);
            self.DisplayAvgSpeed(serverModel.DisplayAvgSpeed);
            self.NameOfDay(serverModel.NameOfDay);
            self.DayNr(serverModel.DayNr);
            self.MonthName(serverModel.MonthName);
            self.Year(serverModel.Year);
            self.IsCanceled(serverModel.IsCanceled);
            self.SenderEventId(serverModel.SenderEventId);
            self.TrainingInvitationType(serverModel.TrainingInvitationType);
            self.ClubLogoId(serverModel.ClubLogoId);
            self.SharingLogoId(serverModel.SharingLogoId);
            self.ClubName(serverModel.ClubName);
            self.IsSender(serverModel.IsSender);
            self.ClubType(serverModel.ClubType);
            self.IsRead(serverModel.SomeResponseExists);
            self.TopLabelText(serverModel.TopLabelText);
            self.InstructorTrainingInvitation(serverModel.InstructorTrainingInvitation);
            self.NiceThatYouJoinedText(serverModel.NiceThatYouJoinedText);
            self.TrainingPlanInstanceId(serverModel.TrainingPlanInstanceId);            
            self.AreSomeNotViewedComments(serverModel.AreSomeNotViewedComments);
            self.UsersWhichWasInvitedCount(serverModel.UsersWhichWasInvitedCount);
            self.AllInvited(serverModel.AllInvited);
            self.Public(serverModel.Public);
            self.UsersWhichWasInvitedIdsInitState = serverModel.UsersWhichWasInvitedIdsInitState;
            self.AllInvitedOld = serverModel.AllInvited;

            if (serverModel.User)
            {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }

            self.UpdateVisibility();

            var pace = self.CalculatePaceStringFromSpeed(self.AvgSpeed());
            self.Pace(pace);

            self.UsersWhichAcceptedInvitation.resetPageNumber();
            self.UsersWhichAcceptedInvitation.EntriesPerPage(6);
            self.UsersWhichAcceptedInvitation.setList(serverModel);         

            self.IsNew(false);

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);

            self.UsersWhichWasInvitedSimpleList().initByModel(serverModel.UsersWhichWasInvitedSimpleList);
            self.LocationModel().initByModel(serverModel.LocationModel);
            self.LocationModel().WindowTitle = translations["SetStartPlace"];
            self.LocationModel().LocationLabelTitle = translations["StartPlace"];

            self.initState = false;		
        }
    };

    self.refreshBasicValues = function (serverModel) {
        if (serverModel) {
            self.initState = true;

            self.StoredServerModel = serverModel;
            self.Id(serverModel.Id);
            self.NewId(serverModel.Id);
            self.Date(serverModel.Date);
            self.Description(serverModel.Description);
            self.Distance(serverModel.Distance);
            self.Duration(serverModel.Duration);
            self.Place(serverModel.Place);
            self.AvgSpeed(serverModel.AvgSpeed);
            var pace = self.CalculatePaceStringFromSpeed(self.AvgSpeed());
            self.Pace(pace);			
            self.Time(serverModel.Time);
            self.Intensivity(serverModel.Intensivity);
            self.DisplayDate(serverModel.DisplayDate);
            self.DisplayDateType(serverModel.DisplayDateType);
            self.DisplayDuration(serverModel.DisplayDuration);
            self.DisplayDistance(serverModel.DisplayDistance);
            self.DisplayPace(serverModel.DisplayPace);
            self.DisplayAvgSpeed(serverModel.DisplayAvgSpeed);
            self.NameOfDay(serverModel.NameOfDay);
            self.DayNr(serverModel.DayNr);
            self.MonthName(serverModel.MonthName);
            self.Year(serverModel.Year);
            self.TrainingInvitationType(serverModel.TrainingInvitationType);
            self.ClubLogoId(serverModel.ClubLogoId);
            self.SharingLogoId(serverModel.SharingLogoId);
            self.ClubName(serverModel.ClubName);
            self.UsersWhichWasInvitedIdsInitState = serverModel.UsersWhichWasInvitedIdsInitState;
            self.AllInvitedOld = serverModel.AllInvitedOld;

            self.TypeManager.SelectValue(serverModel.TypeId, serverModel.SubTypeId, 0);	

            self.UpdateVisibility();   

            self.initState = false;		
        }
    };	

    self.setUserByCurrentUser = function () {
        self.User().UserId(CurrentUser.UserId());
        self.User().Name(CurrentUser.Name());
        self.User().Nick(CurrentUser.Nick());
        self.User().MaleSex(CurrentUser.MaleSex());
        self.User().AvatarFileId(CurrentUser.AvatarFileId());        
    }	    	

    self.initById = function (id, userId, invitationType, isSender, openedFromWall) {
        $('#communitySideTile').css('cursor', 'wait');
        $('#communityTrainings .training').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetTrainingInvitation",
            data: { messageId: id, userId: userId, invitationType: invitationType }
        })
         .done(function (result) {
             $('#communitySideTile').css('cursor', 'auto');
             $('#communityTrainings .training').css('cursor', 'pointer');

             if (result == 'NoData') {
                 var modal = $('#emptyMsgModal');                
                 modal.find('.askMsg').html(translations[result]);
                 modal.modal("show");
             }
             else
             {           
                 self.initByModel(result);
                 self.IsSender(isSender);
                 self.OpenedFromWall(openedFromWall);
                 if (openedFromWall) {
                     self.Collapsed(false);
                 }

                 //             if (self.IsSender())
                 //             {
                 $('#myTrainingInvitationModal').modal('show');
                 //             }
                 //             else
                 //             {
                 //                 $('#someoneTrainingInvitation').modal('show');        
                 //             }  
             }           
         });
    }

    self.initByTrainingPlanInstanceId = function (trainingPlanInstanceSetId) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetTrainingInvitationByTrainingPlan",
            data: { trainingPlanInstanceSetId: trainingPlanInstanceSetId }
        })
         .done(function (result) {
             if (result == 'NoData') {
                 var modal = $('#emptyMsgModal');
                 modal.find('.askMsg').html(translations[result]);
                 modal.modal("show");
             }
             else {
                 self.initByModel(result);
                 self.IsNew(true);
                 self.loadView();
             }
         });
    }

    self.initForAdmin = function (id) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetTrainingInvitationForAdmin",
            data: { messageId: id }
        })
         .done(function (result) {
             self.initByModel(result);

             if (self.CallBackFunction)
             {
                 self.CallBackFunction(self.CallBackFunctionParams);
             }              
         });
    }    	

    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    }

    self.UpdateVisibility = function (clearFields) {
        self.initState = true;
        var type = self.TypeManager.SelectedType();
        var config = '';
        if (type && type.ParamsVisibility) config = type.ParamsVisibility;
        self.ParamsVisibility(new ParamsVisibility(config));
        initTimePicker();

        if (clearFields)
        {
            self.Duration('');
            self.Time('');            
        }
        self.initState = false;
    };

    self.RecalculatePaces = function () {
        self.AvgSpeed.valueHasMutated();
    };

    self.TypeManager.SetChangeCallback(function () {
        self.UpdateVisibility();
        self.RecalculatePaces();
    });    

    self.PaceAvailable = ko.computed(function () {
        var paceVisible = self.ParamsVisibility().ShowPace;

        return paceVisible;
    }); 

    self.SignUpIcon = ko.computed(function () {
        if (self.Collapsed())
        {
            return self.Response() == 'true' ? 'log_out_27' : 'zapiszsie_27';
        }
        else
        {
            return self.Response() == 'true' ? 'log_out_55' : 'zapiszsie_50';
        }
    });

    self.ResignIcon = ko.computed(function () {
        if (self.Collapsed()) {
            return 'log_out_27';
        }
        else {
            return 'log_out_55';
        }
    });

    self.ShowResignIcon = ko.computed(function () {
        return self.Response() == 'saw' || self.Response() == '';
    });

    //self.SignUpText = ko.computed(function () {
    //    return self.Response() ? translations["ImNotJoining"] : translations["ImJoining"];
    //});     

    self.SignUpIconText = ko.computed(function () {
        return self.Response() == 'true' ? translations["ImNotJoining"] : translations["ImJoining"];
    });     

    self.getClearData=function(){
        var data = {
            Id:  self.Id(),
            NewId: self.NewId(),
            Date: self.Date(),
            Description:  self.Description(),
            Distance:  self.Distance(),
            Duration:  self.Duration(),
            Place:  self.Place(),
            AvgSpeed: self.AvgSpeed(),
            Time:  self.Time(),
            Intensivity: self.Intensivity(),
            SubTypeId: self.TypeManager.SelectedSubType().Id,
            TypeId: self.TypeManager.SelectedType().Id,
            IsSender: self.IsSender(),
            SenderId: self.User().UserId(),
            Response: self.Response(),
            SenderEventId: self.SenderEventId(),
            TrainingPlanInstanceId: self.TrainingPlanInstanceId(),
            AllInvited: self.AllInvited(),
            Public: self.Public(),
            LocationModel: self.LocationModel().getClearData(),
            IsNew: self.IsNew(),
            UsersWhichWasInvitedIdsInitState: self.UsersWhichWasInvitedIdsInitState,
            AllInvitedOld: self.AllInvitedOld
        }
        return data;
    };   

    self.CanSave = function () { 
        if (self.TypeManager.SelectedType() == null) return false;
        if ((self.Duration() == '') && (self.Intensivity() == 0) &&
            (self.Distance() == 0) && ((self.AvgSpeed() == 0) ||
            (self.AvgSpeed() == '0,00')))
        {
            return false;
        }
        if ((self.TypeManager.SelectedType().Id != 0) && (self.errors().length == 0)) return true;
        //         if ((self.Duration() == '') && (self.Intensivity() == 0)) 
        //         {
        //             var invalid = false;
        //             if (self.ParamsVisibility().RouteGroup)
        //             {
        //                 invalid = self.Distance() == 0;
        //             }

        //             if (self.ParamsVisibility().SpeedGroup)
        //             {
        //                 invalid = invalid && (self.AvgSpeed() == 0);
        //             }

        //             if (invalid) 
        //                 return false;
        //             else
        //                 return true;
        //         }

        return false; 
    };	

    self.NotOwnerLargerClass = ko.computed(function () { 
        var result = self.ParamsVisibility().SpeedGroup;

        if (result && self.DisplayPace() != '0' && self.ParamsVisibility().ShowPace)
            return 'larger';    

          

        return ''; 
    });        

    self.save = function (fromSaveButton, model, event, share) {
        var modal = $('#emptyMsgModal');
        modal.find('#emptyMsgModalLabel').html(translations.SaveError);
        
        if (self.CanSave())
        {
            self.SaveWasPressed(true);

            //             if (!self.IsSender() && (self.Response() == self.ResponseFromServer())) 
            //                 return;

            $('body').css('cursor', 'wait');

            var data = self.getClearData();

            var strData = JSON.stringify(data);

            $.ajax({
                type: "POST",
                url: ContextPath + "Community/SetTrainingInvitationModel",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                data: strData
            })
            .done(function (result) {
                $('body').css('cursor', 'default');
                if (result.State != 'done') {
                    modal.find('.askMsg').html(translations[result.State]);
                    modal.modal("show");
                }
                else
                {
                    if (self.Parent == trainingInvitationsExplorerModel) {
                        var infoContainer = event ? $(event.target).parent() : null;
                        ShowSavedAlert(infoContainer);
                        self.refreshBasicValues(result.Model);
                    }

                    if (share)
                    {
                        self.refreshBasicValues(result.Model);
                        communityExplorerModel.SelectedTrainingInvitation().refreshBasicValues(result.Model);

                        if (self.GoToExplorer)
                        {                     
                            communityExplorerModel.IgnoreTrainingInvitationsWasPressed = true;
                                                       
                            loadFbPreviewSharingStats('_TrainingInvitationShare', self.Description(), ContextPath + "Community/AddTrainingInvitation");                        
                        }
                        else
                        {
                            loadFbPreviewSharingStats('_TrainingInvitationShare', self.Description());                        
                        }
                    }

                    if (fromSaveButton)
                    {
                        setTimeout(function () { $('#myTrainingInvitationModal').find('button[data-dismiss="modal"]').first().click(); }, 500);                                
                    }
                    else
                    {
                        if (!self.IsSender())
                        {
                            self.UsersWhichAcceptedInvitation.WantsObservedObserving.removeAll();
                            self.UsersWhichAcceptedInvitation.resetPageNumber();
                            self.UsersWhichAcceptedInvitation.EntriesPerPage(6);
                            self.UsersWhichAcceptedInvitation.setList(result);                                         
                            $('.communityScrollUserList').scrollTop(0);                            
                        }
                    }
                    
                    if (self.IsNew()) 
                    {
                        if ((self.ParentExplorer == trainingInvitationsExplorerModel) || self.Refresh)
                        {
                            trainingInvitationsExplorerModel.Start = true;
                            trainingInvitationsExplorerModel.Search();                            
                        }

                        if (self.GoToExplorer && !share)
                        {
                            communityExplorerModel.IgnoreTrainingInvitationsWasPressed = true;
                            window.location = ContextPath + "Community/AddTrainingInvitation";
                        }
                        //communityRefreshTrainings();
                    }

                    if (self.ParentExplorer && (self.ParentExplorer == trainingPlanManager))
                    {
                        trainingPlanManager.currentTrainingPlanUnit().TrainingInvitationId(result.Model.Id);
                    }   

                    if (typeof (userCallendar) != 'undefined' && userCallendar) {
                        userCallendar.coreRefresh();
                    }
                }
            });	            
        } else {
            if (self.TypeManager.SelectedType().Id == 0) {
                modal.find('.askMsg').html(translations.DisciplineIsRequired);
            }
            else if (!self.Time()) {
                modal.find('.askMsg').html(translations.TimeIsRequired);
            }
            else if (!self.Place()) {
                modal.find('.askMsg').html(translations.PlaceIsRequired);
            }
            else {
                var errorText = '<div class="trainingInvitationErrorText">' + translations.AdditionalParamIsRequired;
    
                if (self.ParamsVisibility().RouteGroup)
                {
                    errorText = errorText + ', ' + translations.Distance;
                }

                if (self.ParamsVisibility().SpeedGroup)
                {
                    if (self.ParamsVisibility().ShowPace )
                    {
                        errorText = errorText + ', ' +  translations.AvgPace;
                    }  
                    if (!self.ParamsVisibility().ShowPace)
                    {
                        errorText = errorText + ', ' +  translations.AvgSpeed;
                    }                                       
                }                

                errorText = errorText + '</span></div>';

                modal.find('.askMsg').html(errorText);
            }

            modal.modal("show");          
        }
    }

    self.UpdateWall = function (serverModel) {
        
        var containerDiv = $(document.createElement('div'));
        containerDiv.append(serverModel.RenderedContent);

        var trainingInvitation = $('#communityTrainings .training.invitation.id_' + self.Id());
        trainingInvitation.replaceWith(serverModel.RenderedContent);

        //         var place = $('.trainingInvitationWallMiddleTop', trainingInvitation);            
        
        //         place.html(self.Place());        
    }

    self.openInviteDialog = function () {
        self.Parent.SelectedForExplorer(self);
        communityExplorerModel.SelectedTrainingInvitation().Id(self.Id());
        communityExplorerModel.SelectedTrainingInvitation().NewId(self.Id());
        communityExplorerModel.SelectedTrainingInvitation().AllInvited(self.AllInvited());
        communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvitedCount(self.UsersWhichWasInvitedCount());
        communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvited.loadView();
        //communityExplorerModel.SelectedTrainingInvitation().IsNew(self.IsNew());
        //communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvitedIdsInitState = self.UsersWhichWasInvitedIdsInitState;
    }

    self.openInviteDialogFromModal = function () {
        communityExplorerModel.SelectedTrainingInvitation().UsersWhichWasInvited.loadView();
    }

    self.OpenDeleteInvitation = function () {
        $('#deleteTrainingInvitation').modal('show');
    }    

    self.coreDeleteTrainingInvitation = function() {
        $.ajax({
            type: "POST",
            //             url: ContextPath + "Community/SetStatusToTrainingInvitation",
            url: ContextPath + "Community/DeleteTrainingInvitation",
            data: { messageId: self.Id() }
            //data: { messageId: self.Id(), status: 'canceled' }
        })
        .done(function (result) {
            $('#deleteTrainingInvitation').find('button[data-dismiss="modal"]').first().click();
            setTimeout(function () { $('#myTrainingInvitationModal').find('button[data-dismiss="modal"]').first().click(); }, 500);

            //self.Deleted(true);

            if (self.CallBackFunction)
            {
                self.CallBackFunction(self.CallBackFunctionParams);
            }

            if (self.ParentExplorer == trainingInvitationsExplorerModel) {
                trainingInvitationsExplorerModel.Start = true;
                trainingInvitationsExplorerModel.Search();
            }                        
        });
    }

    self.deleteTrainingInvitationIfCancelNew = function (fromModal) {
        if (self.IsNew() && self.NewId() && self.NewMessageWasCreated() && (fromModal || (!fromModal && !self.CancelWasPressed() && !self.SaveWasPressed()))) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/DeleteTrainingInvitation",
                data: { messageId: self.NewId() }
            })
            .done(function (result) {
            });
        }
    }

    self.SignUp = function() {
        if (self.Response() == 'saw' || self.Response() == 'false' || self.Response() == '') {
            self.Response('true');
        }
        else {
            self.Response('false');
        }

        self.save();

        if (self.Response() == 'true')
        {
            if ($("#myTrainingInvitationModal.fade.in #trainingInvitationsListItem_" + self.Id() + " .communityTrainingInvitationModalSignUpIcon .publishInfoCloudContainer").length > 0){
                $("#myTrainingInvitationModal #trainingInvitationsListItem_" + self.Id() + " .communityTrainingInvitationModalSignUpIcon .publishInfoCloudContainer").fadeIn(850);
                setTimeout(function () { $("#myTrainingInvitationModal #trainingInvitationsListItem_" + self.Id() + " .communityTrainingInvitationModalSignUpIcon .publishInfoCloudContainer").fadeOut(850); }, 3000);
            }
            else
            {
                $("#trainingInvitationsListItem_" + self.Id() + " .communityTrainingInvitationModalSignUpIcon .publishInfoCloudContainer").fadeIn(850);
                setTimeout(function () { $("#trainingInvitationsListItem_" + self.Id() + " .communityTrainingInvitationModalSignUpIcon .publishInfoCloudContainer").fadeOut(850); }, 3000);                
            }
        }
    }

    self.Resign = function () {
        self.Response('false');

        self.save();
    }

    self.Share = function() {
        self.save(true, null, null, true);
    }        

    self.ItemClass = ko.computed(function () { 
        var result = '';

        if (self.IsReadComputed()) result = 'read';
        if (self.CollapsedComputed()) result = result + ' collapsed';
        if (self.TrainingInvitationType() == 'User') result = result + ' user';
        if (self.TrainingInvitationType() == 'Club') result = result + ' club';
        if (self.IsSenderComputed()) result = result + ' sender';
        if (self.OpenedFromWall()) result = 'fromWall';

        return result; 
    });

    self.StripeColor = ko.computed(function () { 
        var result = '';

        if (self.TrainingInvitationType() == 'User') result = '';
        if (self.Collapsed()) result = result + ' collapsed';
        
        return result; 
    });    

    self.setRead = function () {
        if (self.IsRead()) return;
        
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetTrainingInvitationToRead",
            data: { id: self.Id(), read: !self.IsRead(), senderEventId: self.SenderEventId(), receiverId: self.User().UserId()}
        })
        .done(function (result) {
            if (result == '1')
            {
                self.IsRead(!self.IsRead());

                if ((trainingInvitationsExplorerModel.NotReadCount()-1) >= 0)
                {
                    trainingInvitationsExplorerModel.NotReadCount(trainingInvitationsExplorerModel.NotReadCount()-1);
                }
                if ((userBarModel.NotReadTrainingInvitationsCount()-1) >= 0)
                {
                    userBarModel.SetNotReadTrainingInvitationsCount(userBarModel.NotReadTrainingInvitationsCount()-1);
                }
                if ((communityExplorerModel.NewTrainingInvitationsCount() - 1) >= 0) {
                    communityExplorerModel.setNewTrainingInvitationsCount(communityExplorerModel.NewTrainingInvitationsCount() - 1);
                }

                var elem = $('.sideTileCommunity #communityTrainings .training.invitation.id_' + self.Id() + '.noResponse');
                if (elem.length > 0) {
                    communityRefreshTrainings();
                }
            }
        });
    }

    self.click = function (elem, event) {
        if (!self.Clicked())
        {
            $("#trainingInvitationsListItem_" + self.Id() + " .communityScrollUserList").scroll(function() {
                if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
                {
                    var usersWhichAcceptedInvitationExplorerModel = self.UsersWhichAcceptedInvitation;

                    usersWhichAcceptedInvitationExplorerModel.GoToNextPage();   
                }
            });

            var communityComments = self.Comments();

            $("#trainingInvitationsListItem_" + self.Id() + " .communityComments").scroll(function () {
                if (($(this).scrollTop() + $(this)[0].clientHeight) == $(this)[0].scrollHeight) {
                    communityComments.GoToNextPage();
                }
            });

            setTimeout(function() { 
                initTimePicker('#trainingInvitationsListItem_' + self.Id());

                $("#trainingInvitationsListItem_" + self.Id() + " .communityCommentsLine").each(function () {
                    var elem = $(this);
                    var commentIndex = $(this).data().index;

                    if (typeof commentIndex !== 'undefined') {
                        var communityComments = self.Comments();
                        var comment = communityComments.List()[commentIndex];

                        var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                        if (invisibleHeight > 72) comment.IsCommentLinkNeeded(true);
                        else
                            comment.IsCommentLinkNeeded(false);
                    }
                });
            }, 100);

            $("#trainingInvitationsListItem_" + self.Id() + " .communityCommentsAddButton > a").click(function (event) {
                event.stopPropagation();                
            });
            $("#trainingInvitationsListItem_" + self.Id() + " a.checkboxIcon").click(function (event) {
                event.stopPropagation();
            });            

            $("#trainingInvitationsListItem_" + self.Id() + " .communityCommentsStartStopObserve").click(function (event) {
                event.stopPropagation();
            });            

            $("#trainingInvitationsListItem_" + self.Id() + " .communityCommentLink").click(function (event) {
                event.stopPropagation();
            });

            $("#trainingInvitationsListItem_" + self.Id() + " .param.paramNewLine.disciplines").click(function (event) {
                event.stopPropagation();
            });

            setTimeout(function () {
                correctAvatarToolTipPosition($("#trainingInvitationsListItem_" + self.Id()));
            }, 100);            
        }
                
        self.Clicked(true);

        if (!event.isTrigger)
        {
            if (self.Parent.SelectedForExplorer) self.Parent.SelectedForExplorer(self);

            if (!event.srcElement)
            {
                event.srcElement = event.target;
            }

            communityExplorerModel.SelectedTrainingInvitation().ParentExplorer = self.Parent;      
            if (event.srcElement.className.indexOf('delete') != -1)
            {
                communityExplorerModel.SelectedTrainingInvitation().initByModel(self.StoredServerModel);  
            }

            self.setRead(); 

            if (event.srcElement.className.indexOf('communityTrainingInvitationModalSignUpIcon') != -1 ||
                event.srcElement.className.indexOf('activity trainingDetails') != -1 ||
                event.srcElement.className.indexOf('intense') != -1 ||
                event.srcElement.className.indexOf('communityEdit') != -1 ||
                event.srcElement.className.indexOf('share_11') != -1 ||
                event.srcElement.className.indexOf('save') != -1 ||
                event.srcElement.className.indexOf('delete') != -1 ||
                event.srcElement.className.indexOf('addNew') != -1 ||
                event.srcElement.className.indexOf('invite') != -1 ||
                event.srcElement.nodeName.indexOf('INPUT') != -1 ||
                event.srcElement.nodeName.indexOf('TEXTAREA') != -1 ||
                self.CloudContentShowed
                )
            {
                if ($('.cloudContent', '#trainingInvitationsListItem_' + self.Id()).is(':Visible') ||
                    $('.datepicker.dropdown-menu').is(':Visible'))
                {
                    self.CloudContentShowed = true;
                } 
                else
                {
                    self.CloudContentShowed = false;
                }

                return;
            }               

            self.CloudContentShowed = false;                                                 

            if (!event.isTrigger && event.srcElement.className.indexOf('dzyndzolek') == -1)
            {
                var listItem = $('.dzyndzolek', event.currentTarget);
                listItem.click();
            }

            var communityComments = trainingInvitationsExplorerModel.SelectedForExplorer().Comments();

            communityComments.SetCommentsToRead();

            self.AreSomeNotViewedComments(false);
        }
    }     

    self.expanderClick = function () {                
        self.Parent.SelectedForExplorer(self);
        returnToPositionAfterDzynClick($('#trainingInvitationsListItem_' + self.Id()), '.dzyndzolek');
        trainingInvitationsExplorerModel.SelectedForExplorer().Collapsed(!trainingInvitationsExplorerModel.SelectedForExplorer().Collapsed());

        correctAvatarUserName('.userAvatarCommunityMainContainer', '#trainingInvitationsListItem_' + self.Id());
    }

    self.loadView = function (id, userId, invitationType, isSender) {
        $('#myTrainingInvitationModal').remove();
        self.binded = false;        
        if ($("body #myTrainingInvitationModal").length == 0) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetTrainingInvitationModelView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);

                     if (id) {
                         self.initById(id, userId, invitationType, isSender, true);
                         self.IsSender(false);
                         $('#myTrainingInvitationModal').addClass('fromWall');
                     }
                     else
                     {
                         self.initForSender();
                         self.OpenedFromWall(false);
                     }

                     $('#myTrainingInvitationModal').on('shown', function (event) {
                         $('#myTrainingInvitationModal .communityCommentsLine').each(function () {
                             var elem = $(this);
                             var commentIndex = $(this).data().index;

                             if (typeof commentIndex !== 'undefined') {
                                 var communityComments = communityExplorerModel.SelectedCommentsModel;
                                 var comment = communityComments.List()[commentIndex];

                                 var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                                 if (invisibleHeight > 72) comment.IsCommentLinkNeeded(true);
                                 else
                                     comment.IsCommentLinkNeeded(false);
                             }
                         });

                         correctAvatarToolTipPosition($('#myTrainingInvitationModal'));
                     });

                     $('#myTrainingInvitationModal').on('hidden', function () {
                         if (communityExplorerModel.SelectedTrainingInvitation().IsSender())
                             communityExplorerModel.SelectedTrainingInvitation().deleteTrainingInvitationIfCancelNew();

                         communityExplorerModel.SelectedTrainingInvitation().setRead();

                         var communityComments = communityExplorerModel.SelectedCommentsModel;

                         if (communityComments) {
                             communityComments.SetCommentsToRead();
                         }

                         var foundTrainingInvitations = $.grep(trainingInvitationsExplorerModel.List(), function (e) { return e.Comments().MessageId() == communityExplorerModel.SelectedTrainingInvitation().Id(); });

                         $.each(foundTrainingInvitations, function (i, foundTrainingInvitation) {
                             foundTrainingInvitation.AreSomeNotViewedComments(false);
                             foundTrainingInvitation.IsRead(true);
                         });
                     })

                     $("#myTrainingInvitationModal .communityComments").scroll(function () {
                         if (($(this).scrollTop() + $(this)[0].clientHeight) == $(this)[0].scrollHeight) {
                             var communityComments = communityExplorerModel.SelectedCommentsModel;

                             communityComments.GoToNextPage();
                         }
                     });

                     if (!self.binded) {
                         ko.applyBindings(GeneralModel, $("body #myTrainingInvitationModal")[0]);
                         self.binded = true;
                     }
                 }
             });
        }
        else {
            if (id) {
                self.initById(id, userId, invitationType, isSender, true);
            }
            else {
                self.initForSender();
                self.OpenedFromWall(false);
            }
        }
    }

    self.initForSender = function () {
        self.setUserByCurrentUser();
        self.UpdateVisibility();
        $('#myTrainingInvitationModal').modal("show");                   
    }

    self.setNewCommentsToRead = function () {
        self.Comments().SetCommentsToRead();
        self.AreSomeNotViewedComments(false);
    }

    self.communityInviteUserModalHidden = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetInvitedUsersList",
            data: { messageId: self.NewId() }
        })
         .done(function (result) {
             if (result) {
                 var usersCount = result.UsersWhichWasInvitedSimpleList.TotalResuts;

                 self.UsersWhichWasInvitedCount(usersCount);
                
                 if (self.Id()) {
                     trainingInvitationsExplorerModel.SelectedForExplorer().UsersWhichWasInvitedCount(usersCount);
                     trainingInvitationsExplorerModel.SelectedForExplorer().UsersWhichWasInvitedSimpleList().initByModel(result.UsersWhichWasInvitedSimpleList)
                 }
                 else
                 {
                     self.UsersWhichWasInvitedSimpleList().initByModel(result.UsersWhichWasInvitedSimpleList)
                 }

                 self.UsersWhichWasInvitedSimpleList().initByModel(result.UsersWhichWasInvitedSimpleList)
             }
         });
    }  
}

function correctAvatarUserName(userAvatarMainContainer, parent) {
    $(userAvatarMainContainer, parent).each(function () {
        var communityAvatarNick = $('.communityAvatarRightSideText.communityAvatarNick', this);
        var communityAvatarName = $('.communityAvatarRightSideText.communityAvatarName', this);
        var width = ($(this).offset().left + $(this).width()) - communityAvatarNick.offset().left;
        communityAvatarNick.css('width', width + 'px');
        communityAvatarName.css('width', width + 'px');

        if (communityAvatarNick.height() > 16) {
            var topCorrection = (communityAvatarNick.height() - 16);
            var top = 30 - topCorrection;
            communityAvatarNick.css('top', top + 'px');
        } else {
            communityAvatarNick.css('top', 30 + 'px');
        }
    });
}

function CommunityOneFanModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Name = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Name(serverModel.Name);
        }
    };
}

function CommunityScrollUserListModel(parent) {
    var self = this;
    self.Parent = parent;
    self.ControllerModalViewMethodName = '';
    self.WindowName = ko.observable('');
    self.binded = false;

    self.Id = ko.observable(0);
    self.UserList = new CommunityModelSimple(self, 'EnterNameOrNick', '', true);

    self.showDialog = function () {
        $('#communityScrollUserListModal').modal("show");

        if (!self.binded) {
            ko.applyBindings(GeneralModel, $("body #communityScrollUserListModal")[0]);
            self.binded = true;
        }
    }

    self.loadView = function () {
        if ($("body #communityScrollUserListModal").length == 0) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/" + self.ControllerModalViewMethodName,
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);

                     $("#communityScrollUserListModal .communityScrollUserList").scroll(function () {
                         if (($(this).scrollTop() + $(this)[0].clientHeight) == $(this)[0].scrollHeight) {

                             var communityScrollUserListFromModal = communityExplorerModel.CommunityScrollUserList().UserList;

                             communityScrollUserListFromModal.GoToNextPage();
                         }
                     });

                     $('#communityScrollUserListModal').on('shown', function (event) {
                         var communityScrollUserList = communityExplorerModel.CommunityScrollUserList().UserList;

                         communityScrollUserList.SetTextsParameters();
                     })

                     self.showDialog();
                 }
             });
        }
        else {
            self.showDialog();
        }
    }

    self.ShowUserList = function (elementId, type, controllerModalViewMethodName, windowName) {
        self.Id(elementId);
        self.ControllerModalViewMethodName = controllerModalViewMethodName;
        self.WindowName(windowName);
        self.UserList.Type = type;
        self.UserList.WantsObservedObserving.removeAll();
        self.UserList.resetPageNumber();
        self.UserList.EntriesPerPage(6);
        self.UserList.Search(null, self.loadView);
    };
}

function CommunitySimpleUserListModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(parent.Id());
    self.TotalResuts = ko.observable(0);
    self.List = ko.observableArray([]);
    self.AndText = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.TotalResuts(serverModel.TotalResuts);
            self.AndText(serverModel.AndText);

            self.List.removeAll();
            for (var i = 0, len = serverModel.List.length; i < len; i++) {
                var elem = new CommunityOneFanModel(self);
                elem.initByModel(serverModel.List[i]);
                self.List.push(elem);
            }
        }
    };

    self.ShowUsers = function () {
        communityExplorerModel.CommunityScrollUserList().ShowUserList(self.Parent.Id(), 'CommunityInvitedModel', 'GetScrollUserListModalView', translations["Invited"]);
    };
}

function FanModel(parent) {
    var self = this;
    self.Parent = parent;

    self.MessageId = ko.observable(0);
    self.Id = ko.observable(0);
    self.UserId = ko.observable(0);
    self.TotalResuts = ko.observable(0);
    self.IsCurrentUserFan = ko.observable(false);
    self.IsMySelfMessage = ko.observable(false);
    self.ShowContentOpen = ko.observable(false);
    self.ShowContentSuccess = ko.observable(false);
    self.ShowContentFailed = ko.observable(false);    
    self.FanList = ko.observableArray([]);
    self.AndText = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.MessageId(serverModel.MessageId);
            self.Id(serverModel.MessageId);
            self.UserId(serverModel.UserId);
            self.TotalResuts(serverModel.TotalResuts);
            self.IsCurrentUserFan(serverModel.IsCurrentUserFan);
            self.IsMySelfMessage(serverModel.IsMySelfMessage);
            self.AndText(serverModel.AndText);

            if (self.Parent.TargetSideTileManage) {
                self.ShowContentOpen(self.Parent.ShowContentOpen());
                self.ShowContentSuccess(self.Parent.ShowContentSuccess());
                self.ShowContentFailed(self.Parent.ShowContentFailed());
            }

            self.FanList.removeAll();
            for (var i = 0, len = serverModel.FanList.length; i < len; i++) {
                var elem = new CommunityOneFanModel(self);
                elem.initByModel(serverModel.FanList[i]);
                self.FanList.push(elem);
            }
        }
    };

    self.FansControlLabelText = ko.computed(function () {
        if (self.TotalResuts() > 0) {
            return self.IsCurrentUserFan() ? translations["BeFanEqualsMotivation"] : translations["BeFan"];
        }

        return translations["NoFanBeFirst"];
    });

    self.FansControlIcon = ko.computed(function () {
        if (self.IsCurrentUserFan()) {
            return 'kibicuje_50 mod';
        }

        return 'kibicuj_50 gray';
    });

    self.FansCounterLabelText = ko.computed(function () {
        return (self.TotalResuts() > 1) ? translations["Fans"] : translations["Fan"];;
    });

    self.FansContainerClass = ko.computed(function () {
        var result = '';
        if (self.Parent.TargetSideTileManage) {
            if (self.ShowContentOpen()) result = result + ' targetOpen';
            if (self.ShowContentSuccess()) result = result + ' targetSuccess';
            if (self.ShowContentFailed()) result = result + ' targetFailed';
        }
        if (self.TotalResuts() > 0) result = result + ' withFans';
        else result = result + ' noFans';
        if (!self.IsMySelfMessage()) result = result + ' notMyMessage';
        if (self.IsCurrentUserFan()) result = result + ' currentUserFan';
        else result = result + ' notCurrentUserFan';

        return result;
    });

    self.AddFan = function () {
        var data = {
            messageId: self.MessageId(),
            receiverId: self.UserId(),
        };

        var data_tmp = JSON.stringify(data);

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/AddFan",
            data: data_tmp,
            dataType: "json",
            contentType: "application/json; charset=utf-8",
        })
       .done(function (result) {
           if (result) {
               self.initByModel(result);
           }
       });
    };

    self.ShowFans = function () {
        communityExplorerModel.CommunityScrollUserList().ShowUserList(self.Id(), 'CommunityFanModel', 'GetScrollUserListModalView', translations["FansWindowName"]);
    };
}

function UserTargetModel(parent, index, existingTargetModel) {
    var self = this;
    self.Parent = parent;

    self.User = ko.observable(new UserSimpleModel());
    self.TargetSideTileManage = new TargetSideTileManage(existingTargetModel);
    self.Comments = ko.observable(new CommunityCommentsModel(self));
    self.Fans = ko.observable(new FanModel(self));
    self.binded = false;
    self.Index = index;

    self.Target = function () {
        return self.TargetSideTileManage.currentTarget;
    };

    self.ShowContentOpen = ko.computed(function () { 
        var target = self.TargetSideTileManage.currentTarget;

        return (target.Status() == 'Open' && target.CountDownTimer().Show()); 
    });     

    self.ShowContentSuccess = ko.computed(function () { 
        var target = self.TargetSideTileManage.currentTarget;

        return target.Status() == 'Success'; 
    });     

    self.ShowContentFailed = ko.computed(function () { 
        var target = self.TargetSideTileManage.currentTarget;

        return (target.Status() == 'Failed' || (target.Status() == 'Open' && !target.CountDownTimer().Show())); 
    });     

    self.TargetHeader = ko.computed(function () { 
        var user = self.User();

        if (user.MaleSex() == 'K')
        {
            if (self.ShowContentOpen())
            {
                return translations.SheSetTarget;
            }
            if (self.ShowContentSuccess())
            {
                return translations.SheFinishedTarget;
            }
            if (self.ShowContentFailed())
            {
                return translations.SheNotFinishedTarget;
            }
        }
        if (user.MaleSex() == 'M')
        {
            if (self.ShowContentOpen())
            {
                return translations.HeSetTarget;
            }
            if (self.ShowContentSuccess())
            {
                return translations.HeFinishedTarget;
            }
            if (self.ShowContentFailed())
            {
                return translations.HeNotFinishedTarget;
            }
        }            
        if (user.MaleSex() != 'K' && user.MaleSex() != 'M')
        {
            if (self.ShowContentOpen())
            {
                return translations.TargetSet;
            }
            if (self.ShowContentSuccess())
            {
                return translations.FinishedTarget;
            }
            if (self.ShowContentFailed())
            {
                return translations.NotFinishedTarget;
            }
        }

        return ''; 
    }); 

    self.loadView = function (targetId, userId) {
        if ($("body #communityTargetModal").length == 0)
        {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetUserTargetModelView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);

                     //                     $('#communityTargetModal').on('shown', function(event) {
                            
                     //                         //GeneralModel.CheckIfApply($("body #communityTargetModal")[0]);
                     //                     })
                            
                     self.initById(targetId, userId);

                     $( "#communityTargetModal .communityComments" ).scroll(function() {
                         if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
                         {
                             var communityComments = communityExplorerModel.SelectedCommentsModel;

                             communityComments.GoToNextPage();   
                         } 
                     }); 

                     $('#communityTargetModal').on('shown', function(event) {
                         correctActivityTypeInfoText('#communityTargetModal', 60, true);

                         $('#communityTargetModal .communityCommentsLine').each(function () {
                             var elem = $(this);
                             var commentIndex = $(this).data().index;
                            
                             if (typeof commentIndex !== 'undefined')
                             {
                                 var communityComments = communityExplorerModel.SelectedCommentsModel;
                                 var comment = communityComments.List()[commentIndex];

                                 var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                                 if (invisibleHeight > 72)  comment.IsCommentLinkNeeded(true);
                                 else
                                     comment.IsCommentLinkNeeded(false);                                
                             }
                         });

                         correctAvatarToolTipPosition($('#communityTargetModal'));
                     })

                     $('#communityTargetModal').on('hidden', function () {
                         var communityComments = communityExplorerModel.SelectedCommentsModel;     

                         communityComments.SetCommentsToRead();                                               
                     })  
                 }
             });
        }
        else
        {
            self.initById(targetId, userId);
        }
    }  

    self.initById = function (targetId, userId) {
        $('#communitySideTile').css('cursor', 'wait');
        $('#communityTrainings .training').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetUserTargetModel",
            //            contentType: "application/json; charset=utf-8",
            dataType: "json",            
            data: { targetId: targetId, userId: userId }
        })
         .done(function (result) {
             if (result) {
                 $('#communitySideTile').css('cursor', 'auto');
                 $('#communityTrainings .training').css('cursor', 'pointer');

                 if (result == 'NoData') {
                     var modal = $('#emptyMsgModal');                
                     modal.find('.askMsg').html(translations[result]);
                     modal.modal("show");
                 }
                 else
                 {             
                     self.initByModel(result);
                  
                     $('#communityTargetModal').modal("show");

                     if (!self.binded)
                     {
                         ko.applyBindings(GeneralModel, $("body #communityTargetModal")[0]);                                
                         self.binded = true;                    
                     }

                     $('.fbShareTarget .selectedTarget .values .value').fitText({ minFontSize: '10px', maxFontSize: '30px', padding: '13' });

                     //self.TargetSideTileManage.currentTarget.CurrentValue.valueHasMutated();
                     //                     result.Target.CurrentValue = 64;
                     //                     self.TargetSideTileManage.currentTarget.CurrentValue();
                     //self.initByModel(result);

                     //self.initByModel(result);
                     //                    GeneralModel.CheckIfApply($("body #communityTargetModal")[0]);                   
                    
                                        
                 }
             }
         });
    }  

    self.initByModel = function (serverModel) {
        if (serverModel) {

            if (serverModel.Target)
            {
                self.TargetSideTileManage.currentTarget.initByModel(serverModel.Target);
            }
            
            if (serverModel.User)
            {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
                self.User().AdditionalClass(serverModel.User.AdditionalClass);
                self.User().NameOrNick(serverModel.User.NameOrNick);
            }

            if (serverModel.FanModel)
            {
                self.Fans().initByModel(serverModel.FanModel);
            }            

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);           
        }
    };

    self.OpenDeleteTarget = function (trainingId) {
        self.TargetSideTileManage.currentTarget.OpenDeleteTarget(self.CoreDeleteTargetCallBack);
    }

    self.CoreDeleteTargetCallBack = function () {
        if (self.Parent && self.Parent.constructor.name == "ClubTableExplorerModel") {
            targetModelManager.TargetExplorerManager.Search();
        }
    }
}


function CommunityCompetitionModel(parent) {
    var self = this;
    self.Parent = parent;

    self.User = ko.observable(new UserSimpleModel());
    self.binded = false;
    self.UserContestManager = ko.observable(new UserContestManager());
    self.Comments = ko.observable(new CommunityCommentsModel(self));

    self.HasSomeValue = ko.computed(function () { 
        if (self.UserContestManager().currentContest.DisciplineManager.SelectedSubType())
        {
            var paramsVisibility = 
                self.UserContestManager().currentContest.DisciplineManager.SelectedSubType().ParamsVisibility;                  

            var currentContest = self.UserContestManager().currentContest;                

            if (paramsVisibility.ResultType != "none" && currentContest.Result() != null && 
                currentContest.Result() != "00:00:00")
            {
                return true;
            }
            if (paramsVisibility.OpenPlaceVisible && currentContest.OpenPlace() > 0 )
            {
                return true;
            }            
            if (paramsVisibility.PlaceVisible && currentContest.Place() > 0)
            {
                return true;
            }
        }

        return false; 
    }); 

    self.Header = ko.computed(function () { 
        var user = self.User();

        if (user.MaleSex() == 'K')
        {
            return translations.SheParticipatedInCompetition;
        }
        if (user.MaleSex() == 'M')
        {
            return translations.HeParticipatedInCompetition;
        }            
        if (user.MaleSex() != 'K' && user.MaleSex() != 'M')
        {
            return translations.ParticipatedInCompetition;
        }

        return ''; 
    }); 

    self.loadView = function (elemId, userId) {
        if ($("body #communityCompetitionModal").length == 0)
        {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetCommunityCompetitionModelView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);
                            
                     self.initById(elemId, userId);

                     $( "#communityCompetitionModal .communityComments" ).scroll(function() {
                         if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
                         {
                             var communityComments = communityExplorerModel.SelectedCommentsModel;

                             communityComments.GoToNextPage();   
                         } 
                     }); 

                     $('#communityCompetitionModal').on('shown', function(event) {
                         $('#communityCompetitionModal .communityCommentsLine').each(function () {
                             var elem = $(this);
                             var commentIndex = $(this).data().index;
                            
                             if (typeof commentIndex !== 'undefined')
                             {
                                 var communityComments = communityExplorerModel.SelectedCommentsModel;
                                 var comment = communityComments.List()[commentIndex];

                                 var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                                 if (invisibleHeight > 72)  comment.IsCommentLinkNeeded(true);
                                 else
                                     comment.IsCommentLinkNeeded(false);                                
                             }
                         });

                         correctAvatarToolTipPosition($('#communityCompetitionModal'));
                     })  

                     $('#communityCompetitionModal').on('hidden', function () {
                         var communityComments = communityExplorerModel.SelectedCommentsModel;     

                         communityComments.SetCommentsToRead();                                               
                     })                                        
                 }
             });
        }
        else
        {
            self.initById(elemId, userId);
        }
    }  

    self.initById = function (elemId, userId) {
        $('#communitySideTile').css('cursor', 'wait');
        $('#communityTrainings .training').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetCommunityCompetitionModel",
            dataType: "json",            
            data: { communityCompetitionId: elemId, userId: userId }
        })
         .done(function (result) {
             if (result) {
                 $('#communitySideTile').css('cursor', 'auto');
                 $('#communityTrainings .training').css('cursor', 'pointer');

                 if (result == 'NoData') {
                     var modal = $('#emptyMsgModal');                
                     modal.find('.askMsg').html(translations[result]);
                     modal.modal("show");
                 }
                 else
                 {             
                     self.initByModel(result);
                  
                     $('#communityCompetitionModal').modal("show");

                     if (!self.binded)
                     {
                         ko.applyBindings(GeneralModel, $("body #communityCompetitionModal")[0]);                                
                         self.binded = true;                    
                     }
                 }
             }
         });
    }  

    self.initByModel = function (serverModel) {
        if (serverModel) {

            if (serverModel.ContestResult)
            {
                self.UserContestManager().currentContest.initByModel(serverModel.ContestResult);
            }
            
            if (serverModel.User)
            {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);                       
        }
    };   
}

function CommunityCompetitionPlanModel(parent) {
    var self = this;
    self.Parent = parent;

    self.User = ko.observable(new UserSimpleModel());
    self.binded = false;
    self.PeriodsExploreModel = ko.observable(new PeriodsExploreModel());
    self.PeriodsExploreModel().SelectedPeriod(new PeriodModel(self.PeriodsExploreModel(), true));
    self.Comments = ko.observable(new CommunityCommentsModel(self));

    self.Header = ko.computed(function () { 
        var user = self.User();

        if (user.MaleSex() == 'K')
        {
            return translations.SheAddedPlan;
        }
        if (user.MaleSex() == 'M')
        {
            return translations.HeAddedPlan;
        }            
        if (user.MaleSex() != 'K' && user.MaleSex() != 'M')
        {
            return translations.AddedPlan;
        }

        return ''; 
    }); 

    self.loadView = function (elemId, userId) {
        if ($("body #communityCompetitionPlanModal").length == 0)
        {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetCommunityCompetitionPlanModelView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);
                            
                     self.initById(elemId, userId);

                     $( "#communityCompetitionPlanModal .communityComments" ).scroll(function() {
                         if (($(this).scrollTop()+$(this)[0].clientHeight)==$(this)[0].scrollHeight)
                         {
                             var communityComments = communityExplorerModel.SelectedCommentsModel;

                             communityComments.GoToNextPage();   
                         } 
                     }); 

                     $('#communityCompetitionPlanModal').on('shown', function(event) {
                         $('#communityCompetitionPlanModal .communityCommentsLine').each(function () {
                             var elem = $(this);
                             var commentIndex = $(this).data().index;
                            
                             if (typeof commentIndex !== 'undefined')
                             {
                                 var communityComments = communityExplorerModel.SelectedCommentsModel;
                                 var comment = communityComments.List()[commentIndex];

                                 var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                                 if (invisibleHeight > 72)  comment.IsCommentLinkNeeded(true);
                                 else
                                     comment.IsCommentLinkNeeded(false);                                
                             }
                         });

                         correctAvatarToolTipPosition($('#communityCompetitionPlanModal'));
                     }) 

                     $('#communityCompetitionPlanModal').on('hidden', function () {
                         var communityComments = communityExplorerModel.SelectedCommentsModel;     

                         communityComments.SetCommentsToRead();                                               
                     })                                        
                 }
             });
        }
        else
        {
            self.initById(elemId, userId);
        }
    }  

    self.initById = function (elemId, userId) {
        $('#communitySideTile').css('cursor', 'wait');
        $('#communityTrainings .training').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetCommunityCompetitionPlanModel",
            dataType: "json",            
            data: { communityCompetitionPlanId: elemId, userId: userId }
        })
         .done(function (result) {
             if (result) {
                 $('#communitySideTile').css('cursor', 'auto');
                 $('#communityTrainings .training').css('cursor', 'pointer');

                 if (result == 'NoData') {
                     var modal = $('#emptyMsgModal');                
                     modal.find('.askMsg').html(translations[result]);
                     modal.modal("show");
                 }
                 else
                 {             
                     self.initByModel(result);
                  
                     $('#communityCompetitionPlanModal').modal("show");

                     if (!self.binded)
                     {
                         ko.applyBindings(GeneralModel, $("body #communityCompetitionPlanModal")[0]);                                
                         self.binded = true;                    
                     }

                     $('#communityCompetitionPlanModal .communityCompetitionPlanModalNameCore').fitText({ minFontSize: '9px', maxFontSize: '24px', padding: '60' });
                     $("#communityCompetitionPlanModal .periodCategoryLabel").fitText({ minFontSize: '10px', maxFontSize: '16px', padding: '60' });
                 }
             }
         });
    }  

    self.initByModel = function (serverModel) {
        if (serverModel) {

            if (serverModel.Period)
            {
                self.PeriodsExploreModel().SelectedPeriod().initByModel(serverModel.Period);
            }
            
            if (serverModel.User)
            {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);                       
        }
    };    
}

function CommunityTrainingModel(parent) {
    var self = this;
    self.Parent = parent;

    self.User = ko.observable(new UserSimpleModel());
    self.binded = false;
    self.Comments = ko.observable(new CommunityCommentsModel(self));

    self.loadView = function (elemId, userId) {
    }  

    self.initById = function (elemId, userId) {
    }


    self.init = function () {
        self.Comments().init();
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            
            if (serverModel.User)
            {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);                       
        }
    };    
}


function CommunityUserTrainingPlanModel(parent) {
    var self = this;
    self.Parent = parent;

    self.User = ko.observable(new UserSimpleModel());
    self.binded = false;
    self.Comments = ko.observable(new CommunityCommentsModel(self));

    self.loadView = function (elemId, userId) {
        if ($("body #communityUserTrainingPlanModal").length == 0) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Community/GetCommunityUserTrainingPlanModalView",
            })
             .done(function (result) {
                 if (result) {
                     $("body").append(result);

                     self.initById(elemId, userId);

                     $("#communityUserTrainingPlanModal .communityComments").scroll(function () {
                         if (($(this).scrollTop() + $(this)[0].clientHeight) == $(this)[0].scrollHeight) {
                             var communityComments = communityExplorerModel.SelectedCommentsModel;

                             communityComments.GoToNextPage();
                         }
                     });

                     $('#communityUserTrainingPlanModal').on('shown', function (event) {
                         $('#communityUserTrainingPlanModal .communityCommentsLine').each(function () {
                             var elem = $(this);
                             var commentIndex = $(this).data().index;

                             if (typeof commentIndex !== 'undefined') {
                                 var communityComments = communityExplorerModel.SelectedCommentsModel;
                                 var comment = communityComments.List()[commentIndex];

                                 var invisibleHeight = $('.communityComment.hiddenDiv', elem).height();

                                 if (invisibleHeight > 72) comment.IsCommentLinkNeeded(true);
                                 else
                                     comment.IsCommentLinkNeeded(false);
                             }
                         });
                     })

                     $('#communityUserTrainingPlanModal').on('hidden', function () {
                         var communityComments = communityExplorerModel.SelectedCommentsModel;

                         communityComments.SetCommentsToReadByConversationIdId('UserTrainingPlan', self.callBackAfterSetCommentsToRead);
                     })
                 }
             });
        }
        else {
            self.initById(elemId, userId);
        }
    }

    self.callBackAfterSetCommentsToRead = function (serverModel) {
        if (serverModel.addedResponseCount > 0) {
            userBarModel.setNotReadPlanCommentsCount(userBarModel.NotReadPlanCommentsCount() - 1, serverModel.notReadPlanCommentsFirstId);
            userCallendar.TrainingPlanManager().ActivePlan().AreSomeNotViewedComments(false);
        }
    }

    self.initById = function (elemId, userId) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetUserTrainingPlanCommunityModel",
            dataType: "json",
            data: { userTrainingPlanId: elemId}
        })
         .done(function (result) {
             if (result) {
                 if (result == 'NoData') {
                     ShowEmptyMsg(translations[result], translations["ReadError"]);
                 }
                 else {
                     self.initByModel(result);

                     $('#communityUserTrainingPlanModal').modal("show");

                     if (!self.binded) {
                         ko.applyBindings(GeneralModel, $("body #communityUserTrainingPlanModal")[0]);
                         self.binded = true;
                     }
                 }
             }
         });
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {

            if (serverModel.Period) {
                self.PeriodsExploreModel().SelectedPeriod().initByModel(serverModel.Period);
            }

            if (serverModel.User) {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }

            self.Comments().PageNumber(0);
            self.Comments().EntriesPerPage(9);
            self.Comments().initByModel(serverModel.CommentsModel);
        }
    };
}

function CommentModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.User = ko.observable(new UserSimpleModel());
    //    self.Date = ko.observable(dateToString(new Date()));
    self.Date = ko.observable('');
    self.Comment = ko.observable('');
    self.ShowCommentLink = ko.observable(true);
    self.IsCommentLinkNeeded = ko.observable(false);
    self.DateOriginal = ko.observable('');

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Date(serverModel.Date);
            self.Comment(serverModel.Comment);
            self.DateOriginal(serverModel.DateOriginal);

            if (serverModel.User) {
                self.User().UserId(serverModel.User.UserId);
                self.User().Name(serverModel.User.Name);
                self.User().Nick(serverModel.User.Nick);
                self.User().MaleSex(serverModel.User.MaleSex);
                self.User().AvatarFileId(serverModel.User.AvatarFileId);
            }
        }
    };

    self.ShowAll = function () {
        self.ShowCommentLink(false);
    }

    self.checkIsCommentLinkNeeded = function () {
        var invisibleHeight = $('#communityCommentsLine_' + self.Id() + ' .communityComment.hiddenDiv').height();

        if (invisibleHeight > 72) self.IsCommentLinkNeeded(true);
        else
            self.IsCommentLinkNeeded(false);
    }  
        

    //     self.CheckIfShowCommentLink = function () {
    //         var div = document.createElement("div");
    //             div.innerHTML = text;
    //             div.style.position = 'absolute';
    //             div.style.top  = '-9999px';
    //             div.style.left = '-9999px';
    //              div.style.fontFamily = font;
    //              div.style.fontWeight = bold ? 'bold' : 'normal';
    //              div.style.fontSize = size + 'pt';
    //         document.body.appendChild(div);
    //         var size = [div.offsetWidth, div.offsetHeight];
    //         document.body.removeChild(div);        
    //     }    
}

function CommunityCommentsModel(parent) {
    var self = this;
    self.Parent = parent;

    //self.User = ko.observable(new UserSimpleModel());
    self.MessageId = ko.observable(0);
    self.CommentToAdd = ko.observable('');
    
    self.EntriesPerPage = ko.observable(0);
    self.PageNumber = ko.observable(0);
    self.TotalResuts = ko.observable(0);
    //self.CommentToAddResourceString = translations['AddComment'];
    self.CommentFieldActive = ko.observable(false);
    self.MinimizeExpandableArea = ko.observable(false);
    self.StartStopObserveClicked = ko.observable(false);
    self.IsObserving = ko.observable(false);
    //self.CommentCountText = ko.observable('');
    //     self.DataLoaded = ko.observable(false);
    //     self.Start = false;

    self.NrOfPages = ko.computed(function () {
        var epp = self.EntriesPerPage();
        if (!epp) epp = 1;
        return Math.ceil(1.0 * self.TotalResuts() / epp);
    });  

    self.GoToNextPage = function (page) {
        if (self.PageNumber() + 1 < self.NrOfPages())
        {
            self.PageNumber(self.PageNumber() + 1);
            
            self.Search();
        }
    } 
    
    self.CommentCountText = ko.computed(function () {
        if (self.TotalResuts() > 4 || self.TotalResuts() == 0) return self.TotalResuts() + " " + translations["CommentsCountTextMoreThanFour"];
        else
            if (self.TotalResuts() > 1) return self.TotalResuts() + " " + translations["CommentsCountTextMoreThanOne"];
            else 
                if (self.TotalResuts() == 1) return self.TotalResuts() + " " + translations["CommentsCountTextOne"];

        return '';
    });

    self.StartStopObserveColor = ko.computed(function () {
        if (self.StartStopObserveClicked()) return 'modrak';

        return '';
    });   
     
    self.StartStopObserveText = ko.computed(function () {
        if (self.IsObserving()) return translations["StopObserveThisActivity"];

        return translations["ObserveThisActivity"];
    }); 
                  
    self.StartStopObserveIcon = ko.computed(function () {
        if (self.IsObserving()) return 'ok_10 mod';

        return 'zamknij_10 red';
    }); 

    self.StartStopObserveChooseIcon = ko.computed(function () {
        if (!self.IsObserving()) return 'ok_10 mod';

        return 'zamknij_10 red';
    }); 

    self.List = ko.observableArray([]);

    self.init = function () {            
        self.MessageId(0);
        self.TotalResuts(0);
        self.IsObserving(false); 
        self.List.removeAll();
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            //self.CommentToAdd(self.CommentToAddResourceString);
            self.MessageId(serverModel.MessageId);
            self.TotalResuts(serverModel.TotalResuts);
            self.IsObserving(serverModel.IsObserving);
            //self.CommentCountText(serverModel.CommentCountText);

            //             if (serverModel.User) {
            //                 self.User().UserId(serverModel.User.UserId);
            //                 self.User().Name(serverModel.User.Name);
            //                 self.User().Nick(serverModel.User.Nick);
            //                 self.User().MaleSex(serverModel.User.MaleSex);
            //                 self.User().AvatarFileId(serverModel.User.AvatarFileId);
            //             }

            self.List.removeAll();
            if (serverModel.List) {
                for (var i = 0, len = serverModel.List.length; i < len; i++) {
                    var elem = new CommentModel(self);
                    elem.initByModel(serverModel.List[i]);
                    self.List.push(elem);
                }
            }
        }
    };

    self.refreshMessages = function (messageCreationDate) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetCommentsFromDate",
            data: { conversationId: self.MessageId(), messageCreationDate: messageCreationDate }
        })
        .done(function (serverModel) {
            if (serverModel) {
                $.each(serverModel.List, function (i, val) {
                    var commentExists = $.grep(self.List(), function (e) { return e.Id() == serverModel.List[i].Id; }).length > 0;

                    if (commentExists) {
                        self.List().splice(i, 1);
                        self.TotalResuts(self.TotalResuts() - 1);
                    }
                });

                for (var i = 0, len = serverModel.List.length; i < len; i++) {
                    var elem = new CommentModel(self);
                    elem.initByModel(serverModel.List[i]);
                    self.List.unshift(elem);
                    elem.checkIsCommentLinkNeeded();
                    self.TotalResuts(self.TotalResuts() + 1);
                }

                self.sortComments();
            }
        });
    };

    self.sortComments = function () {
        self.List.sort(function (a, b) {
            var aDate = new Date(a.DateOriginal()),
                bDate = new Date(b.DateOriginal());

            return ((aDate < bDate) ? 1 : ((aDate > bDate) ? -1 : 0));
        });
    }

    self.Serialise = function () {
        var data = {
            pageCount: self.EntriesPerPage(),
            messageId: self.MessageId(),
            userId: self.Parent.User().UserId(),
            pageIndex: self.PageNumber(),
            start: self.Start,
        };

        return JSON.stringify(data);
    }

    self.Search = function () {
        if (!$('body.pulsstory').hasClass('wait')) {
            $('body.pulsstory').addClass('wait');
        }

        var data_tmp = self.Serialise();

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetComments",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data_tmp
        })
        .done(function (serverModel) {
            if (serverModel) {
                //                 if (self.Start) self.List.removeAll();

                //                 self.Start = false;
                self.TotalResuts(serverModel.TotalResuts);
                //                 self.DataLoaded(true);

                for (var i = 0, len = serverModel.List.length; i < len; i++) {
                    var elem = new CommentModel(self);
                    elem.initByModel(serverModel.List[i]);
                    self.List.push(elem);
                    elem.checkIsCommentLinkNeeded();
                }
            }

            if ($('body.pulsstory').hasClass('wait')) {
                $('body.pulsstory').removeClass('wait');
            }
        });
    }

    self.addComment = function () {   
        if (self.CommentToAdd() == '') return;

        var data = {
            conversationId: self.MessageId(),
            receiverId: self.Parent.User().UserId(),
            text: self.CommentToAdd(),
            senderEventId: (typeof self.Parent.SenderEventId !== 'undefined' && self.Parent.SenderEventId()) ? self.Parent.SenderEventId() : 0
        };
         
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/AddComment",
            data: data,
            dataType: "json",
        })
       .done(function (result) {
           if (result)
           {                
               var elem = new CommentModel(self);              
              
               elem.Id(result.Id);
               elem.Comment(result.Comment);
               elem.User().UserId(CurrentUser.UserId());
               elem.User().Name(CurrentUser.Name());
               elem.User().Nick(CurrentUser.Nick());
               elem.User().MaleSex(CurrentUser.MaleSex());
               elem.User().AvatarFileId(CurrentUser.AvatarFileId());
               elem.DateOriginal(result.DateOriginal);

               self.List.unshift(elem);
               $( ".communityComments" ).scrollTop(0);

               self.TotalResuts(self.TotalResuts() + 1);

               self.CommentToAdd('');  
               elem.checkIsCommentLinkNeeded();
               self.IsObserving(true);         
           }
       }); 
    };  

    self.AddCommentFromEnter = function(value) {
        self.CommentToAdd(value);
        self.addComment();
    }
    
    self.CommentFieldLostFocus = function (item, event) {
        //         if (event.target.value == '')
        //         {
        //             event.target.value = self.CommentToAddResourceString;
        //             self.CommentToAdd(event.target.value);
        //         }        
    }  
    
    self.CommentFieldClick = function (item, event) {
        //         if (event.target.value == self.CommentToAddResourceString)
        //         {
        //             event.target.value = '';
        //             self.CommentToAdd('');
        //         }
    }   

    self.CommentFieldKeyPressed = function (item, event) {
        if (event && event.keyCode === 13)
        {
            var value = $('#communityCommentsAreaExpandable_' + self.MessageId() + ' textarea')[0].value;
            self.AddCommentFromEnter(value);
            return false;  
        }
        return true;
    }    

    self.CloseExpandableArea = function () {
        //$('.communityCommentsArea .communityCommentsAreaExpandable').height(0); 
        self.MinimizeExpandableArea(true);

        setTimeout(function () {     
            self.MinimizeExpandableArea(false);
        }, 2000);	  
    }  

    self.StartStopObserve = function () {
        self.StartStopObserveClicked(true);

        var data = {
            conversationId: self.MessageId(),
            receiverId: self.Parent.User().UserId(),
            observe: !self.IsObserving(),
        };

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetCommentsObserving",
            data: data
        })
       .done(function (result) {
           if (result)
           {                
               self.IsObserving(!self.IsObserving()); 
           }
       });
    }

    self.SetCommentsToRead = function (commentsBallPressed) {
        var mainElem = $('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId());
        //        var areSomeNotViewedComments = !$('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId() + ' .newComments').hasClass('displayNone');

        //         if (communityExplorerModel.TabTargetsCount() == 0 && 
        //             communityExplorerModel.TabCompetitionsCount() == 0 &&
        //             communityExplorerModel.TabTrainingsCount() == 0)
        //             {
        //                 var elem = $('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId() + ' .observingComments');    

        //                 if (self.IsObserving() && elem.hasClass('displayNone'))
        //                 {
        //                     elem.removeClass('displayNone');
        //                 }
        //                 if (!self.IsObserving() && !elem.hasClass('displayNone'))
        //                 {
        //                     elem.addClass('displayNone');
        //                 }                

        //                 return;
        //             }
    
        var dataInElem = mainElem.data();
        var firstUploadDate, lastUploadDate;

        if (dataInElem)
        {
            firstUploadDate = dataInElem.firstuploaddate;
            lastUploadDate = dataInElem.lastuploaddate;            
        }
        
        var data = {
            conversationId: self.MessageId(),
            firstUploadDate: firstUploadDate,
            lastUploadDate: lastUploadDate,
        };
         
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetCommentsToRead",
            data: data
        })
       .done(function (result) {
           if (result)
           {
               if (parseInt(result) > 0) 
               {
                   if (commentsBallPressed)
                   {
                       var elem = $('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId() + ' .newComments');    
                       if (!elem.hasClass('displayNone'))
                       {
                           elem.addClass('displayNone');

                           if (communityExplorerModel.SelectedTab() == 'Trainings') 
                           {                            
                               communityExplorerModel.setNewTrainingsCommentsCount(communityExplorerModel.NewTrainingsCommentsCount() - 1);
                           }
                           else
                               if (communityExplorerModel.SelectedTab() == 'Competitions') 
                               {
                                   communityExplorerModel.setNewCompetitionsCommentsCount(communityExplorerModel.NewCompetitionsCommentsCount() - 1);
                               }
                               else
                                   if (communityExplorerModel.SelectedTab() == 'Targets') 
                                   {
                                       communityExplorerModel.setNewTargetCommentsCount(communityExplorerModel.NewTargetCommentsCount() - 1);
                                   }
                                   else
                                       if (communityExplorerModel.SelectedTab() == 'TrainingInvitations') 
                                       {
                                           communityExplorerModel.setNewTrainingInvitationsCommentsCount(communityExplorerModel.NewTrainingInvitationsCommentsCount() - 1);

                                           var foundTrainingInvitations = $.grep(trainingInvitationsExplorerModel.List(), function (e) { return e.Comments().MessageId() == self.MessageId(); });

                                           $.each(foundTrainingInvitations, function (i, foundTrainingInvitation) {
                                               foundTrainingInvitation.AreSomeNotViewedComments(false);
                                           });

                                           $('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId() + ' .trainingInvitationWallRightText.displayNone').removeClass('displayNone');
                                           $('.sideTileCommunity #communityTrainings .training.messageId_' + self.MessageId() + ' .trainingInvitationWallRightText.nbsp').addClass('displayNone');
                                       }

                           callBackFromExplorerModelInit();

                           self.SetCommentsToReadInCache();                        
                       }                                        
                   }  
                   else communityRefreshTrainings();              
               }
               //var elem = $('#communityTrainings .training.id_' + self.MessageId() + '.noResponse');
               //               var elemNewComments = $('#communityTrainings .training.id_' + self.MessageId() + ' .newComments');
               //               elem.removeClass('noResponse');

               //               if (!elemNewComments.hasClass('displayNone'))
               //               {
               //                   elemNewComments.addClass('displayNone');
               //               }
           }
       });   
    }

    self.SetCommentsToReadInCache = function () {
        var prefix = 'communityTrainings_' + communityExplorerModel.UrlEnd();                
        var keys = getCacheKeys(prefix);    

        for (var i = 0, len = keys.length; i < len; i++) {
            var keyWithParams = keys[i].substring(prefix.length);
            var result = $.jStorage.get(keyWithParams);
            var ttl = $.jStorage.getTTL(keyWithParams);

            var containerDiv = $(document.createElement('div'));
            containerDiv.append(result);

            var elem = $('.training.messageId_' + self.MessageId() + ' .newComments', containerDiv);    
            if (elem.length && !elem.hasClass('displayNone'))
            {
                elem.addClass('displayNone');                
                addToCache(keyWithParams, containerDiv[0].innerHTML, ttl);
            }

            var elem = $('.training.messageId_' + self.MessageId() + ' .trainingInvitationWallRightText', containerDiv);
            if (elem.length && elem.hasClass('displayNone'))
            {
                $('.training.messageId_' + self.MessageId() + ' .trainingInvitationWallRightText.displayNone', containerDiv).removeClass('displayNone');
                $('.training.messageId_' + self.MessageId() + ' .trainingInvitationWallRightText.nbsp', containerDiv).addClass('displayNone');
                addToCache(keyWithParams, containerDiv[0].innerHTML, ttl);
            }
        }          
    }

    self.SetCommentsToReadByConversationIdId = function (entityType, callBackFunction) {
        $.ajax({
            type: "POST",
            url: ContextPath + "Community/SetCommentsToRead",
            data: { conversationId: self.MessageId(), entityType: entityType }
        })
       .done(function (result) {
           if (result) {
               if (callBackFunction) {
                   callBackFunction(result);
               }
           }
       });
    }
}



function ClubTableExplorerModel() {

    var self = this;

    self.LastLoadDateAsString = '';
    self.LastLoadDateAsStringOld = 'blablabla';
    self.DataLoaded = ko.observable(false);
    self.TargetList = ko.observableArray([]);    
    self.CommunityTableHeader = ko.observable(false);
    self.ClubGradientColor = ko.observable('');
    self.ClubTextColor = ko.observable('');
    self.SelectedTab = ko.observable('Targets');
    self.LastSelectedTab = 'Targets';
    self.Start = true;
    self.Stop = false;
    self.ForClubOnly = false;
    self.IsSearching = ko.observable(false);
    self.SearchPattern = '';
    self.SearchDate = '';
    self.AllPlanned = false;

    self.ElementListClass = ko.computed(function () {        
        if (self.SelectedTab() == 'Friends')
        { 
            if (self.LastSelectedTab == 'Targets') return 'targets';
        }

        if (self.SelectedTab() == 'Targets') return 'targets';
        if (self.SelectedTab() == 'FriendsTargets' && self.TargetList().length > 0) return 'targets';
        if (self.SelectedTab() == 'Open' && self.TargetList().length > 0) return 'targets open';
        if (self.SelectedTab() == 'Closed' && self.TargetList().length > 0) return 'targets closed';
        return '';
    });     

    self.Serialise = function () {
        var data = {
            LastLoadDateAsString: self.LastLoadDateAsString,
            SelectedTab: self.SelectedTab(),
            ForClubOnly: self.ForClubOnly,
            SearchPattern: self.SearchPattern,
            SearchDateAsString: self.SearchDate,
            AllPlanned: self.AllPlanned
        };

        return JSON.stringify(data);
    }    

    self.SearchForElements = function () {
        if (self.Stop) return;

        self.IsSearching(true);
                
        if (self.LastLoadDateAsString == self.LastLoadDateAsStringOld) return;
        
        self.LastLoadDateAsStringOld = self.LastLoadDateAsString;

        if (!$('body.club').hasClass('wait'))
        {
            $('body.club').addClass('wait');    
        }  
        if (!$('body.pulsstory').hasClass('wait'))
        {
            $('body.pulsstory').addClass('wait');    
        }  

        var data_tmp = self.Serialise();    

        $.ajax({
            type: "POST",
            url: ContextPath + "Community/GetClubTableExplorerModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data_tmp
        })
        .done(function (serverModel) {
            if (serverModel) {
                self.DataLoaded(true);
                self.IsSearching(false);

                self.LastLoadDateAsString = serverModel.LastLoadDateAsString;
                self.CommunityTableHeader(serverModel.CommunityTableHeader);
                self.ClubGradientColor(serverModel.ClubGradientColor);
                self.ClubTextColor(serverModel.ClubTextColor);
                    
                if (self.Start)
                {
                    self.TargetList.removeAll();
                }
                self.Start = false;

                if (serverModel.TargetList)
                {
                    for (var i = 0, len = serverModel.TargetList.length; i < len; i++) {
                        var elem = new UserTargetModel(self, self.TargetList().length);
                        elem.initByModel(serverModel.TargetList[i]);
                        elem.TargetSideTileManage.currentTarget.ForExplorer(true);
                        self.TargetList.push(elem);
                    }

                    if (serverModel.TargetList.length == 0) self.Stop = true;
                }

                if (typeof (targetModelManager) != 'undefined' && targetModelManager) {
                    targetModelManager.TargetExplorerManager.CurrentDate(serverModel.CurrentDate);
                }
            }
        
            if ($('body.club').hasClass('wait'))
            {
                $('body.club').removeClass('wait');    
            }          
            if ($('body.pulsstory').hasClass('wait'))
            {
                $('body.pulsstory').removeClass('wait');    
            }

            $('.clubTableExplorerTile .fbShareTarget .currentValue.open .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '30px', padding: '13' });
            $('.clubTableExplorerTile .fbShareTarget .targetValue.open .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '30px', padding: '13' });
            $('.clubTableExplorerTile .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '42px', padding: '13', elemWidth: '302' });
            $('.clubTableExplorerTile .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '42px', padding: '13' });

            $('.clubTableExplorerTile .fbShareTarget .currentValue.open .value.textNotFit').removeClass('textNotFit');
            $('.clubTableExplorerTile .fbShareTarget .targetValue.open .value.textNotFit').removeClass('textNotFit');
            $('.clubTableExplorerTile .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFit').removeClass('textNotFit');
            $('.clubTableExplorerTile .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFit').removeClass('textNotFit');

            //          $('.clubTableExplorerTile .fbShareTarget .activityTypeInfo.textNotFit').fitText({ minFontSize: '10px', maxFontSize: '23px', padding: '0' });
            //          $('.clubTableExplorerTile .fbShareTarget .activityTypeInfo.textNotFit').removeClass('textNotFit');

            correctActivityTypeInfoText('.clubTableExplorerTile', 60);
            correctActivityTypeInfoText('.sideBarTargetTile', 30);
        });
    }

    self.SearchForAllTargets = function () {
        self.SearchForTargets(null, null);
    }

    self.SearchForTargets = function (tab, searchDateAsString) {
        self.LastSelectedTab = self.SelectedTab();
        if (tab) {
            self.SelectedTab(tab);
        }
        else
        {
            self.SelectedTab('Targets');
        }
        self.SearchDate = searchDateAsString;
        self.ResetSettings();
        self.SearchForElements();
    }

    self.SearchForFriendsTargets = function (searchPattern, searchDateAsString, allPlanned) {
        self.LastSelectedTab = self.SelectedTab();
        self.SelectedTab('FriendsTargets');
        self.SearchPattern = searchPattern;
        self.SearchDate = searchDateAsString;
        self.AllPlanned = allPlanned;
        self.ResetSettings();
        self.SearchForElements();
    }

    self.SearchForFriends = function () {
        self.LastSelectedTab = self.SelectedTab();
        self.SelectedTab('Friends');
        communityModelForUsers.SearchPattern(communityModelForUsers.SearchPatternResourceString);
        communityModelForUsers.resetPageNumber();
        communityModelForUsers.OnlyForCurrentClub = true;
        communityModelForUsers.Search();
        $('#addCommunityUserModal').modal('show');
    }

    self.ResetSettings = function () {
        self.LastLoadDateAsString = '';
        self.Start = true;
        self.Stop = false;
        self.LastLoadDateAsStringOld = 'blablabla';        
    }    
}

function correctActivityTypeInfoText(parentClass, heightLimit, correctHand) {
    if ($('.fbShareTarget .activityTypeInfo .activityTypeInfoHidden').length > 0) {
        $('.fbShareTarget .activityTypeInfo', parentClass).each(function () {
            var $this = $(this);
            
            if ($('.activityTypeInfoHidden', $this).height() > heightLimit) {
                if (!$('span.text.trainingTypeName', $this).hasClass('long')) {
                    $('span.text.trainingTypeName', $this).addClass('long');
                }

                if (correctHand && !$('.haveTarget', parentClass).hasClass('haveTargetUp')) {
                    $('.haveTarget', parentClass).addClass('haveTargetUp');
                }
            } else {
                if ($('span.text.trainingTypeName', $this).hasClass('long')) {
                    $('span.text.trainingTypeName', $this).removeClass('long');
                }

                if (correctHand && $('.haveTarget', parentClass).hasClass('haveTargetUp')) {
                    $('.haveTarget', parentClass).removeClass('haveTargetUp');
                }
            }
        });
    }
}

function correctActivityTypeInfo(parent, textNotFitClass, loopCount) {
    //$(document).ready(function () {    
    //    correctActivityTypeInfo('', 'textNotFit');
    //    correctActivityTypeInfo('', 'textNotFitUserBar');
    //correctActivityTypeInfo('.clubTableExplorerTile', '389px');


    if ($('.fbShareTarget .activityTypeInfo.' + textNotFitClass).length == 0) {
        if (loopCount < 5) {
            setTimeout(function () { correctActivityTypeInfo(parent, textNotFitClass, loopCount++); }, 100);
        }
    }
    else {
        $(parent + ' .fbShareTarget .activityTypeInfo.' + textNotFitClass + ' .text:last-child').css('width', 'auto');
        $(parent + ' .fbShareTarget .activityTypeInfo.' + textNotFitClass).each(function () {
            var $this = $(this);
            var activityTypeInfoWidth = $this.width();
            var activityTypeInfoSubDivWidth = $('.activityTypeInfoSubDiv', $this).width() + 60;
            if (activityTypeInfoSubDivWidth < activityTypeInfoWidth) {
                $('.text:last-child', $this).removeAttr("style");
            } else {
                $('.activityTypeInfoSubDiv', $this).css('max-width', activityTypeInfoWidth - 50 + 'px');
            }
        });
        $(parent + ' .fbShareTarget .activityTypeInfo.' + textNotFitClass).removeClass('textNotFit');
    }
}

if (typeof notificationHub !== 'undefined' && notificationHub) {

    $(function () {
        $.extend(notificationHub.client, {
            newComment: function (notificationId, conversationId, messageCreationDate, messageType) {
                notificationHub.server.notificationReceived(notificationId);

                if (messageType == 'TrainingInvitation')
                {
                    var foundTrainingInvitations = $.grep(trainingInvitationsExplorerModel.List(), function (e) { return e.Comments().MessageId() == conversationId; });

                    $.each(foundTrainingInvitations, function (i, foundTrainingInvitation) {
                        foundTrainingInvitation.Comments().refreshMessages(messageCreationDate);
                        foundTrainingInvitation.AreSomeNotViewedComments(true);
                    });

                    //var selectedTrainingInvitationComments = trainingInvitationsExplorerModel.SelectedForExplorer().Comments();

                    //if (selectedTrainingInvitationComments.MessageId() > 0) {
                    //    var messageId = selectedTrainingInvitationComments.MessageId();

                    //    if (conversationId == messageId) {
                    //        selectedTrainingInvitationComments.refreshMessages(messageCreationDate);
                    //    }
                    //}
                }
                //                 else
                //                 {
                communityExplorerModel.init(true, communityCheckForMissingTrainings);

                if (communityExplorerModel.SelectedCommentsModel && communityExplorerModel.SelectedCommentsWindowName && $(communityExplorerModel.SelectedCommentsWindowName).is(":visible")) {
                    var messageId = communityExplorerModel.SelectedCommentsModel.MessageId();

                    if (conversationId == messageId) {
                        communityExplorerModel.SelectedCommentsModel.refreshMessages(messageCreationDate);
                    }
                }
                //                 }
            }
        });
    });
}

$(document).on('click', function (e) {
    var communityTrainings = $('#communityTrainings');
    if (communityTrainings.length == 0) return;    
    if (!communityTrainings.is(':visible')) return;

    if (e.isTrigger) return;

    if (!e.srcElement)
    {
        if (!e.target) return;
        
        e.srcElement = e.target;
    }

    if (!e.srcElement) return;

    if ($(e.srcElement).data().buttonname) return;

    var isSideTileCommunity = GetParentElementByName(e.srcElement, 'sideTileCommunity', true);

    if (isSideTileCommunity) return;

    var isModalShowed = e.srcElement.className == "modal-scrollable"

    if (communityExplorerModel.TrainingInvitationsWasPressed && ((communityExplorerModel.NewTrainingInvitationsCount() > 0))) {

        if (self.IgnoreTrainingInvitationsWasPressed) return;

        var isTrainingInvitationModal = GetParentElementByName(e.srcElement, 'trainingInvitationModal');

        if (isTrainingInvitationModal || isModalShowed) return;

        var isTrainingInvitationExplorer = GetParentElementByName(e.srcElement, 'trainingInvitationsTile');

        if (isTrainingInvitationExplorer) return;

        communityExplorerModel.SetAllTrainingInvitationsToRead();
        communityRefreshTrainings();
    }

    //if (communityExplorerModel.TrainingInvitationsWasPressed && 
    //    userBarModel.NotReadTrainingInvitationsCount() > 0)
    //{

    //}
    if (communityExplorerModel.TargetWasPressed && 
        ((communityExplorerModel.NewTargetsCount() > 0) 
        //         || (communityExplorerModel.NewTargetCommentsCount() > 0)
        )
        )
    {
        var isTrainingInvitationModal = GetParentElementByName(e.srcElement, 'communityTargetModal');
       
        if (isTrainingInvitationModal || isModalShowed) return;

        communityExplorerModel.SetAllTargetsToRead();
        communityRefreshTrainings();
    }
    if (communityExplorerModel.CompetitionsWasPressed && 
        ((communityExplorerModel.NewCompetitionsCount() > 0) 
        //         || (communityExplorerModel.NewCompetitionsCommentsCount() > 0)
        )
        )
    {
        var isTrainingInvitationModal = GetParentElementByName(e.srcElement, 'communityCompetitionModal');
       
        if (isTrainingInvitationModal || isModalShowed) return;

        communityExplorerModel.SetAllCompetitionsToRead();
        communityRefreshTrainings();
    }
    if (communityExplorerModel.TrainingsWasPressed && 
        ((communityExplorerModel.NewTrainingsCount() > 0) 
        //         || (communityExplorerModel.NewTrainingsCommentsCount() > 0)
        )
        )
    {
        var isTrainingInvitationModal = GetParentElementByName(e.srcElement, 'footerInfoModal');
       
        if (isTrainingInvitationModal || isModalShowed) return;

        communityExplorerModel.SetAllTrainingsToRead();
        communityRefreshTrainings();
    }

    if (communityExplorerModel.LiveStreamsWasPressed &&
        ((communityExplorerModel.NewLiveStreamsCount() > 0)
        )
        ) {
        communityExplorerModel.SetAllLiveStreamsToRead();
    }

    if (communityExplorerModel.PublicationsWasPressed &&
        ((communityExplorerModel.NewPublicationsCount() > 0)
        )
        ) {
        communityExplorerModel.SetAllPublicationsToRead();
        communityRefreshTrainings();
    }
});
;var DefaultZones = null;

var HR_zoneTypeId = 1;
var POWER_zoneTypeId = 2;

function ZoneChooserZone(zone, chooser) {
    var self = jQuery.extend(this, zone);
    self.Parent = chooser;

    if (self.LowerBound < self.Parent.ZonesStart()) {
        if (self.LowerBound <= 0) {
            var ll = self.Parent.ZonesStart() - 10;
            if (ll < 0) ll = 0;
            if (ll > 50) ll = 50;
            self.LowerBound = ll;
        }
        self.Parent.ZonesStart(self.LowerBound);
    }

    self.ZoneHeightPerc = ko.computed(function () { return ((self.UpperBound + 1 - self.LowerBound) * 100 / (100 - self.Parent.ZonesStart())) + '%'; });
    self.LowerBoundPerc = ko.computed(function () { return ((self.LowerBound - self.Parent.ZonesStart()) * 100 / (100 - self.Parent.ZonesStart())) + '%'; });
    self.MarkerPerc = ko.computed(function () { return ((self.LowerBound - self.Parent.ZonesStart()) * 100 / (100 - self.Parent.ZonesStart()) - 0.5) + '%'; });

    self.Select = function () {
        var setBottom = true;

        if (self.Parent.OneZone || !self.Parent.BottomZone()) {
            setBottom = true;
        } else {
            if (!self.Parent.UpperZone()) {
                if (self.UpperBound > self.Parent.BottomZone().UpperBound) {
                    setBottom = false;
                } else {
                    self.Parent.UpperZone(self.Parent.BottomZone());
                    self.Parent.UpperZoneValue(self.Parent.BottomZoneValue());
                    setBottom = true;
                }
            } else {
                if (self.UpperBound > self.Parent.UpperZone().UpperBound) {
                    setBottom = false;
                }
                else if (self.UpperBound < self.Parent.BottomZone().UpperBound) {
                    setBottom = true;
                } else if (self.UpperBound == self.Parent.BottomZone().UpperBound) {
                    if (self.Parent.BottomZoneValue() < 0) {
                        setBottom = false;
                    } else {
                        setBottom = true;
                    }
                } else if (self.UpperBound == self.Parent.UpperZone().UpperBound) {
                    if (self.Parent.UpperZoneValue() < 0) {
                        setBottom = false;
                    } else {
                        setBottom = true;
                    }
                } else {
                    setBottom = !self.LastSetBottom;
                }
            }
        }

        self.LastSetBottom = setBottom;
        if (setBottom) {
            self.Parent.BottomZone(this);
            self.Parent.BottomZoneValue(0);
        } else {
            self.Parent.UpperZone(this);
            self.Parent.UpperZoneValue(0);
        }
    }
}

function ZoneChooser(oneZone, zoneTypeId) {
    var self = this;

    self.OneZone = oneZone;
    self.ZoneTypeId = zoneTypeId;
    self.Zones = ko.observableArray([]);

    self.BottomZone = ko.observable(null);
    self.UpperZone = ko.observable(null);

    self.BottomZoneValue = ko.observable(0);
    self.UpperZoneValue = ko.observable(0);

    self.BottomZoneValue.subscribe(function (newValue) {
        if (newValue) {
            if (newValue > 50) { self.BottomZoneValue(50); }
            else if (newValue < -50) { self.BottomZoneValue(-50); }
        }
    });
    self.UpperZoneValue.subscribe(function (newValue) {
        if (newValue) {
            if (newValue > 50) { self.UpperZoneValue(50); }
            else if (newValue < -50) { self.UpperZoneValue(-50); }
        }
    });

    self.ZonesStart = ko.observable(0);

    self.BottomSelectorPosition = ko.computed(function () {
        return (50 + self.BottomZoneValue()) + '%';
    });
    self.UpperSelectorPosition = ko.computed(function () {
        return (50 + self.UpperZoneValue()) + '%';
    });

    self.dragStartPosition = null;
    self.dragStartValue = 0;
    self.zoneWidth = 1;
    self.DraggedZoneValue = null;
    self.DragOver = function (data, event) {
        var target = $(event.target);
        var current = event.originalEvent.clientX;
        if (self.dragStartPosition) {
            var shift = self.dragStartPosition - current;
            var toChange = shift * -100 / self.zoneWidth;

            var newValue = Math.round(self.dragStartValue + toChange);
            self.DraggedZoneValue(newValue);
            //console.log('self.DragOver self.BottomZoneValue: ' + self.BottomZoneValue());
        } else {
            //console.log('self.DragOver self.dragStartPosition: ' + self.dragStartPosition);
        }
        event.preventDefault();
    }

    self.Drop = function (data, event) {
        self.dragStartPosition = null;
        self.dragStartValue = null;
        self.DraggedZoneValue = null;
        event.preventDefault();
    }

    self.DragStart = function (data, event) {
        var DraggedZone = data;
        if ($(event.target).parents('div.selector.bottom').length) {
            self.DraggedZoneValue = self.BottomZoneValue;
        } else {
            self.DraggedZoneValue = self.UpperZoneValue;
        }
        //event.originalEvent.dataTransfer.setData('text/plain', 'move');
        self.dragStartPosition = event.originalEvent.clientX;
        self.dragStartValue = 0;

        var target = $(event.target);
        self.zoneWidth = 100;
        var z = target.closest('.zone');
        if (z.length > 0) {
            self.zoneWidth = z.width();
        }
        //console.log('self.DragStart self.zoneWidth: ' + self.zoneWidth);

        //console.log('self.DragStart self.BottomZoneValue: ' + self.BottomZoneValue());
        self.dragStartValue = parseInt(self.DraggedZoneValue());
        //console.log('self.DragStart self.dragStartValue: ' + self.dragStartValue);
        return true;
    }
    self.Leave = function (data, event) {
        self.dragStartPosition = null;
        self.dragStartValue = null;
        self.DraggedZoneValue = null;
        event.preventDefault();
    }


    self.InitZones = function () {
        if (DefaultZones) {
            self.LoadZones(DefaultZones);
        }
        if (!self.Zones().length) {

            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "Zones/GetDefaultZones",
                data: { zoneTypeId : self.ZoneTypeId}
                }, function (serverModel) {
                    DefaultZones = serverModel;
                    self.LoadZones(DefaultZones);
                }, 36000000 //10h
            );
        }
    }
    self.LoadZones = function (serverModel) {
        self.Zones.removeAll();
        self.ZonesStart(100);
        if (serverModel) {
            for (var i = 0, len = serverModel.length; i < len; i++) {
                var elem = new ZoneChooserZone(serverModel[i], self);
                self.Zones.push(elem);
            }
            if (self.ZonesStart() > 0) {
                var elem = new ZoneChooserZone({
                    Id: 1,
                    Name: '',
                    BaseId: 0,
                    LowerBound: 0,
                    UpperBound: self.ZonesStart() - 1,
                    Color: '#eee',
                    Text: '',
                    Value: ''
                }, self);
                self.Zones.unshift(elem);
            }
        }
    }


    self.SetZoneById = function (zoneId, bottom, clearIfSame) {
        if (clearIfSame) {
            if (self.GetZoneId(bottom) == zoneId) {
                zoneId = 0;
            }
        }
        var zones = self.Zones();
        var selZone = null;
        if (zoneId > 0) {
            for (var i = 0, len = zones.length; i < len; i++) {
                var cZone = zones[i];
                if (cZone.Id == zoneId) {
                    selZone = cZone;
                }
            }
        }
        if (self.OneZone || bottom) {
            self.BottomZone(selZone);
        } else {
            self.UpperZone(selZone);
        }
    }
    self.SetZoneValue = function (zoneValue, bottom) {

        zoneValue = parseInt(zoneValue);

        if (self.OneZone || bottom) {
            self.BottomZoneValue(zoneValue);
        } else {
            self.UpperZoneValue(zoneValue);
        }
    }

    self.GetZoneId = function (bottom) {
        var z;
        if (self.OneZone || bottom) {
            z = self.BottomZone();
        } else {
            z = self.UpperZone();
        }
        return z ? z.Id : 0;
    }
    self.GetZoneValue = function (bottom) {
        if (self.OneZone || bottom) {
            return self.BottomZoneValue();
        } else {
            return self.UpperZoneValue();
        }
    }

    self.SelectedZone = ko.computed(function () { return self.BottomZone(); });
    self.SelectedZoneId = ko.computed(function () { return self.GetZoneId(); });
    self.SelectedZoneValue = ko.computed(function () { return self.GetZoneValue(); });


    //self.InitZones();

};
var TrainingPlanLib = {
    LevelGroups: [//{ value: "", text: translations.None }
                       //  { value: 1, text: translations.LevelGroup_nowicjusz }
                       //, { value: 2, text: translations.LevelGroup_amator }
                       //, { value: 3, text: translations.LevelGroup_zaawansowanyamator }
                       //, { value: 4, text: translations.LevelGroup_zawodnikjunior }
                       //, { value: 5, text: translations.LevelGroup_zawodnik }
                       //, { value: 6, text: translations.LevelGroup_elita }
                       //,
                        { value: 1, text: translations.LevelGroup_poczatkujący }
                        , { value: 3, text: translations.LevelGroup_zaawansowany}
                        , { value: 5, text: translations.LevelGroup_doswiadczony }

    ],
    LoadDistributions: [//{ value: "", text: translations.None }
                          { value: 'jednostajnie', text: translations.LoadDist_jednostajnie }
                        , { value: 'narastajaco', text: translations.LoadDist_narastajaco }
                        , { value: 'opadajaco', text: translations.LoadDist_opadajaco }],
    /*
    PeriodTypes: [//{ value: "", text: translations.None }
                          { value: 1, text: translations.PlanPeriod_przygotowawczy }
                        , { value: 2, text: translations.PlanPeriod_podstawowy }
                        , { value: 3, text: translations.PlanPeriod_rozbudowa }
                        , { value: 4, text: translations.PlanPeriod_szczyt }
                        , { value: 5, text: translations.PlanPeriod_wyscig }
                        , { value: 6, text: translations.PlanPeriod_przejsciowy }],

    Trgets: [//{ value: "", shortcut: "", text: translations.None }
                      //  { value: 1, shortcut: "R", text: translations.PeriodPlanTarget_wypoczynek }
                          { value: 2, shortcut: "E", text: translations.PeriodPlanTarget_wytrzymalosc, bgColor: '#97C672', iconName: 'wytrzym', whiteText: false } //rgb(0,162,82)
                        , { value: 3, shortcut: "F", text: translations.PeriodPlanTarget_sila, bgColor: '#F8B133', iconName: 'sila', whiteText: false } //rgb(235,108,45)
                        , { value: 4, shortcut: "S", text: translations.PeriodPlanTarget_szybkosc, bgColor: 'rgb(35,148,211)', iconName: 'sprint', whiteText: true } //
                        , { value: 5, shortcut: "M", text: translations.PeriodPlanTarget_wydolnoscmiesniowa, bgColor: '#E7412B', iconName: 'sinus', whiteText: true } //rgb(231,57,57)
                        , { value: 6, shortcut: "A", text: translations.PeriodPlanTarget_wydolnoscanaerobowa, bgColor: 'rgb(112,111,111)', iconName: 'anaerob', whiteText: true } //
                        , { value: 7, shortcut: "P", text: translations.PeriodPlanTarget_moc, bgColor: '#454443', iconName: 'moc', whiteText: true } //rgb(29,29,27)
                    //  , { value: 8, shortcut: "T", text: translations.PeriodPlanTarget_test }
    ],

    AvailableProfiles: [
        {
            id: 0,
            name: translations.None,
            minLength: 1,
            periods: []
        },
        {
            id: 1,
            name: 'Standard',
            minLength: 17,
            periods: [
                { id: 1, typeId: 1, name: translations.PlanPeriod_przygotowawczy, startWeek: 1, endWeek: 3, targets: [2], secondaryTargets: [3] },
                { id: 2, typeId: 2, name: translations.PlanPeriod_podstawowy, startWeek: 4, endWeek: 9, targets: [2, 3, 4], secondaryTargets: [5] },
                { id: 3, typeId: 3, name: translations.PlanPeriod_rozbudowa, startWeek: 10, endWeek: 13, targets: [5, 6, 7], secondaryTargets: [2, 3, 4] },
                { id: 4, typeId: 4, name: translations.PlanPeriod_szczyt, startWeek: 14, endWeek: 14, targets: [5, 6, 7], secondaryTargets: [2] },
                { id: 5, typeId: 5, name: translations.PlanPeriod_wyscig, startWeek: 15, endWeek: 15, targets: [5, 6, 7], secondaryTargets: [2] },
                { id: 6, typeId: 6, name: translations.PlanPeriod_przejsciowy, startWeek: 16, endWeek: 17, targets: [2], secondaryTargets: [] },
            ]
        }
    ],
    */

    /*
    self.ValueTypes : [//{ value: "", text: translations.None }
                          { value: 'czas', useMinMax: true, text: translations.PlanValType_czas }
                        , { value: 'dystans', useMinMax: true, text: translations.PlanValType_dystans }
                        , { value: 'ilosc', useMinMax: true, text: translations.PlanValType_ilosc }
                        , { value: 'tetno', useMinMax: false, text: translations.PlanValType_tetno }
                        , { value: 'mleczan', useMinMax: false, text: translations.PlanValType_mleczan }],
    
    self.MinMaxValueTypes : [//{ value: "", text: translations.None }
                          { value: 'tetno', text: translations.PlanValType_tetno }
                        , { value: 'predkosc', text: translations.PlanValType_predkosc }
                        , { value: 'trudnosc', text: translations.PlanValType_trudnosc }
                        , { value: 'kadencja', text: translations.PlanValType_kadencja }
                        , { value: 'moc', text: translations.PlanValType_moc }
                        , { value: 'ciezar', text: translations.PlanValType_ciezar }]
    */
    Profiles: ko.observableArray([]),
    Inited: false,
    Loaded: false,
    ActiveProfileId: ko.observable(1)
};
TrainingPlanLib.SetActiveProfile = function (profileId) {
    var self = TrainingPlanLib;
    self.ActiveProfileId(profileId);
    $.jStorage.set('activePlanProfile', profileId, { TTL: 60000 });
}

TrainingPlanLib.LoadProfiles = function () {
    var self = TrainingPlanLib;
    if (!self.Profiles().length && !self.Inited) {
        self.Inited = true;
        cachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetTrainingPlanProfiles",
            async: false
        }, function (serverModel) {
            if (serverModel) {
                _.each(serverModel.profiles, function (prof) {
                    var p = {
                        id: prof.Id,
                        name: translations[prof.Name],
                        PeriodTypes: [],
                        Trgets: [],
                        AvailableProfiles: [],
                        LevelGroups: TrainingPlanLib.LevelGroups
                    };
                    _.each(prof.PeriodProfiles, function (elem) {
                        var pp = {
                            id: elem.Id,
                            name: translations[elem.Name],
                            minLength: elem.MinLength,
                            targetsData: elem.TargetsData
                        };
                        eval('pp=_.extend(pp,' + elem.JsonDescript + ');');
                        p.AvailableProfiles.push(pp);
                    });
                    _.each(prof.TrainingTargets, function (elem) {
                        p.Trgets.push({
                            value: elem.Id,
                            shortcut: elem.Shortcut,
                            text: translations[elem.Name],
                            bgColor: elem.BgColor,
                            iconName: elem.IconName,
                            whiteText: elem.WhiteText,
                            description: elem.DefalutDescriptionKey ? translations[elem.DefalutDescriptionKey] : ''
                        });
                    });
                    _.each(prof.PeriodTypes, function (elem) {
                        p.PeriodTypes.push({ value: elem.Id, text: translations[elem.Name] });
                    });


                    self.Profiles.push(p);
                });

                var activeProfile = $.jStorage.get('activePlanProfile');
                if (activeProfile) {
                    self.ActiveProfileId(activeProfile);
                } else {
                    self.ActiveProfileId(serverModel.activeProfileId);
                }
                self.Loaded = true;
            }
        }, 360000000 //1h
        );
    }
};

TrainingPlanLib.GetProfile = function (id) {
    var self = TrainingPlanLib;
    self.LoadProfiles();
    return _(self.Profiles()).findWhere({ id: id });
};
TrainingPlanLib.GetProfileForDate = function (date, userId, planId, callback) {
    var self = TrainingPlanLib;
    self.LoadProfiles();

    if (!planId) {
        if (typeof (trainingPlanManager) != 'undefined' && trainingPlanManager) {
            var plan = trainingPlanManager.ActivePlan();
            if (plan) planId = plan.Id();
        }
    }

    if (callback) {
        if (planId) {
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetActiveProfileForPlan",
                data: { planId: planId }
            },
            function (serverModel) {
                if (serverModel) {
                    var prof = self.GetProfile(serverModel);
                    if (callback) callback(prof);
                }
            }, 360000 //6m
            );
        } else {
            cachedAjaxCall({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetActiveProfileForDate",
                data: { userId: userId, date: date }
            },
            function (serverModel) {
                if (serverModel) {
                    var prof = self.GetProfile(serverModel);
                    if (callback) callback(prof);
                }
            }, 360000 //6m
            );
        }
    }
};

TrainingPlanLib.ClearCacheProfileForDate = function (date, userId, planId) {
    if (planId) {
        clearCachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetActiveProfileForPlan",
            data: { planId: planId }
        });
    } else {
        clearCachedAjaxCall({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetActiveProfileForDate",
            data: { userId: userId, date: date }
        });
    }
};

TrainingPlanLib.GetDefaultProfile = function () {
    var self = TrainingPlanLib;
    self.LoadProfiles();
    return self.GetProfile(self.ActiveProfileId());
};

TrainingPlanLib.PeriodTypes = ko.pureComputed(function () {
    var self = TrainingPlanLib;
    var prof = self.GetProfile(self.ActiveProfileId());
    if (!self.Inited || !self.Loaded || !prof) return [];
    return prof.PeriodTypes;
});
TrainingPlanLib.Trgets = ko.pureComputed(function () {
    var self = TrainingPlanLib;
    var prof = self.GetProfile(self.ActiveProfileId());
    if (!self.Inited || !self.Loaded || !prof) return [];
    return prof.Trgets;
});
TrainingPlanLib.AvailableProfiles = ko.pureComputed(function () {
    var self = TrainingPlanLib;
    var prof = self.GetProfile(self.ActiveProfileId());
    if (!self.Inited || !self.Loaded || !prof) return [];
    return prof.AvailableProfiles;
});

TrainingPlanLib.FindPeriodType = function (id) {
    var self = TrainingPlanLib;
    self.LoadProfiles();
    var result = null;
    _.each(self.Profiles(), function (prof) {
        _.each(prof.PeriodTypes, function (elem) {
            if (elem.id == id) result = elem;
        });
    });
    return result;
};
TrainingPlanLib.FindTrget = function (id) {
    var self = TrainingPlanLib;
    self.LoadProfiles();
    var result = null;
    _.each(self.Profiles(), function (prof) {
        _.each(prof.Trgets, function (elem) {
            if (elem.id == id) result = elem;
        });
    });
    return result;
};
TrainingPlanLib.FindProfile = function (id) {
    var self = TrainingPlanLib;
    self.LoadProfiles();
    var result = null;
    _.each(self.Profiles(), function (prof) {
        _.each(prof.AvailableProfiles, function (elem) {
            if (elem.id == id) result = elem;
        });
    });
    return result;
};

;var TrainingPlanWeekWidth = 60;
var TrainingPlanWeekHeight = 100;
var MinPeriodLength = 3;

planUploadBtnClick = function () {
	$("#trainingPlanExcel_fileupload").fileupload({
		dataType: 'json',
		add: function (e, data) {			
			data.submit();
			trainingPlanManager.ActivePlan().WaitImportState(true);
		},
		done: function (e, data) {
		    trainingPlanManager.ActivePlan().WaitImportState(false);
		    if (typeof (trainingPlanManager) != "undefined" && trainingPlanManager && trainingPlanManager.ReloadPlan) {
		        trainingPlanManager.ReloadPlan();
		    }
		}
	});
    $('#trainingPlanExcel_fileupload input').click();
};

///do drag&drop na obciazeniach planu
function DraggedTimePoint(parent, source, previousPoint) {
    var self = this;

    self.Parent = parent;
    self.Value = source;
    self.PreviousPoint = previousPoint;

    self.x2 = ko.computed(function () {
        return TrainingPlanWeekWidth * self.Parent.WeekIndex() + TrainingPlanWeekWidth / 2;
    });
    self.y2 = ko.computed(function () {
        return TrainingPlanWeekHeight - self.Value() * TrainingPlanWeekHeight / self.Parent.Parent.MaxLoadPLan();
    });
    self.x1 = ko.computed(function () {
        var p = self.PreviousPoint ? self.PreviousPoint() : null;
        if (!p) return self.x2();
        return p.x2();
    });
    self.y1 = ko.computed(function () {
        var p = self.PreviousPoint ? self.PreviousPoint() : null;
        if (!p) return self.y2();
        return p.y2();
    });

    self.StartY = 0;
    self.StartX = 0;
    self.StartValue = self.Value();
    self.selectedElement = 0;
    self.containerSelectedElement = 0;
    self.selectElement = function (data, evt) {
        self.selectedElement = $(evt.target);
        self.containerSelectedElement = self.selectedElement.closest('.loadContainer');
        self.StartY = evt.clientY;
        self.StartX = evt.clientX;
        self.StartValue = self.Value();

        self.containerSelectedElement.off("mousemove").on("mousemove", self.moveElement);
        $('body').off("mouseup").on("mouseup", self.deselectElement);
    }
    self.moveElement = function (evt) {
        var dy = self.StartY - evt.clientY;
        self.SetValue(self.StartValue + 1.0 * dy * self.Parent.Parent.MaxLoadPLan() / TrainingPlanWeekHeight);

        var dx = self.StartX - evt.clientX;
        if (dx > TrainingPlanWeekWidth || dx < -TrainingPlanWeekWidth) {
            self.deselectElement(evt);
        }
    }

    self.deselectElement = function (evt) {
        if (self.containerSelectedElement != 0) {
            self.containerSelectedElement.off("mousemove");
            self.containerSelectedElement.off("mouseout");
            $('document').off("mouseup");
            self.selectedElement = 0;
        }
    }

    self.ValueDisplay = ko.observable(HToShortString(self.Value()));
    self.Value.subscribe(function (newValue) {
        if (newValue) {
            var val = self.Value();
            var h = Math.floor(val);
            var mins = Math.round((val - h) * 60);
            self.ValueDisplay(HToShortString(newValue));
            if (val < 0 || val > 24 * 7) self.Value(0);

            self.Parent.DeferedSave();
        }
    });
    self.ValueDisplay.subscribe(function (d) {
        if (d && d.indexOf(':')) {
            var h = parseInt(d.substr(0, d.indexOf(':')));
            var m = parseInt(d.substr(d.indexOf(':') + 1));

            self.Value(h + m / 60.0);
        }
    });

    self.SetValue = function (newVal) {
        if (newVal < 0) newVal = 0;
        newVal = Math.round(newVal * 4) / 4;
        var max = self.Parent.Parent.MaxLoadPLan();
        if (newVal > max * 0.9) {
            self.Parent.Parent.MaxLoadPLan(max * 1.1);
        }
        self.Value(newVal);
    }
}

//#region cele/dni tygodnia/aktywnosci
//targets.js
//#endregion

///opis tygodnia treningow
//week.js

/*
PeriodTargetData = function (parent, target) {
    var self = this;
    self.Parent = parent;
    self.target = target;

    self.Percents = ko.observable('');
    self.Description = ko.observable('');
    self.IsSet =false;
    self.PercentsMode = ko.observable('0');
}
PeriodTargetData.prototype = {

}
*/
///cykl treningowy
//trainingPeriod.js

function JsGraphicEventModelSimple(serverModel) {
    var self = this;

    self.Id = ko.observable(serverModel.Id);
    self.Category = ko.observable(serverModel.Category);
    self.CategoryIcon = ko.observable(serverModel.CategoryIcon);
    self.Date = ko.observable(serverModel.Date);
    self.StartTime = ko.observable(serverModel.StartTime);
    self.EndTime = ko.observable(serverModel.EndTime);
    self.CoachFull = ko.observable(serverModel.CoachFull);
    self.ClubName = ko.observable(serverModel.ClubName);
    self.PlaceFull = ko.observable(serverModel.PlaceFull);
    self.ChangesHistory = ko.observable(new ChangesHistoryModel());

    if (serverModel.ChangesHistory) {
        self.ChangesHistory().initByModel(JSON.parse(serverModel.ChangesHistory));
    }
}

function EventStatusModel(serverModel) {
    var self = this;

    self.Status = ko.observable(serverModel.Status);
    self.PlaceFull = ko.observable(serverModel.PlaceFull);
    self.StartTime = ko.observable(serverModel.StartTime);
}

///obiekt jednej sesji treningowej
//unit.js

///opisowy cel planu
function PlanDescriptTargetModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Content = ko.observable('');


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Content(serverModel.Content);
        }
    };

    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            Content: self.Content(),
        }
        return data;
    };

    self.Remove = function () {
        self.Parent.SeasonTargets.remove(self);
        self.Parent.TrainingTargets.remove(self);
    }
}




function TrainingPlanExportModel(parent, mode) {
    var self = this;
    self.Parent = parent;

    self.Mode = mode;
    self.Range = ko.observable('All');
    self.Weeks = ko.observable('');

    self.exportPlan = function () {

        $('body').dynamicForm({
            ActionName: "UserCallendar/ExportTrainings",
            Parameters: {
                trainingPlanId: self.Parent.ActivePlanId(),
                userId: self.Parent.UserId,
                rangeMode: self.Range(),
                rangeParam: self.getParam()
            }
        });
    };

    self.getParam = function () {
        switch (self.Range()) {
            case 'All':
                return '';
            case 'Current':
                return self.Parent.SelectedUserTrainingWeek().PlanWeekNr();
            case 'Weeks':
                return self.Weeks();
        }
    };

}




//metka do nawigacji
$(document).ready(function () {
    initPersonSwitch();
});

function initPersonSwitch(selector, force) {
    //personsSwitch org
    //personsSwitch fixed
    if (!selector) selector = '.personsSwitch.org';
    var trainingNaviBelt = $(selector);
    if (!trainingNaviBelt.data("initiated") || force) {
        trainingNaviBelt.data("initiated", true);
        if (trainingNaviBelt.length > 0) {

            var scrolContainr =  $(window);

            var top = trainingNaviBelt.offset().top;
           // var left = trainingNaviBelt.offset().left;
            var width = trainingNaviBelt.width();


            scrolContainr.scroll(function (event) {
                // what the y position of the scroll is
                var y = $(this).scrollTop();

                var fixed = trainingNaviBelt.data("fixed");

                // whether that's below the form
                if (y >= top) {
                    if (!fixed) {
                       // left = trainingNaviBelt.offset().left;
                        trainingNaviBelt.data("fixed", true);
                        // if so, ad the fixed class
                        trainingNaviBelt.addClass('fixed');
                        trainingNaviBelt.css('width', width + 'px');
                       // trainingNaviBelt.css('right', 'auto');
                    }
                } else {
                    // otherwise remove it
                    if (fixed) {
                        trainingNaviBelt.data("fixed", false);
                        trainingNaviBelt.removeClass('fixed');
                       // trainingNaviBelt.css('right', '0px');
                         trainingNaviBelt.css('width', 'auto');
                    }
                }
            });
        }
    }
}





;//cele/dni tygodnia/aktywnosci
DetailedMatrixCell = function (cell, type) {
    var self = this;
    self.cell = cell;
    self.type = type;
    self.Selected = ko.computed(self.computeSelected, self);
}
DetailedMatrixCell.prototype = {
    computeSelected: function () {
        var self = this;
        return self.type ? self.cell.typeIds().indexOf(self.type.Id) >= 0 : false;
    },
    addType: function () {
        var self = this;

        var cellTypes = self.cell.typeIds();
        if (cellTypes.indexOf(self.type.Id) < 0) {
            cell.typeIds.push(self.type.Id);
        }
    },
    removeType: function () {
        var self = this;
        self.cell.typeIds.remove(self.type.Id);
    },
    toggleType: function () {
        var self = this;

        var cellTypes = self.cell.typeIds();
        if (cellTypes.indexOf(self.type.Id) < 0) {
            self.cell.typeIds.push(self.type.Id);
        } else {
            self.cell.typeIds.remove(self.type.Id);
        }
    },
}

TrainingTypeTargetDetails = function (type, subMatrix, targetMode) {
    var self = this;
    self.type = type;
    self.subMatrix = subMatrix;
    self.targetMode = targetMode;

    self.DetailedSubMatrix = _.map(subMatrix, function (elem) { return new DetailedMatrixCell(elem, self.type); });

    //add, remove, toggle type to subMatrix Cell
}
TrainingTypeMatrixCell = function (target, day, model) {
    var self = this;
    self.target = target;
    self.day = day;
    self.typeIds = ko.observableArray(model.typeIds);
    self.IsSet = model.isSet;
    self.typeIds.subscribe(function () {
        self.IsSet = true;
    });
}
TrainingTypeMatrixCell.prototype = {
    getClearData: function () {
        var self = this;
        return { targetId: self.target.value, typeIds: self.typeIds(), dayNr: self.day };
    }
}

///aktywnosci ktore trzeba rozwijac w danym tygodniu dla danej zdolnosci/celu
SingleWeekTargetData = function (target, model, manager, week, dayNr, subMatrix) {
    var self = this;
    var typeIds = model.typeIds;
    self.target = target;
    self.manager = manager;
    self.week = week;
    self.targetMode = dayNr == 0;
    //self.detailsMode = dayNr != 0 && target;
    self.dayNr = dayNr;

    self.subMatrix = subMatrix;

    self.AllTypesPrimary = ko.observable(false);
    self.AllTypesSecondary = ko.observable(false);

    self.IsInProfileMainTarget = ko.observable(false);
    self.IsInProfileSecondTarget = ko.observable(false);

    self.Percents = ko.observable(model.percents);
    self.Description = ko.observable(model.desc);
    self.IsSet = model.isSet;
    self.PercentsMode = ko.observable(model.percMode || (model.percents ? '0' : '3'));

    self.Percents.subscribe(function () { self.IsSet = true; });
    self.Description.subscribe(function () { self.IsSet = true; });
    self.PercentsMode.subscribe(function () { self.IsSet = true; if (self.PercentsMode() == '3' || self.PercentsMode() == 3) self.Percents(0); });
    self.PercentsModeDesc = ko.computed(function () {
        return translations['TargetsPercentsMode_' + self.PercentsMode()];
    });

    self.PercentsRight = ko.pureComputed(self.computePercentsRight, self).extend({ rateLimit: 200 });;

    self.typeIds = ko.observableArray([]);
    for (var i = 0, len = typeIds.length; i < len; i++) {
        var tId = typeIds[i];
        if (tId > 0) {
            self.typeIds.push(manager.TrainingTypeManager.GetTypeById(tId));
        } else {
            if (tId == -1) {
                self.AllTypesPrimary(true);
            } else if (tId == -2) {
                self.AllTypesSecondary(true);
            }
        }
    }
    self.AllTypeIds = ko.computed(self.computeAllTypeIds, self);

    //TODO: zrobić proste bindowania, które na podstawie numeru dnia pokażą nazwę
    self.dayName = translations['day_' + self.dayNr];
    self.dayShortCut = translations['dayShort_' + self.dayNr];

    _.bindAll(self, 'SetTypes', 'DeleteType', 'OpenAddType', 'getClearData', 'CopyToNextWeek', 'CopyProfileToTargets', 'togglePercentsMode');
}
SingleWeekTargetData.prototype = {
    computePercentsRight: function () {
        var self = this;
        var mode = self.PercentsMode();
        if (mode == '1') return 0;
        if (mode == '2') return 50;
        if (mode == '3') return 100;

        var perc = parseInt(self.Percents());
        if (isNaN(perc) || perc <= 0) return 100; //0

        var max = 0;
        if (self.week) {
            var td = self.week.TargetsData();
            if (td) { max = td.MaxPercents(); }
        }
        if (max == 0) max = perc;
        return 100 - (90.0 * perc / max + 10.0);
    },
    computeAllTypeIds: function () {
        var self = this;
        var types = _.reduce(self.subMatrix, function (memo, t) {
            if (t) {
                var tids = t.typeIds();
                for (var ti = 0; ti < tids.length; ti++) {
                    var tid = tids[ti];
                    if (memo.indexOf(tid) < 0) {
                        memo.push(tid);
                    }
                }
            }
            return memo;
        },
                _.map(self.typeIds(), function (t) { return t.Id; })
                );
        var result = [];

        for (var i = 0, len = types.length; i < len; i++) {
            var complexTpe = new TrainingTypeTargetDetails(self.manager.TrainingTypeManager.GetTypeById(types[i]), self.subMatrix, !self.targetMode);
            result.push(complexTpe);
        }
        return result;
    },
    SetTypes: function (typesManager) {
        var self = this;

        var types = typesManager.SelectedTypes();

        var oldTypes = self.AllTypeIds();
        self.typeIds.removeAll();
        for (var i = 0, len = types.length; i < len; i++) {
            var ti = types[i];
            self.typeIds.push(ti);

            var index = _.findIndex(oldTypes, function (elem) { return elem.type.Id == ti.Id });
            if (index > -1) {
                oldTypes.splice(index, 1);
            }
        }

        _.each(oldTypes, function (t) {
            _.each(self.subMatrix, function (sm) {
                sm.typeIds.remove(t.type.Id);
            });
        });
        self.IsSet = true;

        //self.week.DeferedSave();
    },
    DeleteType: function (type) {
        var self = this;

        if (type) {
            self.typeIds.remove(type);
            self.IsSet = true;
            self.week.DeferedSave();
        }
    },
    AddType: function (typeId) {
        var self = this;

        var _t_Iter = self.typeIds();
        for (var i = 0, len = _t_Iter.length; i < len; i++) {
            if (_t_Iter[i].Id == typeId) return;
        }

        manager.TrainingTypeManager.SelectTypeById(typeId);
        self.typeIds.push(manager.TrainingTypeManager.SelectedType());
        self.IsSet = true;
        self.week.DeferedSave();
    },
    OpenAddType: function () {
        var self = this;

        self.manager.openAddType(self.SetTypes, self);
    },
    getClearData: function () {
        var self = this;

        var _t = [];
        var _t_Iter = self.AllTypeIds();
        for (var i = 0, len = _t_Iter.length; i < len; i++) {
            _t.push(_t_Iter[i].type.Id);
        }
        if (self.AllTypesPrimary()) _t.push(-1);
        if (self.AllTypesSecondary()) _t.push(-2);

        var dayNr = self.targetMode ? 0 : self.dayNr;
        var targetId = self.targetMode ? self.target.value : 0;

        return { targetId: targetId, typeIds: _t, dayNr: dayNr, percents: self.Percents(), desc: self.Description(), percMode: self.PercentsMode() };
    },
    CopyToNextWeek: function () {
        var self = this;

        var nWeek = self.week.NextWeek();
        if (nWeek) {
            var st = null;
            if (self.dayNr == 0) {
                var _targets = _(nWeek.TargetsData().Targets());
                st = _targets.find(function (weekTarget) { return weekTarget.target.value == self.target.value; });
            } else {
                var _days = _(nWeek.TargetsData().DaysActiTypes());
                st = _days.find(function (weekTarget) { return weekTarget.dayNr == self.dayNr; });
            }
            if (st) {
                st.subMatrix = self.subMatrix.slice();
                st.typeIds.removeAll();
                var types = self.typeIds();
                for (var i = 0, len = types.length; i < len; i++) {
                    var ti = types[i];
                    st.typeIds.push(ti);
                }
                st.IsSet = true;
            }
        }
        return false;
    },
    CopyProfileToTargets: function (override) {
        var self = this;

        if (override) {
            self.AllTypesPrimary(self.IsInProfileMainTarget());
            self.AllTypesSecondary(self.IsInProfileSecondTarget());
        } else {
            self.AllTypesPrimary(self.AllTypesPrimary() || self.IsInProfileMainTarget());
            self.AllTypesSecondary(self.AllTypesSecondary() || self.IsInProfileSecondTarget());
        }
    },
    togglePercentsMode: function () {
        var self = this;
        self.PercentsMode((parseInt(self.PercentsMode()) + 1) % 4);
    }
}

///cele treningowe dla danego tygodnia
TargetsData = function (data, manager, week, customData) {
    var self = this;

    self.Targets = ko.observableArray([]);
    self.DaysActiTypes = ko.observableArray([]);
    self.Matrix = [];
    self.manager = manager;
    self.week = week;
    self.data = null;
    self.customData = null;

    self.Description = ko.observable('');

    self.MaxPercents = ko.computed(self.computeMaxPercents, self);

    self.initData(data, customData);

    /*
        self.PrimaryTargets = ko.computed(function () {
            return _.filter(self.Targets(), function (elem) { return elem.AllTypesPrimary(); });
        });
        self.SecondaryTargets = ko.computed(function () {
            return _.filter(self.Targets(), function (elem) { return elem.AllTypesSecondary(); });
        });
    */

    _.bindAll(self,
'getClearData',
'getSringify',
'SetProfile',
'ClearProfile',
'SetTargets',
'CopyTargetsToNextWeek',
'CopyProfileToTargets',
'EditInfo',
'CopyInfoToNextWeek',
'initData');
}
TargetsData.prototype = {
    computeMaxPercents: function () {
        var self = this;
        var max = _.max(self.Targets(), function (elem) { return parseInt(elem.Percents()); });
        if (max != Number.NEGATIVE_INFINITY) return max.Percents();
        return 0;
    },
    initData: function (data, customData) {
        var self = this;

        self.data = data;
        self.customData = customData;
        self.initDataObj(data ? JSON.parse(data) : [], customData ? JSON.parse(customData) : []);
    },
    initDataObj: function (dataObj, customDataObj) {
        var self = this;

        self.ParsedData = dataObj || [];
        if (_.isArray(self.ParsedData)) {
            self._array = _(self.ParsedData);
        } else {
            self._array = _(self.ParsedData.Array);
        }

        self.CustomParsedData = customDataObj || [];
        if (_.isArray(self.CustomParsedData)) {
            self._customArray = _(self.CustomParsedData);
        } else {
            self._customArray = _(self.CustomParsedData.Array);
        }

        self.Description((self.CustomParsedData ? self.CustomParsedData.Description : null) || self.ParsedData.Description);


        self.Matrix.length = 0;
        self.Targets.removeAll();
        self.DaysActiTypes.removeAll();

        var ts = self.manager.Trgets();

        if (ts) {
            for (var i = 0, len = ts.length; i < len; i++) {
                var subArr = [];
                for (var day = 1; day <= 7; day++) {
                    var model;
                    var typeIds = self._customArray.findWhere({ targetId: ts[i].value, dayNr: day }) || self._array.findWhere({ targetId: ts[i].value, dayNr: day });
                    if (!typeIds) {
                        model = { typeIds: [], isSet: false }
                    } else {
                        model = typeIds;
                        model.isSet = true;
                    }

                    subArr.push(new TrainingTypeMatrixCell(ts[i], day, model));
                }
                self.Matrix.push(subArr);
            }

            for (var i = 0, len = ts.length; i < len; i++) {
                var model;
                var typeIds = self._customArray.findWhere({ targetId: ts[i].value, dayNr: 0 }) || self._array.findWhere({ targetId: ts[i].value, dayNr: 0 });
                if (!typeIds) {
                    model = { typeIds: [], percents: 0, desc: '', isSet: false }
                } else {
                    model = typeIds;
                    model.isSet = true;
                }
                var t = new SingleWeekTargetData(ts[i], model, self.manager, self.week, 0, self.Matrix[i]);

                self.Targets.push(t);
            }
        }
        for (var i = 1; i <= 7; i++) {
            var model;
            var typeIds = self._customArray.findWhere({ dayNr: i, targetId: 0 }) || self._array.findWhere({ dayNr: i, targetId: 0 });
            if (!typeIds) {
                model = { typeIds: [], percents: 0, desc: '', isSet: false }
            } else {
                model = typeIds;
                model.isSet = true;
            }

            var subMatrix = [];
            if (ts) {
                for (var tid = 0, len = ts.length; tid < len; tid++) {
                    subMatrix.push(self.Matrix[tid][i - 1]);
                }
            }

            var t = new SingleWeekTargetData(ts[i], model, self.manager, self.week, i, subMatrix);

            self.DaysActiTypes.push(t);
        }
    },
    getClearData: function () {
        var self = this;

        var _t = [];
        var _t_Iter = self.Targets();
        for (var i = 0, len = _t_Iter.length; i < len; i++) {
            var u = _t_Iter[i];
            if (u.IsSet) _t.push(u.getClearData());
        }
        _t_Iter = self.DaysActiTypes();
        for (var i = 0, len = _t_Iter.length; i < len; i++) {
            var u = _t_Iter[i];
            if (u.IsSet) _t.push(u.getClearData());
        }

        for (var t_it = 0, t_len = self.Matrix.length; t_it < t_len; t_it++) {
            var subMat = self.Matrix[t_it];
            for (var d_it = 0, d_len = subMat.length; d_it < d_len; d_it++) {
                var elem = subMat[d_it];
                if (elem.IsSet) _t.push(elem.getClearData());
            }
        }

        return { Array: _t, Description: self.Description() };
    },
    getSringify: function () {
        var self = this;

        return JSON.stringify(self.getClearData());
    },
    SetProfile: function (profile) {
        var self = this;

        var _targets = _(self.Targets());
        //self.ClearProfile(_targets);
        /*
        _(profile.MainTargets()).each(function (profileTargetId) {
            var matchedSWTarget = _targets.find(function (weekTarget) { return weekTarget.target.value == profileTargetId; });
            if (matchedSWTarget) { matchedSWTarget.IsInProfileMainTarget(true); }
        });
        _(profile.SecondaryTargets()).each(function (profileTargetId) {
            var matchedSWTarget = _targets.find(function (weekTarget) { return weekTarget.target.value == profileTargetId; });
            if (matchedSWTarget) { matchedSWTarget.IsInProfileSecondTarget(true); }
        });*/
    },
    ClearProfile: function (_targets) {
        var self = this;

        if (!_targets) {
            _targets = _(self.Targets());
        }
        _targets.each(function (e) {
            e.IsInProfileMainTarget(false);
            e.IsInProfileSecondTarget(false);
        });
    },
    SetTargets: function () {
        var self = this;
        self.manager.openAddTarget(self);
    },
    CopyTargetsToNextWeek: function () {
        var self = this;
        var nWeek = self.week.NextWeek();
        if (nWeek) {
            nWeek.TargetsData().initDataObj(self.getClearData());
            var _selfTargets = _(self.Targets());
            var _targets = _(nWeek.TargetsData().Targets());
            _targets.each(function (st) {
                selfT = _selfTargets.find(function (weekTarget) { return weekTarget.target.value == st.target.value; });

                st.AllTypesPrimary(selfT.AllTypesPrimary());
                st.AllTypesSecondary(selfT.AllTypesSecondary());
            });
        }
        return false;
    },
    CopyProfileToTargets: function (override) {
        var self = this;
        _.each(self.Targets(), function (t) { t.CopyProfileToTargets(override); });
    },
    EditInfo: function () {
        var self = this;
        self.manager.OpenWeekEdit(self.week);
    },
    CopyInfoToNextWeek: function () {
        var self = this;

        var nWeek = self.week.NextWeek();
        if (nWeek) {
            nWeek.TargetsData().initDataObj(self.getClearData());
        }
        return false;
    }
}
;///opis tygodnia treningow
UserTrainingWeekModel = function (parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.UserId = ko.observable(0);
    self.TrainingPlanId = ko.observable(0);
    self.StatsDataCache = ko.observable(null);
    self.CustomLoadPlan = ko.observable(0);
    self.StartDate = ko.observable('');
    self.TimeLoadPlan = ko.observable(0);
    self.TrimpLoadPlan = ko.observable(0);
    self.UserAvability = ko.observable(0);
    self.WeekNr = ko.observable(0);
    self.MonthNr = ko.observable(0);
    self.Year = ko.observable(0);
    self.InMonthDays = ko.observable(0);
    self.TargetsData = ko.observable(null);
    self.PlanWeekNr = ko.observable(0);

    self.Test = ko.observable(false);
    self.Camp = ko.observable(false);
    self.Competition = ko.observable(false);
    self.CompetitionPriority = ko.observable(0);

    self.PauseDaysArray = [ko.observable(false), ko.observable(false), ko.observable(false), ko.observable(false), ko.observable(false), ko.observable(false), ko.observable(false)];
    self.PauseDaysUnassigned = ko.observable(0);

    self.PreviousWeek = ko.observable(null);
    self.PreviousWeek.subscribe(function (newValue) {
        if (newValue) {
            self.PreviousWeek().NextWeek(self);
        }
    });
    self.NextWeek = ko.observable(null);
    self.WeekIndex = ko.observable(0);

    self.Competitions = ko.observableArray([]);
    self.OnlyPlanCompetitions = ko.observable(false);
    self.MaxPrio = ko.observable(1);


    self.StartDateObj = ko.computed(function () {
        return new Date(self.StartDate());
    });
    self.EndDateObj = ko.computed(function () {
        var ed = new Date(6 * 24 * 60 * 60 * 1000 + self.StartDateObj().valueOf());
        var red = new Date(ed.getFullYear(), ed.getMonth(), ed.getDate(), 23, 59, 59);
        return red;
    });
    self.EndDate = ko.pureComputed(function () {
        return dateToString(self.EndDateObj());
    });

    self.MonthName = ko.pureComputed(function () {
        return translations['monthNr_' + self.MonthNr()];
    });

    self.CurrentLoadPLan = ko.computed(function () {
        return self.TimeLoadPlan();
    });

    self.PlanLoadTop = ko.computed(function () {
        if (!self.StatsDataCache()) return 100;

        var max = self.Parent.MaxLoadPLan() * 3600;
        var val = self.StatsDataCache().PTime;
        if (val > max * 0.9) {
            self.Parent.MaxLoadPLan(val / 3600 * 1.1);
        }
        return 100 - val / (self.Parent.MaxLoadPLan() * 36);
    });
    self.ExecutionLoadTop = ko.computed(function () {
        if (!self.StatsDataCache()) return 100;

        var max = self.Parent.MaxLoadPLan() * 3600;
        var val = self.StatsDataCache().TTime;
        if (val > max * 0.9) {
            self.Parent.MaxLoadPLan(val / 3600 * 1.1);
        }
        return 100 - val / (self.Parent.MaxLoadPLan() * 36);
    });

    self.PlanLoadDisplay = ko.computed(function () {
        if (!self.StatsDataCache() || !self.StatsDataCache().PTime) return '';
        return SToShortString(self.StatsDataCache().PTime);
    });
    self.ExecutionLoadDisplay = ko.computed(function () {
        if (!self.StatsDataCache() || !self.StatsDataCache().TTime) return '';
        return SToShortString(self.StatsDataCache().TTime);
    });

    self.WeekNrDisplay = ko.pureComputed(function () {
        var w = self.WeekNr();
        var p = self.PlanWeekNr();
        if (w) {
            return '' + w + (self.PlanWeekNr() > 0 ? ' / ' + self.PlanWeekNr() : '');
        } else {
            return '' + p;
        }
    });

    self.LoadDisplayCompetitions = ko.pureComputed(function () {
        var coms = self.Competitions();
        return _.find(coms, function (c) {
            d = new Date(c.DateStr) > new Date();
            return c.CompetitionAdded || d;
        });
    });

    self.CurrentLoadPlanPoint = ko.observable(null);
    self.UserAvabilityPoint = ko.observable(null);

    self.BaseObj = null;
    self.DeferedSaveTimeout = null;
    self.IsCallendarSelected = ko.computed(function () {
        var result = false;
        if (userCallendar) {
            if (userCallendar.IgnoreDates()) {
                result = userCallendar.CalendarWeek() == self.PlanWeekNr();
            } else {
                result = userCallendar.CalendarDateObj() >= self.StartDateObj() && userCallendar.CalendarDateObj() <= self.EndDateObj();
            }
        }
        if (result) {
            //userCallendar.SelectedUserTrainingWeek(self);
            var selCol = $('#weekCol_' + self.StartDate() + '_' + self.PlanWeekNr());
            if (selCol.length) {
                self.ScrollToWeek(selCol, true);
            } else {
                setTimeout(self.ScrollToWeek, 700);
            }
        }

        return result;
    });
    self.IgnoreDates = ko.computed(function () {
        return userCallendar && userCallendar.IgnoreDates();
    });

    _.bindAll(self, 'InitPoints', 'CancelChanges', 'initByModel', 'getClearData', 'DeferedSave', 'Save', 'SelectWeek', 'ScrollToWeek', 'SetProfile', 'ClearProfile', 'getSringify', 'CopyProfileToTargets');
}
UserTrainingWeekModel.prototype = {

    InitPoints: function () {
        var self = this;

        self.CurrentLoadPlanPoint(new DraggedTimePoint(self, self.TimeLoadPlan, self.PreviousWeek() ? self.PreviousWeek().CurrentLoadPlanPoint : null));
        self.UserAvabilityPoint(new DraggedTimePoint(self, self.UserAvability, self.PreviousWeek() ? self.PreviousWeek().UserAvabilityPoint : null));
    },

    CancelChanges: function () {
        var self = this;

        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    },

    initByModel: function (serverModel) {
        var self = this;

        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.UserId(serverModel.UserId);
            self.TrainingPlanId(serverModel.TrainingPlanId);
            self.CustomLoadPlan(serverModel.CustomLoadPlan);
            self.StartDate(serverModel.StartDate);
            self.TimeLoadPlan(serverModel.TimeLoadPlan);
            self.TrimpLoadPlan(serverModel.TrimpLoadPlan);
            self.UserAvability(serverModel.UserAvability);
            self.WeekNr(serverModel.WeekNr);
            self.MonthNr(serverModel.MonthNr);
            self.Year(serverModel.Year);
            self.InMonthDays(serverModel.InMonthDays);
            self.TargetsData(new TargetsData(serverModel.TargetsData, self.Parent, self, serverModel.CustomTargetsData));
            self.PlanWeekNr(serverModel.PlanWeekNr);

            self.Test(serverModel.Test);
            self.Camp(serverModel.Camp);
            self.Competition(serverModel.Competition);
            self.CompetitionPriority(serverModel.CompetitionPriority);

            self.PauseDaysUnassigned(serverModel.PauseDaysUnassigned);
            if (serverModel.PauseDaysArray && serverModel.PauseDaysArray.length) {
                var pda = serverModel.PauseDaysArray.split(';');
                for (var i = 0; i < pda.length && i < 7; i++) {
                    self.PauseDaysArray[i](pda[i] == '1');
                }
            }

            koArrayCopyComplex(serverModel.Competitions, self.Competitions, false, function (elem) {
                elem.InUserPlans = ko.observable(elem.UserPeriodPlanId != 0);
                elem.PeriodPlanModel = null;
                elem.UpdateVisibility = function () { }
                elem.ParamsVisibility = function () { return null; }
                elem.ShowPlanOnUserCal = function () { elem.InUserPlans(true); }
                elem.AddPlanToCallendar = function () {
                    if (!elem.PeriodPlanModel) {
                        elem.PeriodPlanModel = new PeriodPlanModel(elem);
                    }
                    if (elem.UserPeriodPlanId) elem.PeriodPlanModel.Load(elem.UserPeriodPlanId);
                    elem.PeriodPlanModel.OpenEdit();
                };
                elem.LogoUrl = ContextPath + "Image/GetImage?width=1000&height=600&id=" + elem.LogoId;
                elem.HasLogo = elem.LogoId != 0;
                return elem;
            });
            self.OnlyPlanCompetitions(_.every(serverModel.Competitions, function (elem) { return elem.OnlyPlan; }));
            var max = _.chain(serverModel.Competitions).filter(function (x) { return x.Priority && x.Priority != '0'; }).min(function (elem) { return elem.Priority; }).value(); //1 jest najwyzsze
            if (max) self.MaxPrio(max.Priority);

            var cache = JSON.parse(serverModel.StatsDataCache)
            self.StatsDataCache(cache);

            /*
            var max = self.Parent.MaxLoadPLan() * 3600;
            if (cache.PTime > max * 0.9) {
                self.Parent.MaxLoadPLan(cache.PTime / 3600 * 1.1);
            }
            if (cache.TTime > max * 0.9) {
                self.Parent.MaxLoadPLan(cache.TTime / 3600 * 1.1);
            }
            */
        }
    },

    getClearData: function () {
        var self = this;

        var pda = '';
        for (var i = 0; i < self.PauseDaysArray.length && i < 7; i++) {
            pda += self.PauseDaysArray[i]() ? '1;' : '0;';
        }

        var data = {
            Id: self.Id(),
            UserId: self.UserId() || self.Parent.UserId,
            TrainingPlanId: self.TrainingPlanId(),
            //StatsDataCache: self.StatsDataCache(),
            CustomLoadPlan: self.CustomLoadPlan(),
            StartDate: self.StartDate(),
            TimeLoadPlan: self.TimeLoadPlan(),
            TrimpLoadPlan: self.TrimpLoadPlan(),
            UserAvability: self.UserAvability(),
            TargetsData: self.TargetsData().getSringify(),
            Test: self.Test(),
            Camp: self.Camp(),
            Competition: self.Competition(),
            CompetitionPriority: self.CompetitionPriority(),
            PlanWeekNr: self.PlanWeekNr(),
            PauseDaysArray: pda,
            PauseDaysUnassigned: parseInt(self.PauseDaysUnassigned())
        }
        return data;
    },

    getSringify: function () {
        var self = this;

        return JSON.stringify(self.getClearData());
    },

    DeferedSave: function () {
        var self = this;

        if (self.DeferedSaveTimeout) clearTimeout(self.DeferedSaveTimeout);
        self.DeferedSaveTimeout = setTimeout(self.Save, 1500);
    },
    Save: function () {
        var self = this;

        if (self.DeferedSaveTimeout) clearTimeout(self.DeferedSaveTimeout);

        var data = self.getSringify();
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetWeek",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data
        })
        .done(function (serverModel) {
            //self.initByModel(result);
            if (serverModel) {
                self.BaseObj = serverModel;
                self.Id(serverModel.Id);
            }
        });
    },

    SelectWeek: function () {
        var self = this;

        if (userCallendar) {
            if (self.StartDate()) {
                userCallendar.CalendarDate(self.StartDate());
            } else {
                userCallendar.CalendarWeek(self.PlanWeekNr());
            }
        }
    },

    ScrollToWeek: function (selCol, notMiddle) {
        var self = this;

        if (!selCol || !selCol.length) {
            selCol = $('#weekCol_' + self.StartDate() + '_' + self.PlanWeekNr());
        }
        if (selCol.length) {
            var container = $('.calBody.plan .loadContainer');
            var contWidth = container.width();
            var margin = notMiddle ? contWidth / 4 : contWidth / 2;
            var scrollTo = -1;
            if (selCol.position().left > contWidth - margin) {
                scrollTo = container.scrollLeft() + selCol.position().left - contWidth + margin;
            } else if (selCol.position().left < margin) {
                scrollTo = container.scrollLeft() - (margin - selCol.position().left);
            }
            if (scrollTo >= 0) container.scrollTo(scrollTo, { duration: 250, easing: 'swing' });
        }
    },

    SetProfile: function (profile) {
        var self = this;

        var td = self.TargetsData();
        if (td) td.SetProfile(profile);
    },
    ClearProfile: function () {
        var self = this;

        var td = self.TargetsData();
        if (td) td.ClearProfile();
    },

    CopyProfileToTargets: function (override) {
        var self = this;

        var td = self.TargetsData();
        if (td) td.CopyProfileToTargets(override);

        self.DeferedSave();
    },
};///cykl treningowy
function TrainingPeriodPlanModel(parent) {
    var self = this;
    self.Parent = parent;

    _.bindAll(self, /*'registerList', 'ReloadPlanLists',*/ 'CancelChanges', 'initByModel', 'getClearData', 'getSringify', 'Save', 'Edit', 'ApplayToUserWeeks',
        'CopyProfileToTargets', 'CopyProfileToTargetsBtn', 'Delete', 'coreDelete',
        'selectLeftElement', 'selectRightElement', 'selectElement', 'moveElement', 'deselectElement', 'shiftPeriod');

    self.saveToken = 0;

    self.Id = ko.observable(0);
    self.InstanceId = ko.observable(0);
    self.PeriodTypeId = ko.observable(0);
    self.PlanId = ko.observable(0);
    self.StartWeek = ko.observable(0);
    self.StartDay = ko.observable(0);
    self.UserId = ko.observable(0);
    self.Days = ko.observable(0);
    self.Name = ko.observable('');
    self.PeriodTarget = ko.observable('');
    self.StartDate = ko.observable('');
    self.EndDate = ko.observable('');
    /*
    self.MainTargets = ko.observableArray([]);
    self.SecondaryTargets = ko.observableArray([]);

    
    self.AvailableMainTrgets = ko.observableArray([]);
    self.AvailableSecondaryTrgets = ko.observableArray([]);
*/
    self.AvailablePeriodTypes = ko.observableArray([]);
    self.TargetsData = ko.observable(new TargetsData('', self.Parent, null, ''));
    //self.Trgets = ko.observableArray([]);
    //self.InitTargetsList();
    self.ReloadPlanLists();

    self.PlanProfileId = self.Parent.ActiveTrainingPlanProfileId;
    self.PlanProfileId.subscribe(function (newValue) {
        if (newValue) {
            self.ReloadPlanLists();
        }
    });

    self.DisplayName = ko.computed(function () {
        var name = self.Name();
        if (name) return name;
        if (self.PeriodTypeId() > 0) {
            var tt = _(TrainingPlanLib.PeriodTypes()).findWhere({ value: self.PeriodTypeId() });
            if (tt) return tt.text;
        }
        return '';
    });

    self.TemplateMode = ko.computed(function () {
        return userCallendar && userCallendar.IgnoreDates();
    });
    self.InstanceMode = ko.computed(function () {
        return !userCallendar || !userCallendar.IgnoreDates();
    });

    self.StartDateObj = ko.computed(function () {
        return new Date(self.StartDate());
    });
    self.EndDateObj = ko.computed(function () {
        return new Date(self.EndDate());
    });

    self.WeekShift = ko.computed(function () {
        if (self.InstanceMode()) {
            var startDate = self.StartDate();
            if (startDate) {
                var a = new Date(self.Parent.ManagerStartDate());
                var b = new Date(startDate);

                var utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
                var utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());

                var weeks = (utc2 - utc1) / (1000 * 60 * 60 * 24 * 7);
                return Math.round(weeks * 10000) / 10000;
            }
        }
        return self.StartWeek() + (self.StartDay()-1)/7.0;
    });

    self.Left = ko.computed(function () {
        return self.WeekShift() * TrainingPlanWeekWidth;
    });
    self.Width = ko.computed(function () {
        return self.Days() * TrainingPlanWeekWidth / 7;
    });

    self.WidthDisplay = ko.computed(function () {
        var d = self.Days();
        var weeksNr = Math.floor(d / 7);
        var daysNr = d % 7;
        //return '' + self.Days() + (self.Days() == 1 ? translations.oneDay : translations.days);
        var result = '';
        if (weeksNr > 0) result += '' + weeksNr + translations.weekShortcut;
        if (weeksNr > 0 && daysNr > 0) result += ' ';
        if (daysNr > 0) result += '' + daysNr + translations.dayShortcut;
        return result;
    });


    this.NrOfWeeks = ko.pureComputed({
        read: function () {
            if (self.TemplateMode()) {
                var ds = self.Days();
                return Math.ceil(ds / 7);
            } else {
                var s = self.StartDateObj();
                var e = self.EndDateObj();
                var diff = e.getTime() - s.getTime();
                var weeks = Math.ceil(diff / 604800000);//1000 * 3600 * 24 * 7

                return weeks;
            }
        },
        write: function (value) {
            if (self.TemplateMode()) {
                var ds = self.Days();
                self.Days(value*7);
            } else {
                var s = self.StartDateObj();
                var diff = s.getTime();
                var weeks = parseInt(value);
                if (!isNaN(weeks)) {
                    diff += 604800000 * weeks - (1000 * 3600 * 24);
                    var e = new Date(diff);
                    self.EndDate(dateToString(e));
                }
            }
        },
        owner: self
    });

    self.PeriodInfo = ko.computed(function () {
        return '' + self.StartDate() + ' &bull; ' + self.WidthDisplay() + ' &bull; ' + self.EndDate();
    });

    self.CanEdit = ko.computed(function () {
        return self.Parent.CanEditActivePlan();
    });

    self.BaseObj = null;

    //#region darag & drop
    self.DragMode = ko.observable(false);
    self.StartX = 0;
    self.StartStartValue = '';
    self.StartEndValue = '';
    self.StartDaysValue = '';
    self.StartDayValue = '';
    self.StartWeekValue = '';
    self.containerSelectedElement = null;
    self.selectedLeft = ko.observable(false);

    self.nextPeriods = [];
    self.previousPeriods = [];
    self.nextMargin = null;
    self.prevMargin = null;
    self.nextShift = 0;
    self.prevShift = 0;
    //#endregion


}
TrainingPeriodPlanModel.prototype = {

    registerList: function (allItems, selIds, newList, multi) {
        var self = this;
        newList.removeAll();
        if (multi) {
            for (var i = 0, len = allItems.length; i < len; i++) {
                var elem = allItems[i];
                var newElem = jQuery.extend({}, elem);
                newElem.Selected = ko.computed(function () {
                    return $.inArray(this.value, selIds()) >= 0;
                }, newElem);
                newElem.Select = function () {
                    var index = $.inArray(this.value, selIds());
                    if (index >= 0) {
                        selIds.splice(index, 1);
                    } else {
                        selIds.push(this.value);
                    }
                }
                newList.push(newElem);
            }
        } else {
            for (var i = 0, len = allItems.length; i < len; i++) {
                var elem = allItems[i];
                var newElem = jQuery.extend({}, elem);
                newElem.Selected = ko.computed(function () {
                    return selIds() == this.value;
                }, newElem);
                newElem.Select = function () {
                    selIds(this.value);
                }
                newList.push(newElem);
            }
        }
    },

    ReloadPlanLists: function () {
        var self = this;
        var profile = TrainingPlanLib.GetDefaultProfile();
        if (profile) {
            self.registerList(profile.PeriodTypes, self.PeriodTypeId, self.AvailablePeriodTypes, false);
            //self.registerList(profile.Trgets, self.MainTargets, self.AvailableMainTrgets, true);
            //self.registerList(profile.Trgets, self.SecondaryTargets, self.AvailableSecondaryTrgets, true);
        }
    },

    InitTargetsList: function () {
        var self = this;
        self.TargetsData(new TargetsData('', self.Parent, null, null));
    },

    CancelChanges: function () {
        var self = this;
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    },

    initByModel: function (serverModel) {
        var self = this;
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.InstanceId(serverModel.InstanceId);
            self.PeriodTypeId(serverModel.PeriodTypeId);
            self.PlanId(serverModel.PlanId);
            self.StartWeek(serverModel.StartWeek);
            self.StartDay(serverModel.StartDay );
            self.UserId(serverModel.UserId);
            self.Days(serverModel.Days);
            self.Name(serverModel.Name);
            self.StartDate(serverModel.StartDate);
            self.EndDate(serverModel.EndDate);
            /*
            var pt = JSON.parse(serverModel.PeriodTarget);
            self.PeriodTarget(pt.Description);
            var _targets = _(self.Trgets());
            _.each(pt.Array, function (t) {
                var target = _targets.find(function (elem) { return elem.target.value == t.targetId;});
                if (target){

                }
            });*/
            self.TargetsData().initData(serverModel.PeriodTarget, serverModel.CustomPeriodTarget);

            /*
            self.MainTargets.removeAll();
            if (serverModel.MainTargets) {
                for (var i = 0, len = serverModel.MainTargets.length; i < len; i++) {
                    self.MainTargets.push(serverModel.MainTargets[i]);
                }
            }

            self.SecondaryTargets.removeAll();
            if (serverModel.SecondaryTargets) {
                for (var i = 0, len = serverModel.SecondaryTargets.length; i < len; i++) {
                    self.SecondaryTargets.push(serverModel.SecondaryTargets[i]);
                }
            }*/
        }
    },

    getClearData: function () {
        var self = this;
        //var _MainTargets = []; var _MainTargets_Iter = self.MainTargets(); for (var i = 0, len = _MainTargets_Iter.length; i < len; i++) { _MainTargets.push(_MainTargets_Iter[i]); }
        //var _SecondaryTargets = []; var _SecondaryTargets_Iter = self.SecondaryTargets(); for (var i = 0, len = _SecondaryTargets_Iter.length; i < len; i++) { _SecondaryTargets.push(_SecondaryTargets_Iter[i]); }
        var data = {
            Id: self.Id(),
            InstanceId: self.InstanceId(),
            PeriodTypeId: self.PeriodTypeId(),
            PlanId: self.PlanId(),
            StartWeek: self.StartWeek(),
            StartDay: self.StartDay(),
            UserId: self.UserId(),
            Days: self.Days(),
            Name: self.Name(),
            PeriodTarget: self.PeriodTarget(),
            StartDate: self.StartDate(),
            EndDate: self.EndDate(),
            //MainTargets: _MainTargets,
            //SecondaryTargets: _SecondaryTargets,
            PeriodTarget: self.TargetsData().getSringify()
        }
        return data;
    },

    getSringify: function () {
        var self = this;
        return JSON.stringify(self.getClearData());
    },

    Save: function (data, evt, bulk) {
        var self = this;
        var data = self.getSringify();
        var isNew = self.Id() == 0;
        var saveToken = ++self.saveToken; //zeby nie nadpisywac jezeli jest duzo zapisów, np podczas przesówania
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetPeriod",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data
        })
        .done(function (serverModel) {
            //self.initByModel(result);
            if (serverModel && saveToken == self.saveToken) {
                //self.BaseObj = serverModel;
                //self.Id(serverModel.Id);
                self.initByModel(serverModel);
                if (serverModel && !bulk) {
                    if (isNew) {
                        self.Parent.PlanPeriods.push(self);
                    }
                    self.Parent.ApplayPeriodsToUserWeeks();
                }
            }
            $('#planPeriodEditorModal').modal('hide');
        });
    },

    Edit: function () {
        var self = this;
        if (self.DragMode()) return;

        self.Parent.currentPlanPeriod(self);

        var container = $('#planPeriodEditModalContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: $('#planPeriodEditorModal'),
            ajaxPath: 'TrainingPlan/ModalPeriodEditor',
            bindData: null,
            callback: function () {
                container.find('#planPeriodEditorModal').modal('show');
            }
        });
    },

    ApplayToUserWeeks: function (_weeks) {
        var self = this;
        if (!_weeks) {
            _weeks = _(self.Parent.UserTrainingWeeks());
        }
        var start = self.StartDateObj();
        var end = self.EndDateObj();
        var validWeeks = _.chain(_weeks).filter(function (e) {
            return e.StartDateObj() < end && e.EndDateObj() > start;
        }).each(function (w) {
            w.SetProfile(self);
        });
    },

    CopyProfileToTargetsBtn: function () {
        var self = this;
        self.CopyProfileToTargets(null, true);
    },
    CopyProfileToTargets: function (_weeks, override) {
        var self = this;
        if (!_weeks) {
            _weeks = _(self.Parent.UserTrainingWeeks());
        }
        var start = self.StartDateObj();
        var end = self.EndDateObj();

        var data = self.TargetsData().getClearData();
        //var customData = self.TargetsData().customData;

        var validWeeks = _.chain(_weeks).filter(function (e) {
            return e.StartDateObj() < end && e.EndDateObj() > start;
        }).each(function (w) {
            //w.SetProfile(self);
            //w.CopyProfileToTargets(override);

            w.TargetsData().initDataObj(data);
        });
    },

    Delete: function () {
        var self = this;
        modalAskModel.Show(
            translations.DeleteTrainingPeriodPlan,
            translations.DeleteTrainingPeriodPlanMsg,
            self.coreDelete,
            translations.Delete);
    },

    coreDelete: function () {
        var self = this;
        $.ajax({
            type: "POST",
            url: ContextPath + (self.TemplateMode() ? "TrainingPlan/DeletePlanPeriod" : "TrainingPlan/DeletePlanPeriodInstance"),
            data: { planPeriodId: self.TemplateMode() ? self.Id() : self.InstanceId() }
        })
         .done(function (result) {
             if (result == "ok") {
                 $('#planPeriodEditorModal').modal('hide');
                 self.Parent.PlanPeriods.remove(self);
             }
         });
    },

    //#region darag & drop
    selectLeftElement: function (data, evt) {
        var self = this;
        self.selectElement(data, evt, true);
    },
    selectRightElement: function (data, evt) {
        var self = this;
        self.selectElement(data, evt, false);
    },

    selectElement: function (data, evt, left) {
        var self = this;
        self.DragMode(true);
        self.containerSelectedElement = $(evt.target).closest('.loadContainer');
        self.StartX = evt.clientX;
        self.StartStartValue = self.StartDateObj().valueOf();
        self.StartEndValue = self.EndDateObj().valueOf();
        self.StartDaysValue = self.Days();
        self.selectedLeft(left);
        self.StartDayValue = self.StartDay();
        self.StartWeekValue = self.StartWeek();

        var _periods = _(self.Parent.PlanPeriods());
        var start = self.StartDateObj();
        self.nextPeriods = _periods.filter(function (elem) { return elem.StartDateObj() > start; });
        if (self.nextPeriods.length) {
            var firstNextStart = _(self.nextPeriods).min(function (elem) { return elem.StartDateObj(); }).StartDateObj();;
            self.nextMargin = (firstNextStart.valueOf() - self.EndDateObj().valueOf()) / (24 * 60 * 60 * 1000) - 1;
        } else {
            self.nextMargin = null;
        }

        self.previousPeriods = _periods.filter(function (elem) { return elem.StartDateObj() < start; });
        if (self.previousPeriods.length) {
            var firstPrevEnd = _(self.previousPeriods).max(function (elem) { return elem.EndDateObj(); }).EndDateObj();;
            self.prevMargin = (self.StartDateObj().valueOf() - firstPrevEnd.valueOf()) / (24 * 60 * 60 * 1000) - 1;
        } else {
            self.prevMargin = null;
        }
        self.nextShift = 0;
        self.prevShift = 0;

        self.containerSelectedElement.off("mousemove").on("mousemove", self.moveElement);
        $('body').off("mouseup").on("mouseup", self.deselectElement);
    },
    moveElement: function (evt) {
        var self = this;
        var dx = evt.clientX - self.StartX;
        var d_days = Math.round(7 * dx / TrainingPlanWeekWidth);

        //if (d_days != 0) {
        if (self.selectedLeft()) {
            var newDays = self.StartDaysValue - d_days;
            if (newDays < MinPeriodLength) {
                newDays = MinPeriodLength;
                d_days = self.StartDaysValue - newDays;
            }
            self.Days(newDays);

            if (self.prevMargin != null && -d_days >= self.prevMargin) {
                self.prevShift = d_days;
            }

            var newVal = self.StartStartValue + d_days * 24 * 60 * 60 * 1000;
            var newDate = new Date(newVal);
            if (newDate < self.EndDateObj()) {
                self.StartDate(dateToString(newDate));
            }

            if (d_days < self.StartDaysValue) {
                self.StartDay(self.StartDayValue + d_days % 7);
                self.StartWeek(self.StartWeekValue + ((d_days - (d_days % 7)) / 7));
            }

        } else {
            var newDays = self.StartDaysValue + d_days;
            if (newDays < newDays) newDays = newDays;
            self.Days(newDays);

            if (self.nextMargin != null && d_days >= self.nextMargin) {
                self.nextShift = d_days;
            }

            var newVal = self.StartEndValue + d_days * 24 * 60 * 60 * 1000;
            var newDate = new Date(newVal);
            if (newDate > self.StartDateObj()) {
                self.EndDate(dateToString(newDate));
            }
            /*
            if (-d_days < self.StartDaysValue) {
                self.StartDay(self.StartDayValue + d_days % 7);
                self.StartWeek(self.StartWeekValue + d_days / 7);
            }*/
        }
        //}
    },

    deselectElement: function (evt) {
        var self = this;
        if (self.DragMode()) {
            self.containerSelectedElement.off("mousemove");
            self.containerSelectedElement.off("mouseout");
            $('document').off("mouseup");

            if (self.StartStartValue != self.StartDateObj().valueOf() || self.StartDaysValue != self.Days()) {
                self.Save();

                if (self.nextShift && self.nextPeriods.length) {
                    _.each(self.nextPeriods, function (elem) { elem.shiftPeriod(self.nextShift - self.nextMargin); });
                }
                if (self.prevShift && self.previousPeriods.length) {
                    _.each(self.previousPeriods, function (elem) { elem.shiftPeriod(self.prevShift + self.prevMargin); });
                }
            }

            setTimeout(function () { self.DragMode(false); }, 100);
            stopPropagation(evt);
        }
    },
    shiftPeriod: function (days) {
        var self = this;
        var newDate = new Date(self.StartDateObj().valueOf() + days * 24 * 60 * 60 * 1000);
        self.StartDate(dateToString(newDate));

        newDate = new Date(self.EndDateObj().valueOf() + days * 24 * 60 * 60 * 1000);
        self.EndDate(dateToString(newDate));

        self.Save();
    }
    //#endregion
};///obiekt jednej sesji treningowej
function TrainingPlanUnitModel(parent, manager, trainingDetails, mappedEvent) {
    var self = this;
    self.Parent = parent;
    self.Manager = manager;

    self.initState = true;

    self.ServerModel = null;

    self.TypeManager = new TrainingTypeControl(
        {
            mainId: 0,
            subId: 0,
            typeChangeCallback: null,
            clubId: 0,
            multi: false,
            baseCtrl: trainingDetails ? trainingDetails.TypeManager : null,
            disciplines: false,
            multiSubs: false,
            allInstedNone: false
        });

    self.ParamsVisibility = ko.observable(new ParamsVisibility(''));
    self.UpdateVisibility = function () {
        var type = self.TypeManager.SelectedType();
        var config = '';
        if (type && type.ParamsVisibility) config = type.ParamsVisibility;
        self.ParamsVisibility(new ParamsVisibility(config));
        self.SetPace();
        initTimePicker();
    };
    self.TypeManager.SetChangeCallback(function () {
        self.UpdateVisibility();
    });

    self.ZoneChooser = new ZoneChooser(true, HR_zoneTypeId);

    self.ShowExercises = true;

    self.EditedInModal = false;

    //#region properties

    self.Calories = ko.observable('');
    //self.CustomZoneId = ko.observable(0);
    //self.CustomZoneValue = ko.observable(0);
    self.DescriptionId = ko.observable(0);
    self.Id = ko.observable(0);
    self.Week = ko.observable(0);
    self.Day = ko.observable(0);
    self.NextUnitId = ko.observable(0);
    self.OwnerId = ko.observable(0);
    self.PlanId = ko.observable(0);
    self.ProgramId = ko.observable(0);
    self.PlanIdProfileContext = ko.observable(0);
    self.PreviousUnitId = ko.observable(0);
    self.Duration = ko.observable('');
    self.Distance = ko.observable('');
    self.AvgCadence = ko.observable('');
    self.AvgPower = ko.observable('');
    self.AvgSpeed = ko.observable('');
    self.Pace = ko.observable('');
    self.CustomTarget = ko.observable('');
    self.Description = ko.observable('');
    self.Irrygation = ko.observable('');
    self.Name = ko.observable('');
    self.SuplementationAfter = ko.observable('');
    self.SuplementationBefore = ko.observable('');
    self.SuplementationDuring = ko.observable('');
    self.Url = ko.observable('');
    self.Male = ko.observable(null);
    self.Levels = ko.observableArray([]);
    self.PeriodTypes = ko.observableArray([]);
    self.PlanTargets = ko.observableArray([]);

    self.PrevDate = null;
    self.Date = ko.observable('');
    self.Time = ko.observable('');
    self.Place = ko.observable('');
    self.LocationModel = ko.observable(new LocationModel(self, function (place) { self.Place(place.Name()); }));
    self.InstanceId = ko.observable(0);
    self.TimeOfDay = ko.observable('');
    self.NoDaySpecified = ko.observable(false);
    self.NoDaySpecifiedEver = ko.observable(false);
    self.NoDaySpecified.subscribe(self.NoDaySpecified_subscribe, self);

    self.ActivityTypes = ko.observable('');
    self.Exercises = ko.observableArray([]);
    self.ExerciseSeries = trainingExerciseManager.BuildSeries(self.Exercises);

    self.AvailableLevelGroups = ko.observableArray([]);
    self.AvailablePeriodTypes = ko.observableArray([]);
    self.AvailableTrgets = ko.observableArray([]);

    self.ShowDescript = ko.observable(0);
    self.ShowTime = ko.observable(0);
    self.ShowDistance = ko.observable(0);
    self.ShowSpeed = ko.observable(0);
    self.ShowPower = ko.observable(0);
    self.ShowCadence = ko.observable(0);
    self.ShowCalories = ko.observable(0);
    self.ShowZone = ko.observable(0);
    self.ShowStart = ko.observable(0);

    self.CustomZoneImage = ko.observable('');
    self.CustomZoneClass = ko.observable('');
    self.CustomZoneDisplayValue = ko.observable('');
    self.AvgCustomZoneDisplayValue = ko.observable('');
    self.TrainingTypeName = ko.observable('');
    self.TrainingTypeIconCss = ko.observable('');

    self.TrainingInvitationId = ko.observable(0);

    self.MappedTrainings = ko.observable([]);
    self.MappedEvents = ko.observable([]);

    self.IsPast = ko.observable(false);

    self.MailAboutExecution = ko.observable(false);

    self.WarmUpSeconds = ko.observable(0);
    self.CoolDownSeconds = ko.observable(0);
    self.WarmUp = ko.observable('');
    self.CoolDown = ko.observable('');
    self.WarmUpDescript = ko.observable('');
    self.CoolDownDescript = ko.observable('');
    self.WarmUpIncluded = ko.observable(false);
    self.CoolDownIncluded = ko.observable(false);

    self.GraphicEventModel = ko.observable(null);
    self.EventStatus = ko.observable(null);
    self.EventId = ko.observable(null);

    self.Library = ko.observable(false);
    self.HasLibrary = ko.observable(false);
    self.HashTags = ko.observableArray([]);

    self.IgonreDates = ko.observable(false);

    self.Id.subscribe(self.Id_subscribe, self);

    self.WarmUp.subscribe(self.WarmUp_subscribe, self);
    self.CoolDown.subscribe(self.CoolDown_subscribe, self);

    self.HiDurationInformed = -1;
    self.Duration.subscribe(self.Duration_subscribe, self);

    self.AdditionalTimeSeconds = ko.computed(self.AdditionalTimeSeconds_computed, self);

    self.TrainingPlanUsersAssignations = ko.observableArray([]);

    self.CoachModel = ko.observable(new SimpleCoachModel(self));
    self.ProgramName = ko.observable('');

    self.LiveStream = ko.observable(false);
    self.LiveStreamVisibility = ko.observable(false);
    self.LiveStreamStartTime = ko.observable('12:00');
    self.LiveStreamGuid = ko.observable('');

    self.PersonExcluds = ko.observableArray([]);

    self.ExcercisesChart = new ExcercisesChartContainer(self);

    self.LiveStreamUrl = ko.computed(self.LiveStreamUrl_computed, self);

    self.WorkInProgres_init = ko.observable(false);
    self.WorkInProgres = ko.observable(false);

    self.LibTitle = ko.observable('');

    //#endregion


    if (self.Manager) {
        self.ReloadPlanLists();
        self.Date.subscribe(self.Date_subscribe, self);
        self.LiveStream.subscribe(self.LiveStream_subscribe, self);
    }

    self.OpenPlanTargets = ko.computed(self.OpenPlanTargets_computed, self);

    self.LongHtmlDate = ko.computed(self.LongHtmlDate_computed, self);

    self.PaceUnit = ko.computed(self.PaceUnit_computed, self);
    self.TimePaceUnit = ko.computed(self.TimePaceUnit_computed, self);

    self.Pace.subscribe(self.Pace_subscribe, self);
    self.AvgSpeed.subscribe(self.AvgSpeed_subscribe, self);

    self.ShowPace = ko.computed(self.ShowPace_computed, self);

    self.SpeedOrPace = ko.computed(self.SpeedOrPace_computed, self);

    self.CoachName = ko.computed(self.CoachName_computed, self);
    self.CoachImg = ko.computed(self.CoachImg_computed, self);


    self.MappedObject = trainingDetails || mappedEvent;
    if (self.MappedObject) {
        self.MappedIds = trainingDetails ? self.MappedTrainings : self.MappedEvents;
        self.MappingId = trainingDetails ? self.InstanceId : self.Id;
        self.Mapped = ko.computed(self.Mapped_computed, self);
    }


    self.AddTrainingUrl = ko.pureComputed(self.AddTrainingUrl_computed, self);


    self.ShowEventTileExpander = ko.computed(self.ShowEventTileExpander_computed, self);

    self.SavingMode = ko.observable(false);

    self.ContainsLevel2Params = ko.computed(self.ContainsLevel2Params_computed, self);

    self.EditHref = ko.computed(self.EditHref_computed, self);

    self.GoToProgramUrl = ko.pureComputed(self.GoToProgramUrl_computed, self);
    self.GoToCommunicationUrl = ko.pureComputed(self.GoToCommunicationUrl_computed, self);

    self.ForUser = ko.pureComputed(self.ForUser_computed, self);    

    self.TrainingPlanUsersAssignationsForDate = '';

    self.initState = false;

    _.bindAll(self
        , "registerList"
        , "ReloadPlanLists"
        , "SelectTimeOfDay"
        , "init"
        , "Clone"
        , "initByModel"
        , "SetPace"
        , "getClearData"
        , "getSringify"
        , "InitById"
        , "Scroll"
        , "InitByInstanceId"
        , "InitForDate"
        , "InitForWeek"
        , "Save"
        , "CopyToLib"
        , "UseFromLib"
        , "Edit"
        , "AddTrainingExercise"
        , "OpenDeletePlan"
        , "CoreDeletePlan"
        , "DeletePlan"
        , "coreDeletePlan"
        , "addNext"
        , "openDataCtrl"
        , "OpenNewTrainingInvitation"
        , "ShowExportWorkoutModal"
        , "ToggleMapping"
        , "GoEdit"
        , "removeEvent"
        , "CancelChanges"
        , "CancelAndClose"
        , "ReloadExcercisesChart"
        , "getExcercise"
        , "GoToCommunication"
        , "SaveAssList"
        , 'LocationModelOpenControl'
        );
}

TrainingPlanUnitModel.prototype = {
    //#region subscribes and compute
    Id_subscribe: function (newValue) {
        var self = this;
        if (newValue) {

            var currUrl = window.location.href;
            if (currUrl.indexOf("PlanUnitEditor?id=") >= 0) {
                if (currUrl.indexOf('' + newValue) < 0) {
                    currUrl = currUrl.replace(/PlanUnitEditor\?id=(\d+)/, ("PlanUnitEditor?id=" + newValue));
                    window.history.replaceState(newValue, document.title, currUrl);
                }
            }
        }
    }
    , Duration_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            var dd = self.Duration();
            if (dd.indexOf(':') < 0) self.Duration(dd + ':00');
            var a = dd.split(':');
            if ((+a[0]) >= 10) {
                if (self.HiDurationInformed != self.Id()) {
                    ShowEmptyMsg(translations.DurationSuspiciousValue10.replace('###', a[0]), translations.SuspiciousValue);
                    self.HiDurationInformed = self.Id();
                }
            } else {
                self.HiDurationInformed = -1;
            }
        }
    }
    , NoDaySpecified_subscribe: function (newValue) {
        var self = this;
        if (newValue) {
            if (newValue) self.NoDaySpecifiedEver(true);
        }
    }
    , WarmUp_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            self.WarmUpSeconds(stringToSeconds(newValue));
        }
    }
    , CoolDown_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            self.CoolDownSeconds(stringToSeconds(newValue));
        }
    }
    , AdditionalTimeSeconds_computed: function () {
        var self = this;
        return (self.WarmUpIncluded() ? 0 : self.WarmUpSeconds()) + (self.CoolDownIncluded() ? 0 : self.CoolDownSeconds());
    }
    , Date_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            self.ReloadPlanLists();
        }
    }

    , OpenPlanTargets_computed: function () {
        var self = this;
        return self.PlanTargets().length;
    }

    , LongHtmlDate_computed: function () {
        var self = this;
        return dateToLongHtml(self.Date());
    }

    , PaceUnit_computed: function () {
        var self = this;
        if (self.ParamsVisibility() && self.ParamsVisibility().PaceParams && self.ParamsVisibility().PaceParams.unit) {
            return self.ParamsVisibility().PaceParams.unit;
        }
        return '';
    }
    , TimePaceUnit_computed: function () {
        var self = this;
        return isMinPaceUnit(self.PaceUnit());
    }

    , Pace_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            var paceParams = self.ParamsVisibility().PaceParams;
            if (paceParams) {
                var speed = calculateSpeedFromPace(newValue, paceParams.unit, paceParams.factor);
                self.initState = true;
                self.AvgSpeed(speed);
                self.initState = false;
            }
        }
    }
    , AvgSpeed_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState) {
            var paceParams = self.ParamsVisibility().PaceParams;
            if (paceParams) {
                var pace = calculatePaceString(newValue, paceParams.unit, paceParams.factor, false, false);
                self.initState = true;
                self.Pace(pace);
                self.initState = false;
            }
        }
    }

, ForUser_computed: function () {
    var self = this;
    return self.Manager.UserId>0;
}

    , ShowPace_computed: function () {
        var self = this;
        return self.ParamsVisibility() && self.ParamsVisibility().ShowPace;
    }

    , SpeedOrPace_computed: function () {
        var self = this;
        if (self.ParamsVisibility() && self.ParamsVisibility().PaceParams && self.ParamsVisibility().PaceParams.unit) {
            return self.Pace();
        } else {
            return self.AvgSpeed();
        }
    }

    , CoachName_computed: function () {
        var self = this;
        var coach = self.CoachModel();
        var id = coach.IdObs();
        if (id < 0) {
            return coach.Nick;
        } else if (id > 0) {
            return coach.DisplayName;
        }
        return '';
    }
    , CoachImg_computed: function () {
        var self = this;
        var coach = self.CoachModel();
        if (coach.IdObs() != 0 && coach.PhotoId) {
            return coach.ImageUrl;
        }
        return '';
    }

    , Mapped_computed: function () {
        var self = this;
        return _(self.MappedObject.MappedPlanIds()).find(function (elem) { return elem == self.MappingId(); });
    }

     , AddTrainingUrl_computed: function () {
         var self = this;
         if (self.MappedTrainings() && self.MappedTrainings().length) {
             return ContextPath + 'User/TrainingDetails?treningId=' + self.MappedTrainings()[0];
         } else {
             return ContextPath + 'User/TrainingDetails?planInstanceId=' + self.InstanceId();
         }
     }

    , ShowEventTileExpander_computed: function () {
        var self = this;
        if (self.GraphicEventModel() && self.GraphicEventModel().ChangesHistory()) {
            return self.GraphicEventModel().ChangesHistory().Changes().length > 0;
        }
        return false;
    }

    , ContainsLevel2Params_computed: function () {
        var self = this;
        return (self.Distance() && self.Distance() != '0')
       || (self.AvgSpeed() && self.AvgSpeed() != '0')
       || (self.AvgPower() && self.AvgPower() != '0')
       || (self.AvgCadence() && self.AvgCadence() != '0')
       || (self.Calories() && self.Calories() != '0')
       || self.ZoneChooser.GetZoneId() != 0;
    }

    , EditHref_computed: function () {
        var self = this;
        if (self.InstanceId()) {
            return ContextPath + 'TrainingPlan/PlanUnitEditor?instanceId=' + self.InstanceId();
        } else {
            return ContextPath + 'TrainingPlan/PlanUnitEditor?id=' + self.Id();
        }
    }

    , GoToProgramUrl_computed: function () {
        var self = this;
        return ContextPath + 'Period/ContestPage?periodId=' + self.ProgramId() + '&tab=TrainingPlan';
    }
    , GoToCommunicationUrl_computed: function () {
        var self = this;
        return ContextPath + 'Comunication/PlanUnitContext?planUnitId=' + self.Id();
    }

    , LiveStreamUrl_computed: function () {
        var self = this;
        return ContextPath + 'LiveStream/Stream?guid=' + self.LiveStreamGuid();
    }

    , LiveStream_subscribe: function (newValue) {
        var self = this;
        if (newValue && !self.initState && !self.LiveStreamGuid()) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Home/GetNewGuid"
            })
            .done(function (result) {
                self.LiveStreamGuid(result);
            });
        }
    }

    //#endregion


    , registerList: function (allItems, selIds, newList) {
        var self = this;
        newList.removeAll();
        if (allItems)
            for (var i = 0, len = allItems.length; i < len; i++) {
                var elem = allItems[i];
                var newElem = jQuery.extend({}, elem);
                newElem.Selected = ko.computed(function () {
                    return $.inArray(this.value, selIds()) >= 0;
                }, newElem);
                newElem.Select = function () {
                    var index = $.inArray(this.value, selIds());
                    if (index >= 0) {
                        selIds.splice(index, 1);
                    } else {
                        selIds.push(this.value);
                    }
                }
                newList.push(newElem);
            }
    }

    , ReloadPlanLists: function () {
        var self = this;
        if (self.Manager) {
            TrainingPlanLib.GetProfileForDate(self.Date, self.Manager.UserId, self.PlanIdProfileContext() || self.PlanId(), function (profile) {
                if (profile) {
                    self.registerList(profile.LevelGroups, self.Levels, self.AvailableLevelGroups);
                    self.registerList(profile.PeriodTypes, self.PeriodTypes, self.AvailablePeriodTypes);
                    self.registerList(profile.Trgets, self.PlanTargets, self.AvailableTrgets);
                }
            });
        }
    }

    , SelectTimeOfDay: function (value) {
        var self = this;
        if (self.TimeOfDay() == value) {
            self.TimeOfDay('');
        } else {
            self.TimeOfDay(value);
        }
    }


    //#region init i save

    , init: function (leaveDiscip) {
        var self = this;
        self.ServerModel = null;
        self.Calories('');
        self.ZoneChooser.InitZones();
        //self.CustomZoneId(0);
        //self.CustomZoneValue(0);
        self.ZoneChooser.SetZoneById(0);
        self.ZoneChooser.SetZoneValue(0);
        self.DescriptionId(0);
        self.Id(0);
        self.Week(0);
        self.Day(0);
        self.NextUnitId(0);
        self.OwnerId(0);
        //self.PlanId(0);
        //self.PlanIdProfileContext(0);
        self.PreviousUnitId(0);
        if (!leaveDiscip) self.TypeManager.SelectValue(0, 0, 0);
        self.Duration('');
        self.Distance('');
        self.AvgCadence('');
        self.AvgPower('');
        self.AvgSpeed('');
        self.CustomTarget('');
        self.Description('');
        self.Irrygation('');
        self.Name('');
        self.SuplementationAfter('');
        self.SuplementationBefore('');
        self.SuplementationDuring('');
        self.Url('');
        self.Male(null);
        self.PrevDate = self.Date();
        self.Date('');
        self.Time('');
        self.Place('');
        self.LocationModel().init();
        self.InstanceId(0);
        self.TrainingInvitationId(0);
        self.ActivityTypes('');
        self.Exercises.removeAll();
        self.TimeOfDay('');
        self.NoDaySpecified(false);

        self.WarmUpSeconds(0);
        self.CoolDownSeconds(0);
        self.WarmUp('');
        self.CoolDown('');
        self.WarmUpDescript('');
        self.CoolDownDescript('');
        self.WarmUpIncluded(false);
        self.CoolDownIncluded(false);

        self.TrainingPlanUsersAssignations.removeAll();

        self.GraphicEventModel(null);
        self.EventStatus(null);
        self.EventId(null);
        /*
        self.Levels().removeAll();
        self.PeriodTypes().removeAll();
        self.PlanTargets().removeAll();
        */
        self.CoachModel(new SimpleCoachModel(self));
        self.ProgramName('');
        self.Library(false);
        self.HasLibrary(false);
        self.HashTags.removeAll();
        self.IgonreDates(false);

        self.LiveStream(false);
        self.LiveStreamVisibility('Private');
        self.LiveStreamStartTime('12:00');
        self.LiveStreamGuid('');

        self.WorkInProgres(false);
        self.LibTitle('');

        self.PersonExcluds.removeAll();
    }

    , Clone: function () {
        var self = this;
        self.ServerModel = null;
        if (self.EditedInModal) {
            var clone = new TrainingPlanUnitModel(self.Parent, self.Manager);
            clone.initByModel(self.getClearData());
            self = clone;
            if (clone.Exercises() && clone.Exercises().length) {
                _.each(clone.Exercises(), function (exe) { exe.Id = 0; });
            }
            self.Manager.TrainingPlanLibrary.AddUnit(self);
            self.Manager.currentTrainingPlanUnit(self);
        }
        //self.Calories('');
        //self.ZoneChooser.InitZones();
        //self.CustomZoneId(0);
        //self.CustomZoneValue(0);
        //self.ZoneChooser.SetZoneById(0);
        //self.ZoneChooser.SetZoneValue(0);
        self.DescriptionId(0);
        self.Id(0);
        self.Week(0);
        self.Day(0);
        self.NextUnitId(0);
        self.OwnerId(0);
        //self.PlanId(0);
        //self.ProgramId(0);
        //self.PlanIdProfileContext(0);
        self.PreviousUnitId(0);
        //if (!leaveDiscip) self.TypeManager.SelectValue(0, 0, 0);
        //self.Duration('');
        //self.Distance('');
        //self.AvgCadence('');
        //self.AvgPower('');
        //self.AvgSpeed('');
        //self.CustomTarget('');
        //self.Description('');
        //self.Irrygation('');
        //self.Name('');
        //self.SuplementationAfter('');
        //self.SuplementationBefore('');
        //self.SuplementationDuring('');
        //self.Url('');
        //self.Male(null);
        self.PrevDate = (new Date()).toISOString().substr(0, 10);
        self.Date('');
        self.Time('');
        //self.Place('');
        //self.InstanceId(0);
        //self.TrainingInvitationId(0);
        //self.ActivityTypes('');
        //self.Exercises.removeAll();
        //self.TimeOfDay('');

        //self.WarmUpSeconds(0);
        //self.CoolDownSeconds(0);
        //self.WarmUp('');
        //self.CoolDown('');
        //self.WarmUpDescript('');
        //self.CoolDownDescript('');
        //self.WarmUpIncluded(false);
        //self.CoolDownIncluded(false);
        //self.Library(false);
        self.HasLibrary(false);
        //self.HashTags.removeAll();

        self.LiveStream(false);
        self.LiveStreamVisibility('Private');
        self.LiveStreamStartTime('12:00');
        self.LiveStreamGuid('');

        _.each(self.Exercises(), function (e) {
            e.Id = 0;
        });
    }

    , initByModel: function (serverModel) {
        var self = this;
        if (serverModel) {
            self.initState = true;

            self.ServerModel = serverModel;

            self.Calories(serverModel.Calories ? serverModel.Calories : '');
            //self.CustomZoneId(serverModel.CustomZoneId);
            //self.CustomZoneValue(serverModel.CustomZoneValue);
            self.ZoneChooser.SetZoneById(serverModel.CustomZoneId);
            self.ZoneChooser.SetZoneValue(serverModel.CustomZoneValue);
            self.DescriptionId(serverModel.DescriptionId);
            self.Id(serverModel.Id);
            self.Week(serverModel.Week);
            self.Day(serverModel.Day);
            self.NextUnitId(serverModel.NextUnitId);
            self.OwnerId(serverModel.OwnerId);
            self.PlanId(serverModel.PlanId);
            self.ProgramId(serverModel.ProgramId);
            self.PlanIdProfileContext(serverModel.PlanIdProfileContext);
            self.PreviousUnitId(serverModel.PreviousUnitId);
            self.TypeManager.SelectValue(serverModel.TrainingTypeId, serverModel.TrainingSubTypeId, 0);
            self.Duration((serverModel.Duration && serverModel.Duration != '00:00') ? serverModel.Duration : '');
            self.Distance(serverModel.Distance ? serverModel.Distance : '');
            self.AvgCadence(serverModel.AvgCadence ? serverModel.AvgCadence : '');
            self.AvgPower(serverModel.AvgPower ? serverModel.AvgPower : '');
            self.AvgSpeed(serverModel.AvgSpeed ? serverModel.AvgSpeed : '');
            self.Pace((serverModel.Pace && serverModel.Pace != '0:00') ? serverModel.Pace : '');
            self.CustomTarget(serverModel.CustomTarget);
            self.Description(serverModel.Description);
            self.Irrygation(serverModel.Irrygation);
            self.Name(serverModel.Name);
            self.SuplementationAfter(serverModel.SuplementationAfter);
            self.SuplementationBefore(serverModel.SuplementationBefore);
            self.SuplementationDuring(serverModel.SuplementationDuring);
            self.Url(serverModel.Url);
            self.Male(serverModel.Male);
            self.Date(serverModel.Date);
            self.Time(serverModel.Time);
            self.Place(serverModel.Place);
            self.LocationModel().initByModel(serverModel.LocationModel);
            self.TimeOfDay(serverModel.TimeOfDay);
            self.InstanceId(serverModel.InstanceId);
            self.TrainingInvitationId(serverModel.TrainingInvitationId);
            self.MappedTrainings(serverModel.MappedTrainings);
            self.MappedEvents(serverModel.MappedEvents);
            self.IsPast(serverModel.IsPast);
            self.MailAboutExecution(serverModel.MailAboutExecution);

            self.WarmUpSeconds(serverModel.WarmUpSeconds);
            self.CoolDownSeconds(serverModel.CoolDownSeconds);
            self.WarmUp(serverModel.WarmUp);
            self.CoolDown(serverModel.CoolDown);
            self.WarmUpDescript(serverModel.WarmUpDescript);
            self.CoolDownDescript(serverModel.CoolDownDescript);
            self.WarmUpIncluded(serverModel.WarmUpIncluded);
            self.CoolDownIncluded(serverModel.CoolDownIncluded);

            if (serverModel.GraphicEventModel) {
                self.GraphicEventModel(new JsGraphicEventModelSimple(serverModel.GraphicEventModel));
            }
            if (serverModel.EventStatus) {
                self.EventStatus(new EventStatusModel(JSON.parse(serverModel.EventStatus)));
            }
            self.EventId(serverModel.EventId);

            self.Levels.removeAll();
            if (serverModel.Levels) {
                for (var i = 0, len = serverModel.Levels.length; i < len; i++) {
                    self.Levels.push(serverModel.Levels[i]);
                }
            }

            self.PeriodTypes.removeAll();
            if (serverModel.PeriodTypes) {
                for (var i = 0, len = serverModel.PeriodTypes.length; i < len; i++) {
                    self.PeriodTypes.push(serverModel.PeriodTypes[i]);
                }
            }

            self.PlanTargets.removeAll();
            if (serverModel.PlanTargets) {
                for (var i = 0, len = serverModel.PlanTargets.length; i < len; i++) {
                    self.PlanTargets.push(serverModel.PlanTargets[i]);
                }
            }

            var index = 1;
            self.ShowDescript((serverModel.Description || serverModel.Name) ? index++ : 0);
            if (serverModel.Description) {
                index++;
            }
            self.ShowTime((serverModel.Duration && serverModel.Duration != '00:00') ? index++ : 0);
            self.ShowDistance((serverModel.Distance) ? index++ : 0);
            self.ShowSpeed((serverModel.AvgSpeed && serverModel.AvgSpeed != '0') ? index++ : 0);
            self.ShowPower((serverModel.AvgPower) ? index++ : 0);
            self.ShowCadence((serverModel.AvgCadence) ? index++ : 0);
            self.ShowCalories((serverModel.Calories) ? index++ : 0);
            self.ShowZone((serverModel.CustomZoneImage) ? index++ : 0);
            self.ShowStart(((serverModel.Time && serverModel.Time != '00:00') || serverModel.TimeOfDay) ? index++ : 0);

            self.CustomZoneImage(serverModel.CustomZoneImage ? serverModel.CustomZoneImage : 'none');
            self.CustomZoneClass(serverModel.CustomZoneClass ? serverModel.CustomZoneClass : '');
            self.CustomZoneDisplayValue(serverModel.CustomZoneDisplayValue ? serverModel.CustomZoneDisplayValue : '');
            self.AvgCustomZoneDisplayValue(serverModel.AvgCustomZoneDisplayValue ? serverModel.AvgCustomZoneDisplayValue : '');

            self.TrainingTypeName(serverModel.TrainingTypeName);
            self.TrainingTypeIconCss(serverModel.TrainingTypeIco);

            /*cwiczenia*/
            self.ActivityTypes(serverModel.ActivityTypes);
            self.Exercises.removeAll();
            if (serverModel.Exercises) {
                for (var i = 0, len = serverModel.Exercises.length; i < len; i++) {
                    var elem = new TrainingExerciseModel(self);
                    elem.initByModel(serverModel.Exercises[i]);
                    self.Exercises.push(elem);
                }
            }

            koArrayCopyComplex(serverModel.TrainingPlanUsersAssignations, self.TrainingPlanUsersAssignations, false, function (elem) {
                elem.EditUrl = ContextPath + "TrainingPlan/PlanUnitEditor?instanceId=" + elem.InstanceId;
                elem.Blocked = ko.observable(elem.Blocked);
                elem.ToggleBlock = function () {
                    if (elem.SameUnit || !elem.InstanceId) {
                        var old = elem.Blocked();
                        elem.Blocked(!old);
                        if (old) {
                            self.PersonExcluds.remove(elem.PersonId);
                        } else {
                            self.PersonExcluds.push(elem.PersonId);
                        }
                    }
                }
                elem.Exclude = function () {
                    $.ajax({
                        type: "POST",
                        url: ContextPath + "TrainingPlan/ExcludeUser",
                        data: { instanceId: elem.InstanceId }
                    });
                    self.TrainingPlanUsersAssignations.remove(elem);
                }
                return elem;
            });

            self.CoachModel().initByModel(serverModel.CoachModel);
            self.ProgramName(serverModel.ProgramName);

            self.Library(serverModel.Library);
            self.HasLibrary(serverModel.HasLibrary);
            var hts = serverModel.HashTags ? _.filter(serverModel.HashTags.split(","), function (elem) { return elem; }) : [];
            koArrayCopy(hts, self.HashTags);

            self.IgonreDates(serverModel.IgonreDates);

            self.NoDaySpecified(serverModel.NoDaySpecified);

            self.LiveStream(serverModel.LiveStream);
            self.LiveStreamVisibility(serverModel.LiveStreamVisibility);
            self.LiveStreamStartTime(serverModel.LiveStreamStartTime);
            self.LiveStreamGuid(serverModel.LiveStreamGuid);

            self.PersonExcluds.removeAll();
            if (serverModel.PersonExcluds) {
                var pExces = serverModel.PersonExcluds.split(";");
                for (var i = 0, len = pExces.length; i < len; i++) {
                    var elem = pExces[i];
                    var intElem = parseInt(elem);
                    self.PersonExcluds.push(intElem);
                }
            }

            self.WorkInProgres_init(serverModel.WorkInProgres);
            self.WorkInProgres(serverModel.WorkInProgres);

            if (self.Manager) {
                self.ReloadPlanLists();
            }

            $('.tinymce').each(function () {
                if (this.renderTinymce != undefined) this.renderTinymce();
            });

            self.UpdateVisibility();

            self.initState = false;
        }
    }

    , SetPace: function () {
        var self = this;
        if (!self.initState && self.ParamsVisibility() && self.ParamsVisibility().PaceParams) {
            var paceStr = calculatePaceString(self.AvgSpeed(), self.ParamsVisibility().PaceParams.unit, self.ParamsVisibility().PaceParams.factor, false, false);
            self.Pace(paceStr != '00:00' ? paceStr : '');
        }
    }

    , getClearData: function () {
        var self = this;
        var _Levels = []; var _Levels_Iter = self.Levels(); for (var i = 0, len = _Levels_Iter.length; i < len; i++) { _Levels.push(_Levels_Iter[i]); }
        var _PeriodTypes = []; var _PeriodTypes_Iter = self.PeriodTypes(); for (var i = 0, len = _PeriodTypes_Iter.length; i < len; i++) { _PeriodTypes.push(_PeriodTypes_Iter[i]); }
        var _PlanTargets = []; var _PlanTargets_Iter = self.PlanTargets(); for (var i = 0, len = _PlanTargets_Iter.length; i < len; i++) { _PlanTargets.push(_PlanTargets_Iter[i]); }

        var _PersonExcluds = self.PersonExcluds().join(';');

        var date = self.Date();
        if (!date) date = self.PrevDate;
        if (!date) {
            var d = new Date();
            date = dateToString(d);
        }

        var exerces = self.ExerciseSeries.GetExercisesToStore();
        if (!exerces.length && self.Exercises().length) {
            self.ExerciseSeries = trainingExerciseManager.BuildSeries(self.Exercises);
            exerces = self.ExerciseSeries.GetExercisesToStore();
        }

        if (!self.PlanId() && self.Manager && self.Manager.ActivePlan()) {
            self.PlanId(self.Manager.ActivePlan().Id());
        }

        var data = {
            UserId: self.Manager ? self.Manager.UserId : 0,
            Calories: self.Calories(),
            CustomZoneId: self.ZoneChooser.GetZoneId(),
            CustomZoneValue: self.ZoneChooser.GetZoneValue(),
            DescriptionId: self.DescriptionId(),
            Id: self.Id(),
            Week: self.Week(),
            Day: self.Day(),
            NextUnitId: self.NextUnitId(),
            OwnerId: self.OwnerId(),
            PlanId: self.PlanId(),
            PlanIdProfileContext: self.PlanIdProfileContext() || self.PlanId(),
            PreviousUnitId: self.PreviousUnitId(),
            TrainingSubTypeId: self.TypeManager.SelectedSubTypeId(),
            TrainingTypeId: self.TypeManager.SelectedTypeId(),
            Duration: self.Duration(),
            Distance: self.Distance(),
            AvgCadence: self.AvgCadence(),
            AvgPower: self.AvgPower(),
            AvgSpeed: self.AvgSpeed(),
            CustomTarget: self.CustomTarget(),
            Description: self.Description(),
            Irrygation: self.Irrygation(),
            Name: self.Name(),
            SuplementationAfter: self.SuplementationAfter(),
            SuplementationBefore: self.SuplementationBefore(),
            SuplementationDuring: self.SuplementationDuring(),
            Url: self.Url(),
            Male: self.Male(),
            Date: date,
            NoDaySpecified: self.NoDaySpecified(),
            Time: self.Time(),
            Place: self.Place(),
            LocationModel: self.LocationModel().getClearData(),
            InstanceId: self.InstanceId(),
            Levels: _Levels,
            PeriodTypes: _PeriodTypes,
            PlanTargets: _PlanTargets,
            MailAboutExecution: self.MailAboutExecution(),
            Exercises: exerces,
            ActivityTypes: self.ActivityTypes(),
            //TimeOfDay: self.TimeOfDay(),
            WarmUp: self.WarmUp(),
            CoolDown: self.CoolDown(),
            WarmUpDescript: self.WarmUpDescript(),
            CoolDownDescript: self.CoolDownDescript(),
            WarmUpIncluded: self.WarmUpIncluded(),
            CoolDownIncluded: self.CoolDownIncluded(),
            HashTags: self.HashTags().join(),
            Library: self.Library(),
            LiveStream: self.LiveStream(),
            LiveStreamVisibility: self.LiveStreamVisibility(),
            LiveStreamStartTime: self.LiveStreamStartTime(),
            LiveStreamGuid: self.LiveStreamGuid(),
            PersonExcluds: _PersonExcluds,
            WorkInProgres: self.WorkInProgres(),
        }
        return data;
    }

    , dragMove: function (targetWeekDay, targetDate) {
        var self = this;
        self.Date(targetDate);
        self.Day(targetWeekDay);
        self.Save(null, null, true);
    }

    , getSringify: function () {
        var self = this;
        return JSON.stringify(self.getClearData());
    }

    , InitById: function (id, editAsCopy) {
        var self = this;
        self.ZoneChooser.InitZones();
        if (id) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetPlanUnitModel",
                data: { id: id }
            })
            .done(function (serverModel) {
                self.initByModel(serverModel);
                if (editAsCopy) self.Clone();
            });
        } else {
            self.init();
        }
    }

    , Scroll: function (selector) {
        var self = this;
        $.scrollTo($(selector), { duration: 1000, easing: 'swing' });
    }

    , InitByInstanceId: function (id, editAsCopy, scrollToEvent) {
        var self = this;
        self.ZoneChooser.InitZones();
        if (id) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetPlanInstanceModel",
                data: { id: id }
            })
            .done(function (serverModel) {
                self.initByModel(serverModel);
                if (editAsCopy) self.Clone();

                if (scrollToEvent) {
                    $('.dzyndzolek.collapsed', '.accordion-group.planEvent').click();
                    if ($('#collapseEvent').length) self.Scroll('#collapseEvent');
                }
            });
        } else {
            self.init();
        }
    }

    , InitForDate: function (date, timeOfDay, planId) {
        var self = this;
        self.ZoneChooser.InitZones();
        self.init();
        self.Date(date);
        if (timeOfDay) {
            self.TimeOfDay(timeOfDay);
        }
        self.PlanId(planId);
        self.initPlanPersons();
    }
    , InitForWeek: function (week, day, timeOfDay, planId) {
        var self = this;
        self.ZoneChooser.InitZones();
        self.init();
        self.Week(week);
        self.IgonreDates(true);
        if (day == null || day < 0) day = 1;
        if (day == 7) day = 0;
        self.Day(day);
        if (timeOfDay) {
            self.TimeOfDay(timeOfDay);
        }
        self.PlanId(planId);
        self.initPlanPersons();
    }
    , initPlanPersons: function (doNotOverride, callback) {
        var self = this;
        var assDate = self.Date();
        var planId = self.PlanId();
        if (planId && (!doNotOverride || !self.TrainingPlanUsersAssignations().length || (assDate && self.TrainingPlanUsersAssignationsForDate != assDate))) {
            if (!self.Manager || self.Manager.GetProgramPersonsResultPerDay[assDate] == undefined) {
                $.ajax({
                    type: "POST",
                    url: ContextPath + "TrainingPlan/GetProgramPersons",
                    data: { planId: planId, date: assDate }
                })
               .done(function (serverModel) {
                   self.Manager.GetProgramPersonsResultPerDay[assDate] = serverModel;
                   self.coreInitPlanPersons(serverModel, callback, assDate);
               });
            } else {
                self.coreInitPlanPersons(self.Manager.GetProgramPersonsResultPerDay[assDate], callback, assDate);
            }
        } else {
            if (callback) callback();
        }
    }
    , coreInitPlanPersons: function (serverModel, callback, date) {
        var self = this;
        if (!self.TrainingPlanUsersAssignationsForDate || date) {
            self.TrainingPlanUsersAssignationsForDate = date;

            koArrayCopyComplex(serverModel, self.TrainingPlanUsersAssignations, false, function (plainelem) {
                var elem = jQuery.extend({}, plainelem);
                elem.EditUrl = '';
                if (elem.Blocked) {
                    self.PersonExcluds.remove(elem.PersonId);
                    self.PersonExcluds.push(elem.PersonId);
                }
                elem.Blocked = ko.observable(elem.Blocked);
                elem.ToggleBlock = function () {
                    var old = elem.Blocked();
                    elem.Blocked(!old);
                    if (old) {
                        self.PersonExcluds.remove(elem.PersonId);
                    } else {
                        self.PersonExcluds.push(elem.PersonId);
                    }
                }
                elem.Exclude = function () {
                    $.ajax({
                        type: "POST",
                        url: ContextPath + "TrainingPlan/ExcludeUser",
                        data: { instanceId: elem.InstanceId }
                    });
                    self.TrainingPlanUsersAssignations.remove(elem);
                }
                return elem;
            });
        }
        if (callback) callback();
    },
    selectSinglePersonInPlan: function (userId) {
        var self = this;
        self.initPlanPersons(true, function () {
            _.each(self.TrainingPlanUsersAssignations(), function (elem) {
                if (elem.UserId == userId || !userId) {
                    if (elem.Blocked()) elem.ToggleBlock();
                } else {
                    if (!elem.Blocked()) elem.ToggleBlock();
                }
            });
        });
    }

    , Save: function (data, event, noReload, callback) {
        var self = this;
        var infoContainer = event ? $(event.target) : null;
        var strData = self.getSringify();
        $('body').css('cursor', 'wait');
        self.SavingMode(true);
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetPlanUnitModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.initByModel(result);
            self.SavingMode(false);

            if (!noReload) {
                ShowSavedAlert(infoContainer);
                //setTimeout(hideTopSlide, 3000);

                if (typeof (userCallendar) != 'undefined' && userCallendar.Days().length) {
                    userCallendar.coreRefresh();
                }

                if (typeof (userCallendar) != 'undefined' && userCallendar.PeriodType() == 'plan') {
                    trainingPlanManager.ReloadPlan();
                }
            }

            clearCachedAjaxCall({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetPlansForDate",
                data: { date: self.Date() }
            });

            if (callback) callback();
        });
    }

    , SaveAssList: function (callback) {
        var self = this;
        var strData = self.getSringify();
        $('body').css('cursor', 'wait');
        self.SavingMode(true);
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetPlanUnitAssList",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            if (callback) callback();
        });
    }

    , GoToCommunication: function () {
        var self = this;
        self.SaveAssList(function () {
            window.open(self.GoToCommunicationUrl(), '_blank');
        });
    }

    , CopyToLib: function (data, event) {
        var self = this;
        if (!self.HasLibrary()) {
            self.LibTitle(self.Name());
            loadAndBindTemplate({
                simpleContainer: 'addToLibModalContainer',
                existingElem: '#addToLibModal',
                ajaxPath: 'TrainingPlan/GetAddToLibModal',
                bindData: null,
                callback: function () {
                    $('#addToLibModal').modal('show');
                }
            });

        } else {
            alert("Already in library");
        }
    }

    , coreCopyToLib: function (data, event) {
        var self = this;
        $('body').css('cursor', 'wait');
        var n = self.Name();
        self.Name(self.LibTitle());
        var strData = self.getSringify();
        self.Name(n);
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/CopyPlanUnitToLib",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            if (self.Id()) {
                self.HasLibrary(true);
            }
        });
    }

    , UseFromLib: function (date, week, day, callback, fromLib, noDaySpecified) {
        var self = this;
        data = self.getClearData();
        data.Date = date;
        data.Week = week;
        data.Day = day;
        data.PlanId = self.Manager.ActivePlan().Id();
        if (noDaySpecified) data.NoDaySpecified = true;

        if (!fromLib) data.Id = 0;

        $('body').css('cursor', 'wait');
        self.SavingMode(true);
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/UseFromLib",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(data)
        })
        .done(function (serverModel) {
            $('body').css('cursor', 'default');

            var unit = self.Manager.AddUnitInText();
            unit.initByModel(serverModel);
            if (callback) callback(unit, serverModel);
        });
    }

    , CancelChanges: function () {
        var self = this;
        if (self.ServerModel) self.initByModel(self.ServerModel);
    }

    , SetDay: function (dayIdx, weekStart, callback) {
        var self = this;
        self.Day(dayIdx);
        if (dayIdx) {
            var dat = new Date(weekStart);
            dat.setDate(dat.getDate() + dayIdx);
            self.Date(dateToString(dat));
        }
        self.NoDaySpecified(false);
        self.Save(null, null, null, callback);
    }

    , AdminActiMap: function (actiModel, callback) {
        var self = this;
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/AdminMapActivity",            
            data: { unitId: self.Id(), instanceId: self.InstanceId(), trainingId: actiModel.ElementId() }
        })
        .done(function (serverModel) {
            $('body').css('cursor', 'default');
            if (callback) callback();
        });
    }

    //#endregion

    , LocationModelOpenControl: function () {
        var self = this;
        if (self.LocationModel().Name() != self.Place()) {
            self.LocationModel().init();
            localisationControl.RemoveMarker();
        }

        self.LocationModel().OpenControl(null, null, self.Place());
    }

    , Edit: function () {
        var self = this;
        self.Manager.currentTrainingPlanUnit(self);
        self.Manager.editUnitInModal();
        self.EditedInModal = true;
    }

    , CancelAndClose: function () {
        var self = this;
        self.CancelChanges();
        $('#planUnitEditorModal').modal('hide');

        self.Manager.TrainingPlanLibrary.postLoad();
    }

    , AddTrainingExercise: function () {
        var self = this;
        var elem = new TrainingExerciseModel(self);
        elem.EditMode(true);
        var len = self.Exercises().length;
        if (len) {
            elem.Index(1 + self.Exercises()[len - 1].Index());
        }

        self.Exercises.push(elem);
    }

    , OpenDeletePlan: function () {
        var self = this;
        $('#removeSessionPlanModal').modal('show');
    }
    , CoreDeletePlan: function () {
        var self = this;
        var id = self.InstanceId() > 0 ? self.InstanceId() : self.Id();
        var url = self.InstanceId() > 0 ? "DeleteTrainingPlanInstance" : "DeleteTrainingPlanUnit";

        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/" + url,
            data: { id: id }
        })
         .done(function (result) {
             if (result) {
                 $('#removeSessionPlanModal').modal('hide');
                 //if (IsAdmin) {
                 //    window.location = ContextPath + "User/Person?SelectedTab=Grafik&userId=" + self.Manager.UserId;
                 //} else {

                 if (result == 'adminPlan') {
                     ShowEmptyMsg(translations.BlockedDelPlanMsg, translations.Info);
                 } else {
                     if (self.EditedInModal) {
                         setTimeout(function () { $('#planUnitEditorModal').modal('hide'); }, 100);
                     } else {
                         if (UrlReferrer) {
                             window.location = UrlReferrer;
                         } else {
                             window.location = ContextPath + "Home/Home";
                         }
                     }
                 }
                 //}
             }
         });
    }

    , DeletePlan: function (data, event, callback) {
        var self = this;
        modalAskModel.Show(
             translations.DeleteTrainingPlan,
             translations.DeleteTrainingPlanMsg,
             function () { self.coreDeletePlan(callback); },
             translations.Delete);
    }
    , coreDeletePlan: function (callback) {
        var self = this;
        var id = self.InstanceId() > 0 ? self.InstanceId() : self.Id();
        var url = self.InstanceId() > 0 ? "DeleteTrainingPlanInstance" : "DeleteTrainingPlanUnit";

        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/" + url,
            data: { id: id }
        })
         .done(function (result) {
             if (result == 'adminPlan') {
                 ShowEmptyMsg(translations.BlockedDelPlanMsg, translations.Info);
             } else {
                 if (result && callback) {
                     callback();
                 }
             }
         });
    }

    , addNext: function () {
        var self = this;
        if (self.Manager) {
            var newPlan = new TrainingPlanUnitModel(null, self.Manager);
            newPlan.init(false); //true jezeli ma zostac dyscyplina
            newPlan.Library(self.Library());
            //newPlan.Time(self.Time());

            // var d = new Date(self.Date());
            // d.setDate(d.getDate() + 1);
            // newPlan.Date(dateToString(d));

            //newPlan.TypeManager.SelectValue(self.TrainingTypeId, self.TrainingSubTypeId, 0);
            self.Manager.currentTrainingPlanUnit(newPlan);
            newPlan.UpdateVisibility();
        }
    }

    , openDataCtrl: function (data, event) {
        var self = this;
        if ($(event.target).hasClass('goToCallendar')) {
            OpenDataCtrlFromKo(data, event);
        }
    }


    , OpenNewTrainingInvitation: function () {
        var self = this;
        var invit = communityExplorerModel.SelectedTrainingInvitation();

        invit.init(true, null);
        invit.initByTrainingPlanInstanceId(self.InstanceId());
        invit.ParentExplorer = trainingPlanManager;
        //        invit.loadView();
    }

    , ShowExportWorkoutModal: function () {
        var self = this;

        $('body').css('cursor', 'wait');
        var strData = self.getSringify();
        $.ajax({
            type: "POST",
            url: ContextPath + "UserCallendar/SetExportModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            $('body').dynamicForm({
                ActionName: "UserCallendar/ExportWorkouts",
                Parameters: {}
            });
        });
    }

    , ToggleMapping: function () {
        var self = this;
        if (self.Mapped()) {
            var arr = self.MappedIds();
            var index = arr.indexOf(self.MappedObject.Id());
            if (index > -1) {
                arr.splice(index, 1);
            }
            self.MappedObject.MappedPlanIds.remove(function (elem) { return elem == self.MappingId(); });
        } else {
            self.MappedIds().push(self.MappedObject.Id());
            self.MappedObject.MappedPlanIds.push(self.MappingId());

            if (self.MappedObject.Exercises) {
                var existExercs = self.Exercises();
                for (var i = 0, len = existExercs.length; i < len; i++) {
                    var exe = existExercs[i].serverModel;
                    exe.Id = 0;
                    var newExe = new TrainingExerciseModel(self.MappedObject);
                    newExe.initByModel(exe);
                    self.MappedObject.Exercises.push(newExe);
                }
            }
        }
    }


    , GoEdit: function () {
        var self = this;
        var goFn = function () {
            window.location = self.EditHref();
        };
        self.MappedObject.saveAndGo(goFn);
    }

    , removeEvent: function () {
        var self = this;
        modalAskModel.Show(translations.DeassignEvent, translations.DeassignEventMsg, function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/DeleteEventAssignation",
                data: { trainingPlanInstanceId: self.InstanceId() }
            })
             .done(function (result) {
                 if (result) {
                     self.GraphicEventModel().Id(0);
                 }
             });
        }, translations.Delete);
    }

    , ReloadExcercisesChart: function () {
        var self = this;

        if (!self.Exercises().length) return;

        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetExcerciseChartData",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: self.getSringify()
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.ExcercisesChart.setZones(result);
            self.ExcercisesChart.initByModel(result);
        });

    }
    , getExcercise: function (id) {
        if (!self.Exercises) return null;

        return _.find(self.Exercises, function (e) { return e.Id() == id; });
    }
};
// plan startu w zawodach

TrainingPlanCompetitionModel = function (parent) {
    var self = this;
    //self.Parent = parent;

    _.bindAll(self, 'AddPlanToCallendar');

    BaseKoModelObject.call(this, parent);

    self.CompetitionId = ko.observable(0);
    self.Id = ko.observable(0);
    self.LogoId = ko.observable(0);
    self.Description = ko.observable('');
    self.EndDate = ko.observable('');
    self.Name = ko.observable('');
    self.Priority = ko.observable(1);
    self.StartDate = ko.observable('');
    self.DisciplineId = ko.observable(0);
    self.CategoryId = ko.observable(0);
    self.PeriodId = ko.observable(0);
    self.PeriodPlanModel = new PeriodPlanModel(self);
    self.ShowPlanOnUserCal = ko.observable(false);

    self.PriorityName = ko.computed(function () {
        var s = _.findWhere(self.Priorities, { value: self.Priority() });
        if (s) return s.text;
        return '';
    });
    self.PriorityIcon = ko.computed(function () {
        var s = _.findWhere(self.Priorities, { value: self.Priority() });
        if (s) return '/Content/images/icons/' + s.icon + '.png';
        return '';
    });
    self.LogoUrl = ko.computed(function () {
        return ContextPath + "Image/GetImage?width=1000&height=600&id=" + self.LogoId();
    });
    self.HasLogo = ko.computed(function () {
        return self.LogoId() != 0;
    });
    self.InUserPlans = ko.computed(function () {
        return self.ShowPlanOnUserCal();
    });

    //self.ContestChooserModel = ko.observable(null);

}
TrainingPlanCompetitionModel.inheritsFrom(BaseKoModelObject);
TrainingPlanCompetitionModel.prototype.Priorities = [{ value: 1, text: translations.Priority_High, icon: 'prio_high_19' }
               , { value: 2, text: translations.Priority_Medium, icon: 'prio_med_19' }
               , { value: 3, text: translations.Priority_Low, icon: 'prio_low_19' }];
TrainingPlanCompetitionModel.prototype.init = function () {
    var self = this;
    self.CompetitionId(0);
    self.Id(0);
    self.LogoId(0);
    self.Description('');
    self.EndDate('');
    self.Name('');
    self.Priority('');
    self.StartDate('');
    self.PeriodId(0);
    self.PeriodPlanModel.init();
    self.InUserPlans(false);
};
TrainingPlanCompetitionModel.prototype.initByModel = function (serverModel) {
    var self = this;
    if (serverModel) {
        self.BaseObj = serverModel;
        self.CompetitionId(serverModel.CompetitionId);
        self.Id(serverModel.Id);
        self.LogoId(serverModel.LogoId);
        self.Description(serverModel.Description);
        self.EndDate(serverModel.EndDate);
        self.Name(serverModel.Name);
        self.StartDate(serverModel.StartDate || serverModel.DateStr);
        self.PeriodId = ko.observable(serverModel.PeriodId);
        self.ShowPlanOnUserCal(serverModel.InUserPlans);

        var prio = serverModel.Priority ? parseInt(serverModel.Priority) : 1;
        if (!prio) prio = 1;
        self.Priority(prio);

        if (serverModel.PeriodPlanModel) {
            self.PeriodPlanModel.initByModel(serverModel.PeriodPlanModel);
        } else {
            //self.PeriodPlanModel.StartTime(prio);
            self.PeriodPlanModel.Priority(prio);
        }


    }
};
TrainingPlanCompetitionModel.prototype.getClearData = function () {
    var self = this;
    var data = {
        CompetitionId: self.CompetitionId(),
        Id: self.Id(),
        LogoId: self.LogoId(),
        Description: self.Description(),
        EndDate: self.EndDate(),
        Name: self.Name(),
        Priority: "" + self.Priority(),
        StartDate: self.StartDate(),
        DisciplineId: self.DisciplineId(),
        CategoryId: self.CategoryId(),
    }
    return data;
};
TrainingPlanCompetitionModel.prototype.Remove = function () {
    var self = this;
    modalAskModel.Show(translations.Delete, translations.DeleteCompetitionPlanMsg, function () { self.Parent.Competitions.remove(self); }, translations.Delete);
};
TrainingPlanCompetitionModel.prototype.Edit = function () {
    var self = this;
    self.OpenEditContestInfo();
    /*
     self.Parent.ActiveCompetition(self);
 
     var container = $('#competiitonEditModalContainer');
     loadAndBindTemplate({
         container: container,
         existingElem: $('#competitionEditorModal'),
         ajaxPath: 'TrainingPlan/ModalCompetitionEditor',
         bindData: null,
         callback: function () {
             container.find('#competitionEditorModal').modal('show');
             
         }
     });
     */
};
TrainingPlanCompetitionModel.prototype.ContestChooserModel = ko.observable(null);
TrainingPlanCompetitionModel.prototype.ChooseInitLoadCallback = function (init) {
    var self = this;
    /*            var init = {
                        selectedContestId: 0,
                        discId: 0,
                        catId: 0,
                        date: (new Date()).toJSON()                    */

    init.selectedContestId = self.CompetitionId();
    if (self.StartDate()) init.date = self.StartDate();
    return init;
}
TrainingPlanCompetitionModel.prototype.ChooseSaveCallback = function (elem) {
    var self = this;
    self.CompetitionId(elem.SelectedContestId());
    self.StartDate(elem.ContestsDate());
    self.Name(elem.ContestsName());

    self.DisciplineId(elem.DisciplineManager.SelectedTypeId());
    self.CategoryId(elem.DisciplineManager.SelectedSubTypeId());
}
TrainingPlanCompetitionModel.prototype.OpenEditContestInfo = function () {
    var self = this;
    var elem = self.ContestChooserModel();
    if (elem == null) {
        elem = new ContestChooserModel(self);


        elem.DisplayDiscipline(true);
        //elem.OnlyFuture = true;
        self.ContestChooserModel(elem);
    }
    elem.InitLoadCallback = function (init) { return self.ChooseInitLoadCallback(init); };
    elem.SaveCallback = function (elem) { self.ChooseSaveCallback(elem); };

    loadAndBindTemplate({
        simpleContainer: 'chooseContestModalPlanContainer',
        existingElem: $('.chooseContestModal'),
        ajaxPath: 'Period/ContestChooserModal',
        bindData: self,
        callback: function () {
            elem.inintDataTables();
            self.ContestChooserModel().openEditContestInfo();
        }
    });
}
TrainingPlanCompetitionModel.prototype.AddPlanToCallendar = function () {
    var self = this;
    self.PeriodPlanModel.OpenEdit();
}
TrainingPlanCompetitionModel.prototype.UpdateVisibility = function () { }
TrainingPlanCompetitionModel.prototype.ParamsVisibility = function () { return null; }

;///obiekt dlugiego planu
function TrainingPlanModel(parent) {
    var self = this;
    self.Parent = parent;

    self.initMode = true;

    //#region properties

    self.DisciplineManager = new TrainingTypeControl(
       {
           mainId: 0,
           subId: 0,
           typeChangeCallback: null,
           clubId: 0,
           multi: true,
           baseCtrl: null,
           disciplines: true,
           multiSubs: false,
           allInstedNone: true,
           selectNoneIfNoChoise: true
       });

    self.TrainingTypeManager = new TrainingTypeControl(
   {
       mainId: 0,
       subId: 0,
       typeChangeCallback: function () {
           //tylko 3
           while (self.TrainingTypeManager.SelectedTypes().length > 3) {
               self.TrainingTypeManager.SelectedTypes()[0].Select();
           }
           //self.CalculateTrainingTypesCols();
       },
       clubId: 0,
       multi: true,
       baseCtrl: null,
       disciplines: false,
       multiSubs: false,
       allInstedNone: true,
       selectNoneIfNoChoise: false
   });

    self.AdditionalTrainingTypeManager = new TrainingTypeControl(
   {
       mainId: 0,
       subId: 0,
       typeChangeCallback: function () {
           //self.CalculateTrainingTypesCols();
       },
       clubId: 0,
       multi: true,
       baseCtrl: null,
       disciplines: false,
       multiSubs: false,
       allInstedNone: true,
       selectNoneIfNoChoise: true
   });

    self.AuthorCoachId = ko.observable(0);
    self.DescriptionId = ko.observable(0);
    self.Id = ko.observable(0);
    self.OwnerId = ko.observable(0);
    self.Weeks = ko.observable(0);
    self.CustomTarget = ko.observable('');
    self.Description = ko.observable('');
    self.EndDate = ko.observable('');
    self.Level = ko.observable('');
    self.Name = ko.observable('');
    self.StartDate = ko.observable('');
    self.Url = ko.observable('');
    self.CanEdit = ko.observable(false);
    self.Male = ko.observable(null);
    self.AuthorCoach = ko.observable(new SimpleCoachModel(self));
    self.Levels = ko.observableArray([]);
    self.PlanTargets = ko.observableArray([]);
    self.SeasonTargets = ko.observableArray([]);
    self.TrainingTargets = ko.observableArray([]);

    self.WithCoach   = ko.observable(false);
    self.ForGroup    = ko.observable(false);
    self.MaxWeekLoad = ko.observable(0);
    self.AvgWeekLoad = ko.observable(0);

    self.UseAdditionalTrainingTypes = ko.observable(false);

    self.TargetUserId = ko.observable(self.Parent.UserId);
    self.UserTrainingPlanId = ko.observable(0);

    self.AreSomeNotViewedComments = ko.observable(false);

    self.PlanProfileId = ko.observable(1);

    self.PeriodProgramId = ko.observable(0);    

    self.Competitions = ko.observableArray([]);
    self.ActiveCompetition = ko.observable(null);

    self.CoachName = ko.computed(function () {
        var coach = self.AuthorCoach();
        var id = coach.IdObs();
        if (id < 0) {
            return coach.Nick;
        } else if (id > 0) {
            return coach.DisplayName;
        }
        return '';
    });
    self.CoachImg = ko.computed(function () {
        var coach = self.AuthorCoach();
        if (coach.IdObs() != 0 && coach.PhotoId) {
            return coach.ImageUrl;
        }
        return '';
    });

    self.setAutoWeeks = function () {
        var sD = new Date(self.StartDate());
        var eD = new Date(self.EndDate());
        var diff = Math.ceil((eD - sD) / (1000 * 60 * 60 * 24 * 7));
        if (diff >= 0) {
            self.initMode = true;
            self.Weeks(diff);
            self.initMode = false;
        }
    }
    self.StartDate.subscribe(function (newValue) {
        if (newValue && !self.initMode) {
            self.setAutoWeeks();
        }
    });
    self.EndDate.subscribe(function (newValue) {
        if (newValue && !self.initMode) {
            self.setAutoWeeks();
        }
    });
    self.Weeks.subscribe(function (newValue) {
        if (newValue && !self.initMode) {
            var sD = new Date(self.StartDate());
            var diff = parseInt(self.Weeks());
            if (diff < 1) {
                self.Weeks(1);
            } else {
                var eD = sD.valueOf() + diff * 1000 * 60 * 60 * 24 * 7;
                var date = new Date(eD);
                self.initMode = true;
                self.EndDate(dateToString(date));
                self.initMode = false;
            }
        }
    });

    self.TrainingTypesCols = ko.observableArray([]);
    self.CallbackToRecalculateCols = [];
    self.CalculateTrainingTypesColsTimeOut = null;
    self.CalculateTrainingTypesColsTimeOut2 = null;
    self.CalculateTrainingTypesCols = function () {
        if (self.CalculateTrainingTypesColsTimeOut) clearTimeout(self.CalculateTrainingTypesColsTimeOut);
        self.CalculateTrainingTypesColsTimeOut = setTimeout(self.coreCalculateTrainingTypesCols, 300);

        if (self.CalculateTrainingTypesColsTimeOut2) clearTimeout(self.CalculateTrainingTypesColsTimeOut2);
        self.CalculateTrainingTypesColsTimeOut2 = setTimeout(self.coreCalculateTrainingTypesCols, 800);
    }
    self.coreCalculateTrainingTypesCols = function () {
        if (!self.initMode) {
            self.TrainingTypesCols.removeAll();

            var mainTypesIds = self.TrainingTypeManager.SelectedTypeIds();
            var additionalTypesIds = self.AdditionalTrainingTypeManager.SelectedTypeIds();

            if (!mainTypesIds.length && (!additionalTypesIds.length || (additionalTypesIds.length == 1 && additionalTypesIds[0]==0))) {
                self.TrainingTypesCols.push({ types: [self.TrainingTypeManager.GetTypeById(0)], ids: [0], Additional: false });
            } else {
                if (mainTypesIds.length) _(mainTypesIds).each(function (elem) {
                    self.TrainingTypesCols.push({ types: [self.TrainingTypeManager.GetTypeById(elem)], ids: [elem], Additional: false });
                });
                if (additionalTypesIds.length && self.UseAdditionalTrainingTypes())
                    self.TrainingTypesCols.push({ types: self.AdditionalTrainingTypeManager.SelectedTypes(), ids: additionalTypesIds, Additional: true });
            }

            if (self.CallbackToRecalculateCols.length) {
                _(self.CallbackToRecalculateCols).each(function (func) { if (func) func(); });
            }
        }
    };

    self.AvailableLevelGroups = ko.observableArray([]);

    self.IgnoreDates = ko.observable(false);

    /*
        self.ColsNr = ko.computed(function () {        
            return self.TrainingTypesCols().length;
        });
        self.ColsWidthPerc = ko.computed(function () {
            return 100.0 / self.TrainingTypesCols();
        });
    */

    //#endregion

    //#region init i save

    self.init = function () {
        self.initMode = true;
        self.AuthorCoachId(0);
        self.DescriptionId(0);
        self.Id(0);
        self.OwnerId(0);
        self.Weeks(0);
        self.CustomTarget('');
        self.Description('');
        self.EndDate('');
        self.Level('');
        //self.LoadPlanData('');
        self.Name('');
        self.StartDate('');
        self.Url('');
        self.CanEdit(false);
        self.Male(null);
        self.AuthorCoach(new SimpleCoachModel(self));
        self.DisciplineManager.DeselectAll();
        self.Levels.removeAll();
        self.PlanTargets.removeAll();
        self.SeasonTargets.removeAll();
        self.TrainingTargets.removeAll();
        self.TrainingTypeManager.DeselectAll();
        self.AdditionalTrainingTypeManager.DeselectAll();
        self.UseAdditionalTrainingTypes(false);

        self.WithCoach(false);
        self.ForGroup(false);
        self.MaxWeekLoad(0);
        self.AvgWeekLoad(0);

        self.TargetUserId(self.Parent.UserId);
        self.UserTrainingPlanId(0);

        self.PlanProfileId(1);
        self.AreSomeNotViewedComments(false);

        self.PeriodProgramId (0);

        self.Competitions.removeAll();

        /*
        self.AdditionalTrainingTypeManager.SelectNoneType();
        self.DisciplineManager.SelectNoneType();
        self.TrainingTypeManager.SelectNoneType();
        */
        self.ReloadPlanLists();

        self.initMode = false;
        self.CalculateTrainingTypesCols();
    };

    self.initDate = function () {
        self.StartDate(dateToString(new Date()));
        self.Weeks(1);
    }

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.initMode = true;

            self.BaseObj = serverModel;
            self.AuthorCoachId(serverModel.AuthorCoachId);
            self.DescriptionId(serverModel.DescriptionId);
            self.Id(serverModel.Id);
            self.OwnerId(serverModel.OwnerId);
            self.Weeks(serverModel.Weeks);
            self.CustomTarget(serverModel.CustomTarget);
            self.Description(serverModel.Description);
            self.EndDate(serverModel.EndDate);
            self.Level(serverModel.Level);
            self.Name(serverModel.Name);
            self.StartDate(serverModel.StartDate);
            self.Url(serverModel.Url);
            self.CanEdit(serverModel.CanEdit);
            self.Male(serverModel.Male);
            self.AuthorCoach().initByModel(serverModel.AuthorCoach);
            self.UseAdditionalTrainingTypes(serverModel.UseAdditionalTrainingTypes);

            self.WithCoach(serverModel.WithCoach);
            self.ForGroup(serverModel.ForGroup);
            self.MaxWeekLoad(serverModel.MaxWeekLoad);
            self.AvgWeekLoad(serverModel.AvgWeekLoad);

            self.TargetUserId(serverModel.TargetUserId);
            self.UserTrainingPlanId(serverModel.UserTrainingPlanId);

            self.AreSomeNotViewedComments(serverModel.AreSomeNotViewedComments);

            self.PlanProfileId(serverModel.PlanProfileId);
            TrainingPlanLib.SetActiveProfile(serverModel.PlanProfileId);

            self.PeriodProgramId(serverModel.PeriodProgramId);

            self.IgnoreDates(serverModel.IgnoreDates);
            if (!serverModel.IgnoreDates && typeof (periodsModel) != 'undefined' && periodsModel && periodsModel.SelectdPeriod()) {
                self.IgnoreDates(periodsModel.SelectdPeriod().IgnoreDates());
            }

            koArrayCopy(serverModel.Levels, self.Levels, false, false);

            koArrayCopy(serverModel.PlanTargets, self.PlanTargets, false, false);

            koArrayCopyComplex(serverModel.SeasonTargets, self.SeasonTargets, false,
				function (e) { var p = new PlanDescriptTargetModel(self); p.initByModel(e); return p; });

            koArrayCopyComplex(serverModel.TrainingTargets, self.TrainingTargets, false,
				function (e) { var p = new PlanDescriptTargetModel(self); p.initByModel(e); return p; });

            koArrayCopyComplex(serverModel.Competitions, self.Competitions, false,
				function (e) { var p = new TrainingPlanCompetitionModel(self); p.initByModel(e); return p; });


            self.DisciplineManager.DeselectAll();
            _(serverModel.DisciplineIds).each(function (id) { self.DisciplineManager.SelectTypeById(id); });

            self.TrainingTypeManager.DeselectAll();
            _(serverModel.TrainingTypesIds).each(function (id) { self.TrainingTypeManager.SelectTypeById(id); });

            self.AdditionalTrainingTypeManager.DeselectAll();
            _(serverModel.AdditionalTrainingTypesIds).each(function (id) { self.AdditionalTrainingTypeManager.SelectTypeById(id); });

            /*
            if (serverModel.LoadPlanData) {
                var loadPlanData = JSON.parse(serverModel.LoadPlanData);
                if (loadPlanData.ApplayedProfile) {
                    //self.ApplayedProfile().initByModel(loadPlanData.ApplayedProfile);
                    //self.ActiveProfileId(loadPlanData.ApplayedProfile.id);
                }
            }
            */
            self.ReloadPlanLists();

            self.initMode = false;
            self.CalculateTrainingTypesCols();
        }
    };

    self.getClearData = function () {
        var loadPlanData = {
            //ApplayedProfile: self.ApplayedProfile().getClearData()
        }

        var data = {
            AuthorCoachId: self.AuthorCoachId(),
            Name: self.Name(),
            DescriptionId: self.DescriptionId(),
            Id: self.Id(),
            OwnerId: self.OwnerId(),
            Weeks: self.Weeks(),
            CustomTarget: self.CustomTarget(),
            Description: self.Description(),
            StartDate: self.StartDate(),
            EndDate: self.EndDate(),
            Level: self.Level(),
            LoadPlanData: JSON.stringify(loadPlanData),
            Url: self.Url(),
            //CanEdit: self.CanEdit(),
            Male: self.Male(),
            //AuthorCoach: self.AuthorCoach().getClearData(),
            Levels: GetDataFromKoArray(self.Levels),
            PlanTargets: GetDataFromKoArray(self.PlanTargets),
            SeasonTargets: GetDataFromKoArray(self.SeasonTargets),
            TrainingTargets: GetDataFromKoArray(self.TrainingTargets),
            TrainingTypesIds: self.TrainingTypeManager.SelectedTypeIds(),
            AdditionalTrainingTypesIds: self.AdditionalTrainingTypeManager.SelectedTypeIds(),
            DisciplineIds: self.DisciplineManager.SelectedTypeIds(),
            UseAdditionalTrainingTypes: self.UseAdditionalTrainingTypes(),
            TargetUserId: self.TargetUserId(),
            UserTrainingPlanId: self.UserTrainingPlanId(),
            PlanProfileId: self.PlanProfileId(),
            Competitions: GetDataFromKoArray(self.Competitions),
            WithCoach  : self.WithCoach  (),
            ForGroup   : self.ForGroup   (),
            MaxWeekLoad: self.MaxWeekLoad(),
            AvgWeekLoad: self.AvgWeekLoad(),
            CoachIds: [],
            MailAnalRequests:false
        }

        if (self.PeriodProgramId() && typeof (periodsModel) != 'undefined' && periodsModel && periodsModel.SelectdPeriod()) {
            var period = periodsModel.SelectdPeriod();
            if (period) {
                data.CoachIds = period.Coaches();
                data.MailAnalRequests = period.MailAnalRequests();
            }
        }

        return data;
    };
    self.getSringify = function () {
        return JSON.stringify(self.getClearData());
    }

    self.Save = function (data, event) {
        var data = self.getSringify();
        var isNew = self.Id() == 0;
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetPlanModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data
        })
        .done(function (serverModel) {
            if (serverModel) {
                TrainingPlanLib.ClearCacheProfileForDate(null, null, serverModel.Id);
                self.initByModel(serverModel);
                self.Parent.ReloadPlan(null, self.Id());
                ShowSavedAlert();
            }
            self.CloseEdit();
        });

        /*
        if (self.PeriodProgramId() && typeof (periodsModel) != 'undefined' && periodsModel && periodsModel.SelectdPeriod()) {
            periodsModel.SelectdPeriod().save();
        }*/
    }

    //#endregion

    self.ExcelDownloadLink = ko.computed(function () {
        return ContextPath + "TrainingPlan/DownloadExcelPlanFile?trainingPlanId=" + self.Id() + "&targetUserId=" + self.TargetUserId();
    }, self);

    self.WaitImportState = ko.observable(false);

    self.Edit = function () {
        self.Parent.ActivePlan(self);

        var container = $('#planEditModalContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: $('#planEditorModal'),
            ajaxPath: 'TrainingPlan/ModalPlanEditor',
            bindData: null,
            callback: function () {
                container.find('#planEditorModal').modal('show');
                $("#trainingPlanExcel_fileupload").fileupload({
                    dataType: 'json',
                    add: function (e, data) {
                        self.WaitImportState(true);
                        data.submit();
                    },
                    done: function (e, data) {
                        var serverModel = data.result;
                        if (serverModel) {
                            self.initByModel(serverModel);
                            self.Parent.ReloadPlan(null, self.UserTrainingPlanId());
                        }
                        self.WaitImportState(false);
                    }
                });
            }
        });
    }

    self.CloseEdit = function () {
        if (!self.Id() && self.Parent.AvailablePlans().length) {
            self.Parent.ActivePlanId(self.Parent.AvailablePlans()[0].id);
        }

        self.CancelChanges();

        $('#planEditorModal').modal('hide');
    }

    self.EditComments = function () {
        communityExplorerModel.openUserTrainingPlanDialog(self.UserTrainingPlanId());
    }

    self.AddSeasonTarget = function () {
        var p = new PlanDescriptTargetModel(self);
        self.SeasonTargets.push(p);
    }
    self.AddTrainingTarget = function () {
        var p = new PlanDescriptTargetModel(self);
        self.TrainingTargets.push(p);
    }
    self.AddCompetitionPlan = function () {
        var p = new TrainingPlanCompetitionModel(self);
        self.Competitions.push(p);
        p.Edit();
    }

    self.Delete = function () {
        modalAskModel.Show(
            translations.DeleteTrainingPlan,
            translations.DeleteTrainingPlanMsg,
            self.coreDelete,
            translations.Delete);
    }

    self.coreDelete = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/DeletePlan",
            data: { planId: self.Id() }
        })
         .done(function (result) {
             if (result == "ok") {
                 $('#planEditorModal').modal('hide');
                 self.Parent.ReloadPlan(null, 0);
             }
         });
    }


    self.registerList = function (allItems, selIds, newList) {
        newList.removeAll();
        if (allItems)
            for (var i = 0, len = allItems.length; i < len; i++) {
                var elem = allItems[i];
                var newElem = jQuery.extend({}, elem);
                newElem.Selected = ko.computed(function () {
                    return $.inArray(this.value, selIds()) >= 0;
                }, newElem);
                newElem.Select = function () {
                    var index = $.inArray(this.value, selIds());
                    if (index >= 0) {
                        selIds.splice(index, 1);
                    } else {
                        selIds.push(this.value);
                    }
                }
                newList.push(newElem);
            }
    }
    self.ReloadPlanLists = function () {
        var profile = TrainingPlanLib.GetProfile(self.PlanProfileId());
        if (profile) {
            self.registerList(profile.LevelGroups, self.Levels, self.AvailableLevelGroups);
        }

    }
    self.ReloadPlanLists();

    self.initMode = false;
}
;
///skladowa profilu planu treningowego
///nz podstawie tych obiektow sa tworzone cykle treningowe
function TrainingPlanProfilePeriodModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.TypeId = ko.observable(0);
    self.Name = ko.observable('');
    self.StartWeek = ko.observable(0);
    self.EndWeek = ko.observable(0);
    self.Targets = ko.observableArray([]);
    self.SecondaryTargets = ko.observableArray([]);

    self.NextPeriod = null;

    self.LeftWeek = ko.computed(function () {
        return Math.round(self.Parent.LengthMultip() * (self.StartWeek() - 1));
    });
    self.RightWeek = ko.computed(function () {
        return Math.round(self.Parent.LengthMultip() * self.EndWeek());
    });
    self.Weeks = ko.computed(function () {
        return self.RightWeek() - self.LeftWeek();
    });

    self.Left = ko.computed(function () {
        return self.LeftWeek() * self.Parent.WeekWidth();
    });
    self.Width = ko.computed(function () {
        return self.Weeks() * self.Parent.WeekWidth();
    });

    self.initByModel = function (model) {
        self.Id(model.id);
        self.TypeId(model.typeId);
        self.Name(model.name);
        self.StartWeek(model.startWeek);
        self.EndWeek(model.endWeek);
        var ts = _(trainingPlanManager.Trgets());
        koArrayCopyComplex(model.targets, self.Targets, false,
            function (e) {
                return ts.findWhere({ value: e });
            });
        koArrayCopyComplex(model.secondaryTargets, self.SecondaryTargets, false,
            function (e) {
                return ts.findWhere({ value: e });
            });
    }
    /*
    self.getClearData = function () {
        var data = {
            id: self.Id(),
            name: self.Name(),
            startWeek: self.LeftWeek() + 1,
            endWeek: self.RightWeek(),
            targets: GetDataFromKoArray(self.Targets, function (e) { return e.value; }),
            secondaryTargets: GetDataFromKoArray(self.SecondaryTargets, function (e) { return e.value; })
        }
        return data;
    };
    */

    //#region przesowanie cykli
    self.DragMode = ko.observable(false);
    self.StartX = 0;
    self.StartValue = self.EndWeek();
    self.selectedElement = 0;
    self.containerSelectedElement = 0;
    self.containerSelectedElementWidth = 0;
    self.selectElement = function (data, evt) {
        if (!self.NextPeriod) return;
        self.DragMode(true);
        self.selectedElement = $(evt.target);
        self.containerSelectedElement = self.selectedElement.closest('.modal');
        self.containerSelectedElementWidth = self.selectedElement.closest('.periods').width();
        self.StartX = evt.clientX;
        self.StartValue = self.EndWeek();

        self.containerSelectedElement.off("mousemove").on("mousemove", self.moveElement);
        $('body').off("mouseup").on("mouseup", self.deselectElement);
    }
    self.moveElement = function (evt) {
        var multip = self.Parent.LengthMultip();
        var dx = self.StartX - evt.clientX;
        var dx_prc = dx / self.containerSelectedElementWidth;
        var weeks = Math.round(dx_prc * self.Parent.CurrentLength());
        var weeksMultip = weeks / multip;

        var thisEnd = self.StartValue - weeksMultip;
        if (self.EndWeek() != thisEnd) {
            var thisEndM = thisEnd * multip;
            var nextstart = thisEndM + 1;
            if (thisEndM > self.LeftWeek() && nextstart <= self.NextPeriod.RightWeek()) {
                self.EndWeek(thisEnd);
                if (self.NextPeriod) {
                    self.NextPeriod.StartWeek(self.EndWeek() + 1);
                }
            }
        }
    }

    self.deselectElement = function (evt) {
        if (self.containerSelectedElement != 0) {
            self.containerSelectedElement.off("mousemove");
            self.containerSelectedElement.off("mouseout");
            $('document').off("mouseup");
            self.selectedElement = 0;
            self.DragMode(false);
        }
    }
    //#endregion
}
///opis profilu planu treningowego
///profil to taki szablon dlugiego planu (przygotownie, baza, rozbudowa, zawody, ...)
function TrainingPlanProfileModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Name = ko.observable('');
    self.MinLength = ko.observable(0);
    self.Periods = ko.observableArray([]);

    self.CurrentLength = ko.computed(function () {
        if (!self.Parent.ActivePlan()) return 1;
        return parseInt(self.Parent.ActivePlan().Weeks());
    });
    self.LengthMultip = ko.computed(function () {
        return 1.0 * self.CurrentLength() / self.MinLength();
    });

    self.WeeksArr = ko.computed(function () {
        var arr = [];
        var len = self.CurrentLength();
        for (var i = 1; i <= len; i++) {
            arr.push(i);
        }
        return arr;
    });
    self.WeekWidth = ko.computed(function () {
        return 100.0 / self.CurrentLength();
    });

    self.initByModel = function (model) {
        if (model) {
            self.Id(model.id);
            self.Name(model.name);
            self.MinLength(model.minLength);
            var previousElem = null;
            koArrayCopyComplex(model.periods, self.Periods, false,
                    function (e) {
                        var p = new TrainingPlanProfilePeriodModel(self); p.initByModel(e);
                        if (previousElem) {
                            previousElem.NextPeriod = p;
                        }
                        previousElem = p;
                        return p;
                    });
        }
    }

    self.getClearData = function () {
        var data = {
            id: self.Id(),
            name: self.Name(),
            minLength: self.CurrentLength(),
            periods: GetDataFromKoArray(self.Periods)
        }
        return data;
    };
    /*
    self.ApplayToUserWeeks = function (_weeks) {
        _(self.Periods()).each(
            function (e) {
                e.ApplayToUserWeeks(_weeks);
            });
    };
    */
}
;
TrainingPlanLibrary = function (parent) {
    var self = this;
    self.Parent = parent;

    self.Pattern = ko.observable('');

    self.InitMode = false;

    self.Vertical = true;

    self.PageNr = -1;
    self.PageSize = 18;

    self.ResultsInPage = -1;
    self.prevSearchData = null;

    self.Loading = false;

    self.AvailableLevelGroups = ko.observableArray([]);
    self.AvailablePeriodTypes = ko.observableArray([]);
    self.AvailableTrgets = ko.observableArray([]);

    self.Level = ko.observable('');
    self.PeriodType = ko.observable('');
    self.PlanTarget = ko.observable('');

    //self.SearchQuery = ko.computed(self.buildSearchQuery, self);

    _.bindAll(self, 'Load', 'Search', 'SlideOpen', 'CoreOpen', 'Close', 'Add', 'bindWayPoint', 'buildSearchQuery', 'postLoad', 'enableDrag');

    self.TypeManager = new TrainingTypeControl(
        {
            mainId: 0,
            subId: 0,
            typeChangeCallback: self.Search,
            clubId: 0,
            multi: false,
            baseCtrl: null,
            disciplines: false,
            multiSubs: false,
            allInstedNone: true
        });

    self.Units = ko.observableArray([]);
    //self.Loaded = ko.observable(false);  


    //self.DragedPlanUnit =null;

    self.Level.subscribe(self.Search, self);
    self.PeriodType.subscribe(self.Search, self);
    self.PlanTarget.subscribe(self.Search, self);

    _.bindAll(self, 'InfinityLoad');
}
TrainingPlanLibrary.prototype = {
    buildSearchQuery: function () {
        var self = this;

        var data = {
            Pattern: self.Pattern(),
            PageSize: self.PageSize,
            PageNr: 0,
            Detailed: true,
            Level: self.Level() || '',
            PeriodType: self.PeriodType() || '',
            PlanTarget: self.PlanTarget() || '',
            TrainingTypeId: self.TypeManager.SelectedTypeId()
        };
        return data;
    }
    , Load: function () {
        var self = this;

        var profile = TrainingPlanLib.GetDefaultProfile();
        if (profile) {
            self.InitMode = true;

            self.RegisterList(profile.LevelGroups, self.AvailableLevelGroups);
            self.RegisterList(profile.PeriodTypes, self.AvailablePeriodTypes);
            self.RegisterList(profile.Trgets, self.AvailableTrgets);

            self.InitMode = false;
        }

        var data = self.buildSearchQuery();

        self.coreLoad(data);
    }
    , Search: function () {
        var self = this;

        if (self.InitMode) return;

        self.PageNr = 0;
        self.ResultsInPage = -1;

        var data = self.buildSearchQuery();

        self.coreLoad(data);
    }
    , InfinityLoad: function () {
        var self = this;

        if (self.Loading) {
            setTimeout(self.InfinityLoad, 100);
        } else {
            self.Loading = true;

            var data = self.buildSearchQuery();
            data.PageNr = ++self.PageNr;

            self.coreLoad(data);
        }
    }
    , coreLoad: function (data) {
        var self = this;
        if (!self.ResultsInPage) return;

        self.Loading = true;

        var strData = JSON.stringify(data);

        if (self.prevSearchData == strData) {
            self.Loading = false;
            return;
        }
        self.prevSearchData = strData;

        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetLibraryOfUnitsModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
       .done(function (serverModel) {
           if (serverModel) {
               self.ResultsInPage = serverModel.length;
               koArrayCopyComplex(serverModel, self.Units, data.PageNr, function (elem) {
                   var unit = new TrainingPlanUnitModel(null, self.Parent);
                   unit.initByModel(elem);
                   return unit;
               });
               if (serverModel.length) {
                   setTimeout(self.postLoad, 250);
               }
               self.Loading = false;
           }
       });
    }
    , AddUnit: function (unit) {
        var self = this;
        self.Units.push(unit);
        setTimeout(self.postLoad, 250);
    }
    , postLoad: function () {
        var self = this;
        self.bindWayPoint();
        self.enableDrag();
        self.enableDrop();
    }
    , enableDrag: function () {
        var self = this;
        $("#topSlideUnitsLibContainer .libUnit").draggable({
            appendTo: "body",
            helper: function () { return $('<img class="draggedIcon" src="' + ContextPath + 'Content/images/icons/plany_44.png" />'); },
            //cursor: "crosshair",
            cursorAt: { left: 20, top: 40 }
            /*, containment: "document", scroll: false*/
            /*start: function(event, ui) {
                self.DragedPlanUnit = ko.dataFor(ui.helper.prevObject[0]);
            },*/
        });
    }
    , enableDrop: function () {
        var self = this;

        self.topSliderDropTimeOut = null;
        $('#topSlider').droppable({
            drop: function () { self.topSliderDropTimeOut = setTimeout(function () { self.topSliderDropTimeOut = null; }, 200); },
            greedy: true
        });
        $(".calBody .body .day").droppable({
            drop: function (event, ui) {
                if (!self.topSliderDropTimeOut) {
                    var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                    var day = ko.dataFor(event.target);

                    if (day && dragedPlanUnit) {
                        day.AddLibPlan(dragedPlanUnit);
                    }
                }
            },
            hoverClass: "dropHighlight"
        });

        self.DropTimeout = null;

        $(".grafik.koGraphic .planDrop").droppable({
            drop: function (event, ui) {
                if (!self.topSliderDropTimeOut) {
                    if (self.DropTimeout) clearTimeout(self.DropTimeout);
                    self.DropTimeout = setTimeout(function () {
                        var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                        var day = ko.dataFor(event.target);
                        var hour = $(event.target).data('hour');

                        if (day && dragedPlanUnit) {

                            eventManager.JsGraphicModelManager.AddFromLib(day, hour, dragedPlanUnit);
                        }
                    }, 100);
                }
            },
            hoverClass: "dropHighlight"
        });

        $(".eog").droppable({
            drop: function (event, ui) {
                if (!self.topSliderDropTimeOut) {
                    if (self.DropTimeout) clearTimeout(self.DropTimeout);
                    self.DropTimeout = setTimeout(function () {
                        var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                        var eventModel = ko.dataFor(event.target);

                        if (eventModel && dragedPlanUnit) {
                            eventManager.currentEvent().cancelEdit();
                            eventModel.editEvent();
                            eventManager.InitLibPlanUnit(dragedPlanUnit.Id(), eventModel.Date, eventModel.StartTime, eventManager.currentEvent());

                            event.stopImmediatePropagation();

                            setTimeout(function () { $('#eventModal a[href="#eventPlan"]').tab('show'); }, 100);
                        }
                    }, 100);
                }
            },
            hoverClass: "dropHighlight"
        });

    }
    , bindWayPoint: function () {
        var self = this;
        var elem = $('.libUnits .libUnit').last()[0];
        var ctx = elem.closest(self.Vertical ? '.libUnits' : '.libUnitsContainer');
        if (elem) {
            var waypoint = new Waypoint({
                element: elem,
                handler: function (direction) {
                    self.InfinityLoad();
                },
                offset: self.Vertical ? 'bottom-in-view' : 'right-in-view',
                horizontal: !self.Vertical,
                context: ctx
            });
        } else {
            alert('buuu!!!');
        }
    }
    , reset: function () {
        var self = this;
        self.Level('');
        self.PeriodType('');
        self.PlanTarget('');
        self.prevSearchData = null;
        self.ResultsInPage = -1;
        self.TypeManager.SelectTypeById(0);
    }
    , SlideOpen: function () {
        var self = this;
        $('body').css('cursor', 'wait');

        self.reset();

        if (!getTopSlide().find('#topSlideUnitsLibContainer').length) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/SlideUnitsLib"
            })
             .done(function (result) {
                 setTopSlideContent(result);
                 setTimeout(function () {
                     topSlideapplayBinding();
                     self.CoreOpen();
                 }, 100);
             });
        } else {
            self.CoreOpen();
        }
    }
    , CoreOpen: function () {
        var self = this;
        self.Vertical = false;
        self.Load();

        showTopSlide(true);
        $('body').css('cursor', 'default');
        JsRequestLog("trainingPlan.js", "TrainingPlanLibrary.CoreOpen", "");
    }
    , Close: function () {
        var self = this;
        hideTopSlide();
    }
    , RegisterList: function (allItems, newList) {
        newList.removeAll();
        newList.push({ text: translations.All.toLowerCase(), value: '' });
        if (allItems)
            for (var i = 0, len = allItems.length; i < len; i++) {
                var elem = allItems[i];
                var newElem = jQuery.extend({}, elem);
                newList.push(newElem);
            }
    }
    , Add: function () {
        var self = this;
        var unit = new TrainingPlanUnitModel(null, self.Parent);
        unit.Library(true);
        self.Units.push(unit);
        unit.Edit();
    }
}
;
///manager obiektow planu treningowego
function TrainingPlanManager() {
    var self = this;

    self.initMode = true;

    //#region init
    TrainingPlanLib.LoadProfiles();

    self.DisciplineManager = new TrainingTypeControl(
        {
            mainId: 0,
            subId: 0,
            typeChangeCallback: null,
            clubId: 0,
            multi: true,
            baseCtrl: null,
            disciplines: true,
            multiSubs: false,
            allInstedNone: true
        });
    self.TrainingTypeManager = new TrainingTypeControl(
        {
            mainId: 0,
            subId: 0,
            typeChangeCallback: null,
            clubId: 0,
            multi: false,
            baseCtrl: null,
            disciplines: false,
            multiSubs: false,
            allInstedNone: true
        });
    self.MultiTrainingTypeManager = new TrainingTypeControl(
        {
            mainId: 0,
            subId: 0,
            typeChangeCallback: null,
            clubId: 0,
            multi: true,
            baseCtrl: null,
            disciplines: false,
            multiSubs: false,
            allInstedNone: true
        });

    self.LevelGroups = ko.observableArray(TrainingPlanLib.LevelGroups);

    self.PeriodTypes = TrainingPlanLib.PeriodTypes;

    self.Trgets = TrainingPlanLib.Trgets;

    self.LoadDistributions = ko.observableArray(TrainingPlanLib.LoadDistributions);

    self.AvailableProfiles = TrainingPlanLib.AvailableProfiles;

    self.PlanProfiles = TrainingPlanLib.Profiles;

    self.WeekDays = [];
    for (var i = 1; i <= 7; i++) {
        self.WeekDays.push({ dayName: translations['day_' + i], dayShortCut: translations['dayShort_' + i] });
    }

    /*
    self.ValueTypes = ko.observableArray(TrainingPlanLib.ValueTypes);

    self.MinMaxValueTypes = ko.observableArray(TrainingPlanLib.MinMaxValueTypes);
*/

    //#endregion

    self.UserId = 0;

    self.WaitState = ko.observable(true);


    //#region callendar
    self.TargetsMode = ko.observable('T');
    self.ActivePlanId = ko.observable(0);
    self.ManagerStartDate = ko.observable('');
    self.ManagerEndDate = ko.observable('');
    self.ActivePlan = ko.observable(new TrainingPlanModel(self));

    self.CanEditActivePlan = ko.computed(function () {
        return self.ActivePlan().CanEdit();
    });

    self.ActivePlanId.subscribe(function (newValue) {
        if (newValue && !self.initMode) {
            self.ReloadPlan();
        }
    });

    self.ProgramMode = ko.observable('plan'); //participants
    self.ProgramMode.subscribe(function (newValue) {
        if (newValue && self.PeriodId()) {
            if (self.ProgramMode() == 'participants') {
                var participantManagerModel = initParticipantManager();
                participantManagerModel.Init(self.PeriodId());
                participantManagerModel.ShowPlanDataTable();
            }
        }
    });

    self.ActiveTrainingPlanProfileId = ko.computed(function () {
        TrainingPlanLib.ActiveProfileId(self.ActivePlan().PlanProfileId());
        return self.ActivePlan().PlanProfileId();
    });

    self.AvailablePlans = ko.observableArray([]);

    self.PlanPeriods = ko.observableArray([]);

    self.ActiveProfileId = ko.observable(null);
    self.ApplayedProfile = ko.observable(new TrainingPlanProfileModel(self));
    self.ActiveProfileId.subscribe(function (newValue) {
        if (!self.initMode) {
            self.ApplayedProfile().initByModel(_(self.AvailableProfiles()).findWhere({ id: self.ActiveProfileId() }));
        }
    });

    self.UserTrainingWeeks = ko.observableArray([]);
    self.SelectedUserTrainingWeek = ko.computed(function () {
        return _.find(self.UserTrainingWeeks(), function (w) { return w.IsCallendarSelected(); });
    });

    self.IsFirstWeekSelected = ko.computed(function () {
        var s = self.SelectedUserTrainingWeek();
        if (!s) return false;
        //return s.PlanWeekNr()==1;
        return s.PreviousWeek() == null || s.PreviousWeek() == s;
    });
    self.IsLastWeekSelected = ko.computed(function () {
        var s = self.SelectedUserTrainingWeek();
        if (!s) return false;
        //var a = self.ActivePlan();
        //if (!a) return false;
        //return s.PlanWeekNr() == self.ActivePlan().Weeks() || ;
        return s.NextWeek() == null || s.NextWeek() == s;
    });

    self.UserTrainingMonths = ko.observableArray([]);

    self.MaxLoadPLan = ko.observable(10);
    self.MaxLoadPLan.subscribe(function (newValue) {
        if (newValue) {
            self.MaxLoadPLan(Math.round(self.MaxLoadPLan()));
        }
    });
    self.LoadPLanTicks = ko.computed(function () {
        var max = self.MaxLoadPLan();
        var step = max / 4;
        var ticks = [
            max,
            Math.round(max / 4 * 3),
            Math.round(max / 2),
            Math.round(max / 4)
        ];
        return ticks;
    });

    self.LoadsCanvaWidth = ko.computed(function () {
        return self.UserTrainingWeeks().length * TrainingPlanWeekWidth;
    });

    self.LoadUserPlansModel = function (serverModel) {
        if (serverModel) {
            self.initMode = true;
            koArrayCopy(serverModel.AvailablePlans, self.AvailablePlans, false, false);

            self.ManagerStartDate(serverModel.FirstMondayDate || serverModel.StartDate);
            self.ManagerEndDate(serverModel.EndDate);

            if (serverModel.ActivePlan) {
                TrainingPlanLib.ActiveProfileId(serverModel.ActivePlan.PlanProfileId);
            }

            koArrayCopyComplex(serverModel.PlanPeriods, self.PlanPeriods, false,
             function (elem) {
                 var p = new TrainingPeriodPlanModel(self);
                 p.initByModel(elem);

                 return p;
             });

            koArrayCopyComplex(serverModel.UserTrainingWeeks, self.UserTrainingWeeks, false,
               function (elem) {
                   var p = new UserTrainingWeekModel(self);
                   p.initByModel(elem);
                   return p;
               });

            var prevWeek = null;
            var pp = self.UserTrainingWeeks();
            for (var i = 0, len = pp.length; i < len; i++) {
                var p = pp[i];
                p.PreviousWeek(prevWeek ? prevWeek : p);
                p.WeekIndex(i);
                if (prevWeek && p.MonthNr() && p.MonthNr() == prevWeek.MonthNr()) {
                    p.InMonthDays(0);
                }
                p.InitPoints();
                if (p.CurrentLoadPLan() > self.MaxLoadPLan()) {
                    self.MaxLoadPLan(p.CurrentLoadPLan() * 1.1);
                }
                if (p.UserAvability() > self.MaxLoadPLan()) {
                    self.MaxLoadPLan(p.UserAvability() * 1.1);
                }

                prevWeek = p;
            }

            if (serverModel.ActivePlan) {
                self.ActivePlanId(serverModel.ActivePlan.Id);
                self.ActivePlan().initByModel(serverModel.ActivePlan);
            } else {
                self.ActivePlanId(0);
                self.ActivePlan().init();
            }

            self.ApplayPeriodsToUserWeeks();

            self.deleyedLoadTrimpChart();

            self.initMode = false;
        }
    }

    self.ReloadPlan = null;

    self.LoadUserPlans = function (userId, activePlanId, doNotOverrideReload, callback) {
        if (userId && _.isNumber(userId)) {
            self.UserId = userId;
        } else {
            userId = self.UserId;
        }
        if (typeof (activePlanId) == 'undefined' || activePlanId == null) activePlanId = self.ActivePlanId(); //jek jest 0 to niech zostanie, zeby dzialalo odswierzenie po delete

        if (!doNotOverrideReload) {
            self.ReloadPlan = self.LoadUserPlans;
        }

        self.initMode = true;
        self.SelectedUserId(self.UserId);
        self.initMode = false;

        self.WaitState(true);
        piorityBusy.Increase();
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/GetUserPlans",
            data: { userId: userId, start: self.ManagerStartDate(), end: self.ManagerEndDate(), activePlanId: activePlanId }
        })
        .done(function (serverModel) {
            self.LoadUserPlansModel(serverModel);

            if (typeof (ShowPlanComments) != 'undefined' && ShowPlanComments) self.showComments();

            if (callback) callback();

            piorityBusy.Decrease();
            self.WaitState(false);
        });
    }
    self.ReloadPlan = self.LoadUserPlans;
    self.LoadUserPlansBtn = function () {
        self.ReloadPlan();
        if (userCallendar) {
            userCallendar.coreRefresh();
        }
    }

    self.PeriodId = ko.observable(0);
    self.LoadPeriodPlan = function (planId, periodId, callback) {
        var uId = self.SelectedUserId();
        self.ReloadCalendar(uId);//, function () {
        if (uId) {
            self.LoadUserPlans(uId, self.ActivePlan().Id(), true);
        } else {
            self.UserId = 0;
            self.PeriodId(periodId);

            self.ReloadPlan = function () { self.LoadPeriodPlan(planId, periodId); };

            self.WaitState(true);
            piorityBusy.Increase();
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetPlan",
                data: { planId: planId, periodId: periodId }
            })
            .done(function (serverModel) {
                self.LoadUserPlansModel(serverModel);
                piorityBusy.Decrease();
                self.LoadPeriodPersons(serverModel.ActivePlan.Id);
                self.WaitState(false);
                if (callback) callback(serverModel.ActivePlan.Id);
            });
        }
        //});
    }

    self.CallendarUderId = 0;
    self.ReloadCalendar = function (userId, callback) {
        if (userCallendar) {
            if (self.CallendarUderId != userId) {
                self.CallendarUderId = userId;
                if (userId) {
                    userCallendar.TrainingPlanId(0);
                    userCallendar.coreRefresh('', '', null, userId, null, null, null, callback);
                } else {
                    userCallendar.TrainingPlanId(self.ActivePlan().Id());
                    userCallendar.coreRefresh('', '', null, 0, null, null, null, callback);
                }
            } else {
                if (callback) callback();
            }
        } else {
            if (callback) callback();
        }
    }

    self.currentTrainingPlanUnit = ko.observable(new TrainingPlanUnitModel(null, self));

    self.currentPlanPeriod = ko.observable(null);

    self.editTrainingPlanUnit = function (unitId, date, instanceId, timeOfDay, loadModal, editAsCopy, planId, scrollToEvent, week, day, noDaySpecified) {
        if (unitId) {
            self.currentTrainingPlanUnit().InitById(unitId, editAsCopy);
        } else if (instanceId) {
            self.currentTrainingPlanUnit().InitByInstanceId(instanceId, editAsCopy, scrollToEvent);
        } else {
            if (date && date != "null") {
                self.currentTrainingPlanUnit().InitForDate(date, timeOfDay, planId);
            } else {
                self.currentTrainingPlanUnit().InitForWeek(week, day, timeOfDay, planId);
            }
            self.currentTrainingPlanUnit().NoDaySpecified(noDaySpecified);
        }
        if (loadModal) {
            self.editUnitInModal();
        } else {
            self.InitEditorControls();
        }
    }
    self.editUnitInModal = function () {
        loadAndBindTemplate(
                {
                    container: $('#planUnitEditModalContainer'),
                    existingElem: '#planUnitEditorModal',
                    ajaxPath: 'TrainingPlan/ModalUnitEditor',
                    ajaxData: null,
                    bindData: null,
                    callback: function () {
                        $('#planUnitEditorModal').modal('show');
                        self.InitEditorControls();
                        initTrainingNaviBelt();
                    }
                });
    }
    self.InitEditorControls = function () {
        initTimePicker();
        //inintUpload();
        $('.tinymce').each(function () {
            if (this.renderTinymce != undefined) this.renderTinymce();
        });
        self.currentTrainingPlanUnit().ZoneChooser.InitZones();
    }

    self.openExcerciseEditor = function (excercise) {
        if (excercise) self.currentExcercise(excercise);

        $('#excerciseEditorModal').modal("show");

        $('.tinymce').each(function () {
            if (this.renderTinymce != undefined) this.renderTinymce();
        });
    }

    self.addPeriod = function (week) {
        var pm = new TrainingPeriodPlanModel(self);
        pm.StartDate(week.StartDate());
        pm.EndDate(week.StartDate());
        pm.StartWeek(week.PlanWeekNr());
        pm.StartDay(1);
        pm.UserId(self.UserId);
        pm.PlanId(self.ActivePlan().Id());
        pm.InitTargetsList();
        pm.Edit();
    }

    self.EditedSingleWeekTargetData = ko.observable(null);
    self.Percents = ko.observable(0);
    self.Description = ko.observable('');
    self.PercentsMode = ko.observable('3');
    self.PercentsMode.subscribe(function () { if (self.PercentsMode() == '3' || self.PercentsMode() == 3) self.Percents(0); });
    self.PercentsModeDesc = ko.computed(function () {
        return translations['TargetsPercentsMode_' + self.PercentsMode()];
    });
    self.togglePercentsMode = function () {
        self.PercentsMode((parseInt(self.PercentsMode()) + 1) % 4);
    }
    //self.openAddTypeCallback = null;
    self.openAddType = function (callback, SingleWeekTargetData) {
        self.MultiTrainingTypeManager.DeselectAll();
        var idsToSelect = SingleWeekTargetData.AllTypeIds();
        _.each(idsToSelect, function (det) { self.MultiTrainingTypeManager.SelectTypeById(det.type.Id); });

        self.EditedSingleWeekTargetData(SingleWeekTargetData);

        self.Percents(SingleWeekTargetData.Percents());
        self.Description(SingleWeekTargetData.Description());
        self.PercentsMode(SingleWeekTargetData.PercentsMode());

        //self.openAddTypeCallback = callback;
        self.MultiTrainingTypeManager.TypeChangeCallback = callback;
        $('.calBody.plan .chooseDiscip').modal('show');
    }
    self.ChooseDiscipline = function (data, event) {
        var eswtd = self.EditedSingleWeekTargetData();
        eswtd.Percents(self.Percents());
        eswtd.Description(self.Description());
        eswtd.PercentsMode(self.PercentsMode());

        if (eswtd.week) {
            eswtd.week.DeferedSave();
        }
        $('.chooseDiscip').modal('hide');
        //if (self.openAddTypeCallback) self.openAddTypeCallback(self.MultiTrainingTypeManager.SelectedTypes());
        self.MultiTrainingTypeManager.TypeChangeCallback = null;
    }
    self.EditedTargetsData = ko.observable(null);
    self.openAddTarget = function (TargetsData) {
        self.EditedTargetsData(TargetsData);
        $('.calBody.plan .chooseTargets').modal('show');
    }
    self.ChooseTargets = function (data, event) {
        self.EditedTargetsData().week.DeferedSave();
        $('.chooseTargets').modal('hide');
    }

    self.EditedWeek = ko.observable(null);
    self.OpenWeekEdit = function (week) {
        self.EditedWeek(week);
        var container = $('#weekEditModalContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: '#weekEditModal',
            ajaxPath: 'TrainingPlan/WeekEditModal',
            bindData: self,
            callback: function () {
                container.find('#weekEditModal').modal("show");
            }
        });
    }
    self.CloseWeekEdit = function () {
        $('#weekEditModal').modal("hide");
        self.EditedWeek().DeferedSave();
    }

    //#endregion

    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    }

    self.addPlan = function () {
        self.ActivePlan().init();
        self.ActivePlan().initDate();
        self.editPlan();
    }
    self.editPlan = function () {
        self.ActivePlan().Edit();
    }

    self.showExportModal = function () {
        var container = $('#planExportModalContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: container.find('#exportModal'),
            ajaxPath: 'UserCallendar/ExportSetup',
            bindData: new TrainingPlanExportModel(self),
            callback: function () {
                container.find('#exportModal').modal("show");
            }
        });
    };

    /*
    self.ApplayProfileToUserWeeks = function () {
        var _weeks = _(self.UserTrainingWeeks());
        if (_weeks.any()) {
            _weeks.each(function (e) { e.ClearProfile(); });
            self.ActivePlan().ApplayProfileToUserWeeks(_weeks);
        }
    };
    */
    self.OpenAppalyProfile = function () {
        $('#planPeriodEditorModal').modal('hide');

        self.ActiveProfileId(self.AvailableProfiles()[0]);

        var container = $('#periodTemplateModalContainer');
        loadAndBindTemplate({
            container: container,
            existingElem: $('#periodTemplateModal'),
            ajaxPath: 'TrainingPlan/ModalPeriodTemplate',
            bindData: null,
            callback: function () {
                container.find('#periodTemplateModal').modal('show');
            }
        });
    }
    self.AppalyProfile = function () {
        var periods = self.ApplayedProfile().Periods();
        var _weeks = _(self.UserTrainingWeeks());
        _(periods).each(function (period) {
            var pm = new TrainingPeriodPlanModel(self);
            pm.UserId(self.UserId);
            pm.PlanId(self.ActivePlan().Id());

            pm.StartWeek(period.LeftWeek());
            pm.StartDay(0);
            pm.Days(period.Weeks() * 7);

            pm.PeriodTypeId(period.TypeId());
            pm.Name(period.Name());
            pm.MainTargets(_(period.Targets()).pluck('value'));
            pm.SecondaryTargets(_(period.SecondaryTargets()).pluck('value'));

            pm.Save(null, null, true);
            self.PlanPeriods.push(pm);
        });
        self.ApplayPeriodsToUserWeeks();
        //setTimeout(function () { self.LoadUserPlans(); }, 1500);
        $('#periodTemplateModal').modal('hide');
    }

    self.ApplayPeriodsToUserWeeks = function (_weeks) {
        /*
                if (!_weeks) {
                    _weeks = _(self.UserTrainingWeeks());
                }
                _weeks.each(function (e) {
                    e.ClearProfile();
                });
                _(self.PlanPeriods()).each(function (e) {
                    e.ApplayToUserWeeks(_weeks);
                });*/
    };


    self.showComments = function () {
        self.ActivePlan().EditComments();
    }


    self.openUserCommunication = function () {
        loadContestComunications(self.UserId);
    }

    self.AddUnitInText = function () {
        var tpum = new TrainingPlanUnitModel(null, self);
        tpum.PlanId(self.ActivePlan().Id());
        tpum.selectSinglePersonInPlan(self.UserId || self.SelectedUserId());
        self.currentTrainingPlanUnit(tpum);
        return self.currentTrainingPlanUnit();
    }

    //#region Library
    self.TrainingPlanLibrary = new TrainingPlanLibrary(self);
    //#endregion

    //#region period group
    self.SelectedUserId = ko.observable(0);
    self.SelectedUserName = ko.observable('');
    self.GetProgramPersonsResult = null;
    self.GetProgramPersonsResultPerDay = [];
    self.PeriodPersons = ko.observableArray([]);
    self.LoadPeriodPersons = function (planId) {
        var self = this;
        planId = planId || self.ActivePlan().Id();
        if (planId && !self.PeriodPersons().length) {
            $.ajax({
                type: "POST",
                url: ContextPath + "TrainingPlan/GetProgramPersons",
                data: { planId: planId, withCommentsCheck: true }
            })
           .done(function (serverModel) {
               self.GetProgramPersonsResult = serverModel;
               koArrayCopyComplex(serverModel, self.PeriodPersons, false, function (plainelem) {
                   var elem = jQuery.extend({}, plainelem);
                   elem.Selected = ko.observable(false);
                   elem.Select = function () {
                       _.each(self.PeriodPersons(), function (pp) { pp.Selected(false); });
                       elem.Selected(true);
                       self.SelectedUserId(elem.UserId);
                       self.SelectedUserName(elem.Nick);
                   }
                   elem.AvatarContent = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
                               + ContextPath + 'Image/GetImage?id=' + elem.AvatarFileId
                               + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
                   elem.AvatarHover = function (callback) {
                       if (callback) callback(this.AvatarContent || elem.AvatarContent);
                   }
                   elem.HoverContent = null;
                   elem.HoverCall = function (callback) {
                       if (elem.HoverContent) {
                           if (callback) callback(result);
                       } else {
                           $.ajax({
                               type: "POST",
                               url: ContextPath + "Groups/GetSmalPersonInfo2",
                               data: { personId: elem.PersonId }
                           })
                                     .done(function (result) {
                                         elem.HoverContent = result;
                                         if (callback) callback(result);
                                     });
                       }
                   }
                   return elem;
               });
               setTimeout(function () { initPersonSwitch(); }, 500);
           });
        }
    }
    self.SelectAllPersons = function () {
        _.each(self.PeriodPersons(), function (pp) { pp.Selected(false); });
        self.SelectedUserId(0);
        self.SelectedUserName('');
    }
    self.SelectedUserId.subscribe(function (newValue) {
        if (!self.initMode) {
            self.ReloadPlan();
        }
    });

    //#endregion

    //#region trimp chart

    self.deleyedLoadTrimpChart = function () {
        $('#trimpChartContainer').html('');

        setTimeout(self.loadTrimpChart, 1000);
    }
    self.loadTrimpChart = function () {

        if (!$('#trimpChartContainer').length) return;

        $.ajax({
            type: "POST",
            url: ContextPath + "Stats/GetProgramTrimpChartData",
            data: {
                start: self.ManagerStartDate(),
                end: self.ManagerEndDate(),
                userId: self.UserId

            }
        })
                .done(function (result) {
                    var maxY = result.maxY * 1.1
                    if (maxY) {
                        var plot = $.plot('#trimpChartContainer', [result.ctl, result.atl, result.tbs],
                            {
                                xaxis: { mode: "time", show: false },
                                yaxis: { show: false, min: -maxY, max: maxY },
                                series: {
                                    lines: { show: true },
                                    points: { show: true },
                                    shadowSize: 0
                                },
                                grid: {
                                    backgroundColor: '#ffffff',
                                    borderWidth: 1,
                                    margin: 0,
                                    labelMargin: 0,
                                    axisMargin: 0,
                                    borderWidth: 0,
                                    minBorderMargin: 0,
                                },
                                legend: {
                                    show: false //noColumns: result.zoneData.length, position: "se"
                                },
                            });
                    }
                    //trimpModel.wait(false);
                });
    }

    //#endregion

    self.initMode = false;
}

var trainingPlanManager = null;
function initTrainingPlanManager(silent) {
    if (!trainingPlanManager) {
        trainingPlanManager = new TrainingPlanManager();
        GeneralModel.addProperty("TrainingPlanManager", trainingPlanManager, silent);
    }
    return trainingPlanManager;
}

;if (!window.ForcedCallendarType) {
    window.ForcedCallendarType = null;
}
function loadUserCallendar(userId, trainingPlanId) {
    //setWait(); 
    if (trainingPlanId) {
        userCallendar.InitForPlan(trainingPlanId);
    } else {
        userCallendar.InitTrainingPlanManager();
        userCallendar.Init(userId);
    }
    var container = $('.userCalTileBody');

    piorityBusy.Increase();
    loadAndBindTemplate({
        container: container,
        existingElem: container.find('.userCallendar'),
        ajaxPath: 'UserCallendar/UserCallendarControl',
        ajaxData: { userId: userId },
        bindData: null,
        callback: function () {
            userCallendar.MainTemplateContainer = container;
            userCallendar.LoadTemplate();
            postBinding(GeneralModel);
            piorityBusy.Decrease();
        }
    });
}

function resetLodedCalendarTemplates() {
    /*
        userCallendar.LoadedTemplates['day'] = false;
        userCallendar.LoadedTemplates['week'] = false;
        userCallendar.LoadedTemplates['month'] = false;
        userCallendar.LoadedTemplates['list'] = false;
        userCallendar.LoadedTemplates['plan'] = false;
        */
    userCallendar.LoadedTemplates = _.mapObject(userCallendar.LoadedTemplates, function (val, key) {
        return false;
    });
}

function postBinding(model) {
    if (model.UserCallendarModel.PeriodType() == 'list') {
        calculateTableSize();
        // hide 'share in FB' for period type equal to list
        $('#clubUserCalendar span.share_22.rightButton').hide();
    } if (model.UserCallendarModel.PeriodType() == 'plan') {
        // hide 'share in FB' for period type equal to plan
        $('#clubUserCalendar span.share_22.rightButton').hide();
    } else {
        $('#clubUserCalendar span.share_22.rightButton').show();
    }
}

function initUserCallendar(userId) {
    userCallendar.Init(userId);
}

function callendarIconChange(currPeriodType, defPeriodType, periodIcon) {
    var base = ContextPath + 'Content/images/icons/' + periodIcon;
    return currPeriodType == defPeriodType ? base + 'cze.png' : base + '.png';
}

function showDeleteRowModal() {
    $('#removeTrainingsModal').modal('show');
}

function showUpdateRowModal() {
    GeneralModel.ModalAskModel.Show(
        translations.UpdateTraining,
        translations.UpdateTrainingMsg,
        GeneralModel.UserCallendarModel.ListMenuModel.ApplyTypeClick,
        translations.Change);
}

function calculateTableSize() {
    $('#clubUserCalendar div.calBodyTableHeader').css('padding-right', '0px');
    var hWidth = $('#clubUserCalendar div.calBodyTableHeader table.userCallendarTable').width();
    var bWidth = $('#clubUserCalendar div.calBodyTable table.userCallendarTable').width();
    $('#clubUserCalendar div.calBodyTableHeader').css('padding-right', Math.round(hWidth - bWidth) + 'px');
}


function getDefaultStartDate() {
    var curr = new Date();
    curr.setMonth(curr.getMonth() - 1);
    return dateToString(curr);
}

function getDefaultEndDate() {
    return dateToString(new Date());
}

function OptionItem(parent, value, text) {
    var self = this;
    self.Parent = parent;

    self.OptionValue = ko.observable(value);
    self.OptionText = ko.observable(text);

    self.OptionClick = function () {
        self.Parent.ChangeVisibilityText(self.OptionText());
        self.Parent.ChangeVisibilityValue(self.OptionValue());
    }

}

function ListMenuModel(parent) {
    var self = this;
    self.Parent = parent;

    self.VisibleTilesClick = function () {
        self.Parent.RefreshListStat(true);
    };

    self.ChangeVisibilityValue = ko.observable(0);
    self.ChangeVisibilityText = ko.observable(translations.ChooseVisibility.toLowerCase());
    self.ChangeVisibilityOptions = ko.observableArray([
        new OptionItem(self, 0, translations.ChooseVisibility.toLowerCase()),
        new OptionItem(self, 1, translations.ShowActivitiesInComunity.toUpperCase()),
        new OptionItem(self, 2, translations.HideActivitiesInComunity.toUpperCase()),
    ]);

    self.TypeManager = new TrainingTypeControl({
        mainId: 0,
        subId: 0,
        typeChangeCallback: function () {
            // enable ? disable zatwierdz
        },
        clubId: null,
        multi: false,
        baseCtrl: null,
        disciplines: false,
        multiSubs: false,
        allInstedNone: false,
        dummy: null,
        selectNoneIfNoChoise: false,
        addNoneSubType: false,
        availableIds: null
    });

    self.ApplyTypeClick = function () {
        if (self.ChangeVisibilityValue() > 0 || self.TypeManager.SelectedSubTypeId() > 0) {

            var trainings = self.Parent.GetSelectedActivities("training");
            var contents = self.Parent.GetSelectedActivities("contests");

            $.ajax({
                type: "POST",
                traditional: true,
                url: ContextPath + "UserCallendar/UpdateActivity",
                data: {
                    trainings: trainings,
                    contents: contents,
                    changeVisibilityMode: self.ChangeVisibilityValue(),
                    selectedTypeId: self.TypeManager.SelectedSubTypeId(),
                }
            })
            .done(function (result) {
                if (result) {
                    self.Parent.coreRefresh();
                }
            });
        }
    }

    self.Refresh = function () {
        self.ChangeVisibilityValue(0);
        self.ChangeVisibilityText(translations.ChooseVisibility.toLowerCase());
        self.TypeManager.SelectTypeById(0);
    }
}

function UserCallendarModel() {
    var self = this;

    self.LoadingState = true;

    self.IgnoreDates = ko.observable(false);

    //#region properties
    self.Loaded = ko.observable(false);
    self.WaitState = ko.observable(false);

    self.SearchPattern = ko.observable('');
    self.SearchBtnClick = function () {
        self.coreRefresh();
    };

    self.TrainingPlanManager = ko.observable(null);
    self.InitTrainingPlanManager = function () {
        if (!self.TrainingPlanManager()) {
            self.TrainingPlanManager(initTrainingPlanManager(true));
        }
        return self.TrainingPlanManager();
    }

    self.AdminClubId = ko.observable(0);
    self.UserId = ko.observable(0);
    self.TrainingPlanId = ko.observable(0);
    self.FullUserDisplayName = ko.observable('');
    self.IsAdmin = ko.observable(false);
    self.Callendar = ko.observable(new UserCallendarEntriesModel(self));
    self.Activities = ko.observableArray([]);

    self.ShowCallendarTypes = ko.observable(true);

    self.NaviBarModel = ko.observable({});

    self.Days = ko.observableArray([]);
    self.Hours = ko.observableArray([]);
    self.MinHour = ko.observable(0);
    self.LastHour = ko.observable(0);
    self.MinDuration = ko.observable(30);
    self.RowHeight = ko.observable(48);

    self.TypeManager = new TrainingTypeControl(0, 0, 0, null, true, null, false, false, true);
    self.TypeManager.SetChangeCallback(function () {
        self.coreRefresh();
    });

    //self.SelectedUserTrainingWeek = ko.observable(null);

    //#endregion

    //#region period types
    if (ForcedCallendarType) {
        self.PeriodType = ko.observable(ForcedCallendarType);
        self.ShowCallendarTypes(false);

        if (ForcedCallendarType != 'plan') {
            $.jStorage.set('UserCallendarPeriodType', ForcedCallendarType, {
                TTL: 0
            });
        }
    } else {
        if (typeof ActivePlanIdToSet !== 'undefined' && (ActivePlanIdToSet > 0)) {
            self.PeriodType = ko.observable('plan');
            $.jStorage.set('UserCallendarPeriodType', 'plan', { TTL: 0 });
        } else {
            var storedPeriodType = $.jStorage.get('UserCallendarPeriodType');
            if (storedPeriodType) {
                self.PeriodType = ko.observable(storedPeriodType);
            } else {
                self.PeriodType = ko.observable('month');
            }
        }
    }
    if (self.PeriodType() == 'plan') {
        self.InitTrainingPlanManager();
    }
    self.PeriodType.subscribe(function (newValue) {
        if (!self.LoadingState) {
            self.NaviBarModel().CurrentDate = '';
            self.Callendar().Weeks.removeAll();
            var previousPeriodType = $.jStorage.get('UserCallendarPeriodType');
            if (ForcedCallendarType != 'plan') {
                $.jStorage.set('UserCallendarPeriodType', newValue, {
                    TTL: 0
                });
            }
            self.LoadTemplate();
            self.coreRefresh();
        }
        if (newValue == 'list' || newValue == 'plan') {
            $('#clubUserCalendar span.share_22.rightButton').hide();
        } else {
            $('#clubUserCalendar span.share_22.rightButton').show();
        }
        if (newValue == 'plan') {
            self.InitTrainingPlanManager().LoadUserPlans(self.UserId());
        }

        self.HighlightMenu();
    });

    self.HighlightMenu = function () {
        var newValue = self.PeriodType();
        if (newValue == 'list') {
            $('.menuContainer li li').removeClass('active');
            $('.menuContainer li li.menuCalList').addClass('active');
        } else if (newValue == 'month') {
            $('.menuContainer li li').removeClass('active');
            $('.menuContainer li li.menuCalMonth').addClass('active');
        } else if (newValue == 'week') {
            $('.menuContainer li li').removeClass('active');
            $('.menuContainer li li.menuCalWeek').addClass('active');
        }
    }

    self.PeriodIcon = [];
    self.PeriodIcon['day'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'day', translations.CallendarDayIcon);
    });
    self.PeriodIcon['week'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'week', translations.CallendarWeekIcon);
    });
    self.PeriodIcon['month'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'month', translations.CallendarMonthIcon);
    });
    self.PeriodIcon['year'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'year', translations.CallendarYearIcon);
    });
    self.PeriodIcon['list'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'list', translations.CallendarPeriodIcon);
    });
    self.PeriodIcon['plan'] = ko.pureComputed(function () {
        return callendarIconChange(self.PeriodType(), 'plan', translations.CallendarPlanIcon);
    });
    self.GetLengthType = function () {
        switch (self.PeriodType()) {
            case 'day': return 1;
            case 'week': return 7;
            case 'month': return 30;
            case 'list': return -1;
            case 'plan': return 7;
        }
        return 0;
    }
    self.LengthType = ko.computed(function () {
        return self.GetLengthType();
    });

    //#endregion

    //#region calendar dates

    self.CalendarDate = ko.observable();
    self.CalendarDate.subscribe(function (newValue) {
        if (!self.LoadingState) {
            var p = newValue.split('-');
            self.LoadingState = true;
            self.CalendarStartDate(dateToString(new Date(p[0], p[1] - 1, 1)));
            self.CalendarEndDate(dateToString(new Date(p[0], p[1], 0)));
            self.LoadingState = false;
            self.coreRefresh();
        }
    });
    self.CalendarDateObj = ko.computed(function () {
        return new Date(self.CalendarDate());
    });
    self.CalendarStartDate = ko.observable();
    self.CalendarStartDate.subscribe(function (newValue) {
        if (!self.LoadingState) {
            self.coreRefresh();
        }
    });
    self.CalendarEndDate = ko.observable();
    self.CalendarEndDate.subscribe(function (newValue) {
        if (!self.LoadingState) {
            self.coreRefresh();
        }
    });

    self.Year = ko.computed(function () {
        var date = self.CalendarDate();
        if (date) {
            var p = date.split('-');
            return p[0];
        } else {
            return 0;
        }
    });

    self.CalendarWeek = ko.observable();
    self.CalendarWeek.subscribe(function (newValue) {
        if (!self.LoadingState) {
            self.LoadingState = true;
            //
            self.LoadingState = false;
            self.coreRefresh();
        }
    });

    //#endregion

    //#region selection

    self.PrevSelActivity = ko.observable(null);

    self.UnselectActivities = function () {
        $.each(self.Activities(), function (index, item) {
            item.Selected(false);
        });
        self.PrevSelActivity(null);
        self.RefreshListStat();
    }
    self.SelectActivities = function () {
        $.each(self.Activities(), function (index, item) {
            item.Selected(true);
        });
        self.PrevSelActivity(null);
        self.RefreshListStat();
    }
    self.SelectActivitiesOnShift = function (curr, prev) {
        var inside = false;
        $.each(self.Activities(), function (index, item) {
            if (item == curr || item == prev) {
                item.Selected(true);
                inside = !inside;
            } else if (inside) {
                item.Selected(true);
            }
        });
    }
    self.GetSelectedActivities = function (filter) {
        var trainings = [];

        $.each(self.Activities(), function (index, item) {
            if (item.Selected() && (filter == undefined || item.Type() == filter)) {
                trainings.push(item.TrainingId());
            }
        });
        return trainings;
    }

    self.RefreshListStatTimeOut = null;
    self.RefreshListStat = function (force) {
        if (self.RefreshListStatTimeOut) {
            clearTimeout(self.RefreshListStatTimeOut);

        }
        var trainings = self.GetSelectedActivities("training");
        var contents = self.GetSelectedActivities("contests");

        self.RefreshListStatTimeOut = setTimeout(function () {
            self.LoadStats('', -1, 0, undefined, true, 'trainingList', trainings, contents, force);
        }, 1000);
    }

    //#endregion

    self.SelectedRows = ko.computed(function () {
        var result = self.GetSelectedActivities().length > 0 ? true : false;
        if (!result) {
            $('div.calStatsContainer').html("");
        }
        return result;
    });

    self.ExportTrainings = function () {
        if (self.SelectedRows()) {
            var trainings = self.GetSelectedActivities("training");
            var contents = self.GetSelectedActivities("contests");

            $('body').dynamicForm({
                ActionName: "User/ExportMultiTraining",
                Parameters: {
                    jsonTrainingIds: JSON.stringify(trainings),
                    jsonContentIds: JSON.stringify(contents)
                }
            });
        }
    };

    self.TrainingDelete = function () {
        var trainings = self.GetSelectedActivities("training");
        var contents = self.GetSelectedActivities("contests");
        $.ajax({
            type: "POST",
            traditional: true,
            url: ContextPath + "UserCallendar/DeleteActivity",
            data: { trainings: trainings, contents: contents }
        })
        .done(function (result) {
            if (result) {
                $('#removeTrainingsModal').modal('hide');
                self.coreRefresh();
                relaodTileTargetDeffered();
            }
        });
    }

    self.init = function () {
        self.AdminClubId(0);
        self.UserId(0);
        self.TrainingPlanId(0);
        self.FullUserDisplayName('');
        self.IsAdmin(false);
        self.Callendar(new UserCallendarEntriesModel());
        self.Months.removeAll();
        self.Years.removeAll();
    };

    self.initByModel = function (serverModel, doNotLoadStats) {
        self.SelectedElement = null;

        if (serverModel) {
            self.LoadingState = true;
            self.DestroyDrag();

            self.IgnoreDates(serverModel.IgnoreDates);

            self.Activities.removeAll();

            self.NaviBarModel(serverModel.NaviBarModel);

            self.AdminClubId(serverModel.AdminClubId);
            self.UserId(serverModel.UserId);
            self.TrainingPlanId(serverModel.TrainingPlanId);
            self.FullUserDisplayName(serverModel.FullUserDisplayName);
            self.IsAdmin(serverModel.IsAdmin);
            self.Callendar().initByModel(serverModel.Callendar);

            self.CalendarDate(serverModel.NaviBarModel.StartDate);
            self.CalendarStartDate(serverModel.NaviBarModel.StartDate);
            self.CalendarEndDate(serverModel.NaviBarModel.EndDate);
            self.CalendarWeek(serverModel.NaviBarModel.WeekNr);

            self.Days.removeAll();
            for (var i = 0, len = serverModel.Days.length; i < len; i++) {
                var day = serverModel.Days[i];
                var eve = new JsGraphicDay(day, self.Days(), null);
                self.Days.push(eve);
            }
            self.Hours.removeAll();
            for (var i = serverModel.MinHour; i <= serverModel.LastHour; i++) {
                self.Hours.push(i + ":00");
            }
            self.MinHour(serverModel.MinHour);
            self.LastHour(serverModel.LastHour);
            self.MinDuration(serverModel.MinDuration);

            if (!doNotLoadStats) {
                var lengthType = self.LengthType();
                if (lengthType == 30) {
                    self.select();
                } else if (lengthType < 30) {
                    if (self.Callendar() && self.Callendar().Weeks() && self.Callendar().Weeks().length) {
                        self.Callendar().Weeks()[0].select();
                    }
                }
            }

            if (self.TrainingPlanManager())
                self.TrainingPlanManager().ActivePlan().CalculateTrainingTypesCols();

            self.EnableDrag(true);

            self.LoadingState = false;
        }
        self.Loaded(true);
    };

    //#region navigation

    self.NextPeriod = function (selectFirst) {
        if (self.PeriodType() == 'list') {
            var next = self.NaviBarModel().NextParam;
            var p = next.split('-');
            var nextEnd = dateToString(new Date(p[0], parseInt(p[1]), parseInt(p[2]) - 1));

            self.coreRefresh(next, nextEnd, null, null, selectFirst != self ? selectFirst : false, false);
        } else if (self.IgnoreDates()) {
            self.coreRefresh(null, null, null, null, selectFirst != self ? selectFirst : false, false, self.CalendarWeek() + 1);
        } else {
            self.coreRefresh(self.NaviBarModel().NextParam, null, null, null, selectFirst != self ? selectFirst : false, false);
        }
    }
    self.PreviousPeriod = function (selectLast) {
        if (self.PeriodType() == 'list') {
            var next = self.NaviBarModel().PreviousParam;
            var p = next.split('-');
            var nextEnd = dateToString(new Date(p[0], parseInt(p[1]), parseInt(p[2]) - 1));

            self.coreRefresh(next, nextEnd, null, null, false, selectLast != self ? selectLast : false);
        } else if (self.IgnoreDates()) {
            var week = self.CalendarWeek() - 1;
            if (week > 0) {
                self.coreRefresh(next, nextEnd, null, null, false, selectLast != self ? selectLast : false, week);
            }
        } else {
            self.coreRefresh(self.NaviBarModel().PreviousParam, null, null, null, false, selectLast != self ? selectLast : false);
        }
    }

    self.Refresh = function () {
        self.coreRefresh();
    }
    self.coreRefresh = function (passedStartDate, passedEndDate, passedLengthType, passedUserId, selectFirst, selectLast, passedWeekNr, calback) {
        $('#clubUserCalendar').css('cursor', 'wait');

        if (!passedStartDate) {
            if (self.PeriodType() == 'list') {
                passedStartDate = self.CalendarStartDate();
            } else {
                passedStartDate = self.CalendarDate();
            }
        }

        if (!passedEndDate) {
            if (self.PeriodType() == 'list') {
                passedEndDate = self.CalendarEndDate();
            } else {
                passedEndDate = null;
            }
        }

        if (!passedWeekNr) {
            passedWeekNr = self.CalendarWeek();
        }

        if (!passedUserId) passedUserId = self.UserId();
        if (!passedLengthType) {
            passedLengthType = self.GetLengthType();
        }

        if (passedLengthType == '') passedLengthType = 0;

        var trainingPlanId = self.TrainingPlanId();

        var filterSubTypeId = (passedLengthType < 0) ? self.TypeManager.SelectedSubType().Id : 0;
        var filterSearchPattern = (passedLengthType < 0) ? self.SearchPattern() : '';

        self.WaitState(true);
        piorityBusy.Increase();
        $('body').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "UserCallendar/GetCalendarModel",
            data: { userId: passedUserId, trainingPlanId: trainingPlanId, startDate: passedStartDate, endDate: passedEndDate, lengthType: passedLengthType, weekNr: passedWeekNr, filterSubTypeId: filterSubTypeId, filterSearchPattern: filterSearchPattern }
        })
         .done(function (result) {
             $('#clubUserCalendar').css('cursor', 'auto');
             if (self.PeriodType() != 'list') {
                 self.initByModel(result, selectFirst || selectLast);
                 if (selectFirst) {
                     self.SelectFirstDay(selectFirst);
                 }
                 if (selectLast) {
                     self.SelectLastDay(selectLast);
                 }
             } else {

                 if (self.ListMenuModel) {
                     self.ListMenuModel.Refresh();
                 }

                 self.initByModel(result, true);
                 var container = $('.calStatsContainer');
                 container.html(result);
                 calculateTableSize();

                 if (self.Activities && self.Activities().length > 700) {
                     $('.infoCloudContainer.warn').addClass('active');
                     setTimeout(function () { $('.infoCloudContainer.warn').removeClass('active'); }, 3 * 1000);
                 }
             }

             if (calback) calback();

             piorityBusy.Decrease();
             self.WaitState(false);
             $('body').css('cursor', 'default');
         });
    }

    self.Init = function (userId) {
        self.coreRefresh('', '', null, userId);
        if (self.PeriodType() == 'plan') {
            self.InitTrainingPlanManager().LoadUserPlans(parseInt(userId), ActivePlanIdToSet);
        }
    };
    self.InitForPlan = function (planId) {
        self.TrainingPlanId(planId);
        self.coreRefresh('', '', null, 0);
    };

    self.SelectedElement = null;
    self.MonthStatCache = null;
    self.LoadStats = function (startDay, lengthType, id, selectedElement, doNotScroll, type, trainingIds, contentIds, force) {

        if (self.PeriodType() == 'list' && !force) {
            return;
        }

        if (self.SelectedElement) {
            self.SelectedElement.unSelect();
        }
        self.SelectedElement = selectedElement;

        $.ajax({
            type: "POST",
            traditional: true,
            url: ContextPath + "Stats/GetUserCallendarStats",
            data: {
                userId: self.UserId(),
                startDay: startDay,
                lengthType: lengthType,
                id: id,
                trainingIds: trainingIds,
                contentIds: contentIds,
                type: type
            }
        })
        .done(function (result) {
            if (lengthType == 3 || lengthType == 2 || lengthType == 1) self.MonthStatCache = result;
            var container = $('.calStatsContainer');
            container.html(result);
            loadStatControl();

            if (trainingIds && trainingIds.length > 100) {
                $('.publishInfoCloudContainer.warn').addClass('active');
                setTimeout(function () { $('.publishInfoCloudContainer.warn').removeClass('active'); }, 3 * 1000);
            }

            var serLoadParam = [['periodPlansTilesContainerModel', 'periodPlansTilesContainer', function (serverModel) {
                var pExplorer = new PeriodsExploreModel(self);
                pExplorer.initByModel(serverModel);
                return pExplorer;
            }]];
            $('.ContestTile_lrContainer').each(function (idx, elem) {
                var contestId = $(elem).data('contestid');
                if (contestId) {
                    serLoadParam.push(['ContestTile_' + contestId + '_lrModel', 'ContestTile_' + contestId + '_lrContainer', function (serverModel) {
                        var m = new ParticipDetails(serverModel, true);
                        return m;
                    }]);
                }
            });

            loadSerialisedJson(serLoadParam);

            container.find('.exercisesList .trainingExercises').each(function (idx, elem) {
                ko.cleanNode(elem);
                ko.applyBindings(GeneralModel, elem);
            });

            self.ScrollToTrainingId(id, container, doNotScroll);
            // pokaz lub nie publikuj na kaflu statystyk
            if (self.PeriodType() == 'list') {
                if (self.GetSelectedActivities().length <= 1) {
                    jQuery('#clubUserCalendar div.calStatsContainer a.share_22').show();
                } else {
                    jQuery('#clubUserCalendar div.calStatsContainer a.share_22').hide();
                }
            } else {
                jQuery('#clubUserCalendar div.calStatsContainer a.share_22').show();
            }
            container.find(".mCustomScrollbar").mCustomScrollbar();
        });
    }

    self.ScrollToTrainingId = function (id, container, doNotScroll) {

        if (!container) container = $('.calStatsContainer');

        var tr;

        var trainingId;
        if (id != 0) {
            trainingId = id;
        }
        if (trainingId) {
            tr = $(container.find("#sessionTraining_" + trainingId + ", #ContestTile_" + trainingId + ", #simpleTraining_" + trainingId + "_underCal, #trainingPlan_" + trainingId)[0]);
        }

        if (!tr || tr.length == 0) {
            tr = $(container.find(".sessionTraining, .simpleTraining, .contestTile")[0]);
        }

        if (tr && tr.length > 0) {
            var chart = tr.find('.hrChart');
            if (chart.length) {
                if (self.PeriodType() != 'list' && !chart.is(':visible')) {
                    tr.children('.header').children(".st_trainingType").click();
                }
            }

            if (!doNotScroll) {
                var belt = tr.find('.headBelt');
                if (belt.length) {
                    scrollToVisible(belt, { duration: 1000, easing: 'swing' });
                } else {
                    scrollToVisible(tr, { duration: 1000, easing: 'swing' });
                }
            }
        } else {
            if (!doNotScroll) {
                var sBelt = $(".simpleTrainingsBelt");
                if (sBelt.length)
                    scrollToVisible(sBelt, { duration: 1000, easing: 'swing' });
            }
        }
    }

    self.clickDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.yearBox').find('.datepicker');
            if (dp) {
                dp.datepicker('show', true);
            }
        }
    };
    self.clickStartDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.rangeBox').find('#rangeBox_from');
            if (dp) {
                dp.datepicker('show', true);
            }
        }
    };
    self.clickEndDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.rangeBox').find('#rangeBox_to');
            if (dp) {
                dp.datepicker('show', true);
            }
        }
    };
    self.clickReloadActivities = function (data, event) {
        if (!self.LoadingState) {
            self.coreRefresh();
        }
    }

    self.openDataCtrl = function (data, event) {
        if ($(event.target).hasClass('goToCallendar')) {
            OpenDataCtrlFromKo(data, event);
        }
    }

    self.select = function (data, event) {
        if (event && event.target && $(event.target).closest('.hrCloud').length) return true;
        self.LoadStats(self.Callendar().Weeks()[0].Days()[0].Date(), 3, 0);
    };

    self.SelectNextDay = function () {
        if (self.SelectedElement) {
            var weeks = self.Callendar().Weeks();
            var selectedDay = null;
            var nextDay = null;
            for (var wI = 0, wlen = weeks.length; wI < wlen && nextDay == null; wI++) {
                var days = weeks[wI].Days();
                for (var dI = 0, dlen = days.length; dI < dlen && nextDay == null; dI++) {
                    var day = days[dI];
                    if (selectedDay) {
                        nextDay = day;
                    } else if (day.Selected()) {
                        selectedDay = day;
                    }
                }
            }
            if (nextDay) {//&& nextDay.ThisMonth()) {
                nextDay.ProgramSelect(true);
            } else {
                self.NextPeriod(self.SelectedElement);
            }
        }
    }
    self.SelectPreviousDay = function () {
        if (self.SelectedElement) {
            var weeks = self.Callendar().Weeks();
            var selectedDay = null;
            var previousDay = null;
            for (var wI = 0, wlen = weeks.length; wI < wlen && selectedDay == null; wI++) {
                var days = weeks[wI].Days();
                for (var dI = 0, dlen = days.length; dI < dlen && selectedDay == null; dI++) {
                    var day = days[dI];
                    if (day.Selected()) {
                        selectedDay = day;
                    } else {
                        previousDay = day;
                    }
                }
            }
            if (previousDay) {// && previousDay.ThisMonth()) {
                previousDay.ProgramSelect(true);
            } else {
                self.PreviousPeriod(self.SelectedElement);
            }
        }
    }

    self.SelectFirstDay = function (selectFirst) {
        if (selectFirst) {
            var selected = false;
            var weeks = self.Callendar().Weeks();
            for (var wI = 0, wlen = weeks.length; wI < wlen && !selected; wI++) {
                var days = weeks[wI].Days();
                for (var dI = 0, dlen = days.length; dI < dlen && !selected; dI++) {
                    var day = days[dI];
                    if (day.Date() > selectFirst.Date()) {
                        day.ProgramSelect(true);
                        selected = true;
                    }
                }
            }
        }
    }
    self.SelectLastDay = function (selectLast) {
        if (selectLast) {
            var selected = false;
            var weeks = self.Callendar().Weeks();
            for (var wI = weeks.length - 1; wI >= 0 && !selected; wI--) {
                var days = weeks[wI].Days();
                for (var dI = days.length - 1; dI >= 0 && !selected; dI--) {
                    var day = days[dI];
                    if (day.Date() < selectLast.Date()) {
                        day.ProgramSelect(true);
                        selected = true;
                    }
                }
            }
        }
    }

    self.clickMonth = function (monthNr) {
        var curM = self.Month();
        if (monthNr == curM) {
            self.select();
        } else {
            self.Month(monthNr);
        }
    };

    self.TrainingPlanUrl = ko.computed(function () {
        return ContextPath + "TrainingPlan/Callendar?userId=" + self.UserId();
    });

    //#endregion

    //#region templates

    self.MainTemplateContainer = null;
    self.LoadedTemplates = [];
    self.LoadedTemplates['day'] = false;
    self.LoadedTemplates['week'] = false;
    self.LoadedTemplates['month'] = false;
    self.LoadedTemplates['list'] = false;
    self.LoadedTemplates['plan'] = false;
    self.LoadTemplate = function () {
        var pt = self.PeriodType();
        if (self.MainTemplateContainer && !self.LoadedTemplates[pt]) {

            var container = null;
            switch (pt) {
                case 'week': container = '.dayCalTemplate'; break;
                case 'month': container = '.monthCalTemplate'; break;
                case 'list': container = '.listCalTemplate'; break;
                case 'plan': container = '.planCalTemplate'; break;
            }
            container = self.MainTemplateContainer.find(container);

            loadAndBindTemplate(
                {
                    container: container,
                    existingElem: null,
                    ajaxPath: 'UserCallendar/GetCallendarTemplate',
                    ajaxData: { templateType: pt },
                    bindData: self,
                    callback: function () {
                        self.LoadedTemplates[pt] = true;

                        if (pt == 'plan') {
                            initTrainingNaviBelt('#planNaviBelt');
                        }
                    }
                });
        }
    }

    //#endregion

    //#region add

    self.AddActivityUrl = ko.computed(function () {
        return ContextPath + "User/TrainingDetails?selectedTab=Callendar&date=" + self.CalendarDate();
    });
    self.Add = function () {
        window.location = self.AddActivityUrl();
    }

    self.AddPlanUrl = ko.computed(function () {
        return ContextPath + "TrainingPlan/PlanUnitEditor?userId=" + self.UserId() + "&date=" + self.CalendarDate() + "&week=" + self.CalendarWeek() + '&planId=' + self.TrainingPlanId();
    });
    self.AddPlan = function () {
        window.location = self.AddPlanUrl();
    }

    self.AddPlanInModal = function (timeOfDay) {
        self.Week.Entries.Callendar.InitTrainingPlanManager().editTrainingPlanUnit(0, self.CalendarDate(), 0, timeOfDay, true);
    }
    self.AddDiary = function () {
        //window.location = ContextPath + "Diary/Index?date=" + self.Date();
        slideUserDiaryModel.Open(self.UserId(), self.CalendarDate(), self.Callendar().Weeks()[0].Days()[0]);
        return false;
    }
    self.AddContest = function () {
        if (self.Future()) {
            alert("Tu bedzie planowanie zawodów :)");
        } else {
            window.location = ContextPath + "Contest/UserAdd?date=" + self.SelectedDate();
        }
    }
    self.AddFile = function () {
        if (self.Future()) {
            alert("Tu bedzie planowanie treningu :)");
        } else {
            window.location = ContextPath + "Upload/Index?date=" + self.SelectedDate();
        }
    }

    self.AddUnassignedPlanUrl = ko.pureComputed(function () {
        var cal = self.Callendar();
        if (!cal) return '';
        return cal.AddUnassignedPlanUrl();
    });

    self.AddTemplate = null;
    self.setAddTemplate = function (calback) {
        if (self.AddTemplate) {
            if (calback) { calback(self.AddTemplate); }
        } else {
            $.ajax({
                type: "POST",
                traditional: true,
                url: ContextPath + "UserCallendar/GetAddTemplate"
            })
           .done(function (result) {
               if (result) {
                   self.AddTemplate = result;
                   if (calback) { calback(self.AddTemplate); }
               }
           });
        }
    }


    self.ActivityInfoCloudTemplate = null;
    self.setActivityInfoCloudTemplate = function (calback) {
        if (self.ActivityInfoCloudTemplate) {
            if (calback) { calback(self.ActivityInfoCloudTemplate); }
        } else {
            $.ajax({
                type: "POST",
                traditional: true,
                url: ContextPath + "UserCallendar/GetActivityInfoCloudTemplate"
            })
           .done(function (result) {
               if (result) {
                   self.ActivityInfoCloudTemplate = result;
                   if (calback) { calback(self.ActivityInfoCloudTemplate); }
               }
           });
        }
    }

    self.ContestResultInfoCloudTemplate = null;
    self.setContestResultInfoCloudTemplate = function (calback) {
        if (self.ContestResultInfoCloudTemplate) {
            if (calback) { calback(self.ContestResultInfoCloudTemplate); }
        } else {
            $.ajax({
                type: "POST",
                traditional: true,
                url: ContextPath + "UserCallendar/GetContestResultInfoCloudTemplate"
            })
           .done(function (result) {
               if (result) {
                   self.ContestResultInfoCloudTemplate = result;
                   if (calback) { calback(self.ContestResultInfoCloudTemplate); }
               }
           });
        }
    }

    self.ContestPlanInfoCloudTemplate = null;
    self.setContestPlanInfoCloudTemplate = function (calback) {
        if (self.ContestPlanInfoCloudTemplate) {
            if (calback) { calback(self.ContestPlanInfoCloudTemplate); }
        } else {
            $.ajax({
                type: "POST",
                traditional: true,
                url: ContextPath + "UserCallendar/GetContestPlanInfoCloudTemplate"
            })
           .done(function (result) {
               if (result) {
                   self.ContestPlanInfoCloudTemplate = result;
                   if (calback) { calback(self.ContestPlanInfoCloudTemplate); }
               }
           });
        }
    }

    //#endregion

    //#region drag&drop on plans
    self.enableDragTimeout = null;
    self.EnableDrag = function (force) {
        if (force || !self.LoadingState) {
            if (self.enableDragTimeout) clearTimeout(self.enableDragTimeout);
            self.enableDragTimeout = setTimeout(self.coreEnableDrag, 750);
        }
    }
    self.coreEnableDrag = function () {
        var daysExist = $('.actiCollContent').length;
        if (daysExist) {
            self.core2EnableDrag();
            setTimeout(self.core2EnableDrag, 750);
        } else {
            setTimeout(self.coreEnableDrag, 500);
        }
    }
    self.core2EnableDrag = function () {
        $(".calendar .day .acti").draggable({
            appendTo: "body",
            helper: function () { return $('<img class="draggedIcon" src="' + ContextPath + 'Content/images/icons/plany_44.png" />'); },
            //cursor: "crosshair",
            cursorAt: { left: 20, top: 50 }
            /*, containment: "document", scroll: false*/
            /*start: function(event, ui) {
                self.DragedPlanUnit = ko.dataFor(ui.helper.prevObject[0]);
            },*/
        });

        setTimeout(function () {
            if ($('.actiCollContent').length && $(".calendar .day .acti").length && !$(".calendar .day .acti.ui-draggable").length) {
                self.enableDragTimeout = setTimeout(self.coreEnableDrag, 250);
            }
        }, 250);

        self.EnableDrop();
    }
    self.DestroyDrag = function () {
        $(".calendar .day .acti.ui-draggable").draggable("destroy");
    }

    self.EnableDrop = function () {
        $(".calBody .body .day").droppable({
            drop: function (event, ui) {
                // if (!self.topSliderDropTimeOut) {
                var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                var day = ko.dataFor(event.target);

                if (day && dragedPlanUnit) {
                    day.AddLibPlan(dragedPlanUnit);
                }
                //  }
            },
            hoverClass: "dropHighlight"
        });

        $(".calBody  .weekNr").droppable({
            drop: function (event, ui) {
                // if (!self.topSliderDropTimeOut) {
                var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                if (dragedPlanUnit) {
                    self.Callendar().AddLibPlan(dragedPlanUnit);
                }
                //  }
            },
            hoverClass: "dropHighlight"
        });
        $(".calBody  .day.unassigned").droppable({
            drop: function (event, ui) {
                // if (!self.topSliderDropTimeOut) {
                var dragedPlanUnit = ko.dataFor(ui.draggable[0]);
                if (dragedPlanUnit) {
                    self.Callendar().AddLibPlan(dragedPlanUnit);
                }
                //  }
            },
            hoverClass: "dropHighlight"
        });

        $(".calBody  .planActi.tPlan").droppable({
            drop: function (event, ui) {
                // if (!self.topSliderDropTimeOut) {
                var acti = ko.dataFor(ui.draggable[0]);
                var plan = ko.dataFor(event.target);
                if (acti && plan && plan instanceof UserCallendarActivityModel && acti instanceof UserCallendarActivityModel) {
                    plan.AdminActiMap(acti);
                }
                //  }
            },
            hoverClass: "dropHighlight"
        });
    }


    //#endregion

    self.ListMenuModel = new ListMenuModel(self);

    self.LoadingState = false;
}

function UserCallendarEntriesModel(parent) {
    var self = this;
    self.Callendar = parent;

    self.MonthNr = ko.observable(0);
    self.EndDate = ko.observable('');
    self.MonthName = ko.observable('');
    self.StartDate = ko.observable('');
    self.Weeks = ko.observableArray([]);

    self.UnassignedPlans = ko.observableArray([]).extend({ rateLimit: 50 });

    self.init = function () {
        self.MonthNr(0);
        self.EndDate('');
        self.MonthName('');
        self.StartDate('');
        self.Weeks.removeAll();
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.MonthNr(serverModel.MonthNr);
            self.EndDate(serverModel.EndDate);
            self.MonthName(serverModel.MonthName);
            self.StartDate(serverModel.StartDate);

            trainingPlanManager.ActivePlan().CallbackToRecalculateCols = [];

            self.Weeks.removeAll();
            for (var i = 0, len = serverModel.Weeks.length; i < len; i++) {
                var elem = new UserCallendarWeekModel(self);
                elem.initByModel(serverModel.Weeks[i]);
                self.Weeks.push(elem);
            }

            koArrayCopyComplex(serverModel.UnassignedPlans, self.UnassignedPlans, false, function (p) {
                var ent = new UserCallendarActivityModel(self);
                ent.initByModel(p);
                return ent;
            });
        }
    };


    self.AddTextUnassigned = function () {
        var newActi = new UserCallendarActivityModel(self);
        newActi.Type('trainingPlan');
        var unit = trainingPlanManager.AddUnitInText();
        unit.Date(self.StartDate());
        unit.Week(self.Callendar.CalendarWeek());
        unit.Day(1);
        unit.NoDaySpecified(true);
        unit.initPlanPersons(true);
        /*
        var typeId = 0;
        if (column.ids.length == 1) {
            typeId = column.ids[0];
            unit.TypeManager.SelectTypeById(typeId);
            newActi.IconInfo({ Icon: column.types[0].Icon });
        }*/
        newActi.DisplayIcon(true);
        var p = unit.getClearData();
        p.MappedTrainings = [];
        p.ShowTime = false;
        p.ShowDistance = false;
        p.ShowSpeed = false;
        p.ShowPace = false;
        p.ShowPower = false;
        p.ShowCadence = false;
        p.ShowCalories = false;
        p.ShowZone = false;
        p.ShowStart = false;
        /*
        if (column.ids.length == 1) {
            p.TrainingTypeId = typeId;
        }*/
        newActi.Details({ Plan: p });
        newActi.EnterEditMode(unit);
        //self.Activities.push(newActi);

        self.UnassignedPlans.push(newActi);
    }
    self.AddUnassignedPlanUrl = ko.pureComputed(function () {
        return ContextPath + "TrainingPlan/PlanUnitEditor?noDaySpecified=true&date=" + self.StartDate() + "&week=" + self.Callendar.CalendarWeek() + "&day=1&userId=" + self.Callendar.UserId() + '&planId=' + self.Callendar.TrainingPlanId();
    });

    self.AddLibPlan = function (planUnit) {
        var self = this;

        planUnit.UseFromLib(self.Callendar.CalendarDate(), self.Callendar.CalendarWeek(), 1, function (unit, serverModel) {
            var newActi = new UserCallendarActivityModel(self);
            newActi.Type('trainingPlan');
            //newActi.Name(unit.Name());

            newActi.IconInfo({ Icon: 'none', Name: unit.Name() });

            newActi.DurationSeconds(stringToSeconds(unit.Duration()));
            newActi.TrainingId(unit.Id());
            newActi.Duration(unit.Duration());
            newActi.Distance(unit.Distance());
            newActi.TotalDuration(unit.Duration());
            newActi.ElementId(unit.Id());
            newActi.Plan(true);
            newActi.AVGHR(unit.CustomZoneDisplayValue());
            newActi.AvgSpeed(unit.AvgSpeed());
            newActi.Energy(unit.Calories());
            newActi.Power(unit.AvgPower());
            newActi.PaceUnit(unit.PaceUnit());
            newActi.Pace(unit.Pace());


            if (unit.TypeManager.SelectedTypeId() > 0) {
                newActi.IconInfo({ Icon: unit.TypeManager.SelectedType().Icon, Name: newActi.IconInfo().Name });
                //newActi.DisplayIcon(false);
            }
            var p = serverModel;
            p.MappedTrainings = [];
            p.ShowTime = unit.ShowTime();
            p.ShowDistance = unit.ShowDistance();
            p.ShowSpeed = unit.ShowSpeed();
            p.ShowPace = unit.ShowPace();
            p.ShowPower = unit.ShowPower();
            p.ShowCadence = unit.ShowCadence();
            p.ShowCalories = unit.ShowCalories();
            p.ShowZone = unit.ShowZone();
            p.ShowStart = unit.ShowStart();
            p.Description = unit.Description();
            newActi.Details({ Plan: p, Description: unit.Description() });
            newActi.prepareTrainingPlanUsersAssignations();

            self.UnassignedPlans.push(newActi);
        },
        true, true);
    }

    self.MansoryLayout = null;
    self.UnassignedPlans.subscribe(function (newValue) {
        if (self.UnassignedPlans().length) {
            if (!self.MansoryLayout) {
                self.MansoryLayoutCalc();
            } else {
                setTimeout(function () { self.MansoryLayout.recalculate(true); }, 300);
            }
        }
        return true;
    });
    self.MansoryLayoutCalc = function () {
        if ($('.day.unassigned .actis .acti').length) {
            setTimeout(function () {
                self.MansoryLayout = Macy({
                    container: '.day.unassigned  .actis',
                    trueOrder: false,
                    waitForImages: false,
                    margin: 10,
                    columns: 4,
                    breakAt: {
                        1024: 3
                    }
                });
            }
            , 100);
        } else {
            setTimeout(self.MansoryLayoutCalc, 300);
        }
    }
}

function UserCallendarWeekModel(parent) {
    var self = this;
    self.Entries = parent;
    self.Selected = ko.observable(false);

    self.Days = ko.observableArray([]);

    self.MaxDayActions = ko.computed(function () {
        var ds = self.Days();
        var minHeight = 0;

        for (var i = 0, len = ds.length; i < len; i++) {
            var d = ds[i];
            var h = d.Trainings().length * 50 + d.PeriodsTypeOrdered().length * 50 + d.ExaclyTargets().length * 81 + d.TrainigPlans().length * 50;

            if (minHeight < h) minHeight = h;
        }
        return minHeight < 400 ? 400 : minHeight;
    });
    self.MinDayHeight = ko.computed(function () {
        return (self.MaxDayActions() + 70) + 'px';
    });
    self.MinSelectedDayBodyHeight = ko.computed(function () {
        return (self.MaxDayActions() - 90) + 'px';
    });
    self.MinSelectedDayHeight = ko.computed(function () {
        return (self.MaxDayActions() + 90) + 'px';
    });

    self.init = function () {
        self.Days.removeAll();
    };

    self.initByModel = function (serverModel) {
        if (serverModel) {

            self.Days.removeAll();
            for (var i = 0, len = serverModel.Days.length; i < len; i++) {
                var elem = new UserCallendarDayModel(self);
                elem.initByModel(serverModel.Days[i], i);
                self.Days.push(elem);
            }

            if (self.Entries.Callendar.PeriodType() == 'plan') {
                self.CalculateSums();
            }
        }
    };

    self.select = function () {
        self.Entries.Callendar.LoadStats(self.Days()[0].Date(), 2, 0, self);
        self.Selected(true);
    };
    self.unSelect = function () {
        self.Selected(false);
    };

    self.GetSums = ko.observableArray([]);
    self.TTimeSum = ko.observable(0);
    self.PTimeSum = ko.observable(0);
    self.CalculateSumsTimeOut = null;
    self.CalculateSums = function () {
        if (self.CalculateSumsTimeOut) clearTimeout(self.CalculateSumsTimeOut);
        self.CalculateSumsTimeOut = setTimeout(self.coreCalculateSums, 300);
    };
    self.coreCalculateSums = function () {
        self.GetSums.removeAll();

        var _days = _(self.Days());

        _days.each(function (day) { day.ComputeActiSums(); });

        var cols = trainingPlanManager.ActivePlan().TrainingTypesCols();
        _(cols).each(function (col) {
            var sumTTime = 0, sumTDist = 0, sumPTime = 0, sumPDist = 0;
            _days.each(function (day) {
                var dayCol = _(day.ActiCols()).findWhere({ Column: col });
                if (dayCol) {
                    sumTTime += dayCol.SumTTime();
                    sumTDist += dayCol.SumTDist();
                    sumPTime += dayCol.SumPTime();
                    sumPDist += dayCol.SumPDist();
                }
            });



            self.GetSums.push({
                Column: col,
                SumPTime: sumPTime,
                SumPDist: sumPDist,
                SumTTime: sumTTime,
                SumTDist: sumTDist,
                //TrainingPlanTargets: targets
            });
        });

        if (!_.contains(trainingPlanManager.ActivePlan().CallbackToRecalculateCols, self.CalculateSums)) {
            trainingPlanManager.ActivePlan().CallbackToRecalculateCols.push(self.CalculateSums);
        }

        var selWeek = trainingPlanManager.SelectedUserTrainingWeek();
        if (selWeek) {
            self.TTimeSum(0);
            self.PTimeSum(0);
            var sumTTime = 0, sumPTime = 0;

            _.each(self.Days(), function (day) {
                _.each(day.getAllTrainsAndPlansWithCompets(), function (acti) {
                    if (acti.Type() == 'training') {
                        sumTTime += acti.DurationSeconds();
                    }
                    if (acti.Type() == 'trainingPlan') {
                        sumPTime += acti.DurationSeconds();
                    }
                })
            });

            if (self.Selected()) {
                var unplans = self.Entries.UnassignedPlans();
                if (unplans && unplans.length) {
                    _.each(unplans, function (acti) {
                        sumPTime += acti.DurationSeconds();
                    });
                }
            }

            self.TTimeSum(sumTTime);
            self.PTimeSum(sumPTime);

            if (!selWeek.StatsDataCache()) {
                selWeek.StatsDataCache({});
            }
            selWeek.StatsDataCache().PTime = self.PTimeSum();
            selWeek.StatsDataCache().TTime = self.TTimeSum();
            selWeek.StatsDataCache.valueHasMutated();
        }
    };
    /*
    self.GetCompetitionInfoCloudContentTemplate = function(callback){
        callback($('#competitionInfoCloudContent').html());
    }*/
}


function UserCallendarDayModel(parent) {
    var self = this;
    self.Week = parent;
    self.Callendar = self.Week.Entries.Callendar;

    //#region properties
    self.DayNr = ko.observable(0);
    self.Date = ko.observable('');
    self.Future = ko.observable(false);
    self.ThisMonth = ko.observable(false);
    self.Today = ko.observable(false);
    self.Activities = ko.observableArray([]).extend({ rateLimit: 50 });
    self.Selected = ko.observable(false);
    self.TotalDuration = ko.observable(0);
    self.MainDuration = ko.observable(0);
    self.AdditionallDuration = ko.observable(0);
    self.Pause = ko.observable(false);
    self.ShortDayName = ko.observable('');
    self.DayName = ko.observable('');
    self.HasAnals = ko.observable(false);

    self.TrainingsDoneCount = ko.observable(0);
    self.TrainingsPlansCount = ko.observable(0);
    self.ContestCount = ko.observable(0);
    self.TargetsCount = ko.observable(0);

    self.weekDay = ko.observable(0);

    self.OutOfPeriod = ko.observable(false);
    //#endregion

    //#region diary
    self.PauseType = ko.observable('');
    self.RenewType = ko.observable('');
    self.WeatherType = ko.observable('');

    self.Dairies = ko.observableArray([]).extend({ rateLimit: 50 });

    self._diary = ko.pureComputed(function () {
        if (!self.Dairies() || !self.Dairies().length) return null;
        return _(self.Dairies());
    });
    SetDiaryProperties(self.FindEntry, self);
    //#endregion

    self.IsAdmin = ko.computed(function () {
        return self.Callendar.IsAdmin();
    });

    //#region filtered activities

    self.Trainings = ko.pureComputed(function () {
        var p = self.FilterActis('training');
        return _(p).filter(function (item) {
            return !item.IconInfo() || item.IconInfo().Icon != 'm';
        });
    });
    self.NoMultiTrainings = ko.pureComputed(function () {
        return _(self.Activities()).filter(function (item) {
            return item.Type() == 'training' && item.DurationSeconds() > 0;
        });
    });
    self.TrainigPlans = ko.pureComputed(function () {
        return self.FilterMultiActis(['trainingPlan', 'grafikEvent']);
    });
    self.TrainigPlansNotMapped = ko.pureComputed(function () {
        return _(self.Activities()).filter(function (item) {
            return (item.Type() == 'trainingPlan' && (!item.Details().Plan.MappedTrainings || !item.Details().Plan.MappedTrainings.length))
            || (item.Type() == 'grafikEvent');
        });
    });

    self.HasTrainigPlans = ko.pureComputed(function () {
        var plans = self.TrainigPlans()
        return (plans && plans.length);
    });

    self.ExaclyTargets = ko.pureComputed(function () {
        var p = self.FilterActis('target');
        return _(p).filter(function (item) {
            return item.Details().DayExacly;
        });
    });

    self.NearestTarget = ko.pureComputed(function () {

        if (!self.Future() && !self.Today()) return null;

        var p = self.FilterActis('target');
        if (p && p.length) {
            p2 = _(p).filter(function (item) {
                return item.Details().TargetModel.Status == 'Open';
            });
            if (p2 && p2.length) { return p2[0]; }
        }

        var days = self.Week.Days();
        var nextWeekDay = self.weekDay() + 1;
        var nextDay = _(days).filter(function (item) {
            return item.weekDay() == nextWeekDay;
        });

        if (nextDay && nextDay.length) {
            return nextDay[0].NearestTarget();
        }
        return null;
    });

    self.DaysToNearestTarget = ko.pureComputed(function () {

        var nt = self.NearestTarget();

        if (!nt) return 0;

        var targetDate = new Date(nt.Details().TargetModel.Deadline);
        var thisDay = new Date(self.Date());

        var amount = targetDate.getTime() - thisDay.getTime();	//calc milliseconds between dates

        if (amount <= 0) return 0;

        amount = Math.floor(amount / 1000);//kill the "milliseconds" so just secs

        var days = Math.floor(amount / 86400);//days

        if (days < 0) days = 0;

        return days;
    });

    self.Periods = ko.pureComputed(function () {
        return self.FilterMultiActis(['competition', 'contests', 'period']);
    });
    self.PeriodsTypeOrdered = ko.pureComputed(function () {
        var p = self.Periods();
        if (!p || !p.length) return [];

        var p1 = _(p).filter(function (item) {
            return !item.LongTermActivity();
        });
        if (!p1 || !p1.length) return [];

        var p2 = _(p1).sortBy(function (period) {
            if (!period || !period.Details() || !period.Details().PeriodType) return 0;
            switch (period.Details().PeriodType) {
                case "Contest": return 25;
                case "Competitions":
                    if (period.Details().Result && period.Details().CategoryModel)
                        return 1;
                    else
                        return 5;
                case "Program": return 15;
                case "Schools": return 10;
                case "Action": return 20;
                case "League": return 30;
                case "Other": return 40;
            }
            return 100;
        });

        return p2;
    });

    self._totalActis = null;
    self.Activities.subscribe(function (newValue) { self._totalActis = null; });
    //#endregion

    self.PaddingForTargets = ko.pureComputed(function () {
        var targets = self.ExaclyTargets();
        if (targets && targets.length) {
            var open = _(targets).filter(function (item) {
                return item.Details().TargetModel.Status == 'Open';
            }).length;
            var notOpen = targets.length - open;
            return (notOpen * 175 + open * 220 + 50) + 'px';
        } else if (self.NearestTarget()) {
            return '200px';
        }
    });

    //#region rows for month
    self.ActiRows = ko.observableArray([]);
    //#endregion

    //#region rows for plan
    self.ActiCols = ko.observableArray([]);
    self.ComputeActiColsTimeOut = null;

    //#endregion

    //#region day classes
    self.WideExpand = ko.observable(false);
    self.MustBeWideExpand = ko.pureComputed(function () {

        //if (self.Stress() || self.Pain() || self.Sleep() || self.Tired()) return true;

        if (self.ExaclyTargets().length != 0
           || self.NearestTarget() != null) return true;

        if (self.Trainings().length == 0
            && self.TrainigPlans().length == 0
            && self.PeriodsTypeOrdered().length == 0) return false;

        if (self.Trainings().length > 0 && self.TrainigPlans().length > 0) return true;
        var maxWidth = $(window).width();
        if (maxWidth < 1100) return true;
        if (/*maxWidth < 1400 &&*/ self.ContestCount() > 0) return true;
        return false;
    });

    self.DayClassModifires = ko.pureComputed(function () {
        var result = ' day' + self.weekDay();
        if (self.WideExpand() || self.MustBeWideExpand()) {
            result += ' wide ';
        }
        return result;
    });

    self.CoreEmptyDay = ko.pureComputed(function () {

        if (self.Trainings().length) {
            return false;
        }
        if (self.TrainigPlans().length) {
            return false;
        }
        if (self.ExaclyTargets().length) {
            return false;
        }

        var p = self.Periods();
        var p1 = _(p).filter(function (item) {
            return !item.LongTermActivity();
        });
        if (p1 && p1.length) {
            return false;
        }

        return true;
    });

    self.EmptyDay = ko.pureComputed(function () {

        if (self.Future()) return false;
        if (self.Today()) return false;

        if (!self.CoreEmptyDay()) return false;

        return true;
    });
    //#endregion

    //#region activity add
    self.VisibleAddIco = ko.observable(false);
    self.ShowAddTimeOut = null;
    self.HideAddTimeOut = null;
    self.HasAddTemplate = false;

    self.AddActivityUrl = ko.pureComputed(function () {
        return ContextPath + "User/TrainingDetails?selectedTab=Callendar&date=" + self.Date();
    });

    self.AddPlanUrl = ko.pureComputed(function () {
        return ContextPath + "TrainingPlan/PlanUnitEditor?date=" + self.Date() + "&week=" + self.Callendar.CalendarWeek() + "&day=" + (self.weekDay() + 1) + '&userId=' + self.Callendar.UserId() + '&planId=' + self.Callendar.TrainingPlanId();
    });
    self.AddContestUrl = ko.pureComputed(function () {
        return ContextPath + "Period/UserContents?addDate=" + self.Date();
    });

    self.Activities.subscribe(self.Activities_subscribe, self);

    //#endregion


    self.PeriodsCollapsed = ko.observable(true);
    self.PeriodsCollapseLabel = ko.pureComputed(function () {
        return self.PeriodsCollapsed() ? translations.Expand : translations.Collapse;
    });

    self.NumberOfVisiblePeriods = ko.pureComputed(function () {
        var nr = 4 - self.Trainings().length;
        if (nr < 1) nr = 1;
        var pl = self.PeriodsTypeOrdered().length;
        if (nr > pl) nr = pl;
        return nr;
    });
    self.VisiblePeriodsHeight = ko.pureComputed(function () {
        if (self.PeriodsCollapsed()) {
            return self.NumberOfVisiblePeriods() * 100 + 'px';
        } else {
            return self.PeriodsTypeOrdered().length * 100 + 'px';
        }
    });


    self.HasDiaryChanges = ko.pureComputed(function () {
        return (self.Dairies() && self.Dairies().length);
    });
    self.ShowDiaryIcon = ko.pureComputed(function () {
        var _diary = self._diary();
        if (!_diary) return false;
        return _diary.filter(function (elem) {
            var type = elem.TypeId();
            return type != 36 && type != 35 && elem.Value();
        }).length > 0;
    });

    self.DiaryNotesCloudVisible = new DefferedHover({
        showConfition: function () {
            return self.Notes();
        }
    });

    self.VisibleActiInfoCloud = ko.pureComputed(function () {
        return _(self.Activities()).find(function (elem) { return elem.VisibleCallendarInfoCloud(); });
    });

    self.ContestChooserModel = ko.observable(null);

    self.PauseDay = ko.pureComputed({
        read: function () {
            var isPause = false;
            if (trainingPlanManager) {
                var week = trainingPlanManager.SelectedUserTrainingWeek();
                if (week) {
                    isPause = week.PauseDaysArray[self.weekDay()]();
                }
            }
            return isPause;
        },
        write: function (value) {
            if (trainingPlanManager) {
                var week = trainingPlanManager.SelectedUserTrainingWeek();
                if (week) {
                    week.PauseDaysArray[self.weekDay()](value);
                    week.DeferedSave();
                }
            }
        },
        owner: self
    });


    //self.PauseHideOnAdd = new DefferedHover();

    _.bindAll(self
        , "FindEntry"
        , "FilterActis"
        , "FilterMultiActis"
        , "getAllTrainsAndPlansWithCompets"
        , "ComputeActiRows"
        , "ComputeActiCols"
        , "coreComputeActiCols"
        , "ComputeActiSums"
        , "AddTextNew"
        , "TogglePauseDay"
        , "showAdd"
        , "hideAdd"
        , "Add"
        , "AddPlan"
        , "AddPlanInModal"
        , "AddDiary"
        , "AddContest"
        , "AddFile"
        , "init"
        , "initByModel"
        , "dayViewSelect"
        , "select"
        , "ProgramSelect"
        , "unSelect"
        , "ChooseInitLoadCallback"
        , "ChooseSaveCallback"
        , "OpenEditContestInfo"
        );
}

UserCallendarDayModel.prototype = {
    //#region diary    
    FindEntry: function (typeId) {
        var self = this;
        if (!self._diary) return null;
        var _diary = self._diary();
        if (!_diary) return null;
        return _diary.find(function (entry) { return entry.TypeId() == typeId; });
    }
    //#endregion
    //#region filtered activities

    , FilterActis: function (actiType) {
        var self = this;
        return _(self.Activities()).filter(function (item) {
            return item.Type() == actiType;
        });
    }
    , FilterMultiActis: function (actiTypes) {
        var self = this;
        var ts = _(actiTypes);
        return _(self.Activities()).filter(function (item) {
            var t = item.Type();
            return ts.any(function (type) { return type == t; });
        });
    }
    , getAllTrainsAndPlansWithCompets: function () {
        var self = this;
        if (self._totalActis === null) {
            self._totalActis = self.Activities().slice(0);
            _.each(self.FilterActis('contests'), function (comp) {
                var dets = comp.Details();
                if (dets && dets.Activities && dets.Activities.length) {
                    _.each(dets.Activities, function (actiElem) {
                        var actObj = new UserCallendarActivityModel(self);
                        actObj.initByModel(actiElem);
                        self._totalActis.push(actObj);
                    });
                }
            });
        }
        return self._totalActis;
    }
    //#endregion
    //#region rows for month
    , ComputeActiRows: function () {
        var self = this;
        var result = [];
        var actis = self.Activities();

        var groups = [];
        var tempG;
        var selectId;

        var takePlans = self.Future() || self.Today();

        for (var i = 0, len = actis.length; i < len; i++) {
            var c = actis[i];
            var t = c.Type();
            if (t == 'training'
                || t == 'contests'
                || t == 'competition'
                || (t == 'target' && c.Details().DayExacly)
                || (t == 'trainingPlan' && takePlans && !c.Details().Plan.MappedTrainings.length)
                || (t == 'period' && takePlans && c.IconInfo().Icon == 'Competitions')) {
                if (c.IconInfo().Icon == 'm' || !c.Multi() || c.Contest() || c.Target() || c.Period()) {
                    //if (!(c.Multi() && c.IconInfo().Icon != 'm')) {
                    tempG = [];
                    groups.push(tempG);
                    selectId = c.TrainingId();
                }
                c.ForSelectId = selectId;
                if (c.IconInfo().Icon != 'm') {
                    tempG.push(c);
                }
            }
        }


        var idx = 0;
        var row = [];
        var maxWidth = $(window).width();
        var rowLength = maxWidth > 1500 ? 4 : (maxWidth > 1250 ? 3 : 2);
        if (!self.Pause() && !self.RenewType() && maxWidth < 1500) rowLength++;
        result.unshift({ row: row });
        for (var i = groups.length - 1; i >= 0; i--) {
            var g = groups[i];
            var glength = g.length;

            if (glength > rowLength) {
                for (var j = glength - 1; j >= 0; j--) {
                    idx++;
                    if (idx > rowLength) {
                        row = [];
                        result.unshift({ row: row });
                        idx = 1;
                    }
                    row.unshift(g[j]);
                }
            } else {
                idx = idx + glength;

                //zawody liczone jako 2, bo ikonka jest szeroka
                if (g.length > 0 && (g[0].Contest() || g[0].Competition())) {
                    idx++;
                }

                if (idx > rowLength) {
                    row = [];
                    result.unshift({ row: row });
                    idx = glength;
                }

                for (var j = glength - 1; j >= 0; j--) {
                    row.unshift(g[j]);
                }
            }
        }

        if (result.length > 2) {
            var toHide = 0;
            var secondRow = result[1].row;
            if (secondRow.length == rowLength) {
                secondRow.pop();
                toHide++;
            }
            for (var i = 2, len = result.length; i < len; i++) {
                toHide += result[i].row.length;
            }
            var elem = new UserCallendarActivityModel(self);
            elem.IconInfo({ Name: toHide, Icon: 'moreActivities', Color: '' });
            secondRow.push(elem);
        }

        self.ActiRows.removeAll();
        for (var i = 0, len = result.length; i < len && i < 2; i++) {
            self.ActiRows.push(result[i]);
        }
    }
    //#endregion
    //#region rows for plan
    , ComputeActiCols: function () {
        var self = this;
        if (self.ComputeActiColsTimeOut) clearTimeout(self.ComputeActiColsTimeOut);
        self.ComputeActiColsTimeOut = setTimeout(self.coreComputeActiCols, 300);
    },
    coreComputeActiCols: function () {
        var self = this;
        self.ActiCols.removeAll();

        var cols = trainingPlanManager.ActivePlan().TrainingTypesCols();

        totalActis = self.getAllTrainsAndPlansWithCompets();

        _(cols).each(function (col) {
            var actis;
            var _col = _(col.ids);
            if (col.ids.length > 0) {
                var cZero = _col.contains(0);
                if (cZero) {
                    var all = _(cols).reduceRight(function (a, b) {
                        if (!a || a == col) return b.ids;
                        if (!b || b == col) return a;
                        return a.concat(b.ids);
                    }, []);
                    all = _(all);
                    actis = _(totalActis).filter(function (item) {
                        if (item.TrainingTypeId() == 0) return true;
                        return !all.contains(item.TrainingTypeId());
                    });
                } else if (col.ids.length == 1) {
                    actis = _(totalActis).filter(function (item) {
                        return item.TrainingTypeId() == col.ids[0];
                    });
                } else {
                    actis = _(totalActis).filter(function (item) {
                        return _col.contains(item.TrainingTypeId());
                    });
                }

                _(actis).each(function (acti) {
                    acti.DisplayIcon(cZero || col.ids.length > 1);
                });

                var aArray = ko.observableArray(actis);
                self.ActiCols.push({
                    Column: col,
                    Activities: aArray,
                    SumPTime: ko.observable(0),
                    SumPDist: ko.observable(0),
                    SumTTime: ko.observable(0),
                    SumTDist: ko.observable(0),
                    TrainingPlanTargets: ko.observable([]),
                    AddTextNew: function () {
                        aArray.push(self.AddTextNew(col));
                    },
                    InfoCloudVisible: new DefferedHover()
                });
            }
        });

        if (!_.contains(trainingPlanManager.ActivePlan().CallbackToRecalculateCols, self.ComputeActiCols)) {
            trainingPlanManager.ActivePlan().CallbackToRecalculateCols.push(self.ComputeActiCols);
        }

    }
    , ComputeActiSums: function () {
        var self = this;
        //self.ActiCols.removeAll();

        self.MainDuration(0);
        self.AdditionallDuration(0);

        var cols = trainingPlanManager.ActivePlan().TrainingTypesCols();

        totalActis = self.getAllTrainsAndPlansWithCompets();

        _(self.ActiCols()).each(function (col) {

            var sumPTime = 0;
            var sumPDist = 0;
            var sumTTime = 0;
            var sumTDist = 0;
            var targets = [];
            var planTargets = [];
            var addSumTTime = 0;

            var addActiFunc = function (acti) {
                var dur = acti.DurationSeconds();
                if (dur) {
                    sumTTime += dur;
                    if (acti.Distance()) sumTDist += parseFloat(acti.Distance().replace(',', '.'));
                    if (acti.Details()) {
                        var t = acti.Details().TrainingPlanTargets;
                        if (t && t.length) {
                            targets = targets.concat(t);
                        }
                        addSumTTime += acti.Details().AdditionalDurationSeconds;
                    }
                }
            }
            var addPlanFunc = function (acti) {
                sumPTime += acti.DurationSeconds();
                if (acti.Distance()) sumPDist += parseFloat(acti.Distance().replace(',', '.'));

                if (acti.Details() && acti.Details().Plan) {
                    var t = acti.Details().Plan.PlanTargets;
                    if (t && t.length) {
                        planTargets = planTargets.concat(t);
                    }
                    //addSumTTime += acti.Details().AdditionalDurationSeconds;
                }
            }

            _(col.Activities()).each(function (acti) {
                if (acti.Type() == 'training') {
                    addActiFunc(acti);
                }
                if (acti.Type() == 'trainingPlan') {
                    addPlanFunc(acti);
                }
            });

            targets = _.uniq(targets.concat(planTargets));
            if (targets.length) {
                var lib = _(TrainingPlanLib.Trgets());
                targets = _(targets).map(function (id) { return lib.findWhere({ value: id }); });
                targets = _(targets).filter(function (t) { return t; });
            }

            if (col.Column.Additional) {
                self.AdditionallDuration(self.AdditionallDuration() + sumTTime + addSumTTime);
            } else {
                self.MainDuration(self.MainDuration() + sumTTime);
                self.AdditionallDuration(self.AdditionallDuration() + addSumTTime);
            }

            col.SumPTime(sumPTime);
            col.SumPDist(sumPDist);
            col.SumTTime(sumTTime);
            col.SumTDist(sumTDist);
            col.TrainingPlanTargets(targets);

        });

        if (!_.contains(trainingPlanManager.ActivePlan().CallbackToRecalculateCols, self.ComputeActiCols)) {
            trainingPlanManager.ActivePlan().CallbackToRecalculateCols.push(self.ComputeActiCols);
        }

    }

    , AddTextNew: function (column) {
        var self = this;
        var newActi = new UserCallendarActivityModel(self);
        newActi.Type('trainingPlan');
        var unit = trainingPlanManager.AddUnitInText();
        unit.Date(self.Date());
        unit.Week(self.Callendar.CalendarWeek());
        unit.Day(self.weekDay() + 1);
        unit.initPlanPersons(true);
        var typeId = 0;
        if (column.ids.length == 1) {
            typeId = column.ids[0];
            unit.TypeManager.SelectTypeById(typeId);
            newActi.IconInfo({ Icon: column.types[0].Icon });
        }
        newActi.DisplayIcon(typeId == 0);
        var p = unit.getClearData();
        p.MappedTrainings = [];
        p.ShowTime = false;
        p.ShowDistance = false;
        p.ShowSpeed = false;
        p.ShowPace = false;
        p.ShowPower = false;
        p.ShowCadence = false;
        p.ShowCalories = false;
        p.ShowZone = false;
        p.ShowStart = false;
        if (column.ids.length == 1) {
            p.TrainingTypeId = typeId;
        }
        newActi.Details({ Plan: p });
        newActi.EnterEditMode(unit);
        self.Activities.push(newActi);
        return newActi;
    }

    , TogglePauseDay: function (column) {
        var self = this;
        self.PauseDay(!self.PauseDay());
    }
    //#endregion
    //#region activity add
    , setAddTemplate: function (day, event) {
        var self = this;
        if (!self.HasAddTemplate) {
            var temp = self.Week.Entries.Callendar.setAddTemplate(function (template) {
                var hrCloud = $(event.target).closest(".day").find('.hrCloud ');
                var content = hrCloud.find('.cloudContent');
                if (!content.length) {
                    hrCloud.prepend(template);
                    content = hrCloud.find('.cloudContent');
                    if (content.length) {
                        ko.cleanNode(content[0]);
                        ko.applyBindings(self, content[0]);
                        self.HasAddTemplate = true;
                    }
                }
            });
        }
    }
    , showAdd: function (day, event) {
        var self = this;
        self.setAddTemplate(day, event);
        if (!self.Week.Entries.Callendar.IsAdmin() || self.Future() || self.Today()) {
            //if (!self.Future()) {
            clearTimeout(self.HideAddTimeOut);
            if (!self.VisibleAddIco()) {
                self.ShowAddTimeOut = setTimeout(function () { self.VisibleAddIco(true); }, 700);
            }
            // }
        }
    }
    , hideAdd: function () {
        var self = this;
        clearTimeout(self.ShowAddTimeOut);
        if (self.VisibleAddIco()) {
            self.HideAddTimeOut = setTimeout(function () { self.VisibleAddIco(false); }, 350);
        }
    }
    , Add: function (data, ev) {
        var self = this;
        if (!self.Future()) {
            if (ev.ctrlKey || ev.which == 2) {
                window.open(self.AddActivityUrl());
            } else {
                window.location = self.AddActivityUrl();
            }
        }
    }
    , AddPlan: function (data, ev) {
        var self = this;
        if (ev.ctrlKey || ev.which == 2) {
            window.open(self.AddPlanUrl());
        } else {
            window.location = self.AddPlanUrl();
        }
    }
    , AddPlanInModal: function (timeOfDay) {
        var self = this;
        self.Week.Entries.Callendar.InitTrainingPlanManager().editTrainingPlanUnit(0, self.Date(), 0, timeOfDay, true);
        self.hideAdd();
    }
    , AddDiary: function () {
        var self = this;
        //window.location = ContextPath + "Diary/Index?date=" + self.Date();
        slideUserDiaryModel.Open(self.Week.Entries.Callendar.UserId(), self.Date(), self);
        self.hideAdd();
        return false;
    }
    , AddContest: function () {
        var self = this;
        if (self.Future()) {
            //window.location = ContextPath + "Period/UserContents?addDate=" + self.Date();
            self.OpenEditContestInfo(self.Date());
        } else {
            window.location = ContextPath + "Contest/UserAdd?date=" + self.Date();
        }
    }
    , AddFile: function () {
        var self = this;
        if (self.Future()) {
            alert("Tu bedzie planowanie treningu :)");
        } else {
            window.location = ContextPath + "Upload/Index?date=" + self.Date();
        }
    }
    , Activities_subscribe: function (newValue) {
        var self = this;
        self.Callendar.EnableDrag();
    }
    //#endregion
    //#region activity add contest plan
    , ChooseInitLoadCallback: function (init) {
        init.selectedContestId = 0;
        return init;
    }
    , ChooseSaveCallback: function (elem) {
        var self = this;
        var periodSubs = new PeriodPlanModelPeriodSubstitute();
        periodSubs.CompetitionId(elem.SelectedContestId());
        periodSubs.StartDate(elem.ContestsDate());
        periodSubs.StartTime(elem.ContestsTime());
        periodSubs.Name(elem.ContestsName());
        var discId = elem.SelectedDisciplineId();
        periodSubs.DisciplineId(discId);
        periodSubs.CategoryId(elem.SelectedCategoryId());

        var periodUserPlan = new PeriodUserPlan(self);
        //periodUserPlan.AddedToList = false;
        periodUserPlan.PeriodPlan(new PeriodPlanModel(periodSubs));
        periodUserPlan.initNew();

        if (discId) periodUserPlan.Disciplines.push(elem.DisciplineManager.GetTypeById(discId));
        //if (discId) periodUserPlan.TypeManager.AvailableIds.push(discId);

        periodUserPlan.OpenEdit(self.Callendar.coreRefresh);
        /*
        self.periodUserPlans.push(periodUserPlan);
        self.periodUserPlans.sort(function (left, right) {
            var l = new Date(left.PeriodPlan().PeriodModel().StartDate());
            var r = new Date(right.PeriodPlan().PeriodModel().StartDate());
            return l == r ? 0 : (l < r ? -1 : 1);
        });*/
    }
    , OpenEditContestInfo: function (addDate) {
        var self = this;
        var elem = self.ContestChooserModel();
        if (elem == null) {
            elem = new ContestChooserModel(self);

            elem.DisplayDiscipline(true);
            //elem.OnlyFuture = true;
            self.ContestChooserModel(elem);
        }
        elem.ContestsDate(addDate);
        elem.CanChangeDate = false;
        elem.LengthType(1);


        elem.InitLoadCallback = function (init) { return self.ChooseInitLoadCallback(init); };
        elem.SaveCallback = function (elem) { self.ChooseSaveCallback(elem); };

        loadAndBindTemplate({
            simpleContainer: 'chooseContestModalPlanContainer',
            existingElem: $('.chooseContestModal'),
            ajaxPath: 'Period/ContestChooserModal',
            bindData: self,
            callback: function () {
                container = $('#chooseContestModalPlanContainer');
                ko.cleanNode(container[0]);
                ko.applyBindings(self, container[0]);

                elem.inintDataTables();
                self.ContestChooserModel().openEditContestInfo(addDate);
            }
        });
    }
    //#endregion

    , init: function () {
        var self = this;
        self.DayNr(0);
        self.Date('');
        self.Future(false);
        self.ThisMonth(false);
        self.Today(false);
        self.Activities().removeAll();
        self.weekDay(0);
        self.Pause(false);
        self.Selected(false);
        self.ShortDayName('');
        self.DayName('');
        self.Dairies.removeAll();
        self.PauseType('');
        self.RenewType('');
        self.WeatherType('');
        self.HasAnals(false);
    }

    , initByModel: function (serverModel, weekDay) {
        var self = this;
        if (serverModel) {
            self.DayNr(serverModel.DayNr);
            self.Date(serverModel.Date);
            self.Future(serverModel.Future);
            self.ThisMonth(serverModel.ThisMonth);
            self.Today(serverModel.Today);
            self.Pause(serverModel.Pause);
            self.Selected(serverModel.Selected);
            self.ShortDayName(serverModel.ShortDayName);
            self.DayName(serverModel.DayName);
            self.HasAnals(serverModel.HasAnals);

            self.TotalDuration(serverModel.TotalDuration);

            self.weekDay(weekDay);

            self.OutOfPeriod(serverModel.OutOfPeriod);

            self.PauseType(serverModel.PauseType);
            self.RenewType(serverModel.RenewType);
            self.WeatherType(serverModel.Weather);

            self.Dairies.removeAll();
            if (serverModel.Dairies) {
                for (var i = 0, len = serverModel.Dairies.length; i < len; i++) {
                    var elem = new DiaryEntryModel();
                    elem.initByModel(serverModel.Dairies[i]);
                    self.Dairies.push(elem);
                }
            }

            self.Activities.removeAll();
            if (serverModel.Activities) {
                for (var i = 0, len = serverModel.Activities.length; i < len; i++) {
                    var elem = new UserCallendarActivityModel(self);
                    var activity = serverModel.Activities[i];
                    elem.initByModel(activity);
                    self.Activities.push(elem);

                    if (
                        (activity.Type == 'training' &&
                        (!activity.Multi || activity.Multi && !activity.Parent))
                     || activity.Type == 'contests' && activity.Parent
                     || activity.Type == 'competitions') {
                        self.Week.Entries.Callendar.Activities.unshift(elem);
                    } else if (activity.Type == 'trainingPlan' && serverModel.Future) {
                        self.Week.Entries.Callendar.Activities.unshift(elem);
                    }
                }
            }
            self.recompute();

        }
    }
    , recompute: function () {
        var self = this;
        if (self.Callendar.PeriodType() == 'month') {
            self.ComputeActiRows();
        }
        if (self.Callendar.PeriodType() == 'plan') {
            self.coreComputeActiCols();
        }
    }

    //#region selection
    , dayViewSelect: function () {
        var self = this;
        if (!self.Selected()) {
            self.ProgramSelect(true);
        }
    }
    , select: function (data, event) {
        var self = this;
        if (event && event.target && $(event.target).closest('.hrCloud').length) return true;

        if (self.CoreEmptyDay()) return;

        self.ProgramSelect(false);
        return true;
    }
    , ProgramSelect: function (doNotScroll) {
        var self = this;
        self.Week.Entries.Callendar.LoadStats(self.Date(), 1, 0, self, doNotScroll);
        self.Selected(true);
    }
    , unSelect: function () {
        var self = this;
        self.Selected(false);
    }
    //#endregion

    , AddLibPlan: function (planUnit) {
        var self = this;

        if (planUnit instanceof TrainingPlanUnitModel) {
            self.realAddLibPlan(planUnit, true);
        } else if (planUnit instanceof UserCallendarActivityModel) {
            if (cntrlIsPressed) {
                setTimeout(function () { self.dragActivityCopy(planUnit); }, 250);
            } else {
                setTimeout(function () { self.dragActivity(planUnit); }, 250);
            }
        }
    }
    , dragActivity: function (activity) {
        var self = this;

        if (!activity.Details().Plan || !activity.Details().Plan.TrainingPlanUsersAssignations) return;

        // activity
        // var id = activity.Details().Id;
        // var instanceId = activity.Details().InstanceId;
        //  var targetWeekDay = self.weekDay();
        // var targetDate = self.Date();

        var maped = _.find(activity.Details().Plan.TrainingPlanUsersAssignations, function (elem) { return elem.MappedTrainingId; });
        if (maped) {
            ShowEmptyMsg(translations.CannotMoveRealisedPlans, translations.Info);
            return;
        }

        activity.EnsurePlanEditObj().dragMove(self.weekDay(), self.Date());

        activity.Day.Activities.remove(activity);
        activity.Day._totalActis = null;
        activity.Day.recompute();

        activity.Day = self;
        self.Activities.push(activity);
        self._totalActis = null;
        self.recompute();
        trainingPlanManager.ActivePlan().CalculateTrainingTypesCols();
    }
    , dragActivityCopy: function (activity) {
        var self = this;

        self.realAddLibPlan(activity.EnsurePlanEditObj(), false);
    }

    , realAddLibPlan: function (planUnit, fromLib) {
        var self = this;

        planUnit.UseFromLib(self.Date(), self.Callendar.CalendarWeek(), self.weekDay() + 1, function (unit, serverModel) {
            var newActi = new UserCallendarActivityModel(self);
            newActi.Type('trainingPlan');
            //newActi.Name(unit.Name());

            newActi.IconInfo({ Icon: 'none', Name: unit.Name() });

            newActi.DurationSeconds(stringToSeconds(unit.Duration()));
            newActi.TrainingId(unit.Id());
            newActi.Duration(unit.Duration());
            newActi.Distance(unit.Distance());
            newActi.TotalDuration(unit.Duration());
            newActi.ElementId(unit.Id());
            newActi.Plan(true);
            newActi.AVGHR(unit.CustomZoneDisplayValue());
            newActi.AvgSpeed(unit.AvgSpeed());
            newActi.Energy(unit.Calories());
            newActi.Power(unit.AvgPower());
            newActi.PaceUnit(unit.PaceUnit());
            newActi.Pace(unit.Pace());


            if (unit.TypeManager.SelectedTypeId() > 0) {
                newActi.IconInfo({ Icon: unit.TypeManager.SelectedType().Icon, Name: newActi.IconInfo().Name });
                //newActi.DisplayIcon(false);
            }
            var p = serverModel;
            p.MappedTrainings = [];
            p.ShowTime = unit.ShowTime();
            p.ShowDistance = unit.ShowDistance();
            p.ShowSpeed = unit.ShowSpeed();
            p.ShowPace = unit.ShowPace();
            p.ShowPower = unit.ShowPower();
            p.ShowCadence = unit.ShowCadence();
            p.ShowCalories = unit.ShowCalories();
            p.ShowZone = unit.ShowZone();
            p.ShowStart = unit.ShowStart();
            p.Description = unit.Description();
            //p.Exercises= unit.
            newActi.Details({ Plan: p, Description: unit.Description() });
            //newActi.EnsurePlanEditObj();
            //newActi.EditModeObj().initPlanPersons(true);
            //newActi.EnterEditMode(unit);
            newActi.prepareTrainingPlanUsersAssignations();
            self.Activities.push(newActi);
            self._totalActis = null;
            self.recompute();
            trainingPlanManager.ActivePlan().CalculateTrainingTypesCols();
        },
        fromLib);
    }
}


function UserCallendarActivityModel(parent) {
    var self = this;
    self.unassigned = false;
    if (parent instanceof UserCallendarDayModel) {
        self.Day = parent;
        self.Callendar = self.Day.Week.Entries.Callendar;
        self.Entries = self.Day.Week.Entries;
    } else if (parent instanceof UserCallendarEntriesModel) {
        self.Day = null;
        self.Callendar = parent.Callendar;
        self.Entries = parent;
        self.unassigned = true;
    }

    //#region params
    self.Type = ko.observable('');
    self.DurationSeconds = ko.observable(0);
    self.TrainingId = ko.observable(0);
    self.Duration = ko.observable('');
    self.IconInfo = ko.observable(null);
    self.Multi = ko.observable(false);
    self.StartTime = ko.observable('');
    self.PersonalBest = ko.observable(false);

    self.Distance = ko.observable('');
    self.TotalDuration = ko.observable('');
    self.Selected = ko.observable(false);

    self.StartSeconds = ko.observable(0);
    self.WeekDayNr = ko.observable(0);

    self.ElementId = ko.observable(0);
    self.Plan = ko.observable(false);
    self.PlanHasEvent = ko.observable(false);

    self.AVGHR = ko.observable('');
    self.AvgSpeed = ko.observable('');
    self.Energy = ko.observable('');
    self.Power = ko.observable('');
    self.PaceUnit = ko.observable('');
    self.Pace = ko.observable('');

    self.Details = ko.observable(null);

    self.TargetModel = ko.observable(null);

    self.InfoCloudVisible = new DefferedHover({
        showConfition: function () {
            return self.Type() != 'trainingPlan' || !self.EditModeObj() || self.EditModeObj().Id();
        }
    });


    //#region infoCloud
    self.VisibleCallendarInfoCloud = ko.observable(false);
    self.HideInfoCloudTimeOut = null;
    self.ShowInfoCloudTimeOut = null;
    self.HasInfoCloudTemplate = null;
    //#endregion

    //#endregion

    //#region activity Types
    self.Training = ko.pureComputed(function () {
        return self.Type() == 'training';
    });
    self.Contest = ko.pureComputed(function () {
        return self.Type() == 'contests';
    });
    self.Target = ko.pureComputed(function () {
        return self.Type() == 'target';
    });
    self.Period = ko.pureComputed(function () {
        return self.Type() == 'period';
    });
    self.Competition = ko.pureComputed(function () {
        return self.Type() == 'competition';
    });

    self.TrainingPlan = ko.pureComputed(function () {
        return self.Type() == 'trainingPlan';
    });

    //#endregion

    //#region icon
    self.IconUrl = ko.pureComputed(function () {
        if (self.IconInfo()) {
            return ContextPath + "Content/images/icons/" + self.IconInfo().Icon + "_22" + (self.Multi() ? "_cze" : "") + ".png";
        } else {
            return '';//ContextPath + "Content/images/chartIcons/small/none.png";
        }
    });
    self.IconColor = ko.pureComputed(function () {
        if (self.IconInfo()) {
            return self.IconInfo().Color;
        } else {
            return "#888";
        }
    });
    self.IconClass = ko.pureComputed(function () {
        if (self.IconInfo()) {
            return self.IconInfo().Icon;
        } else {
            return "none";
        }
    });
    self.IconCss = ko.pureComputed(function () {
        return self.IconClass() + (self.PersonalBest() ? " cze" : "") + (self.Plan() ? ' mod' : "");
    });
    self.DisplayIcon = ko.observable(true);
    //#endregion

    self.Name = ko.pureComputed(function () {
        var dets = self.Details();
        if (dets && dets.ActivityName) return dets.ActivityName;
        if (self.IconInfo()) {
            return self.IconInfo().Name;
        } else {
            return "";
        }
    });

    self.DayExacly = ko.pureComputed(function () {
        if (self.Target()) {
            if (self.Details()) {
                return self.Details().DayExacly;
            }
        }
        return true;
    });
    self.LongTermActivity = ko.pureComputed(function () {
        if (self.Period()) {
            if (self.Details() && self.Details().DaysLength && self.IconInfo().Icon != 'Competitions') {
                return self.Details().DaysLength >= 7;
            }
        }
        return false;
    });
    self.ShowInCommunity = ko.pureComputed(function () {
        if (self.Details()) {
            return self.Details().ShowInCommunity;
        } else {
            return false;
        }
    });

    self.ShowInLiveStream = ko.pureComputed(function () {
        if (self.Details()) {
            return self.Details().Plan && self.Details().Plan.LiveStream
                || self.Details().LiveStreamId && self.Details().LiveStreamId > 0;
        } else {
            return false;
        }
    });

    self.CanWip = ko.pureComputed(function () {
        var det = self.Details();
        if (det) {
            return det.Plan && (!det.Plan.Id || det.Plan.WorkInProgres);
        } else {
            return false;
        }
    });
    self.WorkInProgres = ko.pureComputed(function () {
        var det = self.Details();
        if (det) {
            return det.Plan && (det.Plan.WorkInProgres);
        } else {
            return false;
        }
    });

    self.EditUrl = ko.pureComputed(function () {
        var type = self.Type();
        switch (type) {
            case 'training': return ContextPath + "User/TrainingDetails?treningId=" + self.ElementId();
            case 'trainingPlan':
                if (self.Details().Plan.InstanceId) {
                    return ContextPath + "TrainingPlan/PlanUnitEditor?instanceId=" + self.Details().Plan.InstanceId;
                } else {
                    return ContextPath + "TrainingPlan/PlanUnitEditor?id=" + self.Details().Plan.Id;
                }
        }
        return '#'; //ContextPath + "Home/Home";
    });
    self.AddTrainingUrl = ko.pureComputed(function () {
        if (self.Details()) {
            var plan = self.Details().Plan;
            if (plan) {
                if (plan.MappedTrainings && plan.MappedTrainings.length) {
                    return ContextPath + 'User/TrainingDetails?treningId=' + plan.MappedTrainings[0];
                } else {
                    return ContextPath + 'User/TrainingDetails?planInstanceId=' + plan.InstanceId;
                }
            }
        }
        return '';
    });

    self.HasMappedPlans = ko.pureComputed(function () {
        if (self.Type() != 'training') return false;

        var dets = self.Details();
        var planInstIds = dets ? self.Details().MappedPlanIds : null;

        if (!planInstIds || !planInstIds.length) return false;

        return true;
    });
    self.MappedPlans = ko.pureComputed(function () {
        if (self.Type() != 'training' || !self.Day) return [];

        var dets = self.Details();
        var planInstIds = dets ? self.Details().MappedPlanIds : null;

        if (!planInstIds || !planInstIds.length) return [];

        var result = [];

        var plans = self.Day.TrainigPlans();
        planInstIds = _(planInstIds);

        return _(self.Day.Activities()).filter(function (item) {
            return item.Type() == 'trainingPlan' && planInstIds.any(function (id) { return item.Details().Plan.InstanceId == id; });
        });
    });
    self.TrainingTypeId = ko.pureComputed(function () {
        if (self.Type() == 'training' && self.Details()) {
            return self.Details().TrainingTypeId;
        }
        if (self.Type() == 'trainingPlan' && self.Details() && self.Details().Plan) {
            return self.Details().Plan.TrainingTypeId;
        }

        return 0;
    });


    self.Intensivity = ko.pureComputed(function () {
        var dets = self.Details();
        if (!dets) return 0;
        return dets.Intensivity || 0;
    });
    self.DisplayIntensivity = ko.computed(function () {
        return self.Intensivity();
    });
    self.IntensivityLabel = ko.computed(function () {
        return translations["Intensivity_" + self.DisplayIntensivity()];
    });

    //#region selecting
    self.ForSelectId = 0;

    self.selectedRow = ko.pureComputed(function () {
        return self.Selected() ? 'selected' : 'notSelected';
    });

    self.selectedRowCss = ko.pureComputed(function () {
        var style = self.Type();
        style += self.Selected() ? ' selected ' : ' notSelected';
        return style;
    });

    //#endregion

    //#region position
    self.Top = ko.pureComputed(function () {
        var min = self.Callendar.MinHour();
        var rowHeight = self.Callendar.RowHeight();
        var s = self.StartSeconds();

        var sr = self.StartSeconds() - min * 3600;

        return (sr * rowHeight / 3600) + "px";
    }, this);
    self.Height = ko.pureComputed(function () {
        var rowHeight = self.Callendar.RowHeight();

        return (self.DurationSeconds() * rowHeight / 3600) + "px";
    }, this);
    self.Left = ko.pureComputed(function () {
        var d = self.WeekDayNr();

        return (7.6 + 13.2 * (d - 1)) + "%";
    }, this);
    //#endregion

    //#region edit mode
    self.EditMode = ko.observable(false);
    /*
    self.EditModeTxt = ko.observable('');
    self.EditModeDuration = ko.observable('');
    */
    self.EditModeObj = ko.observable(null);

    //#endregion

    _.bindAll(self
        , "init"
        , "initByModel"
        , "ScrollToTile"
        , "unSelect"
        , "select"
        , "ActivityClick"
        , "selectRow"
        , "EnterEditMode"
        , "LeaveEditMode"
        , "LeaveEditModeWip"
        , "coreLeaveEditMode"
        , "SaveEditMode"
        , "SaveAndGoEdit"
        , "DeletePlanCallback"
        , "Delete"
        , "SetDay"
        , "showInfoCloud"
        , "hideInfoCloud"
        , 'OpenAssignmentsList'
        , 'prepareTrainingPlanUsersAssignations'
        , 'AdminActiMap'
        );
}

UserCallendarActivityModel.prototype = {
    init: function () {
        var self = this;
        self.DurationSeconds(0);
        self.TrainingId(0);
        self.Duration('');
        self.IconInfo(null);
        self.StartTime('');
        self.Distance('');
        self.AVGHR('');
        self.AvgSpeed('');
        self.Energy('');
        self.TotalDuration('');
        self.Selected(false);
        self.StartSeconds(0);
        self.WeekDayNr(0);
        self.DurationSeconds(0);
        self.ElementId(0);
        self.Plan('');
        self.Type('');
        self.Details(null);
        self.PersonalBest(false);
        self.PlanHasEvent(false);
    },
    initByModel: function (serverModel) {
        var self = this;
        if (serverModel) {
            self.DurationSeconds(serverModel.DurationSeconds);
            self.TrainingId(serverModel.ElementId);
            self.Duration(serverModel.Duration);
            self.IconInfo(serverModel.IconInfo);
            self.Multi(serverModel.Multi);
            self.StartTime(serverModel.StartTime);
            self.Distance(serverModel.Distance);
            self.TotalDuration(serverModel.TotalDuration);
            self.StartSeconds(serverModel.StartSeconds);
            self.WeekDayNr(serverModel.WeekDayNr);
            self.DurationSeconds(serverModel.DurationSeconds);
            self.ElementId(serverModel.ElementId);
            self.Plan(serverModel.Plan);
            self.PlanHasEvent(serverModel.PlanHasEvent);
            self.Type(serverModel.Type);
            self.PersonalBest(serverModel.PersonalBest);

            self.Details(serverModel.Details);

            if (serverModel.Details && serverModel.Details.TargetModel) {
                var tm = new TargetModel(null, true);
                tm.initByModel(serverModel.Details.TargetModel, false, true);
                self.TargetModel(tm);

            }

            if (serverModel.Details && serverModel.Details.Plan) {
                var obj = self.Details().Plan;
                var index = 1;

                if (!obj.MappedTrainings || !obj.MappedTrainings.length) {
                    obj.ShowDescript = (serverModel.Details.Description || serverModel.Details.Name) ? index++ : 0;
                    if (serverModel.Details.Description) {
                        index++;
                    }
                }

                obj.ShowTime = (obj.Duration && obj.Duration != '00:00') ? index++ : 0;
                obj.ShowDistance = (obj.Distance) ? index++ : 0;
                obj.ShowSpeed = (obj.AvgSpeed && obj.AvgSpeed != '0') ? index++ : 0;
                obj.ShowPower = (obj.AvgPower) ? index++ : 0;
                obj.ShowCadence = (obj.AvgCadence) ? index++ : 0;
                obj.ShowCalories = (obj.Calories) ? index++ : 0;
                obj.ShowZone = (obj.CustomZoneImage) ? index++ : 0;
                obj.ShowStart = (self.StartTime() != '00:00') ? index++ : 0;

                self.prepareTrainingPlanUsersAssignations(obj);
            }

            if (serverModel.Details && serverModel.Details.PeriodPlanModel) {
                var obj = self.Details().PeriodPlanModel;
                var ppm = new PeriodPlanModel();
                ppm.initByModel(serverModel.Details.PeriodPlanModel);
                self.Details().PeriodPlanModel = ppm;
            }
            if (serverModel.Details && serverModel.Details.SimpleCompetitionPlanModel) {
                var obj = self.Details().SimpleCompetitionPlanModel;
                var ppm = new TrainingPlanCompetitionModel(self);
                ppm.initByModel(obj);
                self.Details().SimpleCompetitionPlanModel = ppm;
            }


            self.AVGHR(serverModel.AVGHR ? serverModel.AVGHR.trim() : null);
            self.AvgSpeed(serverModel.AvgSpeed ? serverModel.AvgSpeed.trim() : null);
            self.Energy(serverModel.Energy ? serverModel.Energy.trim() : null);
            self.Power(serverModel.Power ? serverModel.Power.trim() : null);
            self.PaceUnit(serverModel.PaceUnit ? serverModel.PaceUnit.trim() : null);
            self.Pace(serverModel.Pace ? serverModel.Pace.trim() : null);

            /*
            $(serverModel.SubIconInfo).each(function (index, item) {
                self.SubIconInfo.push(item);
            });
            */
        }
    },
    prepareTrainingPlanUsersAssignations: function (planModel) {
        var self = this;
        if (!planModel) planModel = self.Details().Plan;

        if (planModel && planModel.TrainingPlanUsersAssignations) {
            for (var i = 0; i < planModel.TrainingPlanUsersAssignations.length; i++) {
                var curr = planModel.TrainingPlanUsersAssignations[i];
                if (curr.MappedTrainingId) {
                    curr.EditUrl = ContextPath + "User/TrainingAnalisisView?treningId=" + curr.MappedTrainingId;
                } else {
                    curr.EditUrl = ContextPath + "TrainingPlan/PlanUnitEditor?instanceId=" + curr.InstanceId;
                }
                curr.AvatarContent = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
                        + ContextPath + 'Image/GetImage?id=' + curr.AvatarFileId
                        + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
                curr.AvatarHover = function (callback) {
                    if (callback) callback(this.AvatarContent);
                }
                curr.Exclude = function () {
                    $.ajax({
                        type: "POST",
                        url: ContextPath + "TrainingPlan/ExcludeUser",
                        data: { instanceId: this.InstanceId }
                    }).done(function (result) {
                        // index = _.indexOf (planModel.TrainingPlanUsersAssignations,curr);
                        // planModel.TrainingPlanUsersAssignations.splice(index, 1);                        
                        self.Callendar.Refresh();
                    });
                }
                _.bindAll(curr, "AvatarHover", "Exclude");
            }
        }
    },
    //#region selecting
    select: function (data, event) {
        var self = this;

        if (event && event.target && $(event.target).closest('.hrCloud').length) return true;

        if (self.Callendar.IsAdmin()) {
            if (self.Training()) {
                window.location = ContextPath + "User/Trening?selectedTab=Treningi&treningId=" + self.TrainingId();
            }
        } else {
            var type = '';
            if (self.Training()) {
                type = 'training';
            } else if (self.Contest()) {
                type = 'contest';
            } else if (self.Competition()) {
                type = 'competition';
            } else if (self.Period()) {
                type = 'period';
                self.Day && self.Day.ProgramSelect();
                return;
            } else if (self.Plan()) {
                type = 'plan';
            }
            if (type) {
                self.Callendar.LoadStats('', 0, self.ForSelectId ? self.ForSelectId : self.TrainingId(), self, false, type);
            }
        }
    },
    unSelect: function () {

    },
    ScrollToTile: function () {
        var self = this;
        self.Callendar.ScrollToTrainingId(self.TrainingId());
    },
    ActivityClick: function () {
        var self = this;
        if (self.Callendar.PeriodType() == 'plan' && self.Type() == 'trainingPlan') {
            self.Callendar.InitTrainingPlanManager().editTrainingPlanUnit(0, '', self.TrainingId(), '', true);
        } else {
            self.Callendar.ScrollToTrainingId(self.TrainingId());
        }
    },
    selectRow: function (item, event) {
        var self = this;
        if (self.Type() == 'training' || self.Type() == 'contests') {
            self.Selected(!self.Selected());
            if (self.Selected()) {
                if (event.shiftKey) {
                    var prev = self.Callendar.PrevSelActivity();
                    if (prev != null) {
                        self.Callendar.SelectActivitiesOnShift(self, prev);
                    }
                }
                self.Callendar.PrevSelActivity(self);
            } else {
                self.Callendar.PrevSelActivity(null);
            }
            self.Callendar.RefreshListStat();
        }
    },
    //#endregion
    //#region edit mode
    EnsurePlanEditObj: function (unit) {
        var self = this;
        if (self.Type() == 'trainingPlan') {
            var obj = self.EditModeObj();
            if (!obj) {
                if (!unit) {
                    unit = trainingPlanManager.AddUnitInText();
                    unit.initByModel(self.Details().Plan);
                    // unit.initPlanPersons();
                }
                self.EditModeObj(unit);
            } else {
                return obj;
            }
        }
        return unit;
    },
    EnterEditMode: function (unit) {
        var self = this;
        unit = self.EnsurePlanEditObj(unit);
        if (unit) {

            /*
            rozwiazac problem podwojnego wstawiania
            self.EditModeObj().Name.subscribe(function (newValue) {
                if (newValue && self.EditMode()) {
                    self.SaveEditMode();
                }
            });
            self.EditModeObj().Description.subscribe(function (newValue) {
                if (newValue && self.EditMode()) {
                    self.SaveEditMode();
                }
            });
            self.EditModeObj().Duration.subscribe(function (newValue) {
                if (newValue && self.EditMode()) {
                    self.SaveEditMode();
                }
            });
            */

            if (!self.EditModeObj().SavingMode()) {
                self.InfoCloudVisible.Hide();
                self.EditMode(true);
                if (self.unassigned && self.Entries.MansoryLayout) {
                    setTimeout(function () { self.Entries.MansoryLayout.recalculate(true); }, 300);
                }
            }
        }
    },
    LeaveEditMode: function () {
        var self = this;
        //self.EditModeObj().WorkInProgres(false);
        $(':focus').blur();
        setTimeout(self.coreLeaveEditMode, 100);
    },
    LeaveEditModeWip: function () {
        var self = this;
        self.EnsurePlanEditObj();
        self.EditModeObj().WorkInProgres(true);
        $(':focus').blur();
        setTimeout(self.coreLeaveEditMode, 100);
    },
    LeaveEditModeWipToNormal: function () {
        var self = this;
        self.EnsurePlanEditObj();
        self.EditModeObj().WorkInProgres(false);
        $(':focus').blur();
        setTimeout(self.coreLeaveEditMode, 100);
    },
    coreLeaveEditMode: function (wip) {
        var self = this;
        var emo = self.EditModeObj();

        self.Details().Plan.Name = emo.Name();
        var type = emo.TypeManager.SelectedTypeIconCss();
        self.IconInfo({ Icon: type || 'none', Name: emo.Name() });
        self.Details().Plan.Description = emo.Description();
        self.Details().Description = emo.Description();
        self.Details().Plan.Duration = emo.Duration();
        self.Duration(emo.Duration());
        self.DurationSeconds(stringToSeconds(emo.Duration()));
        self.Details().Plan.ShowTime = emo.Duration() != null && emo.Duration() != '' && emo.Duration() != '00:00';
        self.Details().Plan.TrainingPlanUsersAssignations = [];
        self.Details().Plan.WorkInProgres = emo.WorkInProgres();

        self.Details.valueHasMutated();

        var clBck = function () {
            self.ElementId(emo.InstanceId());
            self.Details().Plan.Id = emo.Id();
            self.Details().Plan.InstanceId = emo.InstanceId();

            if (emo.TrainingPlanUsersAssignations().length) {
                var arr = [];
                for (var i = 0; i < emo.TrainingPlanUsersAssignations().length; i++) {
                    var curr = emo.TrainingPlanUsersAssignations()[i];
                    //curr.ParrentList = emo.TrainingPlanUsersAssignations;
                    if (curr.MappedTrainingId) {
                        curr.EditUrl = ContextPath + "User/TrainingAnalisisView?treningId=" + curr.MappedTrainingId;
                    } else {
                        curr.EditUrl = ContextPath + "TrainingPlan/PlanUnitEditor?instanceId=" + curr.InstanceId;
                    }
                    curr.AvatarContent = '<div class="personalDataContainerRight"><div class="avatarContainer"><div class="avatar"><img src="'
                            + ContextPath + 'Image/GetImage?id=' + curr.AvatarFileId
                            + '&width=156&height=156"></div><div class="transparentDiv"></div></div></div>';
                    curr.AvatarHover = function (callback) {
                        if (callback) callback(this.AvatarContent);
                    };
                    curr.Exclude = function () {
                        $.ajax({
                            type: "POST",
                            url: ContextPath + "TrainingPlan/ExcludeUser",
                            data: { instanceId: this.InstanceId }
                        });
                        //this.ParrentList.remove(this);
                        self.Callendar.Refresh();
                    }
                    _.bindAll(curr, "AvatarHover", "Exclude");
                    arr.push(curr);
                }

                self.Details().Plan.TrainingPlanUsersAssignations = arr;
            } else {
                self.Details().Plan.TrainingPlanUsersAssignations = [];
            }


            self.Details.valueHasMutated();
        };


        self.SaveEditMode(clBck);
        self.EditMode(false);
        self.InfoCloudVisible.HideImediate();

        if (!emo.Description() && !emo.Duration() && !emo.Name() && (!self.DisplayIcon() || !emo.TypeManager.SelectedTypeId())) {
            if (self.Day) {
                self.Day.Activities.remove(self);
                _.each(self.Day.ActiCols(), function (col) {
                    col.Activities.remove(self);
                });
            } else {
                self.Entries.UnassignedPlans.remove(self);
            }
        }

        if (self.Day) {
            self.Day.Week.CalculateSums();
        } else {
            var selectedWeek = _.find(self.Entries.Weeks(), function (w) { return w.Selected(); });
            if (selectedWeek) {
                selectedWeek.CalculateSums();
            }
        }
    },
    SaveEditMode: function (callback) {
        var self = this;
        if (self.Type() == 'trainingPlan') {
            var unit = self.EditModeObj();
            if (unit) {
                /*
                var txt = self.EditModeTxt().trim();
                var beginDur = txt.match(/^[0-2][0-3]:[0-5][0-9] /);
                if (beginDur) {
                    unit.Duration(beginDur[0]);
                    unit.Description(txt.slice(beginDur[0].length).trim());
                    self.Details().Plan.ShowTime = true;
                } else {
                    unit.Description(txt);
                }
                */
                if (self.EditModeObj().Name() || self.EditModeObj().Description() || self.EditModeObj().Duration() || (self.DisplayIcon() && self.EditModeObj().TypeManager.SelectedTypeId())) {
                    self.EditModeObj().Save(null, null, true, callback);
                }
            }
        }
    },
    SaveAndGoEdit: function () {
        var self = this;
        self.SaveEditMode(function () {
            window.location = ContextPath + 'TrainingPlan/PlanUnitEditor?instanceId=' + self.EditModeObj().InstanceId();
        });
    },
    DeletePlanCallback: function () {
        var self = this;
        if (self.Day) {
            self.Day.Activities.remove(self);
            self.Day._totalActis = null;
            self.Day.ComputeActiCols();
            self.Day.Week.CalculateSums();
        } else if (self.Entries.UnassignedPlans) {
            self.Entries.UnassignedPlans.remove(self);
            var selectedWeek = _.find(self.Entries.Weeks(), function (w) { return w.Selected(); });
            if (selectedWeek) {
                selectedWeek.CalculateSums();
            }
        }
    },
    Delete: function () {
        var self = this;
        if (self.Type() == 'trainingPlan') {
            var unit = self.EditModeObj();
            if (unit) {
                if (unit.Id()) {
                    self.EditModeObj().DeletePlan(null, null, self.DeletePlanCallback);
                } else {
                    self.DeletePlanCallback();
                }
            } else {

            }
        } else if (self.Training()) {

        }
    },
    SetDay: function (dayIdx) {
        var self = this;
        var unit = self.EnsurePlanEditObj(unit);
        if (unit) {
            var callback = null;
            self.EditModeObj().SetDay(dayIdx, self.Callendar.CalendarDate(), callback);
        }
    },
    OpenAssignmentsList: function () {
        var self = this;

        trainingPlanAssModel.Plan(self.EditModeObj());
        self.EditModeObj().initPlanPersons(true);

        loadAndBindTemplate({
            simpleContainer: 'trainingPlanAssignmentsModalContainer',
            existingElem: '#trainingPlanAssignmentsModal',
            ajaxPath: 'TrainingPlan/GetProgramPersonsModal',
            //bindData: self.EditModeObj(),
            callback: function () {
                $('#trainingPlanAssignmentsModal').modal('show');
            }
        });
    }
    //#endregion
    //#region infoCloud
    , setInfoCloudTemplate: function (day, event) {
        var self = this;
        if (!self.HasInfoCloudTemplate) {

            var callback = function (template) {
                var container = $(event.target).closest("div.activity");
                var infoCloud = container.find('.actiInfoContainer');
                if (!infoCloud.length && template) {
                    container.append(template);
                    infoCloud = container.find('.actiInfoContainer');
                    if (infoCloud.length) {
                        ko.cleanNode(infoCloud[0]);
                        ko.applyBindings(self, infoCloud[0]);
                        self.HasInfoCloudTemplate = true;
                    }
                }
            };

            var t = self.Type();
            switch (t) {
                case 'training': self.Callendar.setActivityInfoCloudTemplate(callback); break;
                case 'contests': self.Callendar.setContestResultInfoCloudTemplate(callback); break;
                case 'competition': self.Callendar.setContestPlanInfoCloudTemplate(callback); break;
                case 'period': self.Callendar.setContestPlanInfoCloudTemplate(callback); break;
            }
        }
    }
    , showInfoCloud: function (day, event) {
        var self = this;

        if (self.Training() || self.TrainingPlan() || self.Contest() || self.Competition() || self.Period()) {

            self.setInfoCloudTemplate(day, event);

            self.Day && clearTimeout(self.Day.ShowAddTimeOut);

            clearTimeout(self.HideInfoCloudTimeOut);

            clearTimeout(self.HideInfoCloudTimeOut);
            if (!self.VisibleCallendarInfoCloud()) {
                self.ShowInfoCloudTimeOut = setTimeout(function () { self.VisibleCallendarInfoCloud(true); }, 700);
            }
        }
    }
    , hideInfoCloud: function () {
        var self = this;
        clearTimeout(self.ShowInfoCloudTimeOut);
        if (self.VisibleCallendarInfoCloud()) {
            self.HideInfoCloudTimeOut = setTimeout(function () { self.VisibleCallendarInfoCloud(false); }, 750);
        }
    }
       , MappedPlansHover: function (callback) {
           cachedAjaxCall({
               type: "POST",
               url: ContextPath + "UserCallendar/GetMappedPlansInfo"
           }
             , function (result) {
                 if (callback) callback(result);
             }, 3//3600000 //1h
             );
       }
    //#endregion
    , AdminActiMap: function (actiModel) {
        var self = this;
        self.EnsurePlanEditObj().AdminActiMap(actiModel, self.Callendar.Refresh);

    }
}


var userCallendar = new UserCallendarModel();
GeneralModel.addProperty("UserCallendarModel", userCallendar);

function setEqualMapSize(container) {
    var mapHolder = $(".routeMap", container);
    var mapHolderWidth = mapHolder.width();
    //    mapHolder.css('height', mapHolderWidth + 'px');    
    mapHolder.css('width', 430 + 'px');
    mapHolder.css('height', 430 + 'px');
}

function loadTrainingDetailsTile(trainingId, onlyShow, divElem, clickRoute) {

    var divElem = $(divElem).closest(".tile");
    var container = $(".trainingDetailsContainer", divElem);
    var grayElem = $(divElem).closest(".simpleTrainingTile");

    var loaded = container.data("loaded");
    if (loaded) {
        container.hide();
        container.data("loaded", false);

        var dzyndzolek = divElem.find('.up_11');
        dzyndzolek.removeClass('up_11').addClass('down_11');
        dzyndzolek.attr("title", translations.Expand);
        //grayElem.addClass("grayed");
    } else {
        container.show();
        if (loaded == null || loaded == undefined) {
            loadCurrentChart(trainingId, false, container, null, true, 'hr');

            container.find('.detailsMenu a').on('click', function (e) {
                if (!$(this).hasClass('collapsed')) {
                    e.preventDefault();
                    e.stopPropagation();
                }
            });
        }
        container.data("loaded", true);

        container.find('.collapse').on('show', function () {
            container.find('.detailsMenu a[href!="#' + $(this).attr('id') + '"]').addClass("collapsed");
            container.find('.collapse.in').collapse('hide');
        });
        container.find('div.collapse[id^="route_"]').on('show', function () {

            //setEqualMapSize(container);

            MapInitialize(container);
            $.ajax({
                type: "POST",
                url: ContextPath + "User/getTrainingMapCoordinates",
                data: { trainingId: trainingId }
            })
             .done(function (result) {
                 var mapCoordinates;
                 var specialPointsCoordinates;
                 if (result != null) {
                     mapCoordinates = result[0];
                     specialPointsCoordinates = result[1];
                 } else {
                     mapCoordinates = null;
                     specialPointsCoordinates = null;
                 }
                 addMapPoints(container, mapCoordinates, specialPointsCoordinates);
             });
        });
        container.find('div.collapse[id^="speed_"]').on('show', function () {
            var paceunit = $(this).data("paceunit");
            var paceFactor = $(this).data("pacefactor");
            loadSpeedChart(trainingId, false, container, false, { unit: paceunit, factor: paceFactor });
        });
        container.find('div.collapse[id^="cadence_"]').on('show', function () {
            loadCadenceChart(trainingId, false, container);
        });
        container.find('div.collapse[id^="power_"]').on('show', function () {
            loadPowerChart(trainingId, false, container);
        });
        container.find('div.collapse[id^="altitude_"]').on('show', function () {
            loadAltitudeChart(trainingId, false, container);
        });
        container.find('div.collapse[id^="manual_laps_"]').on('show', function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "User/getLaps",
                data: { trainingId: trainingId }
            })
            .done(function (result) {
                var lapsContainer = $('#lapTable', container);
                lapsContainer.html(result);
            });

        });
        container.find('div.collapse[id^="auto_laps_"]').on('show', function () {
            $.ajax({
                type: "POST",
                url: ContextPath + "User/getAutoLaps",
                data: { trainingId: trainingId }
            })
            .done(function (result) {
                var lapsContainer = $('#autoLapTable', container);
                lapsContainer.html(result);
            });
        });


        var dzyndzolek = divElem.find('.down_11');
        dzyndzolek.removeClass('down_11').addClass('up_11');
        dzyndzolek.attr("title", translations.Collapse);
        //grayElem.removeClass("grayed");

        setRouteIfDefault(container, clickRoute);
    }
}

function setRouteIfDefault(container, clickRoute) {
    var iconRoute = container.find('.detailsMenu .route');

    if (iconRoute.length == 0) return;

    var isModalShowed = $('#footerInfoModal.modal.hide.fade.in').length == 1
    if (isModalShowed) {
        if (typeof communityModalShowTab !== 'undefined' && typeof communityModalSelectedTab !== 'undefined' && communityModalShowTab && communityModalSelectedTab) {
            return;
        }
    }

    if (clickRoute && !iconRoute.data().clicked) {
        iconRoute.click();
        iconRoute.data().clicked = true;
    }
}

function loadSessionDetailsTile(trainingId, onlyShow, divElem, onlyOpen) {
    var divElem = $(divElem).closest(".tile");
    var container = $(".sessionDetailsContainer", divElem);
    var grayElem = $(divElem).closest(".simpleTrainingTile");

    var containsHr = container.find('.hrChart').length > 0;

    setTimeout(function () { //timeout bo  jak jest zwiniety kafelek to nigdy nie jest visible
        if (container.find('.simpleTrainingTile .share_11:visible').length > 0) {
            divElem.find('.share_11.multi').hide();
        } else {
            divElem.find('.share_11.multi').show();
        }
    }, 550);

    var loaded = container.data("loaded");
    if (loaded) {
        if (!onlyOpen) {
            container.hide();
            container.data("loaded", false);

            var dzyndzolek = divElem.children('.tileFooter').find('.up_11');
            dzyndzolek.removeClass('up_11').addClass('down_11');
            dzyndzolek.attr("title", translations.Expand);
            //grayElem.addClass("grayed");
        }
    } else {

        container.show();

        if (containsHr) {
            if (loaded == null || loaded == undefined) {
                loadCurrentChart(trainingId, true, container.find('.sessionHrChart'), null, true, 'hr');
            }
        }
        container.data("loaded", true);

        /*
        container.parents('.sessionTraining').find('.multiSessionNavi a').click(function () {
            var pos = $('body').scrollTop();

            var hrefId = $(this).attr('href');
            var currId = hrefId.replace("#", "");

            container.parents('.sessionTraining').find('.multiSessionNavi a[href!="' + hrefId + '"]').removeClass("active");
            container.parents('.sessionTraining').find('.multiSessionNavi a[href="' + hrefId + '"]').addClass("active");
            container.children('div[id!="' + currId + '"]').hide();

            var cllapseElem = container.children(hrefId);
            //if (!cllapseElem.hasClass('hidden')) {
            if (!cllapseElem.is(":visible")) {
                cllapseElem.show();

                var init = cllapseElem.data("initiated");
                if (!init) {
                    cllapseElem.data("initiated", true);
                    var trainingId = cllapseElem.data("trainingid");
                    if (trainingId) {
                        loadTrainingDetailsTile(trainingId, onlyShow, $('#sessionSimpleTraining_' + trainingId + ' .st_trainingType'));
                    }
                }
            }
            setTimeout(function () { $('body').scrollTop(pos); });
        });
        */

        var dzyndzolek = divElem.children('.tileFooter').find('.down_11');
        dzyndzolek.removeClass('down_11').addClass('up_11');
        dzyndzolek.attr("title", translations.Collapse);
        //grayElem.removeClass("grayed");
    }
}


function toggleMultiTrainingTile(elem) {
    var divElem = $(elem).closest(".tile");
    var container = $(".trainingDetailsContainer", divElem);

    var loaded = container.data("loaded");
    if (loaded) {
        container.hide();
        container.data("loaded", false);

        var dzyndzolek = divElem.find('.up_11');
        dzyndzolek.removeClass('up_11').addClass('down_11');
        dzyndzolek.attr("title", translations.Expand);
        //grayElem.addClass("grayed");
    } else {
        container.show();
        if (loaded == null || loaded == undefined) {

            //ustawienie wiekszej wysokosci wykresow, jak jest duzo serii
            $('.multiChartLegend').each(function (index) {
                var t = $(this);
                var height = (t.height() - 50) * 1.147;//mnożnik, bo po dodaniu chb zwieksza sie wysokosc
                if (height > 230) {
                    var chart = t.closest('.box').find('.span9 .collapseHead').next('div');
                    var width = chart.width();
                    var maxHeight = width * 2 / 3;
                    if (height > maxHeight) {
                        chart.height(maxHeight);
                    } else {
                        chart.height(height);
                    }
                }
            });

            var paceunit = container.data("paceunit");
            var paceFactor = container.data("pacefactor");
            var paceParams = { unit: paceunit, factor: paceFactor };

            eventTableExist = true;
            var openChart = $('#hrChart.in');
            if (openChart.length) {
                loadSeveralHrChart(trainingIds, $('#hrChart', container));
            } else {
                openChart = $('#speed.in');
                if (openChart.length) {
                    loadSpeedChart(trainingIds, false, $('#speed', container), false, paceParams);
                } else {
                    openChart = $('#speed.in');
                    if (openChart.length) {
                        loadCadenceChart(trainingIds, false, $('#cadence', container));
                    } else {
                        openChart = $('#power.in');
                        if (openChart.length) {
                            loadPowerChart(trainingIds, false, $('#power', container));
                        }
                    }
                }
            }
        }
        container.data("loaded", true);

        container.find('div#speed').on('show', function () {
            loadSpeedChart(trainingIds, false, $('#speed', container), false, paceParams);
        });
        container.find('div#cadence').on('show', function () {
            loadCadenceChart(trainingIds, false, $('#cadence', container));
        });
        container.find('div#power').on('show', function () {
            loadPowerChart(trainingIds, false, $('#power', container));
        });

        var dzyndzolek = divElem.find('.down_11');
        dzyndzolek.removeClass('down_11').addClass('up_11');
        dzyndzolek.attr("title", translations.Collapse);
    }

    //obsluga usuwania treningow z porownania
    if (trainingIds) {
        $('.userSettingsContainer.little .remove').each(function (index) {
            var x = $(this);
            var container = x.closest('.compTrain');
            if (container.length) {
                var tId = container.data('trainingid');
                if (tId) {
                    x.show();
                    x.click(function () {
                        trainingIds = trainingIds.replace("" + tId, "").replace(";;", ";");
                        if (trainingIds && trainingIds.charAt(0) == ';') {
                            trainingIds = trainingIds.slice(1, trainingIds.length - 1);
                        }
                        container.remove();
                        var chbs = $(".toggleOnChart[data-id='" + tId + "']");
                        chbs.each(function (index) {
                            var chbContainer = $(this);
                            chb = chbContainer.find('input');
                            if (chb.length && chb.is(':checked')) {
                                chb.change();
                            }
                            chbContainer.closest('tr').remove();
                        });
                    });
                }
            }
        });
    }
}

function loadContestDetailsTile(trainingId, onlyShow, elem) {

    var divElem = $(elem).closest(".contestTile");
    var container = $(".contestDetailsContainer", divElem);
    var containsHr = container.find('.hrChart').length > 0;

    var loaded = container.data("loaded");
    if (loaded) {
        container.hide();
        container.data("loaded", false);

        var dzyndzolek = divElem.find('.up_11');
        dzyndzolek.removeClass('up_11').addClass('down_11');
        dzyndzolek.attr("title", translations.Expand);
        //grayElem.addClass("grayed");
    } else {
        container.show();
        if (containsHr) {
            if (loaded == null || loaded == undefined) {
                loadCurrentChart(trainingId, true, container.find('.sessionHrChart'), null, true, 'hr');
            }
        }
        container.data("loaded", true);



        var dzyndzolek = divElem.find('.down_11');
        dzyndzolek.removeClass('down_11').addClass('up_11');
        dzyndzolek.attr("title", translations.Collapse);
        //grayElem.removeClass("grayed");
    }
}

/*$( window ).resize(function() {
    $('.simpleTraining div.collapse[id^="route_"]').each(function () {
        var container = $(this);

        var mapHolderContainer = $(".routeMap", container);
        var mapHolderContainerWidth = mapHolderContainer.width();
        var mapHolderContainerHeight = mapHolderContainer.height();
        
        if (mapHolderContainerWidth != mapHolderContainerHeight && container.css('height') != "0px")
        {
            var mapHolder = $(".routeMap", container)[0];
            mapData = jQuery.data(mapHolder, "map");

//             var center = mapData.getCenter();
//             var zoom = mapData.getZoom();

            //setEqualMapSize(container);
            //google.maps.event.trigger(mapData, 'resize');

//             var bounds = new google.maps.LatLngBounds();
//             bounds.extend(new google.maps.LatLng(center.lat(), center.lng()));
//             mapData.fitBounds(bounds);
//             mapData.setZoom(zoom);
        }
    });
});*/



function TrainingPlanAssModel() {
    var self = this;

    self.Plan = ko.observable(null);

    self.Close = function (data, event, callback) {
        self.Plan().SaveAssList(function () {
            $('.trainingPlanAssignmentsModal').modal('hide');
            if (callback) callback();
        });
    }

}
var trainingPlanAssModel = new TrainingPlanAssModel();
GeneralModel.addProperty("TrainingPlanAssModel", trainingPlanAssModel);


;var reloadFunction;
var lastPersonId;
var periodChange = false;
var afterDelete;

var previousTabContent = null;
var tabContentName = null;

function loadTrenings(userId) {

    if (tabContentName == 'history') return;

    $('.personTrainings').css('cursor', 'wait');

    $('.datapicker').datepicker('hide');
    var startDate = periodChange ? '' : $('#startDate').val();
    var endDate = periodChange ? '' : $('#endDate').val();
    var periodId = $('#periodSelect').val();
    if (!periodId) periodId = 0;

    //setWait();

    $.ajax({
        type: "POST",
        url: ContextPath + "User/GetTrenings",
        data: { userId: userId, startDate: startDate, endDate: endDate, periodId: periodId }
    })
     .done(function (result) {
         lastPersonId = userId;

         var container = $('#history');

         if (tabContentName && tabContentName == 'calendar') {
             previousTabContent = container.html();
         }

         container.html(result);

         tabContentName = 'history';

         loadTrainngsList(userId);

         $('.personTrainings').css('cursor', 'initial');
     });
}

function loadBodyParameters(userId) {
    if (tabContentName == 'bodyParameters') return;

    $('.personTrainings').css('cursor', 'wait');

    $.ajax({
        type: "POST",
        url: ContextPath + "Diary/BodyParameters",
        data: { userId: userId }
    })
     .done(function (result) {
         var container = $('#bodyParameters');

         container.html(result);

         tabContentName = 'bodyParameters';
         
         userDiaryModelForAdmin.Load(userId, null, true);
         
         GeneralModel.CheckIfApply(container.children('div')[0]);  
         
         initTrainingNaviBelt();

         $('.personTrainings').css('cursor', 'initial');
     });
}

function loadStatistics(userId) {
    if (typeof UserId !== 'undefined') {
        UserId = userId;
    }
        
    if (tabContentName == 'statistics') return;

    $('.personTrainings').css('cursor', 'wait');

    $.ajax({
        type: "POST",
        url: ContextPath + "Stats/UserStatistics",
        data: { partial: true, userId: userId, doNotBind:true }
    })
     .done(function (result) {
         var container = $('#statistics');

         ko.cleanNode(container[0]);
         container.html(result);
         GeneralModel.CheckIfApply(container[0]);

         tabContentName = 'statistics';
                 
         $('.personTrainings').css('cursor', 'initial');
     });
}

function loadActivity(userId) {
    if (typeof UserId !== 'undefined') {
        UserId = userId;
    }
    if (tabContentName == 'activity') return;

    $('.personTrainings').css('cursor', 'wait');

    $.ajax({
        type: "POST",
        url: ContextPath + "Coaches/Activity",
        data: { userId: userId, start:null, end: null }
    })
     .done(function (result) {
         var container = $('#activity');

         ko.cleanNode(container[0]);
         container.html(result);
         GeneralModel.CheckIfApply(container[0]);

         tabContentName = 'activity';

         $('.personTrainings').css('cursor', 'initial');
     });
}


/*******************************************
********************************************
MANAGE TRAINING
********************************************
*******************************************/

function acceptTraining(id) {
    $.ajax({
        type: "POST",
        url: ContextPath + "User/AcceptTraining",
        data: { id: id }
    })
     .done(function (result) {
         if (lastPersonId) {
             reloadFunction(lastPersonId);
         } else {
             reloadFunction();
         }
     });
}

function rejectTraining(id) {
    $.ajax({
        type: "POST",
        url: ContextPath + "User/RejectTraining",
        data: { id: id }
    })
     .done(function (result) {
         if (lastPersonId) {
             reloadFunction(lastPersonId);
         } else {
             reloadFunction();
         }
     });
}
function classify(id) {
    $.ajax({
        type: "POST",
        url: ContextPath + "User/ClassifyTraining",
        data: { id: id }
    })
     .done(function (result) {
         if (lastPersonId) {
             reloadFunction(lastPersonId);
         } else {
             reloadFunction();
         }
     });
}
function unClassify(id) {
    $.ajax({
        type: "POST",
        url: ContextPath + "User/UnClassifyTraining",
        data: { id: id }
    })
     .done(function (result) {
         if (lastPersonId) {
             reloadFunction(lastPersonId);
         } else {
             reloadFunction();
         }
     });
}


function editTraining(trainingId) {
    trainingDetailsManager.editTraining(trainingId);
}

/*******************************************
********************************************
END MANAGE TRAINING
********************************************
*******************************************/


function getChbVal(id) {
    var filterCheck = $('#' + id);
    var filter;
    if (filterCheck.length == 0) {
        filter = true;
    } else {
        filter = filterCheck.attr('checked');
        filter = filter ? true : false;
    }
    return filter;
}

/*******************************************
********************************************
TRAININGS LISTS
********************************************
*******************************************/


var eventTableExist = false;
var bindEventLegend = null;

var checkedTrainings = [];

function toggleTrainingRow(ctrl) {
    var input =$(ctrl);
    var checked = input.is(':checked');
    var id = input.data("id");
    var onList = $.inArray(id, checkedTrainings)>=0;
    if (checked) {
        if (!onList) {
            checkedTrainings.push(id);
            toggleTrainingRowLoadDetails(id);
        }
    } else {
        if (onList) {
            var index = checkedTrainings.indexOf(id);
            checkedTrainings.splice(index, 1);
            removeToggleTrainingRowDetails(index);
        }
    }

    var a = $(".compareTrs");
    if (checkedTrainings.length == 0) {
        a.addClass("disabled");
    } else {
        a.removeClass("disabled");
    }
    var params = checkedTrainings.join(";");
    a.attr("href", ContextPath + "User/Trenings?ids=" + params);
}
function compareselectedTrainings() {
    if (checkedTrainings.length > 0) {
        var params = checkedTrainings.join(";");
        window.location = ContextPath + "User/Trainings?ids=" + params;
    }
}

function removeToggleTrainingRowDetails(index)
{
    var container = $('.allSelectedTrainingsList');
    
    if (container.length) {
        $('.selectedTrainingContainer', container)[index].remove();

        removeMarkElemAsLeftRight('.selectedTrainingContainer', container);

        toggleTrainingRowLayoutDetails();
    }
}


function toggleTrainingRowLoadDetails(id)
{
    $('.personTrainings').css('cursor', 'wait');    
    
    $.ajax({
        type: "POST",
        url: ContextPath + "User/Trening",
        data: { treningId: id, selectedTab: null, partial: true }
    })
     .done(function (result) {
         var container = $('.allSelectedTrainingsList');
         
         var selectedTrainingContainer = document.createElement("div");
         selectedTrainingContainer.className = "selectedTrainingContainer";
         $(selectedTrainingContainer).append(result);   

         container.append(selectedTrainingContainer);
         
         loadStatControl();         
         
         $('.personTrainings').css('cursor', 'initial');     
         
         toggleTrainingRowLayoutDetails();
     });    
}

function toggleTrainingRowLayoutDetails()
{
     var container = $('.allSelectedTrainingsList');

     if ($('.selectedTrainingContainer', container).length == 1 || container.width()<1190)
     {
         $('.selectedTrainingContainer', container).css('width', '100%')
         $('.selectedTrainingContainer', container).removeClass('left');
     }
     else
     {
         $('.selectedTrainingContainer', container).css('width', '50%')

         markElemAsLeftRight(container, '.selectedTrainingContainer');
     }     
}

var trainingsAoColumns = [
          {
              "bSortable": false,
              "bVisible": true,
              "sWidth": "15px",
              "mRender": function (data, type, full) {
                  var idata = parseInt(data);
                  var checked = $.inArray(idata, checkedTrainings);
                  return '<input type="checkbox" data-id="' + data + '" ' + (checked>=0?'checked="checked"':'') + 'onchange="toggleTrainingRow(this);" />';
              }
          }, //0 - id
          { "bVisible": true }, //1 - nick
          { "sWidth": "70px" }, //2 - date
          { "sWidth": "60px" }, //3 - time
          { "sWidth": "60px" }, //4 - duration
          { "sWidth": null }, //5 - discipline
          { "sWidth": null }, //6 - name,distance
          { "sWidth": "65px" }, //7 - avghr
          { "sWidth": "65px" }, //8 - distance
          { "sWidth": "55px", "sType": "title-string" }, //9 - status
          { "sWidth": "200px" } //10 - buttons
];

function getTrainingsTableConfig(userId, eventId) {
    return {
        "bDestroy": true,
        "oLanguage": dataTableLanguageOptions,
        "aoColumnDefs": [
              { "aDataSort": [0, 1, 2], "aTargets": [0] },
              { "aDataSort": [1, 2, 0], "asSorting": ["desc", "asc"], "aTargets": [1] },
              { "aDataSort": [2, 1, 0], "aTargets": [2] }
        ],
        "aaSorting": [[2, "desc"]],
        "bAutoWidth": false,
        "aoColumns": trainingsAoColumns,
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": ContextPath + "User/getTrainingListJson",
        "sServerMethod": "POST",
        "bStateSave": true,
        "fnServerParams": function (aoData) {
            $('.datapicker').datepicker('hide');
            var startDate = periodChange ? '' : $('#startDate').val();
            var endDate = periodChange ? '' : $('#endDate').val();
            var periodId = $('#periodSelect').val();
            var clubId = $('#clubSelect').val();
            if (!periodId) periodId = 0;
            if (!eventId) eventId = $('#eventId').val();
            periodChange = false;
            aoData.push({ name: "startDate", value: startDate });
            aoData.push({ name: "endDate", value: endDate });
            aoData.push({ name: "clubId", value: clubId });
            aoData.push({ name: "periodId", value: periodId });
            aoData.push({ name: "eventId", value: eventId });
            aoData.push({ name: "userId", value: userId });
            aoData.push({ name: "filterForRankings", value: getChbVal('FilterNonRanked') });
            aoData.push({ name: "filterOnlyEvents", value: getChbVal('FilterOnlyEvents') });
        },
        "fnDrawCallback": function (oSettings) {
            var obj = JSON.parse(oSettings.jqXHR.responseText);
            $('#startDate').val(obj.startDate);
            $('#endDate').val(obj.endDate);

            eventTableExist = true;
            if (bindEventLegend) {
                bindEventLegend('table');
            }
        }
    };
}

function bindTrainingTableNavi(table, container) {
    reloadFunction = function () { table.fnDraw(false); }

    periodChange = false;
    $('.datapicker', container).datepicker({ weekStart: 1 }).on('changeDate', function (ev) {
        $('#periodSelect').val("-1");
        table.fnDraw();
    });

    $('.datapicker', container).change(function (ev) {
        $('#periodSelect').val("-1");
        table.fnDraw();
    });
    $('#periodSelect').change(function () {
        periodChange = true;
        table.fnDraw();
    });
    $('#clubSelect').change(function () {
        table.fnDraw();
    });

    $('#FilterNonRanked').change(function (ev) {
        table.fnDraw();
    });
    $('#FilterOnlyEvents').change(function (ev) {
        table.fnDraw();
    });
}

function loadTrainngsList(userId) {

    var container = $('#tabContainer');

    var customConfig = getTrainingsTableConfig(userId);

    var table = $('.userTraininsDataTable', container).dataTable(customConfig);

    bindTrainingTableNavi(table, container);
}

function loadNewTreningsListJson() {
    var container = $('#tabContainer');

    var eventId = $('#eventId').val();
    if (typeof (eventId) == 'undefined') eventId = 0;

    var cols = trainingsAoColumns.slice();
    cols[2] = { "sWidth": "70px", "bVisible": eventId == 0 };
    cols[3] = { "sWidth": "60px", "bVisible": eventId == 0 };

    var customConfig = getTrainingsTableConfig(0, eventId);
    customConfig["aoColumns"] = cols;
    customConfig["sAjaxSource"] = ContextPath + "User/GetNewTreningsJson";

    var table = $('.traininsDataTable', container).dataTable(customConfig);

    bindTrainingTableNavi(table, container);
}

function loadAllTreningsListJson() {
    var container = $('#tabContainer');

    var eventId = $('#eventId').val();
    if (typeof (eventId) == 'undefined') eventId = 0;

    var cols = trainingsAoColumns.slice();
    cols[2] = { "sWidth": "70px", "bVisible": eventId == 0 };
    cols[3] = { "sWidth": "60px", "bVisible": eventId == 0 };

    var customConfig = getTrainingsTableConfig(0, eventId);
    customConfig["aoColumns"] = cols;
    customConfig["sAjaxSource"] = ContextPath + "User/AllTrainingsJson";

    var table = $('.traininsDataTable', container).dataTable(customConfig);

    bindTrainingTableNavi(table, container);
}

function loadAllTreningsCoachListJson() {
    var container = $('#tabContainer');

    var eventId = $('#eventId').val();
    if (typeof (eventId) == 'undefined') eventId = 0;

    var cols = trainingsAoColumns.slice();
    cols[2] = { "sWidth": "70px", "bVisible": eventId == 0 };
    cols[3] = { "sWidth": "60px", "bVisible": eventId == 0 };

    var customConfig = getTrainingsTableConfig(0, eventId);
    customConfig["aoColumns"] = cols;
    customConfig["sAjaxSource"] = ContextPath + "User/AllTrainingsCoachJson";

    var table = $('.traininsDataTable', container).dataTable(customConfig);

    bindTrainingTableNavi(table, container);
}

function loadAnalTrainingsJson() {
    var container = $('#tabContainer');

    var eventId = $('#eventId').val();
    if (typeof (eventId) == 'undefined') eventId = 0;

    var cols = trainingsAoColumns.slice();
    cols[2] = { "sWidth": "70px", "bVisible": eventId == 0 };
    cols[3] = { "sWidth": "60px", "bVisible": eventId == 0 };
    cols[5] = { "sWidth": "65px" };
    cols[6] = { "sWidth": "65px" };
    cols.splice(7, 0, { "sWidth": "65px" });

    var customConfig = getTrainingsTableConfig(0, eventId);
    customConfig["aoColumns"] = cols;
    customConfig["sAjaxSource"] = ContextPath + "User/GetAnalTrainingsJson";

    var table = $('.traininsDataTable', container).dataTable(customConfig);

    bindTrainingTableNavi(table, container);
}

function loadTrainingsJson(listType) {
    switch (listType) {
        case 'ForAccept': loadNewTreningsListJson(); break;
        case 'All': loadAllTreningsListJson(); break;
        case 'ForAnalyse': loadAnalTrainingsJson(); break;
        case 'Coached': loadAllTreningsCoachListJson(); break;
    }
}

/*******************************************
********************************************
END TRAININGS LISTS
********************************************
*******************************************/




/*******************************************
********************************************
TARGET TRAININGS LISTS
********************************************
*******************************************/

var TargetTrainings = function () {
    var self = this;

    self.DataTable = null;
    self.TaregtId = 0;
    self.GroupTarget = false;

    self.init = function (targetId, groupTarget) {
        self.TaregtId = targetId;
        self.GroupTarget = groupTarget;

        var container = $('.groupTargetStats.trainings');

        var eventId = 0;

        var cols = trainingsAoColumns.slice();
       // cols[0] = { "bVisible":false };
        cols[2] = { "sWidth": "70px", "bVisible": eventId == 0 };
        cols[3] = { "sWidth": "60px", "bVisible": eventId == 0 };
        cols[6] = {  "bVisible": false};

        cols.splice(8, 0,
            { "sWidth": "65px" },
            { "sWidth": "65px" },
            { "sWidth": "65px" }
            );

        var customConfig = getTrainingsTableConfig(0, eventId);
        customConfig["aoColumns"] = cols;
        customConfig["sAjaxSource"] = ContextPath + "User/TargetTrainingsJson";

        customConfig["fnServerParams"]= function (aoData) {
            $('.datapicker').datepicker('hide');


            var clubId = $('#clubSelect').val();
            if (!periodId) periodId = 0;
           
            aoData.push({ name: "targetId", value: self.TaregtId });
            aoData.push({ name: "periodId", value: periodId });
           // aoData.push({ name: "filterForRankings", value: getChbVal('FilterNonRanked') });
           // aoData.push({ name: "filterOnlyEvents", value: getChbVal('FilterOnlyEvents') });
        }


        self.DataTable = $('.traininsDataTable', container).dataTable(customConfig);

        bindTrainingTableNavi(self.DataTable, container);

        $('#footerInfoModalBody').css('cursor', 'initial');
    }

    self.Redraw = function (reloadPageing) {
        if (self.DataTable) {
            self.DataTable.fnDraw(reloadPageing);
        }
    }
}

acceptTargetTraining = function (tarainingId, targetId) {
    setTargetTrainingStatus(tarainingId, targetId, true, '');
}
rejectTargetTraining = function (tarainingId, targetId) {

    modalAskModel.Show(
             translations.Reject,
             translations.RejectActivityMsg,
             function () { setTargetTrainingStatus(tarainingId, targetId, false, $('#GenericAskModal input[type="text"').val()); },
             translations.Reject);


    
}
setTargetTrainingStatus = function (tarainingId, targetId, status, msg) {
    $.ajax({
        type: "POST",
        url: ContextPath + "Target/SetTargetTrainingStatus",
        data: { tarainingId: tarainingId, targetId: targetId, status:status, msg: msg }
    })
         .done(function (result) {
             targetModelManager.PeriodExplorerManager.TargetTrainings.Redraw(false);
         });
}

/*******************************************
********************************************
END TARGET TRAININGS LISTS
********************************************
*******************************************/;
var ICON_POSITION_MARKER = ContextPath + 'Content/images/icons/position_marker.png';
var ICON_POSITION_MARKER_WEST = ContextPath + 'Content/images/icons/position_marker_west.png';
var ICON_START_MARKER = ContextPath + 'Content/images/icons/start_marker.png';
var ICON_FINISH_MARKER = ContextPath + 'Content/images/icons/finish_marker.png';
var ICON_CHECKPOINT_MARKER = ContextPath + 'Content/images/icons/checkpoint_marker.png';
var ICON_POSITION_MARKER_GROUP = ContextPath + 'Content/images/icons/position_marker_group.png';
var ICON_POSITION_MARKER_GROUP_WEST = ContextPath + 'Content/images/icons/position_marker_group_west.png';
var ICON_LAP_MARKER = ContextPath + 'Content/images/icons/punkt_17.png';
var ICON_LAP_AUTO_MARKER = ContextPath + 'Content/images/icons/punkt_auto_17.png';



var template_marker = [
    '<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30.584px" height="27.417px" viewBox="0 0 30.584 27.417" xml:space="preserve">',

    '<path fill="#3F3F3E" d="M27.383,0.744H3.686c-1.352,0-2.864,1.1-2.864,2.451v7.269v2.294v0.028l0.003,0.002 ',
	'c0.018,1.34,1.107,2.422,2.448,2.422h9.637c1.966,0,6.252-0.021,7.194-0.353c0.433-0.151,0.857-0.512,0.857-0.512l1.665,11.935 ',
    'c0.042,0.244,0.246,0.439,0.493,0.463l0.061,0.004c0.229,0,0.436-0.141,0.521-0.356l2.529-11.565l0.627,0.344 ',
    'c0.082,0.047,0.177,0.07,0.271,0.07h0.254c1.353,0,2.451-1.1,2.451-2.453V3.195C29.834,1.844,28.733,0.744,27.383,0.744z"/>',


    '<path fill="#FDCC6A" d="M27.383,1.863H3.686c-0.734,0-1.744,0.598-1.744,1.332v7.196v2.366v0.028c0,0.003,0.006,0.003,0.006,0.003 ',
	'c0.021,0.721,0.604,1.301,1.325,1.301h8.85c1.229-0.007,6.444,0.211,8.367-0.742c0.161-0.08,0.639-0.452,0.817-0.452l0.176,0.016 ',
	'c0.236,0.055,0.529,0.307,0.567,0.529l1.339,10.649l2.028-10.264c0.06-0.154,0.181-0.271,0.332-0.326 ',
	'c0.146-0.053,0.325-0.039,0.462,0.037l1.061,0.582h0.11c0.733,0,1.331-0.598,1.331-1.332V3.195 ',
    'C28.714,2.461,28.116,1.863,27.383,1.863z"/>',
    '<text text-anchor="middle" ',
    'x="15" y="12" font-family="Arial" font-size="11">6666</text>',
    '</svg>'
].join('\n');

var template_marker_left = [
    '<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30.584px" height="27.417px" viewBox="0 0 30.584 27.417" xml:space="preserve">',

  	'<path fill="#3F3F3E" d="M27.347,0.972H3.65c-1.352,0-2.864,1.1-2.864,2.451v9.591c0,1.197,0.837,2.441,2.041,2.416 c0.271-0.006,0.672-0.146,1.025-0.344c0.183-0.104,0.858-0.512,0.858-0.512l1.665,11.935c0.042,0.244,0.246,0.439,0.493,0.463 l0.06,0.004c0.23,0,0.436-0.141,0.522-0.357l2.53-11.565l0.627,0.344c0.082,0.047,0.176,0.07,0.27,0.07h16.469 c1.352,0,2.451-1.1,2.451-2.453V3.423C29.798,2.072,28.698,0.972,27.347,0.972z"/> ',
	'<path fill="#FCCC6A" d="M27.347,14.346H11.021L9.96,13.764c-0.137-0.076-0.316-0.09-0.462-0.037 c-0.151,0.055-0.272,0.172-0.332,0.326L7.138,24.318L5.799,13.668c-0.039-0.223-0.332-0.475-0.567-0.529l-0.177-0.016 c-0.179,0-0.517,0.166-0.624,0.311c0,0-1.239,0.896-1.741,0.795c-0.599-0.119-0.784-0.688-0.784-1.215V3.423 c0-0.734,1.01-1.332,1.744-1.332h23.697c0.734,0,1.332,0.598,1.332,1.332v9.591C28.679,13.749,28.081,14.346,27.347,14.346z"/> ',
    '<text text-anchor="middle" ',
    'x="15" y="12" font-family="Arial" font-size="11">6666</text>',
    '</svg>'
].join('\n');


var template_marker_group = [
    '<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30.584px" height="27.417px" viewBox="0 0 30.584 27.417" xml:space="preserve">',
  	'<path fill="#3F3F3E" d="M27.347,0.706H3.65c-1.352,0-2.864,1.1-2.864,2.451v7.269v2.294v0.028l0.003,0.002 c0.018,1.34,1.107,2.423,2.448,2.423h9.637c1.966,0,6.252-0.023,7.194-0.354c0.434-0.15,0.859-0.512,0.859-0.512l1.664,11.936 c0.043,0.244,0.246,0.439,0.494,0.463l0.059,0.004c0.23,0,0.436-0.141,0.523-0.357l2.529-11.566l0.627,0.344 c0.082,0.047,0.176,0.07,0.27,0.07h0.254c1.352,0,2.451-1.099,2.451-2.453V3.157C29.798,1.806,28.698,0.706,27.347,0.706z"/>',
    '<path fill="#3F3F3E" d="M12.466,1.825H3.65c-0.734,0-1.744,0.598-1.744,1.332v7.196v2.366v0.028 c0,0.003,0.006,0.003,0.006,0.003c0.021,0.721,0.604,1.301,1.325,1.301h8.85c0.097,0,0.377,0,0.377,0 c0-3.842-0.005-7.314-0.005-11.086L12.466,1.825z"/>',
    '<path fill="#FCCC6A" d="M27.347,1.825h-14.88l-0.003,12.227c1.711,0.019,6.239,0.127,7.991-0.742 c0.16-0.08,0.639-0.452,0.816-0.452l0.178,0.016c0.234,0.055,0.527,0.307,0.566,0.529l1.34,10.65l2.027-10.265 c0.061-0.154,0.182-0.271,0.332-0.326c0.146-0.053,0.326-0.039,0.463,0.037l1.061,0.582h0.109c0.734,0,1.332-0.598,1.332-1.332 V3.157C28.679,2.423,28.081,1.825,27.347,1.825z"/>',
    '<path fill="#FFFFFF" d="M7.84,7.612C7.986,7.59,8.116,7.574,8.229,7.563s0.219-0.017,0.315-0.017 c0.092,0,0.202,0.006,0.331,0.017c0.13,0.011,0.256,0.026,0.381,0.049v2.943c-0.318,0.118-0.633,0.196-0.942,0.234 s-0.576,0.057-0.796,0.057c-0.475,0-0.891-0.074-1.246-0.223s-0.653-0.354-0.894-0.618C5.138,9.741,4.959,9.426,4.84,9.06 C4.722,8.692,4.663,8.294,4.663,7.862c0-0.431,0.062-0.831,0.186-1.201c0.125-0.368,0.304-0.688,0.538-0.958 c0.234-0.269,0.524-0.479,0.87-0.631c0.345-0.15,0.735-0.226,1.172-0.226c0.26,0,0.506,0.019,0.74,0.056 C8.403,4.94,8.661,5.019,8.941,5.137C8.93,5.358,8.889,5.574,8.819,5.784c-0.07,0.211-0.143,0.389-0.218,0.534 c-0.108-0.037-0.206-0.07-0.295-0.098c-0.09-0.027-0.174-0.048-0.255-0.064C7.97,6.141,7.889,6.129,7.808,6.119 C7.728,6.111,7.642,6.107,7.55,6.107c-0.437,0-0.774,0.147-1.011,0.441C6.301,6.842,6.183,7.28,6.183,7.862 c0,0.577,0.116,1.007,0.348,1.29c0.232,0.283,0.577,0.425,1.035,0.425c0.103,0,0.194-0.008,0.274-0.024V7.612z"/>',
    '<text text-anchor="middle" x="21" y="12" font-family="Arial" font-size="11">6666</text>',
    '</svg>'
].join('\n');


var template_marker_group_left = [
    '<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30.584px" height="27.417px" viewBox="0 0 30.584 27.417" xml:space="preserve">',
    '<path fill="#3F3F3E" d="M27.347,0.706H3.65c-1.352,0-2.864,1.1-2.864,2.451v9.591c0,1.197,0.837,2.441,2.041,2.416 c0.271-0.006,0.672-0.146,1.025-0.344c0.183-0.104,0.858-0.512,0.858-0.512l1.665,11.936c0.042,0.244,0.246,0.439,0.493,0.463 l0.06,0.004c0.23,0,0.436-0.141,0.522-0.357l2.53-11.566l0.627,0.344c0.082,0.047,0.176,0.07,0.27,0.07h16.469 c1.352,0,2.451-1.1,2.451-2.453V3.157C29.798,1.806,28.698,0.706,27.347,0.706z"/>',
    '<path fill="#3F3F3E" d="M3.646,1.898c-0.734,0-1.744,0.598-1.744,1.332v9.591c0,0.527,0.186,1.096,0.784,1.215 c0.502,0.102,1.741-0.795,1.741-0.795c0.107-0.145,0.445-0.311,0.624-0.311l0.177,0.016c0.235,0.055,0.528,0.307,0.567,0.529 l1.339,10.649L9.163,13.86c0.06-0.154,0.181-0.271,0.332-0.326c0.146-0.053,0.325-0.039,0.462,0.037l1.061,0.583h1.58 c0-4.202,0-8.24,0-12.255H3.646z"/>',
    '<path fill="#FCCC6A" d="M27.343,1.898H12.597c0,4.016,0,8.054,0,12.255h14.746c0.734,0,1.332-0.598,1.332-1.333V3.23 C28.675,2.496,28.077,1.898,27.343,1.898z"/>',
    '<path fill="#FFFFFF" d="M7.836,7.612C7.982,7.59,8.112,7.574,8.225,7.563s0.219-0.017,0.315-0.017 c0.092,0,0.202,0.006,0.331,0.017c0.13,0.011,0.256,0.026,0.381,0.049v2.943C8.934,10.674,8.62,10.752,8.31,10.79 s-0.576,0.057-0.796,0.057c-0.475,0-0.891-0.074-1.246-0.223s-0.653-0.354-0.894-0.618c-0.24-0.265-0.419-0.58-0.538-0.946 C4.718,8.692,4.659,8.294,4.659,7.862c0-0.431,0.062-0.831,0.186-1.201c0.125-0.368,0.304-0.688,0.538-0.958 c0.234-0.269,0.524-0.479,0.87-0.631c0.345-0.15,0.735-0.226,1.172-0.226c0.26,0,0.506,0.019,0.74,0.056 C8.399,4.94,8.657,5.019,8.937,5.137C8.926,5.358,8.885,5.574,8.815,5.784c-0.07,0.211-0.143,0.389-0.218,0.534 c-0.108-0.037-0.206-0.07-0.295-0.098c-0.09-0.027-0.174-0.048-0.255-0.064C7.966,6.141,7.885,6.129,7.804,6.119 c-0.08-0.008-0.166-0.012-0.258-0.012c-0.437,0-0.774,0.147-1.011,0.441C6.297,6.842,6.179,7.28,6.179,7.862 c0,0.577,0.116,1.007,0.348,1.29c0.232,0.283,0.577,0.425,1.035,0.425c0.103,0,0.194-0.008,0.274-0.024V7.612z"/>',
    '<text text-anchor="middle" x="21" y="12" font-family="Arial" font-size="11">6666</text>',
    '</svg>'
].join('\n');

function MapInitialize(container) {

    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    var mapData = null;
    if (mapHolder != null)
        mapData = jQuery.data(mapHolder, "map");

    if (mapHolder != null && mapData == null) {

        google.maps.visualRefresh = true;
        var startLatlng = new google.maps.LatLng(52.40669237449765, 16.90713026560843);
        var mapOptions = {
            center: startLatlng,
            zoom: 10,
            zoomControl: true,
            streetViewControl: false,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        //var placeholder = document.getElementById("map-canvas");
        mapData = new google.maps.Map(mapHolder, mapOptions);

        jQuery.data(mapHolder, "map", mapData);

        function toggleBounce() {
            if (marker.getAnimation() != null) {
                marker.setAnimation(null);
            } else {
                marker.setAnimation(google.maps.Animation.BOUNCE);
            }
        }

        var trainingPathCoordinates = [
//                         startLatlng,
        ];

        var trainingPath = new google.maps.Polyline({
            path: trainingPathCoordinates,
            strokeColor: "#FF0000",
            strokeOpacity: getStrokeOpacity(),
            strokeWeight: 2
        });

        trainingPath.setMap(mapData);

        jQuery.data(mapHolder, "mapPath", trainingPath);
    }
}

function ShowMapCooridinates(container) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    var mapData = null;
    if (mapHolder != null)
        mapData = jQuery.data(mapHolder, "map");

    if (mapData != null) {


        google.maps.event.addListener(mapData, 'mousemove', function (event) {
            displayCoordinates(container, event.latLng, event);
        });
    }

    $(document).keydown(function (e) {

        if (e.shiftKey) {
            activateCoordinatesDisplay(mapHolder, true);

        }

    });

    $(document).keyup(function (event) {
        activateCoordinatesDisplay(mapHolder, false);
    });
}

function activateCoordinatesDisplay(mapHolder, active) {
    if (mapHolder != null) {
        jQuery.data(mapHolder, "activeCoordinateDisplay", active);
        var mapData = jQuery.data(mapHolder, "map");
        if (mapData != null) {
            if (active) {
                mapData.setOptions({ draggableCursor: 'crosshair' });
            } else {
                mapData.setOptions({ draggableCursor: '' });
            }
        }
    }
}

function displayCoordinates(container, point, event) {
    var mapHolder = $(".routeMap", container)[0];
    if (mapHolder != null) {
        var active = jQuery.data(mapHolder, "activeCoordinateDisplay");
        if (active) {

            var lat = point.lat();
            lat = lat.toFixed(4);
            var lng = point.lng();
            lng = lng.toFixed(4);

            var positionHolder = $(".positionLabel", container)[0];
            if (positionHolder != null) {
                var distance = getRouteDistance(container, point);
                var msg = '';
                if (distance >= 0) {
                    msg += 'd: ' + distance.toFixed(4) + '  ';
                }

                msg = msg + lat + ' ' + lng;
                positionHolder.innerHTML = msg;
            }
        }
    }
}

function getRouteDistance(container, pnt) {
    var mapHolder = $(".routeMap", container)[0];
    if (mapHolder != null) {
        var lastRouteIndex = jQuery.data(mapHolder, "mapPathLastIndex");
        if (lastRouteIndex == null) {
            lastRouteIndex = 0;
        }

        var tolerance = 5 * (10e-5);
        var map = jQuery.data(mapHolder, "map");
        if (map != null) {
            var zoom = map.getZoom();
            if (zoom >= 17) {
                tolerance = 5 * (10e-6);
            } else if (zoom >= 14) {
                tolerance = (10e-5);
            }
        }

        var position = -1;
        for (var i = 0; i <= lastRouteIndex; i++) {
            var mapPathKey = "mapPath";
            if (i > 0) {
                mapPathKey = mapPathKey + i;
            }
            var trainingPath = jQuery.data(mapHolder, mapPathKey);


            if (google.maps.geometry.poly.isLocationOnEdge(pnt, trainingPath, tolerance)) {
                var path = getOptimizedPath(trainingPath)
                var toleranceInKm = calculateLineDistance(0, 0, tolerance, tolerance, false);
                var closestPointIndex = getClosestPoint(path, pnt, 0, 0, toleranceInKm);
                if (closestPointIndex != -1) {
                    var closestPointDistance = getPointIndexDistance(path, closestPointIndex);
                    var closestPoint = path[closestPointIndex];
                    var pointDist = calculateLineDistance(pnt.lat(), pnt.lng(), closestPoint.lat(), closestPoint.lng(), false);
                    position = closestPointDistance + pointDist;
                    break;
                }
            }
        }
    }
    return position;
}

function getMapParams(mapParent) {
    if (mapParent != null) {
        var contSelector = ".routeMap";
        var mapHolderList = $(contSelector, mapParent);

        var mapHolder = mapHolderList != null ? mapHolderList[0] : null;
        if (mapHolder != null) {
            var map = jQuery.data(mapHolder, "map");
            if (map != null) {
                var mapCentre = map.getCenter();

                var mapData = new Object({
                    centerMapLat: mapCentre.lat(),
                    centerMapLng: mapCentre.lng(),
                    mapTypeId: map.getMapTypeId(),
                    zoom: map.getZoom(),
                });

                return mapData;
            }
        }
    }
    return null;
}

function setMapParams(mapParent, mapParams) {
    if (mapParams != null && mapParent != null) {
        var contSelector = ".routeMap";
        var mapHolderList = $(contSelector, mapParent);

        var mapHolder = mapHolderList != null ? mapHolderList[0] : null;
        if (mapHolder != null) {
            var map = jQuery.data(mapHolder, "map");
            if (map != null) {

                mapCentre = map.setCenter(new google.maps.LatLng(mapParams.centerMapLat, mapParams.centerMapLng));
                map.setZoom(mapParams.zoom);
                map.setMapTypeId(mapParams.mapTypeId);
            }
        }
    }
}



var mapIndex = 0;
function buildPath() {
    for (var i = 0; i < 10; i++) {
        setTimeout(function () {
            addPoint();
        }, i * 200);
    }
}

function addPoint(lat, long) {
    //setTimeout(function () {
    trainingPath.getPath().push(new google.maps.LatLng(lat, long));
    //}, 500);

}

function clearMapPoints(container, gotoPZ) {
    var points = gotoPZ ? [[52.40669237449765, 16.90713026560843]] : [];
    addMapPoints(container, [], null, null, null);

    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    var mapData = jQuery.data(mapHolder, "map");
    clearTrack(mapData, 'track');

    if (gotoPZ) {

        if (mapData != null) {
            var startLatlng = new google.maps.LatLng(52.40669237449765, 16.90713026560843);
            mapData.setCenter(startLatlng);
            mapData.setZoom(10);
        }
    }
}

function addMapPoints(container, points, specialPoints, editablePoints, editableSpecialPoints) {
    addMultiMapPoint(container, points, specialPoints, editablePoints, editableSpecialPoints, null, 0, null, null);
}

function addMultiMapPoint(container, points, specialPoints, editablePoints, editableSpecialPoints, positionPoints, index, bounds, routeType) {

    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    if (!mapHolder) return;

    var mapData = null;
    var trainingPath = null;
    var lastStartMarker = null;

    if (mapHolder) {

        var mapPathKey = "mapPath";
        if (index > 0) {
            mapPathKey = mapPathKey + index;
            jQuery.data(mapHolder, "mapPathLastIndex", index);
        }

        mapData = jQuery.data(mapHolder, "map");
        if (!mapData) return;
        trainingPath = jQuery.data(mapHolder, mapPathKey);
        lastStartMarker = jQuery.data(mapHolder, "startMarker");


        if (index > 0 && trainingPath == null) {
            trainingPath = new google.maps.Polyline({
                path: [],
                strokeColor: getRouteColor(routeType),//"#FF0000",
                strokeOpacity: getStrokeOpacity(routeType),
                strokeWeight: 2
            });

            trainingPath.setMap(mapData);
            jQuery.data(mapHolder, mapPathKey, trainingPath);
        }
    }

    if (lastStartMarker != null) {
        lastStartMarker.setMap(null);
    }

    if (trainingPath != null) {
        trainingPath.getPath().clear();
        trainingPath.setOptions({ strokeColor: getRouteColor(routeType), strokeOpacity: getStrokeOpacity(routeType) });
    }

    if (mapData != null && points != null && points.length > 0) {
        var lat = points[0][0];
        var long = points[0][1];

        var startLatlng = new google.maps.LatLng(lat, long);

        mapData.panTo(startLatlng);
        /*
        var marker = new google.maps.Marker({
            position: startLatlng,
            animation: google.maps.Animation.DROP,
            map: mapData,
            //icon : startImage,
            title: "Start",
            icon: '/Content/images/icons/miejsce_40_cze.png'
        });

        jQuery.data(mapHolder, "startMarker", marker);
        */
        if (bounds == null) {
            bounds = new google.maps.LatLngBounds();
        }
        //  Go through each...
        for (var i = 0, length = points.length; i < length; i++) {
            //  And increase the bounds to take this point
            var lat = points[i][0];
            var long = points[i][1];
            //bounds.extend(new google.maps.LatLng(lat, long));
            bounds.extend(new google.maps.LatLng(lat, long));
        }
        mapData.fitBounds(bounds);
    }

    if (trainingPath != null && points != null) {
        for (var i = 0, length = points.length; i < length; i++) {
            var lat = points[i][0];
            var long = points[i][1];
            trainingPath.getPath().push(new google.maps.LatLng(lat, long));
        }
    }

    if (mapData != null && index == 0)
        clearTrack(mapData, 'notEditableSpecialPoints');

    if (specialPoints != null || editableSpecialPoints != null) {
        var editable = false;
        if (editableSpecialPoints != null) {
            clearTrack(mapData, 'service');
            clearTrack(mapData, 'viewpoint');
            specialPoints = editableSpecialPoints;
            editable = true;
        }

        for (var i = 0, length = specialPoints.length; i < length; i++) {
            var lat = specialPoints[i][0];
            var long = specialPoints[i][1];
            var type = specialPoints[i][2];

            var imgUrl = '';
            var title = '';

            var markerType;

            var createIcon = true;

            var marginX = 0;
            var marginY = 0;
            if (type === 0) {
                imgUrl = '/Content/images/icons/service_marker.png';
                title = translations.Servicepoint;
                markerType = 'service';

            } else if (type === 1) {
                imgUrl = '/Content/images/icons/viewpoint_marker.png';
                title = translations.Viewpoint;
                markerType = 'viewpoint';

            } else if (type === 4) {
                imgUrl = ICON_CHECKPOINT_MARKER;
                markerType = 'checkpoint';
                title = translations.MeasurementPoint;
                marginX = 5;
                marginY = 5;

            } else if (type === 5) {
                imgUrl = ICON_START_MARKER;
                markerType = 'start';
                marginX = 9;
                marginY = 8;
                title = translations.StartPoint;

            } else if (type === 6) {
                imgUrl = ICON_FINISH_MARKER;
                markerType = 'finish';
                marginX = 9;
                marginY = 8;
                title = translations.FinishPoint;

            } else if (type === 7) {
                imgUrl = ICON_LAP_MARKER;
                markerType = 'lap';
                // marginX = 0;
                // marginY = 20;
                title = translations.FinishPoint;

            } else if (type === 8) {
                imgUrl = ICON_LAP_AUTO_MARKER;
                markerType = 'lap_auto';
                // marginX = 0;
                // marginY = 20;
                title = translations.FinishPoint;
                //createIcon = false;
            }

            var iconImage = null;
            if (createIcon) {
                iconImage = {
                    url: imgUrl,
                    anchor: new google.maps.Point(marginX, marginY)
                };

            } else {
                iconImage = imgUrl;
            }


            if (editable) {
                if (markerType != null
                    && markerType != 'start'
                    && markerType != 'finish'
                    && markerType != 'lap'
                    && markerType != 'lap_auto') {
                    createMarker(new google.maps.LatLng(lat, long), mapData, markerType, false, null);
                }

            } else {
                var marker = new google.maps.Marker({
                    position: new google.maps.LatLng(lat, long),
                    map: mapData,
                    title: title,
                    icon: iconImage,
                });//icon: imgUrl
                var notEditableSpecialPoints = mapData.get('notEditableSpecialPoints');
                if (notEditableSpecialPoints == null) {
                    notEditableSpecialPoints = [];
                    mapData.set('notEditableSpecialPoints', notEditableSpecialPoints);
                }
                notEditableSpecialPoints.push(marker);
            }
        }

    } else if (editableSpecialPoints == null) {
        clearTrack(mapData, 'service');
        clearTrack(mapData, 'viewpoint');
        clearTrack(mapData, 'checkpoint');
        clearTrack(mapData, 'finish');
        clearTrack(mapData, 'start');
        clearTrack(mapData, 'elevation');
    }

    if (positionPoints != null) {
        //do nothing

    }

    if (editablePoints != null) {
        //clearTrack(mapData, 'track');

        var track = [];
        mapData.set('track', track);

        var bounds = new google.maps.LatLngBounds();
        var startLatlng;

        for (var i = 0, length = editablePoints.length; i < length; i++) {
            var lat = editablePoints[i][0];
            var long = editablePoints[i][1];
            var type = editablePoints[i][2];
            if (i == 0) {
                startLatlng = new google.maps.LatLng(lat, long);
            }
            var pos = new google.maps.LatLng(lat, long);
            var markerType = 'track';
            createMarker(pos, mapData, markerType, type == 4, 'Point: ' + i.toString());
            // bounds.extend(new google.maps.LatLng(lat, long));
        }

        //mapData.panTo(startLatlng);
        // mapData.fitBounds(bounds);
        if (startLatlng != null) {
            mapData.setZoom(10);
            fitRoute(mapData);
        }
    }
    if (mapData != null)
        google.maps.event.trigger(mapData, 'resize');
}

function clearRouteParticipants(container) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    var mapData = null;
    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");
        if (mapData != null) {
            clearSpecialPointMarker(mapData.get('notEditableSpecialPoints'), 'position');
        }
    }
}

function drawRouteParticipants(container, participPointList) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;
    var trainingPath = null;
    var lastStartMarker = null;

    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");
        clearSpecialPointMarker(mapData.get('notEditableSpecialPoints'), 'position');
        if (participPointList != null) {
            for (var i = 0, length = participPointList.length; i < length; i++) {
                var participPoint = participPointList[i];

                var positionPoints = participPoint.PositionPoints;

                if (positionPoints == null && participPoint.StartPoint != null) {
                    positionPoints = [];

                    positionPoints.push(participPoint.StartPoint);
                    if (participPoint.StartPoint != participPoint.EndPoint && participPoint.EndPoint != null) {
                        positionPoints.push(participPoint.EndPoint);
                    }
                    participPoint.PositionPoints = positionPoints;
                }
                if (positionPoints != null) {
                    drawPositionPoints(mapData, participPoint);
                }

            }
            google.maps.event.trigger(mapData, 'resize');
        }
    }
}



function getMarkerIcon(number, left) {
    var template = left ? template_marker_left : template_marker;
    var svg = template.replace('6666', number);
    return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
}

function getGroupMarkerIcon(number, left) {
    var template = left ? template_marker_group_left : template_marker_group;
    var svg = template.replace('6666', number);
    return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
}

function drawPositionPoints(mapData, participPoint) {
    var positionPoints = participPoint.PositionPoints;
    var tooltip = participPoint.Tooltip;
    var info = participPoint.Info;
    var number = participPoint.Number;
    var multi = participPoint.Multi;
    var markerPoint = participPoint.MarkerPoint;
    var bearing = participPoint.Bearing;
    if (positionPoints != null) {

        var distancePath = new google.maps.Polyline({
            path: [],
            strokeColor: "#00FF00",
            strokeOpacity: 0.8,
            strokeWeight: 2
        });

        distancePath.setMap(mapData);

        for (var i = 0, length = positionPoints.length; i < length; i++) {
            var lat = positionPoints[i][0];
            var long = positionPoints[i][1];
            //var type = positionPoints[i][2];
            if (i == 0 && markerPoint == null) {
                markerPoint = positionPoints[i];
            }

            distancePath.getPath().push(new google.maps.LatLng(lat, long));
        }

        if (markerPoint != null) {
            var lat = markerPoint[0];
            var long = markerPoint[1];

            var title = tooltip != null ? tooltip : '';

            var label = number != null ? number : null;
            var imgUrl = getMarkerIcon(label, false);
            var labelOrigin = new google.maps.Point(15, 8);
            var anchor = new google.maps.Point(23, 25);

            if (bearing != null && bearing <= 0) {
                imgUrl = getMarkerIcon(label, true);
                anchor = new google.maps.Point(7, 25);
            }

            if (multi != null) {
                label = multi.toString();
                // imgUrl = ICON_POSITION_MARKER_GROUP_WEST;
                labelOrigin = new google.maps.Point(20, 8);
                imgUrl = getGroupMarkerIcon(label, false);
                if (bearing != null && bearing <= 0) {
                    //   imgUrl = ICON_POSITION_MARKER_GROUP;
                    anchor = new google.maps.Point(7, 25);
                    imgUrl = getGroupMarkerIcon(label, true);
                }
            }

            var marker = new google.maps.Marker({
                position: new google.maps.LatLng(lat, long),
                map: mapData,
                title: title,
                icon: {
                    url: imgUrl,
                    anchor: anchor,

                }
            });

            if (mapData.get('idleListner') == null) {
                google.maps.event.addListener(mapData, 'idle', function () {
                    refreshLabels();
                });


                setTimeout(function () {
                    refreshLabels();
                }, 2000);


                mapData.set('idleListner', 'true');
            }

            marker.set('markerType', 'position');
            marker.set('participPoint', participPoint);

            if (info != null && info.length > 0) {

                marker.addListener('click', function () {

                    var infoWindow = mapData.get('infoWindow');
                    if (infoWindow != null) {
                        infoWindow.close();
                    }

                    infoWindow = new google.maps.InfoWindow({
                        content: info
                    });

                    mapData.set('infoWindow', infoWindow);

                    infoWindow.open(mapData, marker);
                });
            }

            var notEditableSpecialPoints = mapData.get('notEditableSpecialPoints');
            if (notEditableSpecialPoints == null) {
                notEditableSpecialPoints = [];
                mapData.set('notEditableSpecialPoints', notEditableSpecialPoints);
            }
            notEditableSpecialPoints.push(marker);
        }
    }
}

//draw lap points
function drawLapRoutePoints(container, lap) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;

    if (mapHolder == null) return;

    mapData = jQuery.data(mapHolder, "map");


    var distancePath = jQuery.data(mapHolder, "lapRoutePath");
    if (distancePath) {
        distancePath.getPath().clear();
    }

    if (mapData != null && lap != null && lap.RoutePoints != null) {
        if (distancePath == null) {
            distancePath = new google.maps.Polyline({
                path: [],
                strokeColor: "#00FF00",
                strokeOpacity: 0.8,
                strokeWeight: 2
            });
            distancePath.setMap(mapData);

            jQuery.data(mapHolder, "lapRoutePath", distancePath);
        }

        for (var i = 0, length = lap.RoutePoints.length; i < length; i++) {
            var lat = lap.RoutePoints[i][0];
            var long = lap.RoutePoints[i][1];
            distancePath.getPath().push(new google.maps.LatLng(lat, long));
        }
    }

}


function drawLapPoint(container, lap) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;

    if (mapHolder == null) return;

    mapData = jQuery.data(mapHolder, "map");


    var tooltip = null;//participPoint.Tooltip;
    var info = null;//participPoint.Info;
    var number = null;//participPoint.Number;
    var multi = null;//participPoint.Multi;

    var bearing = null;//participPoint.Bearing;
    if (mapData != null && lap != null && lap.MarkerPoint != null) {
        var markerPoint = lap.MarkerPoint;

        if (markerPoint != null) {
            var lat = markerPoint[0];
            var long = markerPoint[1];

            var title = tooltip != null ? tooltip : '';

            var label = number != null ? number : null;
            var imgUrl = getMarkerIcon(label, false);

            var anchor = new google.maps.Point(7, 7);

            if (bearing != null && bearing <= 0) {
                imgUrl = getMarkerIcon(label, true);
                anchor = new google.maps.Point(7, 6);
            }

            var labelOrigin = new google.maps.Point(8, 8);

            imgUrl = lap.Auto ? ICON_LAP_AUTO_MARKER : ICON_LAP_MARKER;

            info = getLapInfo(lap);

            var lapLabel = lap.Auto ? lap.StaticContent.SplitDistance.toFixed(0) : lap.StaticContent.Index.toString();

            var marker = new google.maps.Marker({
                position: new google.maps.LatLng(lat, long),
                map: mapData,
                title: title,
                icon: {
                    url: imgUrl,
                    anchor: anchor,
                    labelOrigin: labelOrigin
                },
                label: {
                    text: lapLabel,
                    color: 'white',
                    fontSize: '7px',
                },
            });

            marker.set('markerType', 'lap');

            if (info != null && info.length > 0) {

                marker.addListener('mouseout', function () {

                    var infoWindow = mapData.get('lapInfoWindow');
                    if (infoWindow != null) {
                        infoWindow.close();
                    }
                });

                marker.addListener('mouseover', function () {

                    var infoWindow = mapData.get('lapInfoWindow');
                    if (infoWindow != null) {
                        infoWindow.close();
                    }

                    infoWindow = new google.maps.InfoWindow({
                        content: info,
                    });

                    mapData.set('lapInfoWindow', infoWindow);

                    infoWindow.open(mapData, marker);
                });

            }

            var notEditableSpecialPoints = mapData.get('notEditableSpecialPoints');
            if (notEditableSpecialPoints == null) {
                notEditableSpecialPoints = [];
                mapData.set('notEditableSpecialPoints', notEditableSpecialPoints);
            }
            notEditableSpecialPoints.push(marker);
        }
    }
}

function getLapInfo(lap) {
    var lapHtml = "<div class='chartLap' style = 'display:block;    width: 220px; position:relative;bottom:0px'";
    //lapHtml += lap.Auto ? ' autoLap ' : ' manualLap ';
    //lapHtml += (o.left < sideTresh) ? 'right' : 'left';
    //lapHtml += "' style='left:" + o.left + "px;' ";

    lapHtml += "><div class='info' style = 'display:block; width: 220px; position:relative; padding:0px; bottom:0px'>";

    if (lap.StaticContent.DurationText) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/cz_trwa_32.png" /></div><div class="value">' + lap.StaticContent.DurationText + '</div></div>';
    }
    if (lap.StaticContent.DistanceText) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/dystans_32.png" /></div><div class="value">' + lap.StaticContent.DistanceText + '</div></div>';
    }
    if (lap.StaticContent.AvgHr) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/serce_przerywane_32.png" /></div><div class="value">' + lap.StaticContent.AvgHr + ' bpm</div></div>';
    }
    if (lap.AvgSpeedText() && (!lap.ShowPace)) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/predkosc_30.png" /></div><div class="value">' + lap.StaticContent.AvgSpeedText + ' km/h' + '</div></div>';
    }
    if (lap.ShowPace && lap.PaceText() != '0') {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/tempo_30.png" /></div><div class="value">' + lap.PaceText() + ' ' + lap.PaceUnit + '</div></div>';
    }
    if (lap.StaticContent.Cadence && (!lap.Auto || !lap.AvgHr)) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/kadencja_30.png" /></div><div class="value">' + lap.StaticContent.Cadence + ' rpm</div></div>';
    }
    if (lap.StaticContent.StartText) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/cz_rozpocz_32.png" /></div><div class="value">' + lap.StaticContent.StartText + '</div></div>';
    }
    if (lap.StaticContent.SplitDistanceText) {
        lapHtml += '<div class="lapRow"><div class="icon "><img src="' + ContextPath + 'Content/images/icons/dystans_okrazenie_32.png" /></div><div class="value">' + lap.StaticContent.SplitDistanceText + '</div></div>';
    }

    lapHtml += "</div></div>";
    return lapHtml;
}

function drawCurrentPositionPoint(container, points, distance) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];
    var mapData = null;
    if (mapHolder != null)
        mapData = jQuery.data(mapHolder, "map");

    if (mapData != null) {
        if (!distance) {
            clearTrack(mapData, 'elevation');
        } else {
            var positionPoint = calculateDistancePoints(points, distance);
            if (positionPoint && positionPoint.length > 0) {
                drawElevationPoint(container, positionPoint[0]);
            } else {
                clearTrack(mapData, 'elevation');
            }
        }
    }

}


function calculateDistancePoints(points, startDistance, endDistance) {
    //var points = route.Points;
    var lastPoint = null;
    var distance = 0;
    var startPoint = null;
    var endPoint = null;
    var positionPoints = null;
    if (points != null) {
        var maxDistanceDifference = 0.2;
        for (var i = 0, length = points.length; i < length; i++) {
            if (lastPoint != null) {
                var lat = points[i][0];
                var long = points[i][1];

                var prevDistance = distance;
                var pointDistance = calculateLineDistance(lastPoint[0], lastPoint[1], lat, long, false);

                distance += pointDistance;

                //for (var y = 0, partLen = routeParticipList.length; y < partLen; y++)
                {
                    //  routeParticip = routeParticipList[y];

                    if (startDistance >= prevDistance && startDistance <= distance) {
                        startPoint = lastPoint;

                        if (positionPoints == null) {
                            positionPoints = [];
                        }
                        var bearing = calculateBearing(lastPoint[0], lastPoint[1], lat, long);

                        var distanceToPoint = startDistance - prevDistance;
                        if (distanceToPoint > maxDistanceDifference) {
                            startPoint = calculateBearingPoint(lastPoint[0], lastPoint[1], distanceToPoint, bearing);
                        }

                        positionPoints.push(startPoint);
                    }

                    if (startPoint != null && endPoint == null
                        && endDistance != null && endDistance != startDistance
                        && endDistance > 0) {

                        if (endDistance >= prevDistance && endDistance <= distance) {
                            endPoint = lastPoint;

                            var distanceToPoint = endDistance - prevDistance;
                            if (distanceToPoint > maxDistanceDifference) {
                                var bearing = calculateBearing(lastPoint[0], lastPoint[1], lat, long);
                                endPoint = calculateBearingPoint(lastPoint[0], lastPoint[1], distanceToPoint, bearing);
                            }
                            positionPoints.push(endPoint);

                        } else {
                            positionPoints.push(lastPoint);
                        }
                    }

                }
            }
            lastPoint = points[i];
        }

        if (distance > 0 && lastPoint != null) {
            //for (var y = 0, partLen = routeParticipList.length; y < partLen; y++)
            {
                //  routeParticip = routeParticipList[y];

                if (startPoint == null && startDistance >= distance) {
                    startPoint = lastPoint;
                    if (positionPoints == null) {
                        positionPoints = [];
                    }
                    positionPoints.push(lastPoint);

                } else if (startPoint != null && endPoint == null
                    && endDistance > 0
                    && endDistance >= distance) {
                    endPoint = lastPoint;
                    positionPoints.push(lastPoint);
                }
            }
        }
    }
    return positionPoints;
}


function getParticipantsMarkers(container) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;

    var participantMarkerList = [];

    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");
        var specialPoints = mapData.get('notEditableSpecialPoints');
        if (specialPoints != null) {
            for (var i = 0, length = specialPoints.length; i < length; i++) {
                var marker = specialPoints[i];

                var mType = marker.get('markerType');

                if (mType === 'position') {
                    participantMarkerList.push(marker);
                }
            }
        }

    }
    return participantMarkerList;
}

function getMarker(specialPoints, markerType) {
    for (var i = 0, length = specialPoints.length; i < length; i++) {
        var marker = specialPoints[i];

        var mType = marker.get('markerType');

        if (mType === markerType) {
            return marker;
        }
    }
    return null;

}

function drawElevationPoint(container, elevationDistancePoint) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;

    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");

        if (elevationDistancePoint == null) {
            clearSpecialPointMarker(mapData.get('notEditableSpecialPoints'), 'elevation');

        } else {

            var lat = elevationDistancePoint[0];
            var long = elevationDistancePoint[1];

            var marker = null;
            var markerType = 'elevation';
            var notEditableSpecialPoints = mapData.get('notEditableSpecialPoints');
            if (notEditableSpecialPoints != null) {
                marker = getMarker(notEditableSpecialPoints, markerType)
            }

            if (marker != null) {
                marker.setPosition(new google.maps.LatLng(lat, long));

            } else {
                var imgUrl = '/Content/images/icons/elevation_marker.png';
                var title = '';

                var anchor = new google.maps.Point(9, 9);

                marker = new google.maps.Marker({
                    position: new google.maps.LatLng(lat, long),
                    map: mapData,
                    title: title,

                    icon: {
                        url: imgUrl,
                        anchor: anchor,

                    }
                });

                marker.set('markerType', markerType);


                if (notEditableSpecialPoints == null) {
                    notEditableSpecialPoints = [];
                    mapData.set('notEditableSpecialPoints', notEditableSpecialPoints);
                }
                notEditableSpecialPoints.push(marker);
            }
        }
    }
}

function refreshLabels() {
    var labels = document.querySelectorAll("[style*='custom-label']")
    for (var i = 0; i < labels.length; i++) {
        var matches = labels[i].getAttribute('style').match(/custom-label-(\d+)/);
        if (matches != null) {
            labels[i].innerHTML = matches[1];
        }
    }
}


function getRouteColor(routeType) {
    var color = "#FF0000";

    if (routeType != null) {
        if (routeType == 2) {
            color = "#EA00FF";
        } else if (routeType == 3) {
            color = "#FFF200";
        }
    }

    return color;
}

//draw track


function AllowEditMap(container) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;
    var trainingPath = null;
    var lastStartMarker = null;

    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");

        var directionsDisplay = mapData.get('directionsDisplay');

        if (directionsDisplay == null) {


            mapData.set('markerType', 'track');

            var directionsService = new google.maps.DirectionsService();

            google.maps.event.addListener(mapData, 'dragend', function (event) { dragEnd(mapData) });
            google.maps.event.addListener(mapData, 'dragstart', function (event) { dragStart(mapData) });


            google.maps.event.addListener(mapData, 'mouseup', function (event) {
                var drag = mapData.get('draggable');
                var over = mapData.get('markerOver');
                if ((drag == null || drag) && (over == null || over == false)) {
                    var snapToRoad = mapData.get('snapToRoad');
                    createMarker(event.latLng, mapData, mapData.get('markerType'), snapToRoad);
                }
            });

            mapData.controls[google.maps.ControlPosition.RIGHT].push(createTrackControls(mapData, { title: 'Follow Roads', label: 'Follow Roads' }));

            var mapStyles = [
                {
                    featureType: "poi",
                    elementType: "labels.icons",
                    stylers: [
                        { visibility: "off" }
                    ]
                }
            ];

            mapData.setOptions({
                panControl: false,
                zoomControl: true,
                typeControl: false,

                mapTypeControlOptions: {
                    position: google.maps.ControlPosition.LEFT
                    //style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
                },

                zoomControlOptions: {
                    style: google.maps.ZoomControlStyle.SMALL,
                    position: google.maps.ControlPosition.RIGHT_CENTER
                },

                styles: mapStyles
            });

            /*
            google.maps.event.addListener(mapData, 'tilesloaded', function () {
                var buttons = $("#map-canvas", "#DrawTrackMap").find('img[src="https://maps.gstatic.com/mapfiles/api-3/images/mapcnt3.png"]');
                $("#map-canvas", "#DrawTrackMap").find('img[src$="images/mapcnt3.png"]')
                buttons.attr('src', '/Content/images/icons/zoom.png');
                buttons.css('width', '18'); 
                buttons.css('height', '35'); 
                buttons.css('top', '0px'); 
                buttons.css('left', '0px'); 
            });
            */

            directionsDisplay = new google.maps.DirectionsRenderer();
            directionsDisplay.setOptions({ suppressMarkers: true, preserveViewport: true });

            directionsDisplay.setMap(mapData);


            mapData.set('directionsDisplay', directionsDisplay);
            mapData.set('directionsService', directionsService);




            //

            var input = /** @type {HTMLInputElement} */(document.getElementById('pac-input'));
            mapData.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

            var autocomplete = new google.maps.places.Autocomplete(input);
            autocomplete.bindTo('bounds', mapData);

            var searchBox = new google.maps.places.SearchBox(/** @type {HTMLInputElement} */(input));

            // Listen for the event fired when the user selects an item from the
            // pick list. Retrieve the matching places for that item.
            google.maps.event.addListener(searchBox, 'places_changed', function () {
                var places = searchBox.getPlaces();

                if (places.length == 0) {
                    return;
                }
                /*
                for (var i = 0, marker; marker = markers[i]; i++) {
                    marker.setMap(null);
                }
                */

                // For each place, get the icon, place name, and location.
                //markers = [];
                var bounds = new google.maps.LatLngBounds();
                for (var i = 0, place; place = places[i]; i++) {
                    /* var image = {
                         url: place.icon,
                         size: new google.maps.Size(71, 71),
                         origin: new google.maps.Point(0, 0),
                         anchor: new google.maps.Point(17, 34),
                         scaledSize: new google.maps.Size(25, 25)
                     };
 
                     // Create a marker for each place.
                     var marker = new google.maps.Marker({
                         map: map,
                         icon: image,
                         title: place.name,
                         position: place.geometry.location
                     });
 
                     markers.push(marker);
                     */
                    bounds.extend(place.geometry.location);
                }

                mapData.fitBounds(bounds);
                mapData.setZoom(10);
            });
            //
            google.maps.event.addListener(mapData, 'bounds_changed', function () {
                var bounds = mapData.getBounds();
                searchBox.setBounds(bounds);
            });

            google.maps.event.trigger(mapData, 'resize');
        }
    }
}



function RefreshMap(container) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, container)[0];

    var mapData = null;
    var trainingPath = null;
    var lastStartMarker = null;

    if (mapHolder != null) {
        mapData = jQuery.data(mapHolder, "map");
    }
    google.maps.event.trigger(mapData, 'resize');
}

function dragEnd(map) {
    var drag = map.get('draggable');
    map.set('draggable', true)
    //drawTrack(map, true);
}

function dragStart(map) {
    var drag = map.get('draggable');
    map.set('draggable', false)
}

function markerMouseOver(map, flag) {
    map.set('markerOver', flag)
}


function moveMarker(marker, dragEnd, map) {
    var lines = map.get('lines');
    if (lines != null) {
        for (i = 0; i < lines.length; i++) {
            var line = lines[i];

            var markers = line.get('markers')

            var index = markers.indexOf(marker)
            if (index > -1) {
                var snapToRoad = line.get('snapToRoad');
                if (snapToRoad != null && snapToRoad) {
                    if (dragEnd) {
                        var directionsDisplay = map.get('directionsDisplay');
                        directionsService = map.get('directionsService');
                        calcRoute(markers, directionsService, directionsDisplay, map, line);
                    }

                } else if (line.getPath().length > 0) {
                    rebuildPath(line, markers);
                }
            }
        }
    }
}

function calculateDistance(map) {
    var distance = 0;
    var lines = map.get('lines');
    if (lines != null) {
        for (i = 0; i < lines.length; i++) {
            var line = lines[i];

            var markers = line.get('markers')
            var snapToRoad = line.get('snapToRoad');
            if (snapToRoad != null && snapToRoad) {
                var roadDistance = line.get('distance');
                if (roadDistance != null) {
                    distance += roadDistance;
                }

            } else if (line.getPath().length > 0) {
                var ml = markers.length;
                var prevMarker;
                for (j = 0; j < ml; j++) {
                    if (prevMarker != null) {
                        var marker = markers[j];
                        distance += calculateLineDistance(prevMarker.getPosition().lat(), prevMarker.getPosition().lng(), marker.getPosition().lat(), marker.getPosition().lng(), false);
                    }
                    prevMarker = markers[j];
                }
            }
        }
    }
    return distance;
}

function calculateFullDistance() {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, '#DrawTrackMap')[0];

    if (mapHolder != null) {
        var map = jQuery.data(mapHolder, "map");
        if (map != null) {
            distance = calculateDistance(map);

            //if (trainingDetailsManager != null && trainingDetailsManager.CurrentTrack != null)
            {
                //trainingDetailsManager.CurrentTrack.Distence(distance);
                //$('.statValue', '#DrawTrackModal').html(distance > 0 ? (distance).formatNumber(2) : 0);
                $('#distanceValue', '#DrawTrackMapMenu').html(distance > 0 ? (distance).formatNumber(2) : 0);
            }
        }
    }
}

function removeMarker(marker, map) {
    marker.setMap(null);
    var markerType = marker.get('markerType');

    if (markerType === 'track') {
        var drawFull = false;
        var lines = map.get('lines');
        if (lines != null) {
            var linesToRedraw = [];
            for (i = 0; i < lines.length; i++) {
                var line = lines[i];

                var markers = line.get('markers');

                var index = markers.indexOf(marker)
                if (index == 0 || index == markers.length - 1) {
                    drawFull = true;
                } else if (index > -1) {
                    markers.splice(index, 1);

                    var snapToRoad = line.get('snapToRoad');
                    if (snapToRoad != null && snapToRoad) {
                        if (dragEnd) {
                            var directionsDisplay = map.get('directionsDisplay');
                            directionsService = map.get('directionsService');
                            calcRoute(markers, directionsService, directionsDisplay, map, line);
                        }
                    } else if (line.getPath().length > 0) {
                        rebuildPath(line, markers);
                    } else {
                        //snap to road
                    }
                }
            }
        }


        var track = map.get('track');
        if (track != null) {
            var index = track.indexOf(marker)
            if (index > -1) {
                track.splice(index, 1);
                if (drawFull)
                    drawFullTrack(map, true);
            }
        }
    } else {
        var specialPoints = map.get('specialPoints');
        if (specialPoints != null) {
            var index = specialPoints.indexOf(marker)
            if (index > -1) {
                specialPoints.splice(index, 1);
            }
        }
    }
}

function rebuildPath(line, markers) {
    line.getPath().clear();
    var ml = markers.length;
    for (j = 0; j < ml; j++) {
        line.getPath().push(markers[j].getPosition());
    }
}


function createMarker(location, map, markerType, snapToRoad, tooltip) {

    var imgUrl = '/Content/images/icons/track_marker.png'
    var title = '';
    var createIcon = true;
    var marginX = 5;
    var marginY = 5;
    if (markerType === 'viewpoint') {
        imgUrl = '/Content/images/icons/viewpoint_marker.png'
        title = translations.Viewpoint;

    } else if (markerType === 'service') {
        imgUrl = '/Content/images/icons/service_marker.png'
        title = translations.Servicepoint;

    } else if (markerType === 'checkpoint') {
        imgUrl = ICON_CHECKPOINT_MARKER;
        title = translations.MeasurementPoint;

    } else if (markerType === 'finish') {
        imgUrl = ICON_FINISH_MARKER;
        //createIcon = false;
        marginX = 9;
        marginY = 8;
        title = translations.FinishPoint;

    } else if (markerType === 'start') {
        imgUrl = ICON_START_MARKER;
        title = translations.StartPoint;
        //createIcon = false;
        marginX = 9;
        marginY = 8;

    } else if (markerType === 'lap') {
        imgUrl = ICON_LAP_MARKER;
        title = translations.StartPoint;
        //createIcon = false;
        //marginX = 0;
        // marginY = 20;

    } else if (markerType === 'lap_auto') {
        imgUrl = ICON_LAP_AUTO_MARKER;
        title = translations.StartPoint;
        //createIcon = false;
        // marginX = 0;
        // marginY = 20;

    }

    var iconImage = null;
    if (createIcon) {
        iconImage = {
            url: imgUrl,
            anchor: new google.maps.Point(marginX, marginY)
        };
    } else {
        iconImage = imgUrl;
    }

    var marker = new google.maps.Marker({
        position: location,
        draggable: true,
        icon: iconImage,
        title: title,
        crossOnDrag: false,
        map: map
    });
    /*
    var myWindow = new google.maps.InfoWindow({
        content: '<input type="checkbox" name="vehicle" value="Airplane" /><input type="button" value="Blah" />',
    });

    myWindow.open(map, marker);
    */




    marker.set('markerType', markerType);

    google.maps.event.addListener(marker, 'dragstart', function (event) {
        dragStart(map);
    });
    google.maps.event.addListener(marker, 'dragend', function (event) {
        moveMarker(marker, true, map);
        dragEnd(map);
        calculateFullDistance();
    });

    google.maps.event.addListener(marker, 'drag', function () {
        moveMarker(marker, false, map);
    });

    marker.addListener('dblclick', function () {
        removeMarker(marker, map);
        markerMouseOver(map, false);
        calculateFullDistance();
    });

    marker.addListener('mouseout', function () {
        markerMouseOver(map, false);
    });

    marker.addListener('mouseover', function () {
        markerMouseOver(map, true);
    });


    if (markerType === 'track') {
        placeTrackMarker(marker, map, snapToRoad);

    } else {
        var specialPoints = map.get('specialPoints');
        if (specialPoints == null) {
            specialPoints = [];
            map.set('specialPoints', specialPoints);
        }
        specialPoints.push(marker);
    }
    if (tooltip == null)
        calculateFullDistance();
}

function placeTrackMarker(marker, map, snapToRoad) {

    var track = map.get('track');
    if (track == null) {
        track = [];
        map.set('track', track);
    }

    marker.set('snapToRoad', snapToRoad == null ? false : snapToRoad);

    track.push(marker);
    drawNewMarker(marker, map);
}

function drawNewMarker(marker, map) {
    var track = map.get('track');
    if (track != null) {

        var length = track.length;
        if (length > 1) {
            var newTrack = [];

            var lastMarker = track[length - 2];

            var lines = map.get('lines');
            var lastLine = null;
            var lastLineMarkers;
            if (lines != null && lines.length > 0) {
                lastLine = lines[lines.length - 1];
                lastLineMarkers = lastLine.get('markers');
            }

            var snapToRoad = marker.get('snapToRoad');
            if (lastLine != null && lastLineMarkers != null && lastLineMarkers.length < 10 && lastMarker.get('snapToRoad') == snapToRoad) {
                //expand last existing path
                if (snapToRoad) {
                    lastLineMarkers.push(marker);

                    var directionsDisplay = map.get('directionsDisplay');
                    directionsService = map.get('directionsService');
                    calcRoute(lastLineMarkers, directionsService, directionsDisplay, map, lastLine);


                } else {
                    lastLine.getPath().push(marker.getPosition());
                    lastLineMarkers.push(marker);
                }

            } else {
                newTrack.push(lastMarker);
                newTrack.push(marker);
                drawTrack(map, newTrack, false);
            }
        }
    }
}




function drawFullTrack(map, clear) {
    var track = map.get('track');
    drawTrack(map, track, clear);
}

function drawTrack(map, track, clear) {
    if (track != null) {

        var lines = map.get('lines');
        if (lines == null) {
            lines = [];
            map.set('lines', lines);
        }

        var directionsDisplay = map.get('directionsDisplay');

        var path;

        if (clear) {
            clearLines(lines, directionsDisplay);
            lines = [];
            map.set('lines', lines);
        }

        var lastMarker;

        var snapRoads = []; //odcinki do obliczen

        var snapMarkers = [];

        snapRoads.push(snapMarkers);
        //lines.push(path);

        for (var i = 0, length = track.length; i < length; i++) {
            var marker = track[i];

            var snapToRoad = marker.get('snapToRoad');
            if (snapToRoad) {
                if (lastMarker != null) {
                    if (!lastMarker.get('snapToRoad')) {

                        if (snapMarkers.length > 0) {
                            snapMarkers = [];
                            snapRoads.push(snapMarkers);
                        }
                        snapMarkers.push(lastMarker)
                    } else {
                        if (snapMarkers.length > 5) {
                            snapMarkers = [];
                            snapRoads.push(snapMarkers);
                            snapMarkers.push(lastMarker)
                        }
                    }
                }
                snapMarkers.push(marker);

            } else {
                if (lastMarker != null) {
                    if (lastMarker.get('snapToRoad')) {

                        path = new google.maps.Polyline({
                            path: [],
                            strokeColor: "#FF0000",
                            strokeOpacity: getStrokeOpacity(),
                            strokeWeight: 2
                        });

                        path.set('markers', []);

                        lines.push(path);
                        path.getPath().push(lastMarker.position);
                        path.get('markers').push(lastMarker);
                    }
                }
                if (path == null) {
                    path = new google.maps.Polyline({
                        path: [],
                        strokeColor: "#FF0000",
                        strokeOpacity: getStrokeOpacity(),
                        strokeWeight: 2
                    });
                    path.set('markers', []);
                    lines.push(path);
                }

                path.getPath().push(marker.position);
                path.get('markers').push(marker);
            }
            lastMarker = marker;
        }

        for (i = 0; i < lines.length; i++) {
            lines[i].setMap(map);
        }

        //var path = map.set('path', path);



        directionsService = map.get('directionsService');

        for (var i = 0, length = snapRoads.length; i < length; i++) {

            var newLine = null;
            if (snapRoads[i].length > 0) {
                newLine = new google.maps.Polyline({
                    path: [],
                    strokeColor: '#FF0000',
                    strokeOpacity: getStrokeOpacity(),
                    strokeWeight: 2
                });

                newLine.setMap(map);
                if (lines != null) {
                    lines.push(newLine);
                }
            }
            calcRoute(snapRoads[i], directionsService, directionsDisplay, map, newLine);
        }
    }
}

function getStrokeOpacity(routeType) {
    return routeType != null && routeType == 3 ? 1.0 : 0.5;
}

function clearLines(lines, directionsDisplay) {
    for (i = 0; i < lines.length; i++) {
        lines[i].setMap(null);
    }

    directionsDisplay.setDirections({ routes: [] });
}

function clearTrack(map, markerType) {
    if (!map) return;
    if (markerType === 'track') {
        var lines = map.get('lines');
        if (lines != null) {
            var directionsDisplay = map.get('directionsDisplay');
            clearLines(lines, directionsDisplay);
            lines = [];
            map.set('lines', lines);
        }

        var track = map.get('track');
        if (track != null) {

            for (var i = 0, length = track.length; i < length; i++) {
                var marker = track[i];
                marker.setMap(null);
            }
            track = [];
            map.set('track', track);
        }
    } else if (markerType == 'notEditableSpecialPoints') {
        var specialPoints = map.get('notEditableSpecialPoints');
        if (specialPoints != null) {
            for (var i = 0, length = specialPoints.length; i < length; i++) {
                var marker = specialPoints[i];
                marker.setMap(null);
            }
            map.set('notEditableSpecialPoints', []);
        }

    } else {
        var specialPoints = map.get('specialPoints');
        clearSpecialPointMarker(specialPoints, markerType);
        clearSpecialPointMarker(map.get('notEditableSpecialPoints'), markerType);

    }
}

function clearSpecialPointMarker(specialPoints, markerType) {
    if (specialPoints != null) {

        var removeIndexList = [];

        for (var i = 0, length = specialPoints.length; i < length; i++) {
            var marker = specialPoints[i];

            var mType = marker.get('markerType');

            if (mType === markerType) {
                marker.setMap(null);
                removeIndexList.push(i);
            }
        }

        for (var i = removeIndexList.length - 1; i >= 0; i--) {
            specialPoints.splice(removeIndexList[i], 1);
        }
    }
}

//----

function resizeButton(controlUI, map) {
    jQuery.data(controlUI, "fullscreen", false);

    google.maps.event.addDomListener(controlUI, 'click', function () {
        var fullscreen = jQuery.data(controlUI, "fullscreen");
        var drawTrackModal = $('#DrawTrackModal');

        var center = map.getCenter();
        var zoom = map.getZoom();

        if (fullscreen == null || !fullscreen) {
            $(controlUI).addClass('mapButton-active');
            jQuery.data(controlUI, "fullscreen", true);

            $('#DrawTrackModal').modal('hide');
            $('#DrawTrackModalFullScreen').modal('show')

            $('#DrawTrackMap').appendTo('#MapPlace');
            $('#DrawTrackMapMenu').appendTo('#MenuPlace');

            $("#DrawTrackMap").css({ "width": "100%" });
            $("#DrawTrackMap").css({ "height": "100%" });

            $("#map-canvas", "#DrawTrackMap").css({ "height": "100%" });

        } else {
            $(controlUI).removeClass('mapButton-active');
            jQuery.data(controlUI, "fullscreen", false);

            $('#DrawTrackModalFullScreen').modal('hide')

            $('#DrawTrackMap').appendTo('#DrawTrackMapBody');
            $('#DrawTrackMapMenu').appendTo('#DrawTrackMapMenuBody');

            $("#map-canvas", "#DrawTrackMap").css({ "height": "400px" });

            $("#DrawTrackMap").css({ "width": "100%" });
            $("#DrawTrackMap").css({ "height": "100%" });

            var dialog = $('#DrawTrackModal');
            dialog.modal({
                backdrop: 'static'
            });
            dialog.modal('show');
        }
        google.maps.event.trigger(map, 'resize');


        var bounds = new google.maps.LatLngBounds();
        bounds.extend(center);
        map.fitBounds(bounds);
        map.setZoom(zoom);

        fitRoute(map);
    });

    return controlUI;
}

function fitRoute(map) {
    var track = map.get('track');
    if (track != null) {
        var bounds = new google.maps.LatLngBounds();
        var length = track.length;
        if (length > 1) {
            for (var i = 0; i < length; i++) {
                bounds.extend(track[i].getPosition());
            }
            map.fitBounds(bounds);
        }
    }
}

function createTrackControls(map, options) {
    var extControlDiv = document.createElement('DIV');

    var controlDiv = document.createElement('DIV');
    extControlDiv.appendChild(controlDiv);
    controlDiv.className = "mapControl";

    var container = $(".checkboxContainer");
    $(".checkboxContainer").click(function () {
        var bDivId = "SnapCheck";
        var snapToRoad = document.getElementById(bDivId).style.display == 'block';
        (snapToRoad) ? document.getElementById(bDivId).style.display = 'none' : document.getElementById(bDivId).style.display = 'block';
        snapToRoads(!snapToRoad, map);
    });

    google.maps.event.addDomListener(container, 'click', function () {
        var bDivId = "SnapCheck";
        var snapToRoad = document.getElementById(bDivId).style.display == 'block';
        (snapToRoad) ? document.getElementById(bDivId).style.display = 'none' : document.getElementById(bDivId).style.display = 'block';
        snapToRoads(!snapToRoad, map);
    });


    //clear button
    var controlButton = $("#clearAllMarkers").click(function () {
        $('#deleteTrackPointsModal').modal('show');
    });

    //fulscreen button
    var fulscreenDiv = document.createElement('div');
    fulscreenDiv.title = translations.Fullscreen;
    fulscreenDiv.className = "mapButton";

    controlDiv.appendChild(fulscreenDiv);

    var fulscreenButton = document.createElement('input');
    fulscreenButton.className = "mapButton";
    fulscreenButton.type = "image";
    fulscreenButton.src = "/Content/images/icons/full_24.png"
    fulscreenDiv.appendChild(fulscreenButton);
    resizeButton(fulscreenButton, map);

    return extControlDiv;
}

function hideMapControls(mapParent, hide) {
    var mapConrol = $(".mapControl", mapParent)[0];
    if (mapConrol != null) {
        mapConrol.style.display = hide ? 'none' : 'block';
    }

    var map = jQuery.data(mapParent, "map");
    if (map != null) {
        if (hide) {
            map.setOptions({
                panControl: false,
                zoomControl: false,
                typeControl: false,
                disableDefaultUI: true
            });


        } else {
            map.setOptions({
                panControl: false,
                zoomControl: true,
                typeControl: false,
                disableDefaultUI: false,

                mapTypeControlOptions: {
                    position: google.maps.ControlPosition.LEFT
                },

                zoomControlOptions: {
                    style: google.maps.ZoomControlStyle.SMALL,
                    position: google.maps.ControlPosition.RIGHT_CENTER
                },
            });
        }
    }

    var input = $('#pac-input', mapParent);
    if (input != null) {
        input[0].style.display = hide ? 'none' : 'block';
    }

}

function clearAllMarkers(map) {
    var markerType = map.get('markerType');
    clearTrack(map, markerType);
    calculateFullDistance();
}

function DeleteTrackPoints() {
    $('#deleteTrackPointsModal').modal('hide');
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, '#DrawTrackMap')[0];
    if (mapHolder != null) {
        var map = jQuery.data(mapHolder, "map");
        clearAllMarkers(map);
    }
}


function selectSnapToRoad() {
    var bDivId = "SnapCheck";
    var snapToRoad = document.getElementById(bDivId).style.display == 'block';
    (snapToRoad) ? document.getElementById(bDivId).style.display = 'none' : document.getElementById(bDivId).style.display = 'block';
    snapToRoads(!snapToRoad, map);
}

function snapToRoads(snapToRoad, map) {
    map.set('snapToRoad', snapToRoad);
}

function calcRoute(markers, directionsService, directionsDisplay, map, line) {
    if (markers.length > 1) {
        var start = markers[0].getPosition();
        var end = markers[markers.length - 1].getPosition();
        var waypts = [];

        for (var i = 1; i < markers.length - 1; i++) {
            waypts.push({
                location: markers[i].getPosition(),
                stopover: true
            });
        }

        var travelMode = map.get('travelMode');
        if (travelMode == null) {
            travelMode = google.maps.TravelMode.BICYCLING
        }

        var request = {
            origin: start,
            destination: end,
            waypoints: waypts,
            optimizeWaypoints: false,
            travelMode: travelMode//DRIVING
        };

        drawRoute(request, directionsService, markers, map, line);
    }
}

function drawRoute(request, directionsService, markers, map, line) {
    directionsService.route(request, function (response, status) {
        if (status == google.maps.DirectionsStatus.OK) {

            createPolyline(response, markers, map, line);
            //directionsDisplay.setDirections(response);
            //var route = response.routes[0];
            calculateFullDistance();

        } else {

            setTimeout(function () { drawRoute(request, directionsService, markers, map, line); }, 200)
        }
    });
}

function calculateRouteDistance(directionResult) {
    var distance = 0;
    var routes = directionResult.routes;
    if (routes != null) {
        for (var i = 0, length = routes.length; i < length; i++) {
            var legs = routes[i].legs;
            if (legs != null) {
                for (var j = 0, legsLength = legs.length; j < legsLength; j++) {
                    var dist = legs[j].distance;
                    if (dist != null) {
                        distance += dist.value;
                    }
                }
            }
        }
    }
    return distance / 1000;
}

function calculateLineDistance(pos1Latitude, pos1Longitude, pos2Latitude, pos2Longitude, inMiles) {
    var R = (inMiles) ? 3960 : 6371;
    var dLat = toRadian(pos2Latitude - pos1Latitude);
    var dLon = toRadian(pos2Longitude - pos1Longitude);
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(toRadian(pos1Latitude)) * Math.cos(toRadian(pos2Latitude)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.asin(Math.min(1, Math.sqrt(a)));
    var d = R * c;
    return d;
}

function toRadian(val) {
    return (Math.PI / 180) * val;
}

function toDegrees(val) {
    return val * (180 / Math.PI);
}

function calculateBearing(pos1Latitude, pos1Longitude, pos2Latitude, pos2Longitude) {
    var dLat = toRadian(pos2Latitude - pos1Latitude);
    var dLon = toRadian(pos2Longitude - pos1Longitude);

    var lat1 = toRadian(pos1Latitude);
    var lat2 = toRadian(pos2Latitude);

    var y = Math.sin(dLon) * Math.cos(lat2);
    var x = Math.cos(lat1) * Math.sin(lat2) -
            Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
    var brng = toDegrees(Math.atan2(y, x));
    return brng;
}

function calculateDistancePoint(pos1Latitude, pos1Longitude, pos2Latitude, pos2Longitude, distanceInKm) {
    var bearing = calculateBearingPoint(pos1Latitude, pos1Longitude, pos2Latitude, pos2Longitude);
    return calculateBearingPoint(pos1Latitude, pos1Longitude, distanceInKm, bearing);
}

function calculateBearingPoint(pos1Latitude, pos1Longitude, distance, bearing) {
    var R = 6371;
    var lat1 = toRadian(pos1Latitude);
    var brng = toRadian(bearing);
    var lon1 = toRadian(pos1Longitude);

    var lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance / R) +
            Math.cos(lat1) * Math.sin(distance / R) * Math.cos(brng));

    var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat1),
                         Math.cos(distance / R) - Math.sin(lat1) * Math.sin(lat2));
    var result = [];
    result.push(toDegrees(lat2));
    result.push(toDegrees(lon2));
    return result;//new google.maps.LatLng(toDegrees(lat2), toDegrees(lon2));
}


function createPolyline(directionResult, markers, map, line) {
    if (line == null) {
        line = new google.maps.Polyline({
            path: directionResult.routes[0].overview_path,
            strokeColor: '#FF0000',
            strokeOpacity: getStrokeOpacity(),
            strokeWeight: 2
        });

        line.setMap(map);

        line.set('markers', markers);
        line.set('snapToRoad', true);
        line.set('distance', calculateRouteDistance(directionResult));

        var lines = map.get('lines');
        if (lines != null) {
            lines.push(line);
        }

    } else {
        line.setPath(directionResult.routes[0].overview_path);
        line.set('markers', markers);
        line.set('distance', calculateRouteDistance(directionResult));
        line.set('snapToRoad', true);
    }
}




function selectRoadType(roadType) {
    var travelMode = google.maps.TravelMode.BICYCLING;
    if (roadType == 'walk') {
        travelMode = google.maps.TravelMode.WALKING;
    } else if (roadType == 'driving') {
        travelMode = google.maps.TravelMode.DRIVING;
    }
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, '#DrawTrackModal')[0];

    if (mapHolder != null) {
        var map = jQuery.data(mapHolder, "map");
        map.set('travelMode', travelMode);
    }
}


function selectMapMarker(markerType) {
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, '#DrawTrackMap')[0];

    if (mapHolder != null) {
        var map = jQuery.data(mapHolder, "map");
        map.set('markerType', markerType);

        if (markerType === 'track') {
            $("#trackMarker").attr("src", "/Content/images/icons/track_marker_selected.png");
            $("#serviceMarker").attr("src", "/Content/images/icons/service_marker.png");
            $("#viewpointMarker").attr("src", "/Content/images/icons/viewpoint_marker.png");

            $("#clearMarker").attr("src", "/Content/images/icons/track_marker.png");

        } else if (markerType === 'service') {
            $("#trackMarker").attr("src", "/Content/images/icons/track_marker.png");
            $("#serviceMarker").attr("src", "/Content/images/icons/service_marker_selected.png");
            $("#viewpointMarker").attr("src", "/Content/images/icons/viewpoint_marker.png");

            $("#clearMarker").attr("src", "/Content/images/icons/service_marker.png");

        } else if (markerType === 'viewpoint') {
            $("#trackMarker").attr("src", "/Content/images/icons/track_marker.png");
            $("#serviceMarker").attr("src", "/Content/images/icons/service_marker.png");
            $("#viewpointMarker").attr("src", "/Content/images/icons/viewpoint_marker_selected.png");

            $("#clearMarker").attr("src", "/Content/images/icons/viewpoint_marker.png");
        }
    }
}


function TrackModel(parent) {
    var self = this;

    self.initState = true;


    self.Name = ko.observable('');
    self.Description = ko.observable('');
    self.Identifier = ko.observable('');
    self.FileId = ko.observable('');
    self.TypeManager = new TrainingTypeControl(0, 0, 0);

    self.Distance = ko.observable('');

    self.Elevations = null;

    self.PathForElevation = null;

    self.AssignedTrack = ko.computed(function () {

        //if (trainingDetailsManager != null && trainingDetailsManager.CurrentTrack != null)
        if (self != null && self.Identifier() != null && self.Identifier() != '') {
            $.ajax({
                type: "POST",
                url: ContextPath + "User/GetTrack",
                data: { trackId: self.Identifier() }
            })
               .done(function (result) {
                   //                   var res = result;
                   //                   self.FileId(res.File);
                   //                   self.Description(res.Description);
               });

            return self.Identifier();
        }
        return self.Identifier();
    });

    self.AssignedDescription = ko.computed(function () {
        return self != null ? self.Description() : '';
    });

    self.MapUrl = ko.computed(function () {
        return ContextPath + "Image/GetImage?id=" + self.FileId();
    });

    self.MapSelected = ko.computed(function () {
        return !(self.FileId() == '' || self.FileId() == '0');
    });

    self.mapLoaded = false;

    self.initMap = function (specialPointsCoordinates, editableMapCoordinates) {
        if (!self.mapLoaded) {
            var mapParent = $('#DrawTrackMap');
            MapInitialize(mapParent);
            AllowEditMap(mapParent);
            self.mapLoaded = true;
            //addMapPoints(mapParent, [], specialPointsCoordinates, editableMapCoordinates);

        }
    }

    self.refreshMap = function () {
        var mapParent = $('#DrawTrackMap');
        RefreshMap(mapParent);
    }

    self.infoCloudContainerHide = function () {
        var modeal = $('#DrawTrackModal');
        $(".infoCloudContainer", modeal).fadeOut(850)
    }

    self.OpenDeleteTrack = function (trainingId) {
        $('#removeTrackModal').modal('show');
    }

    self.OpenUpdateElevation = function (trainingId) {
        var contSelector = ".routeMap";
        var mapHolder = $(contSelector, '#DrawTrackMap')[0];
        if (mapHolder != null) {
            jQuery.data(mapHolder, "trackmodel", self);
        }

        $('#updateTrackElevationModal').modal('show');
    }

    self.initState = false;

    self.updateDrawnTrackCallback = null;

    self.initNew = function () {
        self.initState = true;
        self.mapLoaded = false;
        self.Name('');
        self.Description('');
        self.Identifier('');
        self.FileId('');
        self.Distance = "";
        self.TypeManager.SelectValue(0, 0, 0);
        self.initState = false;
    }

    self.readElevation = function (elevations, status) {
        var chartDiv = document.getElementById('elevation_chart');
        if (status !== 'OK') {
            ShowEmptyMsg(translations.UpdateTrackElevationFailed, translations.Error);
            return;
        }

        self.Elevations = [];

        var markerPoint = [];

        if (self.PathForElevation != null) {
            var pathForElevation = [];
            //var distance = 0;
            var prevPoint = null;
            for (var i = 0; i < self.PathForElevation.length; i++) {
                point = self.PathForElevation[i];
                if (prevPoint == null) {
                    pathForElevation.push(point);
                }
                if (prevPoint != null && prevPoint.lat() != point.lat() && prevPoint.lng() != point.lng()) {
                    pathForElevation.push(point);
                }
                prevPoint = point;
            }

            //var pathForElevation = self.PathForElevation;
            var lastIndex = 0;
            var lastDistance = 0;
            var distance = 0;
            var maxDistance = 0;
            for (var j = 0; j < elevations.length; j++) {
                var currentElevation = elevations[j];

                var closestPointIndex = getClosestPoint(pathForElevation, currentElevation.location, lastIndex, lastDistance);
                if (closestPointIndex != -1) {
                    var closestPointDistance = getPointIndexDistance(pathForElevation, closestPointIndex);
                    lastDistance = closestPointDistance;

                    var point = pathForElevation[closestPointIndex];

                    lastIndex = closestPointIndex;
                    if (maxDistance < closestPointDistance) {
                        maxDistance = closestPointDistance;
                        if (closestPointDistance != null /*&& closestPointDistance > lastDistance*/) {
                            var distanceElevation = [];
                            distanceElevation.push(closestPointDistance);
                            distanceElevation.push(currentElevation.elevation);
                            self.Elevations.push(distanceElevation);

                            var mpoints = [];
                            markerPoint.push(mpoints);
                            mpoints.push(point);
                            mpoints.push(currentElevation);
                            mpoints.push(closestPointDistance);
                            mpoints.push(closestPointIndex);

                        }
                    }
                }
            }

            //
            //var contSelector = ".routeMap";
            // var mapHolder = $(contSelector, '#DrawTrackMap')[0];
            // if (mapHolder != null)
            {
                /*
                var map = jQuery.data(mapHolder, "map");



                for (var j = 0; j < markerPoint.length; j++) {
                    var currentMarkerPoint = markerPoint[j];



                    var marker = new google.maps.Marker({
                        position: new google.maps.LatLng(currentMarkerPoint[1].location.lat(), currentMarkerPoint[1].location.lng()),
                        map: map,
                        title: 'ele: ' + j.toString() + ", d:" + currentMarkerPoint[2].toString() + ", closest:" + currentMarkerPoint[3].toString(),
                        icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=' + j + '|00FF00|000000'

                    });


                    var marker = new google.maps.Marker({
                        position: new google.maps.LatLng(currentMarkerPoint[0].lat(), currentMarkerPoint[0].lng()),
                        map: map,
                        title: 'point: ' + j.toString() + ", d:" + currentMarkerPoint[2].toString(),

                        icon: {
                            url: ICON_CHECKPOINT_MARKER,
                            anchor: new google.maps.Point(5, 5)
                        }

                    });

                }
                */

                /*
                for (var j = 0; j < elevations.length; j++) {
                    var currentElevation = elevations[j];

                    var marker = new google.maps.Marker({
                        position: new google.maps.LatLng(currentElevation.location.lat(), currentElevation.location.lng()),
                        map: map,
                        title: 'ele: ' + j.toString(),
                    });
                }
                 */
                /*
                for (var j = 0; j < pathForElevation.length && j < 1260; j++) {
                    var currentElevation = pathForElevation[j];

                    var marker = new google.maps.Marker({
                        position: new google.maps.LatLng(currentElevation.lat(), currentElevation.lng()),
                        map: map,
                        title: 'point: ' + j.toString(),
                        icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=' + j + '|FF0000|000000'

                    });
                }
                */
            }

            //
        }
    }

    self.saveDrawnTrack = function () {
        var contSelector = ".routeMap";
        var mapHolder = $(contSelector, '#DrawTrackModal')[0];

        if (mapHolder != null) {
            var map = jQuery.data(mapHolder, "map");

            var lines = map.get('lines');
            var points = [];
            var specialPointsList = [];
            var servicePoints = [];
            var viewpointPoints = [];
            var editablePoints = [];

            var specialPoints = map.get('specialPoints');
            if (specialPoints != null) {
                for (var i = 0, length = specialPoints.length; i < length; i++) {
                    var marker = specialPoints[i];
                    var markerType = marker.get('markerType');
                    var pointCorrindinates = [];
                    pointCorrindinates.push(marker.getPosition().lat());
                    pointCorrindinates.push(marker.getPosition().lng());


                    if (markerType === 'service') {
                        pointCorrindinates.push(0);
                        servicePoints.push(pointCorrindinates);
                    } else if (markerType === 'viewpoint') {
                        viewpointPoints.push(pointCorrindinates);
                        pointCorrindinates.push(1);
                    } else {

                    }
                }
                if (servicePoints.length > 0 || viewpointPoints.length > 0) {
                    //specialPointsList.push(servicePoints);
                    //specialPointsList.push(viewpointPoints);

                    specialPointsList.push.apply(specialPointsList, servicePoints);
                    specialPointsList.push.apply(specialPointsList, viewpointPoints);
                }
            }


            if (lines != null) {

                for (var i = 0, length = lines.length; i < length; i++) {
                    var line = lines[i];
                    var markers = line.get('markers')

                    if (line.getPath().length > 0) {

                        var path = line.getPath();
                        // Iterate over the vertices.
                        for (var j = 0, pathLength = path.getLength() ; j < pathLength; j++) {
                            var point = path.getAt(j);
                            var pointCorrindinates = [];
                            pointCorrindinates.push(point.lat());
                            pointCorrindinates.push(point.lng());
                            points.push(pointCorrindinates);
                        }
                    }

                    var snapToRoad = line.get('snapToRoad');
                    if (snapToRoad != null && snapToRoad) {

                    } else if (line.getPath().length > 0) {
                    }
                }
            }

            var track = map.get('track');
            if (track != null) {
                for (var i = 0, length = track.length; i < length; i++) {
                    var marker = track[i];
                    var snapToRoad = marker.get('snapToRoad');

                    var pointCorrindinates = [];
                    pointCorrindinates.push(marker.getPosition().lat());
                    pointCorrindinates.push(marker.getPosition().lng());
                    pointCorrindinates.push(snapToRoad != null && snapToRoad ? 1 : 0);
                    editablePoints.push(pointCorrindinates);
                }
            }

            var distance = calculateDistance(map);
            //updateDrawnTrack(points, specialPointsList, editablePoints, distance);
            if (self.updateDrawnTrackCallback != null) {
                self.updateDrawnTrackCallback(points, specialPointsList, editablePoints, distance, self.Elevations, self.TypeManager.SelectedType());
            }
        }
    }
}


function UpdateTrackElevation() {
    $('#updateTrackElevationModal').modal('hide');
    var contSelector = ".routeMap";
    var mapHolder = $(contSelector, '#DrawTrackMap')[0];
    if (mapHolder != null) {
        var map = jQuery.data(mapHolder, "map");
        var mapPathKey = "mapPath";
        var trainingPath = jQuery.data(mapHolder, mapPathKey);
        var pathArr = trainingPath.getPath();

        var path = [];
        for (var i = 0; i < pathArr.length; i++) {
            path.push(pathArr.getAt(i));
            //path.push(new google.maps.LatLng(pathArr.getAt(i).lat(), pathArr.getAt(i).lng()));

        };

        var trackModel = jQuery.data(mapHolder, "trackmodel");
        getElevation(path, trackModel);

    }
}


function getElevation(path, trackModel) {
    var elevator = new google.maps.ElevationService;
    trackModel.PathForElevation = path;
    elevator.getElevationAlongPath({
        'path': path,
        'samples': 128
    }, trackModel.readElevation);
}



function getClosestPoint(path, point, lastIndex, lastDistance, toleranceInKm) {
    if (toleranceInKm == null) {
        toleranceInKm = 0.005;
    }
    var d = -1;
    var i;
    var index = -1;
    var fullDistance = 0;
    for (i = 0; i < (path.length)/* && i < lastIndex + 5*/; i++) {
        // if (i >= lastIndex-1)
        {// - 1
            var p1 = path[i];

            if (i > 0) {
                var prevP = path[i - 1];
                var pointDist = calculateLineDistance(prevP.lat(), prevP.lng(), p1.lat(), p1.lng(), false);

                if (fullDistance >= lastDistance) {
                    var prevDist = calculateLineDistance(point.lat(), point.lng(), prevP.lat(), prevP.lng(), false);
                    var curDist = calculateLineDistance(point.lat(), point.lng(), p1.lat(), p1.lng(), false);

                    if ((prevDist + curDist - toleranceInKm) <= pointDist) {

                        index = i - 1;
                        break;
                    }
                }
                fullDistance += pointDist;
            }
        }
    }
    return index;
}

function getPointIndexDistance(path, index) {
    var i;
    var prevPoint = null;
    var distance = 0;
    var resultDistance = 0;
    for (i = 0; i < path.length && i <= index ; i++) {
        var p1 = path[i];

        if (prevPoint != null) {
            distance += calculateLineDistance(prevPoint.lat(), prevPoint.lng(), p1.lat(), p1.lng(), false);
        }
        prevPoint = p1;
    }
    return distance;
}

function getPointsArray(trainingPath) {
    var path = [];
    var pathArr = trainingPath.getPath();
    for (var i = 0; i < pathArr.length; i++) {
        path.push(pathArr.getAt(i));
    }
    return path;
}

function getOptimizedPath(trainingPath) {
    var path = [];
    var pathArr = trainingPath.getPath();
    var prevPoint = null;
    for (var i = 0; i < pathArr.length; i++) {
        point = pathArr.getAt(i);
        if (prevPoint == null) {
            path.push(point);
        }
        if (prevPoint != null && prevPoint.lat() != point.lat() && prevPoint.lng() != point.lng()) {
            path.push(point);
        }
        prevPoint = point;
    }
    return path;
}

;
function AdvertismentModel(parent) {
    var self = this;
    self.Parent = parent;

    self.ClubId = ko.observable(0);
    self.Description = ko.observable('');
    self.DescriptionId = ko.observable(0);
    self.FileId = ko.observable(0);
    self.Id = ko.observable(0);
    self.Name = ko.observable('');
    self.Priority = ko.observable("2");
    self.Sequence = ko.observable(0);
    self.Url = ko.observable('');
    self.Active = ko.observable(false);

    self.BS_Name = ko.observable('');
    self.BS_Active = ko.observable(true);

    self.ImageUrl = ko.computed(function () { return ContextPath + "Image/GetImage?id=" + self.FileId(); });

    self.ActivityLabel = ko.computed(function () {
        return self.Active() ? translations.StateVisible : translations.StateInvisible;
    });

    self.CaruselTime = ko.computed(function () {
        var time = 10000;
        if (self.Priority() == '1') {
            time = 6000;
        } else if (self.Priority() == '3') {
            time = 16000;
        }
        return time;
    });

    self.init = function () {
        self.ClubId(0);
        self.Description('');
        self.DescriptionId(0);
        self.FileId(0);
        self.Id(0);
        self.Name('');
        self.Priority('2');
        self.Sequence(0);
        self.Url('');
        self.Active(false);

        self.BS_Name('');
        self.BS_Active(true);
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;

            self.ClubId(serverModel.ClubId);
            self.Description(serverModel.Description);
            self.DescriptionId(serverModel.DescriptionId);
            self.FileId(serverModel.FileId);
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
            self.Priority("" + serverModel.Priority);
            self.Sequence(serverModel.Sequence);
            self.Url(serverModel.Url);
            self.Active(serverModel.Active);

            self.BS_Name(serverModel.Name);
            self.BS_Active(serverModel.Active);
        }
    };

    self.getClearData = function () {
        var data = {
            ClubId: self.ClubId(),
            Description: self.Description(),
            DescriptionId: self.DescriptionId(),
            FileId: self.FileId(),
            Id: self.Id(),
            Name: self.Name(),
            Priority: self.Priority(),
            Sequence: self.Sequence(),
            Url: self.Url(),
            Active: self.Active(),
        }
        return data;
    };

    self.Save = function () {
        var isNew = self.Id() == -1;
        var data = self.getClearData();
        var strData = JSON.stringify(data);
        $.ajax({
            type: "POST",
            url: ContextPath + "ClubAMsg/SetModel",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: strData
        })
        .done(function (result) {
            self.initByModel(result);
            ShowSavedAlert();
            if (isNew) {
                self.Parent.List.push(self);
                self.Select();
            }
        });
    }


    self.deleteAd = function (groupId) {
        $('#removeAdModal').modal('show');
    }
    self.coreDeleteAd = function () {
        $('#removeAdModal').modal('hide');

        $.ajax({
            type: "POST",
            url: ContextPath + "ClubAMsg/Delete",
            data: { id: self.Id() }
        })
         .done(function (result) {
             self.Parent.List.remove(self);
             self.Parent.SelectdAd(new AdvertismentModel(self.Parent));
         });
    }


    self.Select = function () {
        if (self.Parent) {
            if (self.Parent.SelectdAd() != self) { self.CancelChanges(); }
            self.Parent.SelectdAd(self);
            if (typeof (self.Parent.inintControls) != 'undefined') {
                self.Parent.inintControls();
            }
        }
    }
    self.Selected = ko.computed(function () {
        if (!self.Parent || typeof (self.Parent.SelectdAd) === 'undefined') return false;
        return self.Parent.SelectdAd() == self;
    });

    self.SetImage = function (id) {
        if (id > 0) {
            self.FileId(id);
            $.ajax({
                type: "POST",
                url: ContextPath + "ClubAMsg/CheckImage",
                data: { id: id }
            })
           .done(function (result) {
               if (result && result != 'ok') {
                   var modal = $('#emptyMsgModal');
                   modal.find('.askMsg').html(result);
                   modal.find('#emptyMsgModalLabel').html(translations.WrongImage);
                   modal.modal("show");
               }
           });
        }
    }

    self.UserClick = function () {
        if (self.Description()) {
            self.Select();
            $('#adDetails').modal('show');
        } else if (self.Url()) {
            window.open(self.Url());
        }
    }
}

function UserAdvertismentManager() {
    var self = this;

    self.List = ko.observableArray([]);

    self.SelectdAd = ko.observable(null);
    self.SelectdAd(new AdvertismentModel(self));

    self.Carusel = null;

    self.InitUser = function () {
        if (typeof tileAd !== 'undefined' && tileAd) {

            self.List.removeAll();
            for (var i = 0, len = tileAd.length; i < len; i++) {
                var elem = new AdvertismentModel(self);
                elem.initByModel(tileAd[i]);
                self.List.push(elem);
            }

            self.Carusel = $('#userAdBarContainer');
            self.Carusel.carousel();

            var width = self.Carusel.width();
            self.Carusel.height(width/0.49);
        }
    }
}

function AdminAdvertismentManager() {
    var self = this;

    self.initMode = true;

    self.List = ko.observableArray([]);


    self.List.subscribe(function (newValue) {
        if (newValue && !self.initMode) {
            if (self.UpdateSeqsTimeOut != null) {
                clearTimeout(self.UpdateSeqsTimeOut);
            }
            self.UpdateSeqsTimeOut = setTimeout(function () { self.UpdateSeqs(); }, 500);
        }
    });

    self.UpdateSeqsTimeOut = null;
    self.UpdateSeqs = function () {
        var seqs = [];
        var list = self.List();
        for (var i = 0, len = list.length; i < len; i++) {
            var elem = list[i];
            seqs.push(elem.Id());
        }

        $.ajax({
            type: "POST",
            url: ContextPath + "ClubAMsg/SetSequences",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(seqs)
        })
        .done(function (result) {

        });
    }


    self.SelectdAd = ko.observable(null);
    self.SelectdAd(new AdvertismentModel(self));

    self.initNewAd = function () {
        var em = new AdvertismentModel(self);
        em.init();
        em.Id(-1);

        var maxSeq = _(self.List()).max(function (item) { return item.Sequence(); });
        em.Sequence(maxSeq + 1);

        self.SelectdAd(em);
        self.inintControls();
    }

    self.InitAdmin = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "ClubAMsg/GetAdminListModel"
        })
        .done(function (serverModel) {
            if (serverModel) {
                self.initMode = true;

                self.List.removeAll();

                for (var i = 0, len = serverModel.length; i < len; i++) {
                    var elem = new AdvertismentModel(self);
                    elem.initByModel(serverModel[i]);
                    self.List.push(elem);
                }

                self.initMode = false;
            }
        });
    }

    self.inintControls = function () {
        $("#adFileUpload").fileupload({
            dataType: 'json',
            add: function (e, data) {
                data.submit();
            },
            done: function (e, data) {
                self.SelectdAd().SetImage(data.result[0].url);
            }
        });
        $('.tinymce').each(function () { if (this.renderTinymce != undefined) this.renderTinymce(); });
    }

    self.initMode = false;
}

var adminAdvertismentManager;
function initAdminAdvertismentManager() {
    adminAdvertismentManager = new AdminAdvertismentManager();
    GeneralModel.addProperty("AdminAdvertismentManager", adminAdvertismentManager);
}

var userAdvertismentManager;
function initUserAdvertismentManager() {
    userAdvertismentManager = new UserAdvertismentManager();

    GeneralModel.addProperty("UserAdvertismentManager", userAdvertismentManager);

    $(document).ready(function () {
        userAdvertismentManager.InitUser();
    });
}


function userAdClick(adElem) {
    var elem = $(adElem);
    var desc = elem.find('.hiddenDescript');
    if (desc.text().trim()) {
        var modal = $('#adDetails');
       
        modal.find('.myModalLabel2').html(elem.data('name'));

        var url = elem.data('url');
        var link = modal.find('.link');
        if (url) {
            link.show();
            var a = link.find('a');
            a.attr('href',url);
            a.html(url);
        } else {
            link.hide();
        }

        var img = elem.find('img');
        var mImg = modal.find('.image img');
        mImg.attr('src', img.attr('src'));

        modal.find('div.description').html(desc.html());

        modal.modal('show');

    } else {
        window.open(elem.data('url'));
    }
}

 function inintAdCarusel () {
        var  Carusel = $('#userAdBarContainer');
        Carusel.carousel({ interval: 10000 });
        Carusel.on('slid', function () {
            Carusel.carousel('pause');
            var to = Carusel.find('.clubAInfoTile.active').data("interval");
            if (!to) to = 10000;
            setTimeout(function () { Carusel.carousel('next'); }, to);
        });
        var width = Carusel.width();
        Carusel.height(width / 0.49);

        $('#targetSideTile .dzyndzolek').click();
};//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function (element, valueAccessor) {
        var list = valueAccessor();
        var elem = $(element);
        handle = '';
        if (elem.data('handle')) {
            handle = elem.data('handle');
        }
        elem.sortable({
            handle: handle,
            update: function (event, ui) {
                //retrieve our actual data item
                var item = ko.dataFor(ui.item[0]);
                //figure out its new position
                var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                //remove the item and add it back in the right spot
                if (position >= 0) {
                    list.remove(item);
                    list.splice(position, 0, item);
                }
                ui.item.remove();
            }
        });
    }
};

//connect items with observableArrays
ko.bindingHandlers.sortableMultiList = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        var elem = $(element);
        elem.data("sortList", valueAccessor()); //attach meta-data
        handle = '';
        if (elem.data('handle')) {
            handle = elem.data('handle');
        }
        $(element).sortable({
            handle: handle,
            update: function (event, ui) {
                var item = ko.dataFor(ui.item[0]);
                if (item) {
                    //identify parents
                    var originalParent = item.ParentList;
                    var newParent = ui.item.parent().data("sortList");
                    //figure out its new position
                    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                    if (position >= 0) {
                        originalParent.remove(item);
                        newParent.splice(position, 0, item);
                        item.ParentList = newParent;
                    } 
                    ui.item.remove();                    
                } 
            },
            connectWith: '.' + allBindingsAccessor().connectWith
        });
    }
};

//attach meta-data
ko.bindingHandlers.sortableItem = {
    init: function (element, valueAccessor) {
        var options = valueAccessor();
        options.item.ParentList = options.parentList;
    }
};;var refreshUserBarModelHandler = null;

function UserBarModel() {
    var self = this;

    self.NotReadNewsCount = ko.observable(0);
    self.NotReadArticlesCount = ko.observable(0);
    self.NotReadTrainingInvitationsCount = ko.observable(0);
    self.NotReadAnalysisCount = ko.observable(0);
    self.NotReadAnalysisTrainingId = '0';
    self.NotReadPlanCommentsCount = ko.observable(0);
    self.NotReadPlanCommentsFirstId = 0;
    self.NotReadPlansCount = ko.observable(0);
    self.NotReadPlansPlanId = 0;
    self.DataLoaded = ko.observable(false);
    self.UserClubs = ko.observableArray([]);
    self.CurrentClub = ko.observable(new ClubSimpleModel(self));
    self.ShowSomeClubs = ko.observable(false);
    self.UserTarget = ko.observable(new UserTargetModel());

    self.SessionLost = ko.observable(false);

    self.init = function (refreshCacheClient) {
        if (self.SessionLost()) return;

        var userBarModelTmp = $.jStorage.get('UserBarModel');

        if (!userBarModelTmp || refreshCacheClient) {
            //var refreshStringUserBarModel =  

            //             var refreshStringUserBarModel = $.jStorage.get('refreshStringUserBarModel');

            //             if (refreshCacheServerString)
            //                 $.jStorage.set('refreshStringUserBarModel', refreshCacheServerString, { TTL: 0 });

            //             if (refreshCacheServerString) 
            //                 refreshStringUserBarModel = refreshCacheServerString;

            setTimeout(function () {
                self.loadFromServer();
            }, 250);
        }
        else {
            self.initfromCache(userBarModelTmp);
        }
    }
    self.clearCache = function () {
        $.jStorage.deleteKey('UserBarModel');
    }

    self.loadFromServer = function () {
        if (!canLoadCommunityWall) {
            setTimeout(function () { self.loadFromServer(); }, 100);
            return;
        }

        $.ajax({
            type: "POST",
            url: ContextPath + "Home/GetUserBarModelJson",
            //data: { userId: CurrentUser.UserId(), refreshString: refreshStringUserBarModel }
            error: function (xhr, ajaxOptions, thrownError) {
                if (xhr.status == 0) {
                    notUnloading = false;
                }
                if (xhr.status == 401) {
                    self.SessionLost(true);
                }
            },
            statusCode: {
                //                     500: function () {
                //                         alert("Une erreur s'est produite !");
                //                     },
                //                     403: function(){
                //                         alert("badTelecommandeCode")
                //                     },
                //                     404: function(){
                //                         alert("freeboxNotFound")
                //                     }
            },
        })
       .done(function (serverModel) {
           if (_.isString(serverModel) && serverModel.indexOf('<!DOCTYPE html>') > -1) { //przyszedl html zamiast modelu
               self.SessionLost(true);
           }

           if (!serverModel.TimeoutOccured) {
               self.initByModel(serverModel);
           }
       });
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.NotReadNewsCount(serverModel.NotReadNewsCount);
            self.NotReadArticlesCount(serverModel.NotReadArticlesCount);
            self.NotReadTrainingInvitationsCount(serverModel.NotReadTrainingInvitationsCount);
            self.NotReadAnalysisCount(serverModel.NotReadAnalysisCount);
            self.NotReadAnalysisTrainingId = serverModel.NotReadAnalysisTrainingId;
            self.NotReadPlanCommentsCount(serverModel.NotReadPlanCommentsCount);
            self.NotReadPlanCommentsFirstId = serverModel.NotReadPlanCommentsFirstId;
            self.NotReadPlansCount(serverModel.NotReadPlansCount);
            self.NotReadPlansPlanId = serverModel.NotReadPlansPlanId;

            if (serverModel.SimpleCurrentClub)
                self.CurrentClub().initByModel(serverModel.SimpleCurrentClub);

            communityExplorerModel.setNewTrainingInvitationsCount(serverModel.NotReadTrainingInvitationsCount);

            self.UserClubs.removeAll();
            if (serverModel.SimpleMyClubs) {
                for (var i = 0, len = serverModel.SimpleMyClubs.length; i < len; i++) {
                    var elem = new ClubSimpleModel(self);
                    elem.initByModel(serverModel.SimpleMyClubs[i]);
                    self.UserClubs.push(elem);
                    if (!self.ShowSomeClubs() && elem.Id() > 0) {
                        self.ShowSomeClubs(true);
                    }
                }
            }

            self.DataLoaded(true);

            var strData = self.serialize();

            addToCache("UserBarModel", strData, 3600000);
        }
    };

    self.setNotReadNewsCount = function (count) {
        self.NotReadNewsCount(count);

        self.refreshCache();
        //         var userBarModelTmp = $.jStorage.get('UserBarModel');

        //         if (userBarModelTmp != null)
        //         {             
        //             var data = JSON.parse(userBarModelTmp);

        //             data.NotReadNewsCount = count;

        //             var strData = self.serialize();

        //             addToCache("UserBarModel", strData, 3600000);
        //         }
    }

    self.setNotReadArticlesCount = function (count) {
        self.NotReadArticlesCount(count);

        self.refreshCache();
    }

    self.SetNotReadTrainingInvitationsCount = function (count) {
        self.NotReadTrainingInvitationsCount(count);

        self.refreshCache();
    }

    self.setNotReadAnalysisCount = function (count, trainingId) {
        self.NotReadAnalysisCount(count);
        self.NotReadAnalysisTrainingId = trainingId;

        self.refreshCache();
    }

    self.setNotReadPlanCommentsCount = function (count, notReadPlanCommentsFirstId) {
        self.NotReadPlanCommentsCount(count);
        self.NotReadPlanCommentsFirstId = notReadPlanCommentsFirstId;

        self.refreshCache();
    }

    self.setCurrentClub = function (club) {
        if (club) {
            self.CurrentClub().initByModel(JSON.parse(club));
        } else {
            self.CurrentClub().init();
        }

        self.refreshCache();
    }

    self.refreshCache = function () {
        var userBarModelTmp = $.jStorage.get('UserBarModel');

        if (userBarModelTmp != null) {
            var strData = self.serialize();

            addToCache("UserBarModel", strData, 3600000);
        }
    }

    self.serialize = function () {
        var userClubsTmp = [];
        var userClubs = self.UserClubs();
        for (var i = 0, len = userClubs.length; i < len; i++) {
            userClubsTmp.push(userClubs[i].serialize());
        }

        var data = {
            NotReadNewsCount: self.NotReadNewsCount(),
            NotReadArticlesCount: self.NotReadArticlesCount(),
            NotReadTrainingInvitationsCount: self.NotReadTrainingInvitationsCount(),
            NotReadAnalysisCount: self.NotReadAnalysisCount(),
            NotReadAnalysisTrainingId: self.NotReadAnalysisTrainingId,
            NotReadPlanCommentsCount: self.NotReadPlanCommentsCount(),
            NotReadPlanCommentsFirstId: self.NotReadPlanCommentsFirstId,
            NotReadPlansCount: self.NotReadPlansCount(),
            NotReadPlansPlanId: self.NotReadPlansPlanId,
            CurrentClub: self.CurrentClub().serialize(),
            UserClubs: userClubsTmp
        };
        var strData = JSON.stringify(data);

        return strData;
    }

    self.initfromCache = function (userBarModelTmp) {
        var data = JSON.parse(userBarModelTmp);

        self.NotReadNewsCount(data.NotReadNewsCount);
        self.NotReadArticlesCount(data.NotReadArticlesCount);
        self.NotReadTrainingInvitationsCount(data.NotReadTrainingInvitationsCount);
        self.NotReadAnalysisCount(data.NotReadAnalysisCount);
        self.NotReadAnalysisTrainingId = data.NotReadAnalysisTrainingId;
        self.NotReadPlanCommentsCount(data.NotReadPlanCommentsCount);
        self.NotReadPlanCommentsFirstId = data.NotReadPlanCommentsFirstId;
        self.CurrentClub().deserialize(data.CurrentClub);

        self.UserClubs.removeAll();
        if (data.UserClubs) {
            for (var i = 0, len = data.UserClubs.length; i < len; i++) {
                var elem = new ClubSimpleModel(self);
                elem.initByModel(JSON.parse(data.UserClubs[i]));
                self.UserClubs.push(elem);
            }
        }

        self.DataLoaded(true);
    }

    self.openNews = function () {
        window.location = ContextPath + "News/NewsExplorer";

        //         newsModelListForModalFromWall.Start = true;
        //         newsModelListForModalFromWall.Search();  

    }

    self.openArticles = function () {
        window.location = ContextPath + "Articles/NewArticlesExplorer";
    }

    self.openTrainingInvitations = function () {
        communityExplorerModel.IgnoreTrainingInvitationsWasPressed = true;
        window.location = ContextPath + "Community/SearchForTrainingInvitations";
    }

    self.openAnalyzes = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "User/SetTrainingAnalsToRead",
            data: { notReadAnalysisTrainingId: self.NotReadAnalysisTrainingId }
        })
        .done(function (result) {
            //self.setNotReadAnalysisCount(result.NotReadAnalysisCount, result.NotReadAnalysisTrainingId);
            window.location = ContextPath + "User/TrainingAnalisisView?treningId=" + self.NotReadAnalysisTrainingId;
        });
    }

    self.openPlans = function () {
        $.ajax({
            type: "POST",
            url: ContextPath + "TrainingPlan/SetPlansToRead",
            data: { notReadPlanId: self.NotReadPlansPlanId }
        })
        .done(function (result) {
            window.location = ContextPath + "TrainingPlan/PlanUnitEditor?instanceId=" + self.NotReadPlansPlanId + "&scrollToEvent=true";
        });
    }

    self.enterCommunity = function () {
        $('#selectCommunityModal').modal('show');
    }

    self.leaveCommunity = function () {
        if (self.UserClubs().length) {
            userBarModel.setCurrentClub(self.UserClubs()[0].serialize());
        } else {
            userBarModel.setCurrentClub(null);
        }
        //         if (window.location.pathname.indexOf('NewsClubExplorer') != -1)
        //         {
        //             setClub(0,  '',  '',  0, true, 'News/NewsExplorer');
        //         }
        //         else        
        setClub(0, '', '', 0, true);
    }

    self.openPlanComments = function () {
        window.location = ContextPath + "TrainingPlan/Callendar?userTrainingPlanId=" + userBarModel.NotReadPlanCommentsFirstId + '&openComment=true';

        //         if (typeof (userCallendar) != 'undefined') {
        //             if (userCallendar.PeriodType() != 'plan') {
        //                 userCallendar.PeriodType('plan');                
        //             }
        //         }
    }

    //self.init();
}

var userBarModel = new UserBarModel();
GeneralModel.addProperty("UserBarModel", userBarModel);

$(document).ready(function () {
    if ($('body.admin').length > 0) return;
    if ($('body.logonPage').length > 0) return;
    if ($('body.notLogged').length > 0) return;
    if ($('body.periodPage').length > 0) return;

    userBarModel.init(true);

    refreshUserBarModelHandler = setInterval(function () {
        userBarModel.init(true);
    }, 180000);

    if (typeof tileTarget !== 'undefined' && tileTarget) {
        userBarModel.UserTarget().initByModel(tileTarget);
        userBarModel.UserTarget().TargetSideTileManage.currentTarget.ForUserBarTile(true);
        $('#targetSideTile .fbShareTarget .currentValue.open .value.textNotFitUserBar').fitText({ minFontSize: '10px', maxFontSize: '18px', padding: '13' });
        $('#targetSideTile .fbShareTarget .targetValue.open .value.textNotFitUserBar').fitText({ minFontSize: '10px', maxFontSize: '18px', padding: '13' });
        $('#targetSideTile .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFitUserBar').fitText({ minFontSize: '10px', maxFontSize: '24px', padding: '150' });
        $('#targetSideTile .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFitUserBar').fitText({ minFontSize: '10px', maxFontSize: '24px', padding: '150' });
        //$('#targetSideTile .fbShareTarget.success .haveTarget.failed.textNotFitUserBar').fitText({ minFontSize: '10px', maxFontSize: '18px', padding: '13' });

        $('#targetSideTile .fbShareTarget .currentValue.open .value.textNotFitUserBar').removeClass('textNotFitUserBar');
        $('#targetSideTile .fbShareTarget .targetValue.open .value.textNotFitUserBar').removeClass('textNotFitUserBar');
        $('#targetSideTile .fbShareTarget.success .currentValue.success.notPersonalTargetModel .value.textNotFitUserBar').removeClass('textNotFitUserBar');
        $('#targetSideTile .fbShareTarget.success .currentValue.success.personalTargetModel .value.textNotFitUserBar').removeClass('textNotFitUserBar');
    }
});

function relaodTileTarget() {
    $.ajax({
        type: "POST",
        url: ContextPath + "Target/GetTargetTile"
    })
        .done(function (result) {
            userBarModel.UserTarget().initByModel(result);
        });
}
function relaodTileTargetDeffered() {
    setTimeout(relaodTileTarget, 1000);
}
;function UserDiaryModel(zoneType, useCharts) {

    var self = this;

    self.ZoneType = zoneType;
    self.UseCharts = useCharts;
    self.OnlyZones = false;

    self.UpdateMode = true;

    self.UserId = 0;

    self.UserSet = ko.observableArray([]);
    self.CurrentValues = ko.observable(new DiaryDaySetModel(self));

    self.TypeDictionary = [];

    self.Sets = ko.observableArray([]);

    self.CurrentDate = ko.observable("");
    self.CanSuggest = ko.observable(false);
    self.BodyParams = ko.observable(true);

    self.ContainsChart = ko.observable(false);
    self.Chart = null;
    self.ChartOptions = null;
    self.CurentChartTypes = ko.observableArray([]);
    self.CurrentZoneChart = ko.observable(null);

    self.ChartSets = [
        { Name: translations.BodyParameters, typeIds: [2, 5, 6, 7] }
        /*
        { Name: translations.Waga, typeIds: [2] },
        { Name: translations.PercOfBody, typeIds: [5, 6, 7] },
        { Name: translations.PercOfWater, typeIds: [5] },
        { Name: translations.PercOfFat, typeIds: [6] },
        { Name: translations.PercOfMusc, typeIds: [7] }
        */
    ];

    self.CurrentChartSet = ko.observable(null);
    self.CurrentChartSet.subscribe(function (newValue) {
        if (newValue) {
            self.CustomChartName(newValue.Name);
            self.CurrentZoneChart(null);
            self.LoadChartData(newValue.typeIds);
        }
    });
    self.CurrentZoneChart.subscribe(function (newValue) {
        if (newValue && !self.UpdateMode) {
            self.CustomChartName("");
            self.CurrentChartSet(null);
            self.LoadChartData([], newValue);
        }
    });

    self.SetBodyParamsChart = function () {
        self.CurrentChartSet(self.ChartSets[0]);
    }
    self.SetZoneChart = function () {
        if (self.ZonesModel && self.ZonesModel.AvailableTypes().length) {
            self.CurrentZoneChart(self.ZonesModel.AvailableTypes()[0]);
        } else {
            self.ContainsChart(false);
        }
    }

    self.CustomChartName = ko.observable("");
    self.CurentChartName = ko.computed(function () {
        var types = self.CurentChartTypes();
        if (types && types.length == 1 && isNaN(types[0])) {
            return types[0].Name();
        }
        return self.CustomChartName();
    });

    self.UserDiaryHistory = ko.observable(new UserDiaryHistory());

    self.ZonesModel = (self.ZoneType && (typeof (ZoneSets) !== 'undefined')) ? new ZoneSets(self) : null;

    self.NextDate = ko.observable('');
    self.PreviousDate = ko.observable('');
    self.ParamStart = ko.observable('');
    self.LengthType = ko.observable(365);
    self.ChartDisplayDate = ko.observable('');


    self.TrainingTypeManager = new TrainingTypeControl(
    {
        mainId: 0,
        subId: 0,
        typeChangeCallback: null,
        clubId: null,
        multi: false,
        baseCtrl: null,
        disciplines: false,
        multiSubs: false,
        allInstedNone: true
    });

    self.PaceSetsCalculator = ko.observable(null);


    self.ForReloadDiarySetId = null;
    self.ForReloadDate = null;
    self.Load = function (userId, initDate, reloadCharts, diarySetId) {
        if (userId) self.UserId = userId;
        if (diarySetId) self.ForReloadDiarySetId = diarySetId;
        if (initDate) self.ForReloadDate = initDate;

        if (!diarySetId && self.ForReloadDiarySetId) diarySetId = self.ForReloadDiarySetId;

        self.UpdateMode = true;
        self.ParamStart(initDate ? initDate : (new Date()).toISOString().substr(0, 10));
        self.UpdateMode = false;

        if (!userId) userId = 0;
        if (!initDate) initDate = '';

        $.ajax({
            type: "POST",
            url: ContextPath + "Diary/GetUserModel",
            data: { userId: userId, date: initDate, dayDiary: !self.BodyParams(), diarySetId: diarySetId }
        })
         .done(function (result) {
             self.initByModel(result, initDate);

             if (reloadCharts && !self.OnlyZones) {
                 setTimeout(function () { self.CurrentChartSet(self.ChartSets[0]); }, 150);
             }
         });

        if (self.ZonesModel) {
            var afterCallback = reloadCharts && self.OnlyZones ? self.SetZoneChart : null;
            self.ZonesModel.LoadSets(userId, initDate, afterCallback);
        }
    }
    self.Reload = function () {
        self.Load(self.UserId, self.ForReloadDate, true, self.ForReloadDiarySetId);
    }

    self.CurrentDate.subscribe(function (newValue) {
        if (newValue && !self.UpdateMode) {
            self.Load(self.UserId, newValue);
        }
    });

    self.initByModel = function (serverModel, initDate) {
        if (serverModel) {

            self.UpdateMode = true;

            self.CurrentDate(initDate ? initDate : serverModel.CurrentDate);

            self.UserSet.removeAll();
            for (var i = 0, len = serverModel.UserSet.length; i < len; i++) {
                var ds = new DiaryTypeModel();
                ds.initByModel(serverModel.UserSet[i]);

                self.TypeDictionary["_" + ds.Id()] = ds;

                self.UserSet.push(ds);
            }

            self.Sets.removeAll();
            if (serverModel.Sets) {
                for (var i = 0, len = serverModel.Sets.length; i < len; i++) {
                    self.Sets.push(serverModel.Sets[i]);
                }
            }

            var curr = self.CurrentValues();
            if (!curr) {
                curr = new DiaryDaySetModel(self);
                self.CurrentValues(curr);
            }
            curr.initByModel(serverModel.CurrentValues, self.TypeDictionary);            

            self.CanSuggest(serverModel.CanSuggest);

            self.UpdateMode = false;
        }
    }

    self.SaveSet = function (data, event, afterSave) {
        var infoContainer = event ? $(event.target) : null;

        if (self.ZonesModel) self.ZonesModel.SaveSet();

        var data = self.CurrentValues().getStingify(self.CurrentDate());

        $('body').css('cursor', 'wait');

        $.ajax({
            type: "POST",
            url: ContextPath + "Diary/SaveSet",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: data
        })
         .done(function (result) {
             self.initByModel(result);
             ShowSavedAlert(infoContainer);
             if (afterSave) {
                 afterSave(self)
             }
             self.ReloadCharts();

             $('body').css('cursor', 'default');
         });
    }

    self.ReloadCharts = function () {
        var types = self.CurentChartTypes();
        var zone = self.CurrentZoneChart();
        if (types && types.length || zone) {
            self.LoadChartData(types, zone);
        }
    }

    self.LoadChartData = function (types, trainingType) {
        if (!self.UseCharts) return;

        var ids = [];
        if (types) {
            for (var i = 0, len = types.length; i < len; i++) {
                var elem = types[i];
                if (isNaN(elem)) {
                    ids.push(elem.Id());
                } else {
                    ids.push(elem);
                }
            }
        }

        var trainingTypeId = trainingType ? trainingType.Id : -1;

        var data = {
            UserId: self.CurrentValues().UserId(),
            StartDate: self.ParamStart(),
            LengthType: self.LengthType(),
            TypeIds: ids,
            ZonesTypeId: self.ZoneType,
            TrainingTypeId: trainingTypeId
        };

        $.ajax({
            type: "POST",
            url: ContextPath + "Diary/GetChartData",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(data)
        })
         .done(function (result) {
             self.UpdateMode = true;
             self.NextDate(result.NextDate);
             self.PreviousDate(result.PreviousDate);
             self.ParamStart(result.StartDate);
             self.LengthType(result.LengthType);
             self.ChartDisplayDate(result.CurrentDate);

             self.UpdateMode = false;

             self.OpenChart(result, types, trainingType);
         });
    }

    self.OpenChart = function (data, types, zoneType) {
        if (!self.UseCharts) return;

        var container = $('#currentChartContainer');
        var noData = $('#noChartData');

        self.UpdateMode = true;
        if (types == null) {
            self.CurentChartTypes.removeAll();
        } else {
            if (types !== self.CurentChartTypes()) {
                self.CurentChartTypes.removeAll();
                for (var i = 0, len = types.length; i < len; i++) {
                    self.CurentChartTypes.push(types[i]);
                }
            }
        }
        self.CurrentZoneChart(zoneType);
        self.UpdateMode = false;

        if (data.series.length) {

            self.ContainsChart(true);
            container.show();
            noData.hide();

            var unit = data.Unit;
            if (!unit) unit = '';
            var lUnit = data.LeftUnit;

            function unitFormatter(v, axis) {
                return v.toFixed(1/*axis.tickDecimals*/) + "&nbsp;" + unit;
            }

            var tickSize = typeof (zoneType) != "undefined" ? null : (data.max - data.min) / 10;

            var yaxes = [];
            yaxes.push({ tickFormatter: unitFormatter, position: "right", labelWidth: 60, min: data.min, max: data.max, tickSize: tickSize, unit: unit });
            if (data.LeftUnit) {
                function leftUnitFormatter(v, axis) {
                    return v.toFixed(1) + "&nbsp;" + lUnit;
                }
                var lTickSize = typeof (zoneType) != "undefined" ? null : (data.LeftMax - data.LeftMin) / 10;
                yaxes.push({ tickFormatter: leftUnitFormatter, position: "left", labelWidth: 60, min: data.LeftMin, max: data.LeftMax, tickSize: lTickSize, unit: lUnit });
            }

            self.ChartOptions = {
                xaxis: { mode: "time", tickLength: 5, monthNames: monthNames, dayNames: dayNames, minTickSize: [1, "day"], min: data.minX, max: data.maxX },
                yaxes: yaxes,
                grid: {
                    backgroundColor: '#ffffff',
                    borderWidth: 0,
                    hoverable: true
                },
                legend: {
                    show: false, //typeof (zoneType) != "undefined" && data.series.length > 1,
                    noColumns: 4,
                    position: "ne"
                    //,container: $('#lengthsLegend')
                },
                colors: data.colors ? data.colors : ((data.series.length > 1 ? getChartColors(false) : ['#ee8008'])),
                series: {
                    lines: { show: true, lineWidth: 2 },
                    points: { show: true }/*,
                    curvedLines: {
                        active: true
                    }
                    */
                }
            };

            // data.series[0].curvedLines = { apply: true };

            self.Chart = $.plot(container, data.series, self.ChartOptions);

            if (data.contests) {
                for (var i = 0, len = data.contests.length; i < len; i++) {
                    var o = self.Chart.pointOffset({ x: data.contests[i].Time, y: 0 });
                    container.append("<span title='" + data.contests[i].Name + "' class='wyniki_22' style='position:absolute;left:" + (o.left + 12) + "px;bottom:23px;' ></span>");
                }
            }

            function showTooltip(x, y, contents) {
                $('<div id="tooltip">' + contents + '</div>').css({
                    position: 'absolute',
                    display: 'none',
                    top: y - 30,
                    left: x + 5,
                    border: '1px solid #fdd',
                    padding: '2px',
                    'background-color': '#fee',
                    opacity: 0.80
                }).appendTo("body").fadeIn(200);
            }
            var previousPoint = null;
            container.bind("plothover", function (event, pos, item) {
                if (item) {
                    if (previousPoint != item.dataIndex) {
                        previousPoint = item.dataIndex;

                        $("#tooltip").remove();
                        var y = item.datapoint[1];
                        var date = dateToString(new Date(item.datapoint[0]));

                        var text = '';
                        var u = (item.series.yaxis.position == 'left' && lUnit) ? lUnit : unit;
                        if (item.series.label && item.datapoint.length <= 2) text += item.series.label + ' ';
                        text += date + ': ' + y + " " + u;
                        showTooltip(item.pageX, item.pageY, text);
                    }
                }
                else {
                    $("#tooltip").remove();
                    previousPoint = null;
                }
            });
        } else {
            container.hide();
            noData.show();
            self.ContainsChart(false);
        }
    }

    self.OpenZoneChartModal = function ( zoneType, trainingType) {
        loadAndBindTemplate({
            simpleContainer: 'zoneChartModalContainer',
            existingElem: '#zoneChartModal',
            ajaxPath: 'Diary/ZoneChartModal',
            bindData: null,
            callback: function () {
                $('#zoneChartModal').modal("show");

                //self.LoadChartData(null, zoneType);

                    self.CurrentZoneChart(trainingType);

               // self.CurrentZoneChart(zoneType);
            }
        });
    }

    self.GoToNextDate = function () {
        self.ParamStart(self.NextDate());
        //self.ReloadCharts();
    }
    self.GoToPreviousDate = function () {
        self.ParamStart(self.PreviousDate());
        //self.ReloadCharts();
    }
    self.LengthType.subscribe(function (newValue) {
        if (!self.UpdateMode && newValue) {
            self.ReloadCharts();
        }
    });
    self.ParamStart.subscribe(function (newValue) {
        if (!self.UpdateMode && newValue) {
            self.ReloadCharts();
        }
    });


    self.getCallendarIcon = function (length) {
        var ico = translations.CallendarDayIcon;
        switch (length) {
            case 7: ico = translations.CallendarWeekIcon; break;
            case 30: ico = translations.CallendarMonthIcon; break;
            case 365: ico = translations.CallendarYearIcon; break;
            case 1000: ico = "periodAll_"; break;
        }
        return ContextPath + 'Content/images/icons/' + ico + (self.LengthType() == length ? 'cze' : '') + '.png';
    }

    self.WeekIconSrc = ko.computed(function () { return self.getCallendarIcon(7); });
    self.MonthIconSrc = ko.computed(function () { return self.getCallendarIcon(30); });
    self.YearIconSrc = ko.computed(function () { return self.getCallendarIcon(365); });
    self.AllIconSrc = ko.computed(function () { return self.getCallendarIcon(1000); });
    self.CurrentIconSrc = ko.computed(function () { return self.getCallendarIcon(self.LengthType()); });

    self.clickDatePicker = function (data, event) {
        var elem = (event) ? $(event.target) : null;
        if (elem) {
            var dp = elem.parents('.yearBox').find('.datepicker');
            if (dp) {
                dp.datepicker('show');
            }
        }
    };

    self.UpdateMode = false;
}


var basicEntriesTypes = [1, 2, 3];
var nonAdvanceEntriesTypes = [1, 2, 3, 23, 26, 41, 42, 43, 44, 45, 46, 47, 48];
var hrEntriesTypes = [23, 26, 9];
var bodyParamsEntriesTypes = [1, 2, 5, 6, 7];
var powerEntriesTypes = [41];
var paceEntriesTypes = [42, 43, 44, 45, 46];
var vdotEntriesTypes = [47];
var rmEntriesTypes = [48];


function DiaryDaySetModel(parent) {

    var self = this;

    self.Parent = parent;

    self.Date = ko.observable("");
    self.UserId = ko.observable("");
    self.Entries = ko.observableArray([]).extend({ rateLimit: 50 });
    self.GroupedEntries = ko.observableArray([]);

    self.BodyParams = ko.observable(true);
    self.DiarySetId = ko.observable(0);

    self.PauseDays = ko.observable(0);

    self.LoadingState = ko.observable(false);

    self.filterByTypeIds = function (types) {
        var ens = self.Entries();
        var result = [];

        for (var i = 0, len = ens.length; i < len; i++) {
            var elem = ens[i];
            if (jQuery.inArray(elem.TypeId(), types) >= 0) {
                result.push(elem);
            }
        }
        return result;
    }
    self.filterByTypeIdsNegative = function (types) {
        var ens = self.Entries();
        var result = [];

        for (var i = 0, len = ens.length; i < len; i++) {
            var elem = ens[i];
            if (jQuery.inArray(elem.TypeId(), types) < 0) {
                result.push(elem);
            }
        }
        return result;
    }

    self.BasicEntries = ko.pureComputed(function () {

        return self.filterByTypeIds(basicEntriesTypes);
    });
    self.AdvanceEntries = ko.pureComputed(function () {

        return self.filterByTypeIdsNegative(nonAdvanceEntriesTypes);
    });
    self.HrEntries = ko.pureComputed(function () {
        return self.filterByTypeIds(hrEntriesTypes);
    });
    self.PowerEntries = ko.pureComputed(function () {
        return self.filterByTypeIds(powerEntriesTypes);
    });
    self.BodyParamsEntries = ko.pureComputed(function () {
        return self.filterByTypeIds(bodyParamsEntriesTypes);
    });
    self.PaceEntries = ko.pureComputed(function () {
        return self.filterByTypeIds(paceEntriesTypes);
    });
    self.VdotEntriesTypes = ko.pureComputed(function () {
        return self.filterByTypeIds(vdotEntriesTypes);
    });
    self.RmEntries = ko.pureComputed(function () {
        var ens = self.Entries();
        var result = [];

        for (var i = 0, len = ens.length; i < len; i++) {
            var elem = ens[i];
            if (jQuery.inArray(elem.TypeId(), rmEntriesTypes) >= 0 && elem.AdditionalParam()) {
                result.push(elem);
            }
        }
        return result;
    });

    self.BodyParamsEntriesAnyValue = ko.pureComputed(function () {
        return _.filter(self.BodyParamsEntries(), function (elem) { return elem.InitValue() }).length;
    });

    self.PaceSets = null;
    if (typeof (PaceSets) != 'undefined') {
        self.PaceSets = new PaceSets(self.Parent);
    }

    self.initByModel = function (serverModel, typeDictionary) {
        if (serverModel) {
            self.LoadingState(true);

            self.Date(serverModel.Date);
            self.UserId(serverModel.UserId);
            self.BodyParams(serverModel.BodyParams);
            self.DiarySetId(serverModel.DiarySetId);

            self.PauseDays(serverModel.PauseDays);

            self.Entries.removeAll();
            for (var i = 0, len = serverModel.Entries.length; i < len; i++) {
                var ds = new DiaryEntryModel(self);
                ds.initByModel(serverModel.Entries[i], typeDictionary);

                self.Entries.push(ds);
            }

            self.GroupedEntries.removeAll();
            var sets = self.Parent.Sets();
            for (var i = 0, len = sets.length; i < len; i++) {
                var s = jQuery.extend({}, sets[i]);
                s.Entries = self.filterByTypeIds(s.TypeIds);
                self.GroupedEntries.push(s);
            }

            if (self.PaceSets) setTimeout(self.PaceSets.Init, 100);

            self.LoadingState(false);
        }
    }

    self.getStingify = function (date) {

        var entries = [];
        var entrs = self.Entries();
        for (var i = 0, len = entrs.length; i < len; i++) {
            var en = entrs[i];
            entries.push({
                Id: en.Id(),
                TypeId: en.TypeId(),
                Value: en.Value(),
                ValueStr: en.ValueStr(),
                Date: en.Date(),
                SuggestedValue: en.SuggestedValue(),
                TrainingTypeId: en.TrainingTypeId(),
                AdditionalParam: en.AdditionalParam()
            });
        }

        var data = {
            Date: date,
            UserId: self.UserId(),
            BodyParams: self.BodyParams(),
            DiarySetId: self.DiarySetId(),
            Entries: entries,
            PauseDays: self.PauseDays()
        };

        var strData = JSON.stringify(data);

        return strData;
    }
}

function DiaryTypeModel() {

    var self = this;

    self.Id = ko.observable(0);
    self.Name = ko.observable("");
    self.Description = ko.observable("");
    self.Type = ko.observable("");
    self.Unit = ko.observable("");
    self.Values = ko.observableArray([]);
    self.MinValue = ko.observable(0);
    self.MaxValue = ko.observable(0);
    self.HasMinValue = ko.observable(false);
    self.HasMaxValue = ko.observable(false);
    self.MultipleValues = ko.observable(false);
    self.Position = ko.observable(0);
    self.HasSuggestion = ko.observable(false);
    self.Static = ko.observable(false);

    self.ForTrainingType = ko.observable(false);
    self.UseAdditionalParam = ko.observable(false);
    self.AdditionalParamUnit = ko.observable("");
    self.DefaultAdditionals = ko.observableArray([]);

    self.HasUnit = function () {
        return self.Unit() != '';
    };

    self.TextInput = ko.computed(function () {
        return self.Type() == 'decimal' || self.Type() == 'int';
    });
    self.DdlInput = ko.computed(function () {
        return self.Type() == 'seqEnum' || self.Type() == 'intEnum';
    });

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
            self.Description(serverModel.Description);
            self.Type(serverModel.Type);
            self.Unit(serverModel.Unit);
            self.MinValue(serverModel.MinValue);
            self.MaxValue(serverModel.MaxValue);
            self.HasMinValue(serverModel.HasMinValue);
            self.HasMaxValue(serverModel.HasMaxValue);
            self.MultipleValues(serverModel.MultipleValues);
            self.Position(serverModel.Position);
            self.HasSuggestion(serverModel.HasSuggestion);
            self.Static(serverModel.Static);

            self.ForTrainingType(serverModel.ForTrainingType);
            self.UseAdditionalParam(serverModel.UseAdditionalParam);
            self.AdditionalParamUnit(serverModel.AdditionalParamUnit);
            self.DefaultAdditionals(serverModel.DefaultAdditionals);

            self.Values.removeAll();
            if (serverModel.Values) {
                for (var i = 0, len = serverModel.Values.length; i < len; i++) {
                    self.Values.push({ text: serverModel.Values[i], value: ("" + (i + 1)) });
                }
            }
        }
    }
}


function DiaryEntryModel(parent) {

    var self = this;

    self.Parent = parent;

    self.Id = ko.observable(0);
    self.TypeId = ko.observable("");
    self.Value = ko.observable("");
    self.ValueStr = ko.observable("");
    self.PrevValue = ko.observable("");
    self.InitValue = ko.observable("");
    self.Date = ko.observable("");
    self.Type = ko.observable("");
    self.SuggestedValue = ko.observable("");
    self.InitSuggestedValue = ko.observable("");
    self.Trend = ko.observable("");
    self.TrainingTypeId = ko.observable("");
    self.AdditionalParam = ko.observable("");
    self.DefaultAddParam = ko.observable("");

    self.ChartedEntry = ko.computed(function () {
        if (!self.Parent) return false;
        var chartTypes = self.Parent.Parent.CurentChartTypes();
        if (!chartTypes || chartTypes.length == 0) return false;

        for (var i = 0, len = chartTypes.length; i < len; i++) {
            var elem = chartTypes[i];
            if (isNaN(elem)) {
                if (self.TypeId() == elem.Id()) {
                    return true;
                }
            } else {
                if (self.TypeId() == elem) {
                    return true;
                }
            }
        }

        return false;
    });

    self.ShowTrendIco = ko.computed(function () {
        return self.Trend() != 4;
    });
    self.TrendIcoUrl = ko.computed(function () {
        var trend = self.Trend();
        if (trend == 1) {
            return ContextPath + "Content/images/trend_maleje_26.png";
        } else if (trend == 2) {
            return ContextPath + "Content/images/trend_rosnie_26.png";
        } else {
            return ContextPath + "Content/images/trend_staly_26.png";
        }
    });
    self.TrendIco = ko.computed(function () {
        var trend = self.Trend();
        if (trend == 1) {
            return "trend_maleje_26";
        } else if (trend == 2) {
            return "trend_rosnie_26";
        } else {
            return "trend_staly_26";
        }
    });
    self.TrendHint = ko.computed(function () {
        var trend = self.Trend();
        if (trend == 1) {
            return translations.GoesDown;
        } else if (trend == 2) {
            return translations.GoesUp;
        } else {
            return translations.DoNotChange;
        }
    });

    self.initByModel = function (serverModel, typeDictionary) {
        if (serverModel) {

            self.Id(serverModel.Id);
            self.TypeId(serverModel.TypeId);
            //self.Value(serverModel.Value);
            self.Date(serverModel.Date);
            if (typeDictionary) {
                self.Type(typeDictionary["_" + serverModel.TypeId]);
            }
            self.InitValue(serverModel.Value);
            //self.SuggestedValue(serverModel.SuggestedValue);
            self.InitSuggestedValue(serverModel.SuggestedValue);
            self.Trend(serverModel.Trend);

            self.TrainingTypeId(serverModel.TrainingTypeId);
            self.AdditionalParam(serverModel.AdditionalParam);
            self.DefaultAddParam(serverModel.DefaultAddParam);

            if (!self.Parent || serverModel.Date == self.Parent.Parent.CurrentDate()) {
                self.Value(serverModel.Value);
                self.PrevValue(serverModel.PrevValue);
            } else {
                self.PrevValue(serverModel.Value != '' ? serverModel.Value : serverModel.PrevValue);
            }
            self.ValueStr(serverModel.ValueStr);
        }
    }

    self.openHistory = function () {
        if (!self.Parent) return false;

        $.ajax({
            type: "POST",
            url: ContextPath + "Diary/GetDiaryHistory",
            data: { userId: self.Parent.UserId, typeId: self.TypeId(), trainingTypeId: self.TrainingTypeId(), additionalParam: self.AdditionalParam() }
        })
         .done(function (result) {
             self.Parent.Parent.UserDiaryHistory().initByModel(result);
             self.Parent.Parent.UserDiaryHistory().Type(self.Type());
             $('#diaryHistoryModal').modal("show");
         });
    }

    self.openChart = function () {
        return false;
        if (!self.Parent) return false;

        self.Parent.Parent.CurrentChartSet(null);
        self.Parent.Parent.LoadChartData([self.Type()]);
    }

    self.HasUnit = ko.computed(function () {
        return self.Type() && self.Type().HasUnit();
    });

    self.ParamsVisibility = ko.observable(new ParamsVisibility(''));
    self.TrainingType = ko.computed(function () {
        if (self.Parent && self.Parent.Parent && self.Parent.Parent.TrainingTypeManager) {
            var type = self.Parent.Parent.TrainingTypeManager.GetTypeById(self.TrainingTypeId());
            self.ParamsVisibility(GetParamsVisibility(type));

            return type;
        } else {
            return null;
        }
    });

    self.ReadSpeed = function (value) {
        var pv = self.ParamsVisibility();
        if (pv.ShowPace) {
            return calculatePaceString(value, pv.PaceParams.unit, pv.PaceParams.factor, false, '');
        } else {
            return value;
        }
    }
    self.WriteSpeed = function (value) {
        var pv = self.ParamsVisibility();
        if (pv.ShowPace) {
            return calculateSpeedFromPace(value, pv.PaceParams.unit, pv.PaceParams.factor);
        } else {
            return value;
        }
    }

    self.ValueToSpeed = ko.pureComputed({
        read: function () {
            return self.ReadSpeed(self.Value());
        },
        write: function (value) {
            self.Value(self.WriteSpeed(value));
        },
        owner: self
    });

    self.InitValueToSpeed = ko.pureComputed({
        read: function () {
            return self.ReadSpeed(self.InitValue());
        },
        write: function (value) {
            self.InitValue(self.WriteSpeed(value));
        },
        owner: self
    });

    self.PrevValueToSpeed = ko.pureComputed({
        read: function () {
            return self.ReadSpeed(self.PrevValue());
        },
        write: function (value) {
            self.Value(self.PrevValue(value));
        },
        owner: self
    });

    self.Clone = function (trainingTypeId, additionalParam) {
        var result = new DiaryEntryModel(self.Parent);

        result.TypeId(self.TypeId());
        result.Value('');
        result.PrevValue(null);
        result.InitValue(self.InitValue());
        result.Date(self.Date());
        result.Type(self.Type());
        //result.SuggestedValue(self.SuggestedValue());
        //result.InitSuggestedValue(self.InitSuggestedValue());
        result.Trend(4); //none
        result.TrainingTypeId(trainingTypeId != null ? trainingTypeId : self.TrainingTypeId());
        result.AdditionalParam(additionalParam != null ? additionalParam : self.AdditionalParam());
        result.DefaultAddParam(self.DefaultAddParam());

        self.Parent.Entries.push(result);

        return result;
    }
}

function UserDiaryHistory() {
    var self = this;
    self.Entries = ko.observableArray([]);
    self.Type = ko.observable("");

    self.initByModel = function (serverModel) {
        if (serverModel) {

            self.Entries.removeAll();
            for (var i = 0, len = serverModel.Entries.length; i < len; i++) {
                var ds = new DiaryHistoryEntry(serverModel.Entries[i]);
                self.Entries.push(ds);
            }
        }
    }
}
function DiaryHistoryEntry(serverModel) {
    var self = this;

    self.Date = serverModel.Date;
    self.EntryId = serverModel.EntryId;
    self.Value = serverModel.Value;
    self.Suggested = serverModel.Suggested;
}


function SetDiaryProperties(FindEntry, self) {
    self.Weather = ko.computed(function () {
        return FindEntry(32);
    });
    self.Altitude = ko.computed(function () {
        return FindEntry(34);
    });
    self.Temperature = ko.computed(function () {
        return FindEntry(31);
    });
    self.Wind = ko.computed(function () {
        return FindEntry(33);
    });
    self.AirPressure = ko.computed(function () {
        return FindEntry(38);
    });
    self.Humidity = ko.computed(function () {
        return FindEntry(39);
    });


    self.SleepIntense = ko.observable(null);
    self.Sleep = ko.computed(function () {
        var val = FindEntry(27);
        self.SleepIntense(new IntensivityModel(val ? val.Value : null, "IntensivitySleep_", "Dream"));
        return val;
    });

    self.TiredIntense = ko.observable(null);
    self.Tired = ko.computed(function () {
        var val = FindEntry(28);
        self.TiredIntense(new IntensivityModel(val ? val.Value : null, "IntensivityTired_", "Tired"));
        return val;
    });

    self.StressIntense = ko.observable(null);
    self.Stress = ko.computed(function () {
        var val = FindEntry(29);
        self.StressIntense(new IntensivityModel(val ? val.Value : null, "IntensivityStress_", "Stress"));
        return val;
    });

    self.PainIntense = ko.observable(null);
    self.Pain = ko.computed(function () {
        var val = FindEntry(30);
        self.PainIntense(new IntensivityModel(val ? val.Value : null, "IntensivityPain_", "Pains"));
        return val;
    });
    self.ComfortIntense = ko.observable(null);
    self.Comfort = ko.computed(function () {
        var val = FindEntry(40);
        self.ComfortIntense(new IntensivityModel(val ? val.Value : null, "IntensivityComfort_", "Comforts"));
        return val;
    });


    self.RestHr = ko.computed(function () {
        return FindEntry(26);
    });

    self.BodyHeight = ko.computed(function () {
        return FindEntry(1);
    });
    self.BodyMass = ko.computed(function () {
        return FindEntry(2);
    });
    self.BodyWaterPrec = ko.computed(function () {
        return FindEntry(5);
    });
    self.BodyFatPrec = ko.computed(function () {
        return FindEntry(6);
    });
    self.BodyMeatPres = ko.computed(function () {
        return FindEntry(7);
    });

    self.Notes = ko.computed(function () {
        return FindEntry(37);
    });
    self.Renewal = ko.computed(function () {
        return FindEntry(36);
    });
    self.InactiveDay = ko.computed(function () {
        return FindEntry(35);
    });
}

function SlideUserDiaryModel() {

    var self = this;

    self.UserDiaryModel = ko.observable(null);
    self.Loaded = ko.observable(false);

    self.Init = function (userId, initDate) {
        var model = self.UserDiaryModel();
        if (model == null) {
            model = new UserDiaryModel(0, false);
            model.BodyParams(false);
            self.UserDiaryModel(model);
            self.Loaded(true);
        }

        if (model.CurrentValues() && model.CurrentValues().Entries()) {
            model.CurrentValues().Entries.removeAll();
        }
        model.Load(userId, initDate, true);
    }

    self.CallendarDayToUpdate = null;
    self.Open = function (userId, initDate, callendarDayToUpdate) {
        $('body').css('cursor', 'wait');

        if (!getTopSlide().find('#topSlideDiaryContainer').length) {
            $.ajax({
                type: "POST",
                url: ContextPath + "Diary/SlideDiary"
            })
             .done(function (result) {
                 setTopSlideContent(result);
                 setTimeout(function () {
                     topSlideapplayBinding();
                     self.CoreOpen(userId, initDate, callendarDayToUpdate);
                     if (callendarDayToUpdate && callendarDayToUpdate.hideAdd)  callendarDayToUpdate.hideAdd();
                 }, 100);
             });
        } else {
            self.CoreOpen(userId, initDate, callendarDayToUpdate);
        }
    }

    self.CoreOpen = function (userId, initDate, callendarDayToUpdate) {
        self.Init(userId, initDate);
        self.CallendarDayToUpdate = callendarDayToUpdate;
        showTopSlide();
        $('body').css('cursor', 'default');
        JsRequestLog("diary.js", "SlideUserDiaryModel.CoreOpen", "");
    }

    self.Date = ko.computed(function () {
        if (!self.Loaded()) return '';
        return self.UserDiaryModel().CurrentDate();
    });

    self.LongHtmlDate = ko.computed(function () {
        if (!self.Loaded()) return '';
        return dateToLongHtml(self.UserDiaryModel().CurrentDate());
    });
    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    }

    self.EntriesAvailable = ko.computed(function () {
        if (!self.Loaded()) return false;
        var cv = self.UserDiaryModel().CurrentValues();
        if (cv == null || cv.LoadingState() || !cv.Entries().length) return false;
        return true;
    });

    self.FindEntry = function (typeId) {
        if (!self.Loaded()) return null;
        var cv = self.UserDiaryModel().CurrentValues();
        if (cv == null || !cv.Entries().length) return null;
        var arr = [];
        arr.push(typeId);
        var res = cv.filterByTypeIds(arr);
        if (!res || !res.length) return null;
        return res[0];
    }

    SetDiaryProperties(self.FindEntry, self);

    self.PauseDays = ko.pureComputed({
        read: function () {
            if (!self.Loaded()) return 0;
            var cv = self.UserDiaryModel().CurrentValues();
            if (cv == null) return 0;
            return cv.PauseDays();
        },
        write: function (value) {
            if (!self.Loaded()) return;
            var cv = self.UserDiaryModel().CurrentValues();
            if (cv == null) return ;
            cv.PauseDays(value);
        }
    });

    self.BodyParams = ko.computed(function () {
        if (!self.Loaded()) return [];
        var cv = self.UserDiaryModel().CurrentValues();
        if (cv == null) return [];
        return cv.filterByTypeIds(bodyParamsEntriesTypes);
    });

    self.HasMultiVal = function (value, diaryEntry) {
        var entry = diaryEntry;
        if (!entry) return false;
        var val = entry.Value();
        return val.indexOf(';' + value + ';') > -1;
    }
    self.ToggleMultiVal = function (value, diaryEntry) {
        var entry = diaryEntry;
        if (!entry) return false;
        var val = entry.Value();
        var newVal = ';' + value + ';';
        if (val.indexOf(newVal) > -1) {
            entry.Value(val.replace(newVal, ''));
        } else {
            entry.Value(entry.Value() + newVal);
        }
    }

    self.HasRenewal = function (value) {
        return self.HasMultiVal(value, self.Renewal());
    }
    self.ToggleRenewal = function (value) {
        self.ToggleMultiVal(value, self.Renewal());
    }

    /*
    self.HasInactiveDay = function (value) {
        return self.HasMultiVal(value, self.InactiveDay());
    }
    self.ToggleInactiveDay = function (value) {
        self.ToggleMultiVal(value, self.InactiveDay());
    }
    */
    self.ToggleTotalInactiveDay = function (value) {
        var entry = self.InactiveDay();
        if (!entry) return;
        if (entry.Value()) {
            entry.Value('');
        } else {
            entry.Value('1');
        }
    }

    self.Save = function (data, event) {

        var model = self.UserDiaryModel();
        if (model) {
            model.SaveSet(data, event, self.OnSaveUpdate);
        } else {
            self.Close();
        }
    }
    self.Close = function () {
        hideTopSlide();
    }

    self.OnSaveUpdate = function (userDiaryModel) {

            if (self.CallendarDayToUpdate) {
                if (self.PauseDays() > 1) {
                    self.CallendarDayToUpdate.Callendar.Refresh();
                } else {
                var date = self.Date();
                if (date != self.CallendarDayToUpdate.Date()) {
                    self.CallendarDayToUpdate = _.find(self.CallendarDayToUpdate.Week.Days(), function (d) { return d.Date() == date; });
                }

                if (self.CallendarDayToUpdate) {
                    var entries = userDiaryModel.CurrentValues().Entries();
                    var calDiaries = self.CallendarDayToUpdate.Dairies;
                    if (calDiaries) {
                        calDiaries.removeAll();

                        for (var i = 0, len = entries.length; i < len; i++) {
                            var entry = entries[i];
                            calDiaries.push(entry);
                        }
                    }


                    self.CallendarDayToUpdate.Pause(self.InactiveDay().Value());
                    self.CallendarDayToUpdate.PauseType(self.InactiveDay().Value());
                    self.CallendarDayToUpdate.WeatherType(self.Weather().Value());

                    var renewal = self.Renewal().Value();
                    var renewType = '';
                    if (renewal) {
                        var renewals = renewal.split(';');
                        for (var i = 0, len = renewals.length; i < len; i++) {
                            var re = renewals[i];
                            if (re) {
                                if (renewType) renewType += ', ';
                                renewType += translations['diaryRenewal_' + re];
                            }
                        }
                    }
                    self.CallendarDayToUpdate.RenewType(renewType);
                }
            }
        }

        var udm = GeneralModel.UserDiaryModel;
        if (udm != 'undefined' && udm) {
            udm.Reload();
        }        

        self.Close();
    }
}

var slideUserDiaryModel = new SlideUserDiaryModel();
GeneralModel.addProperty("SlideUserDiaryModel", slideUserDiaryModel);


;
function PeriodKnowladgeBaseModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Index = ko.observable(0);
    self.ContentTransAble = new translateAble.Text();
    self.NameTransAble = new translateAble.Text();
    self.Visible = ko.observable(false);
    self.Type = ko.observable('HtmlText');

    self.init = function () {
        self.Id(0);
        self.Index(0);
        self.ContentTransAble.init();
        self.NameTransAble.init();
        self.Visible(false);
        self.Type('HtmlText');
    };

    self.BaseObj = null;

    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }

    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.Index(serverModel.Index);
            self.ContentTransAble.initByModel(serverModel.Content);
            self.NameTransAble.initByModel(serverModel.Name);
            self.Visible(serverModel.Visible);
            self.Type(serverModel.Type);
        }
    };

    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            Index: self.Index(),
            Content: self.ContentTransAble.getClearData(),
            Name: self.NameTransAble.getClearData(),
            Visible: self.Visible(),
            Type: self.Type(),
        }
        return data;
    };

    self.Up = function () {
        var array = self.Parent.Entries();
        var i = self.Parent.Entries.indexOf(self);
        if (i > 0) {
            self.Parent.Entries.splice(i - 1, 2, array[i], array[i - 1]);
            var tmp = array[i].Index();
            array[i].Index(array[i - 1].Index());
            array[i - 1].Index(tmp);
        }
    }

    self.Down = function () {
        var array = self.Parent.Entries();
        var i = self.Parent.Entries.indexOf(self);
        if (i + 1 < array.length) {
            self.Parent.Entries.splice(i, 2, array[i + 1], array[i]);
            var tmp = array[i].Index();
            array[i].Index(array[i + 1].Index());
            array[i + 1].Index(tmp);
        }
    }

    self.Delete = function () {
        modalAskModel.Show(
           translations.DeleteKnowladgeBase,
           translations.DeleteKnowladgeBaseMsg,
           function () { self.coreDelete(); },
           translations.Delete);
    };

    self.coreDelete = function () {
        self.Parent.Entries.remove(self);
        if (self.Parent.Selected() == self) {
            self.Parent.Selected(null);
        }
    };

    self.ActivityLabel = ko.computed(function () {
        return self.Visible() ? translations.StateVisible : translations.StateInvisible;
    });

    self.TypeText = ko.computed(function () {
        return translations[self.Type()];
    });
}


function KnowladgeBaseModel() {
    var self = this;

    self.Entries = ko.observableArray([]);
    self.Selected = ko.observable(null);
    self.PeriodId = ko.observable(0);

    self.Initialize = function (periodId) {
        if (!periodId) {
            periodId = GeneralModel.PeriodsModel.SelectdPeriod().Id();
        }
        self.PeriodId(periodId);
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/GetKnowladgebase",
            data: { periodId: periodId }
        })
        .done(function (result) {
            self.initializeByModel(result);
            $('#tinyPreparation').each(function () {
                if (this.renderTinymce != undefined) this.renderTinymce();
            });
        });
    };
   
    self.initializeByModel = function (result, selId, selIndex) {
        if (result.Entries) {
            self.Entries.removeAll();
            for (var i = 0; i < result.Entries.length; i++) {
                var entry = new PeriodKnowladgeBaseModel(self);
                entry.initByModel(result.Entries[i]);
                self.Entries.push(entry);
                if ((!selId && !selIndex && i == 0) || (selId && selId === entry.Id()) || (selIndex && selIndex == entry.Index())) {
                    self.Selected(entry);
                }
            }
        }
    };

    self.AddEntry = function () {        
        var entry = new PeriodKnowladgeBaseModel(self);        
        var len = self.Entries().length;
        if (len > 0) {
            entry.Index(self.Entries()[len - 1].Index() + 1);
        }
        self.Entries.push(entry);
        self.Selected(entry);
    };

    self.RemoveEntry = function () {
        var entry = self.Selected();
        self.Entries.remove(entry);
        self.Selected(self.Entries[self.Entries.lenght - 1]);
    };

    self.Save = function () {

        /*if (!self.CheckValid()) {
            return;
        }*/

        var configuration = {};        
        configuration.Entries = [];
        for (var i = 0, len = self.Entries().length; i < len; i++) {
            configuration.Entries.push(self.Entries()[i].getClearData());
        }
        configuration.PeriodId = self.PeriodId();
       
        if (self.Selected()) {
            var selId = self.Selected().Id();
            var selIndex = self.Selected().Index();
        }
        
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "Period/SaveKnowladgebase",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(configuration)
        }).done(function (result) {
            $('body').css('cursor', 'default');
            self.initializeByModel(result, selId, selIndex);

            ShowSavedAlert();
        });
    };

    self.Cancel = function () {
        self.Initialize();
    };

}

var knowladgeBaseModel = new KnowladgeBaseModel();
GeneralModel.addProperty("KnowladgeBaseModel", knowladgeBaseModel);;function ConnectionStringModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Server = ko.observable('');
    self.Database = ko.observable('');
    self.UID = ko.observable('');
    self.Password = ko.observable('');

    self.copy = '';

    self.initByString = function (connectionString) {
        if (typeof connectionString !== 'string') {
            return;
        }
        var pairs = connectionString.split(";");
        for (var i = 0, len = pairs.length; i < len; i++) {
            var pair = pairs[i].trim();
            var keyValue = pair.split("=");
            switch (keyValue[0].toLowerCase()) {
                case "server":
                    self.Server(keyValue[1]);
                    break;
                case "database":
                    self.Database(keyValue[1]);
                    break;
                case "uid":
                    self.UID(keyValue[1]);
                    break;
                case "password":
                    self.Password(keyValue[1]);
                    break;
            }
        }
    };

    self.getConnectionString = ko.computed(function () {
        return "server=" + self.Server() + "; "
             + "database=" + self.Database() + "; "
             + "UID=" + self.UID() + "; "
             + "password=" + self.Password();
    }, self);

    self.createCopy = function () {
        self.copy = self.getConnectionString();
    };

    self.cancel = function () {
        self.initByString(self.copy);
    };

}


function CategoryInfo(parent) {
    var self = this;
    self.Parent = parent;

    self.Name = ko.observable('');
    self.Male = ko.observable(false);
    self.Pro = ko.observable(false);

    self.init = function () {
        self.Name('');
        self.Male(false);
        self.Pro(false);
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Name(serverModel.Name);
            self.Male(serverModel.Male);
            self.Pro(serverModel.Pro);
        }
    };

    self.getClearData = function () {
        var data = {
            Name: self.Name(),
            Male: self.Male(),
            Pro: self.Pro(),
        }
        return data;
    };
}


function LiveResultMattTypeModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.Name = ko.observable(0);

    self.init = function () {
        self.Id(0);
        self.Name(0);
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.Name(serverModel.Name);
        }
    };

    self.getClearData = function () {
        var data = {
            Id: self.Id(),
            Name: self.Name(),
        }
        return data;
    };

}

function LiveResultRunModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Distance = ko.observable(0);
    self.Id = ko.observable(0);
    self.PeriodId = ko.observable(0);
    self.Categories = ko.observableArray([]);
    self.Description = ko.observable('');
    self.EventName = ko.observable('');
    self.ExternalEventId = ko.observable('');
    self.ExternalRunId = ko.observable('');
    self.PlannedEndTime = ko.observable('');
    self.PlannedStartTime = ko.observable('');
    self.ReadsConnectionString = ko.observable(new ConnectionStringModel(self));
    self.RunName = ko.observable('');
    self.ExternalApiLogin = ko.observable('');
    self.ExternalApiPassword = ko.observable('');
    self.ReadsStartTime = ko.observable('');
    self.PeriodName = ko.observable('');
    self.EnableReads = ko.observable(false);
    self.StringIdentifire = ko.observable('');
    self.StringIdentifireIsFree = ko.observable(true);
    self.StringIdentifireDB = ko.observable('');

    self.IdentifireChange = false;
    self.StringIdentifire.subscribe(function (newValue) {
        if (newValue && !self.IdentifireChange) {
            self.IdentifireChange = true;
            var ecode = encodeURI(decodeURI(newValue));
            if (ecode != newValue) {
                self.StringIdentifire(ecode);
            }
            self.CheckStringIdentifire();
            self.IdentifireChange = false;
        }
    });

    self.init = function () {
        self.Distance(0);
        self.Id(0);
        self.PeriodId(0);
        self.Description('');
        self.EventName('');
        self.ExternalEventId('');
        self.ExternalRunId('');
        self.PlannedEndTime('');
        self.PlannedStartTime('');
        self.ReadsConnectionString(new ConnectionStringModel(self));
        self.RunName('');
        self.ExternalApiLogin('');
        self.ExternalApiPassword('');
        self.EnableReads(false);
        self.ReadsStartTime('');
        self.StringIdentifire('');
        self.StringIdentifireDB('');
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Distance(serverModel.Distance);
            self.Id(serverModel.Id);
            self.PeriodId(serverModel.PeriodId);
            koArrayCopyComplex(serverModel.Categories, self.Categories, false, function (e) { var p = new CategoryInfo(self); p.initByModel(e); return p; });
            self.Description(serverModel.Description);
            self.EventName(serverModel.EventName);
            self.ExternalEventId(serverModel.ExternalEventId);
            self.ExternalRunId(serverModel.ExternalRunId);
            self.PlannedEndTime(serverModel.PlannedEndTime);
            self.PlannedStartTime(serverModel.PlannedStartTime);
            self.ReadsConnectionString().initByString(serverModel.ReadsConnectionString);
            self.RunName(serverModel.RunName);
            self.ExternalApiLogin(serverModel.ExternalApiLogin);
            self.ExternalApiPassword(serverModel.ExternalApiPassword);
            self.PeriodName(serverModel.PeriodName);
            self.EnableReads(serverModel.EnableReads);
            self.ReadsStartTime(serverModel.ReadsStartTime);
            self.IdentifireChange = true;
            self.StringIdentifire(serverModel.StringIdentifire);
            self.IdentifireChange = false;
            self.StringIdentifireDB(serverModel.StringIdentifire);
        }
    };



    self.getClearData = function () {
        var data = {
            Distance: self.Distance(),
            Id: self.Id(),
            PeriodId: self.PeriodId(),
            Categories: GetDataFromKoArray(self.Categories),
            Description: self.Description(),
            EventName: self.EventName(),
            ExternalEventId: self.ExternalEventId(),
            ExternalRunId: self.ExternalRunId(),
            PlannedEndTime: self.PlannedEndTime(),
            PlannedStartTime: self.PlannedStartTime(),
            ReadsConnectionString: self.ReadsConnectionString().getConnectionString(),
            RunName: self.RunName(),
            ExternalApiLogin: self.ExternalApiLogin(),
            ExternalApiPassword: self.ExternalApiPassword(),
            EnableReads: self.EnableReads(),
            ReadsStartTime: self.ReadsStartTime(),
            StringIdentifire: self.StringIdentifire()
        }
        return data;
    };

    self.openDataCtrl = function (data, event) {
        OpenDataCtrlFromKo(data, event);
    };

    self.CheckStringIdentifire = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/IsPeriodIdentifireFree",
            data: { periodId: self.PeriodId(), identifire: self.StringIdentifire() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.StringIdentifireIsFree(result);
        });
    };

    self.AddNewCategory = function () {
        self.Categories.push(new CategoryInfo(self));
    }
}

function LiveResultCheckPointModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Distance = ko.observable(0);
    self.Id = ko.observable(0);
    self.ExternalId = ko.observable('');
    self.Index = ko.observable(0);
    self.LiveResultMattTypeId = ko.observable(0);
    self.LiveResultMattType = ko.pureComputed({
        read: function () {
            var types = self.Parent.LiveResultMattTypes();
            for (var i = 0; i < types.length; i++) {
                if (types[i].Id() == self.LiveResultMattTypeId()) {
                    return types[i];
                }
            }
            return null;
        },
        write: function (value) {
            if (value) {
                self.LiveResultMattTypeId(value.Id());
            }
        },
        owner: self
    });
    self.LiveResultRunId = ko.observable(0);
    self.TrainingTypeId = ko.observable(0);
    self.TrainingType = ko.pureComputed({
        read: function () {
            if (self.Parent.TrainingTypesModel() != null) {
                var types = self.Parent.TrainingTypesModel().List;
                for (var i = 0; i < types.length; i++) {
                    if (types[i].Id == self.TrainingTypeId()) {
                        return types[i];
                    }
                }
            }
            return null;
        },
        write: function (value) {
            if (value) {
                self.TrainingTypeId(value.Id);
            }
        },
        owner: self
    });

    self.HardwareId = ko.observable('');
    self.Name = ko.observable('');
    self.MinimalFromPrevious = ko.observable('00:00:00');

    self.CanCalculate = ko.computed(function () {
        var ttid = self.TrainingTypeId();
        return self.Distance() && ttid >= 1 && ttid <= 3;
    }, self);

    self.ShouldSetMin = ko.computed(function () {
        var min = self.MinimalFromPrevious();
        if (min != '00:00:00') return false;
        var id = self.HardwareId();
        var selfIndex = self.Parent.LiveResultCheckPoints.indexOf(self);
        if (selfIndex == 0) return false;
        var prev = self.Parent.LiveResultCheckPoints()[selfIndex - 1];
        if (!prev) return false;
        var prevId = prev.HardwareId();
        if (id != prevId) return false;
        return true;
    }, self);

    self.init = function () {
        self.Distance(0);
        self.Id(0);
        self.Index(0);
        self.ExternalId('');
        self.LiveResultMattTypeId(0);
        self.LiveResultRunId(0);
        self.TrainingTypeId(0);
        self.HardwareId('');
        self.Name('');
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Distance(serverModel.Distance);
            self.Id(serverModel.Id);
            self.ExternalId(serverModel.ExternalId);
            self.Index(serverModel.Index);
            self.LiveResultMattTypeId(serverModel.LiveResultMattTypeId);
            self.LiveResultRunId(serverModel.LiveResultRunId);
            self.TrainingTypeId(serverModel.TrainingTypeId);
            self.HardwareId(serverModel.HardwareId);
            self.Name(serverModel.Name);
            self.MinimalFromPrevious(serverModel.MinimalFromPrevious);
        }
    };

    self.getClearData = function (copy) {
        var data = {
            Distance: self.Distance(),
            Id: copy ? 0 : self.Id(),
            Index: self.Index(),
            ExternalId: self.ExternalId(),
            LiveResultMattTypeId: self.LiveResultMattTypeId(),
            LiveResultRunId: self.LiveResultRunId(),
            TrainingTypeId: self.TrainingTypeId(),
            HardwareId: self.HardwareId(),
            Name: self.Name(),
            MinimalFromPrevious: self.MinimalFromPrevious()
        }
        return data;
    };

    self.canDelete = ko.computed(function () {
        var result = true;
        var zones = self.Parent.LiveResultStartZones();
        for (var i = 0, len = zones.length; i < len && result; i++) {
            result &= zones[i].LiveResultCheckPoint() != self;
        }
        return result;
    }, self);

    self.Delete = function () {
        if (self.canDelete()) {
            modalAskModel.Show(
               translations.DeleteCheckpoint,
               translations.DeleteCheckpointMsg,
               function () { self.coreDelete(); },
               translations.Delete);
        } else {
            var modal = $('#emptyMsgModal');
            modal.find('#emptyMsgModalLabel').html(translations.CannotDelete);
            modal.find('.askMsg').html(translations.CannotDeleteCheckpoint);
            modal.modal("show");
        }
    };

    self.coreDelete = function () {
        self.Parent.LiveResultCheckPoints.remove(self);
        if (self.Parent.SelectedCheckPoint() == self) {
            self.Parent.SelectedCheckPoint(null);
        }
    };

    self.Up = function () {
        var array = self.Parent.LiveResultCheckPoints();
        var i = self.Parent.LiveResultCheckPoints.indexOf(self);
        if (i > 0) {
            self.Parent.LiveResultCheckPoints.splice(i - 1, 2, array[i], array[i - 1]);
            var tmp = array[i].Index();
            array[i].Index(array[i - 1].Index());
            array[i - 1].Index(tmp);
        }
    }

    self.Down = function () {
        var array = self.Parent.LiveResultCheckPoints();
        var i = self.Parent.LiveResultCheckPoints.indexOf(self);
        if (i + 1 < array.length) {
            self.Parent.LiveResultCheckPoints.splice(i, 2, array[i + 1], array[i]);
            var tmp = array[i].Index();
            array[i].Index(array[i + 1].Index());
            array[i + 1].Index(tmp);
        }
    }

    self.CalculateMinFromPrevious = function () {
        if (!self.CanCalculate()) return;

        var d = self.Distance(); //w metrach

        var predDist = 0;

        var cps = self.Parent.LiveResultCheckPoints();
        var prev = null;
        for (var i = 0, len = cps.length; i < len && prev != self; i++) {
            if (prev) predDist = prev.Distance();
            prev = cps[i];
        }

        d = d - predDist;

        var speed = 0; // m/s
        switch (self.TrainingTypeId()) {
            case 1: speed = 16.7; break; //rower: 60km/h
            case 2: speed = 7; break; //bieg: 25km/h
            case 3: speed = 2.18; break; //plywanie: 46s/100m
        }
        if (speed == 0) return;
        var secs = d / speed;

        self.MinimalFromPrevious(MsToString(secs * 1000, false));
    }
}

function LiveResultStartZoneModel(parent) {
    var self = this;
    self.Parent = parent;

    self.Id = ko.observable(0);
    self.ExternalId = ko.observable('');
    self.LiveResultCheckPointId = ko.observable(0);
    self.LiveResultCheckPoint = ko.pureComputed({
        read: function () {
            var checkpoints = self.Parent.LiveResultCheckPoints();
            for (var i = 0; i < checkpoints.length; i++) {
                if (checkpoints[i].Id() == self.LiveResultCheckPointId()) {
                    return checkpoints[i];
                }
            }
            return null;
        },
        write: function (value) {
            if (value) {
                self.LiveResultCheckPointId(value.Id());
            }
        },
        owner: self
    });

    self.LiveResultRunId = ko.observable(0);
    self.GateCloseTime = ko.observable('');
    self.GateOpenTime = ko.observable('');

    self.Name = ko.observable('');
    self.StartTime = ko.observable('');

    self.NumberFrom = ko.observable('');
    self.NumbersTo = ko.observable('');

    self.AssignedParticipantsCount = ko.observable(0);

    self.StartNumbers = ko.computed(function () {
        return self.NumberFrom() + '-' + self.NumbersTo();
    }, self);

    self.init = function () {
        self.Id(0);
        self.ExternalId('');
        self.LiveResultCheckPointId(0);
        self.LiveResultRunId(0);
        self.GateCloseTime('');
        self.GateOpenTime('');
        self.Name('');
        self.StartTime('');
        self.NumberFrom('');
        self.NumbersTo('');
    };

    self.BaseObj = null;
    self.CancelChanges = function () {
        if (self.BaseObj) {
            self.initByModel(self.BaseObj);
        }
    }


    self.initByModel = function (serverModel) {
        if (serverModel) {
            self.BaseObj = serverModel;
            self.Id(serverModel.Id);
            self.ExternalId(serverModel.ExternalId);
            self.LiveResultCheckPointId(serverModel.LiveResultCheckPointId);
            self.LiveResultRunId(serverModel.LiveResultRunId);
            self.GateCloseTime(serverModel.GateCloseTime);
            self.GateOpenTime(serverModel.GateOpenTime);
            self.Name(serverModel.Name);
            self.StartTime(serverModel.StartTime);
            self.NumberFrom(serverModel.NumberFrom);
            self.NumbersTo(serverModel.NumbersTo);
            self.AssignedParticipantsCount(serverModel.AssignedParticipantsCount);
        }
    };

    self.GateTimeValidationMessage = ko.computed(function () {
        if (self.GateOpenTime() > self.GateCloseTime()) {
            return translations.GateTimeInvalidMessage;
        }
        return "";
    }, self);

    self.getClearData = function (copy) {
        var data = {
            Id: copy ? 0 : self.Id(),
            ExternalId: self.ExternalId(),
            LiveResultCheckPointId: self.LiveResultCheckPointId(),
            LiveResultRunId: self.LiveResultRunId(),
            GateCloseTime: self.GateCloseTime(),
            GateOpenTime: self.GateOpenTime(),
            Name: self.Name(),
            StartTime: self.StartTime(),
            NumberFrom: self.NumberFrom(),
            NumbersTo: self.NumbersTo(),
        }
        return data;
    };

    self.Delete = function () {
        modalAskModel.Show(
           translations.DeleteZone,
           translations.DeleteZoneMsg,
           function () { self.coreDelete(); },
           translations.Delete);
    };

    self.coreDelete = function () {
        self.Parent.LiveResultStartZones.remove(self);
        if (self.Parent.SelectedStartZone() == self) {
            self.Parent.SelectedStartZone(null);
        }
    };

}

function ExternalConfigurationModel() {
    var self = this;

    self.AvailableEvents = ko.observableArray([]);
    self.AvailableRuns = ko.observableArray([]);

    self.IsConnected = ko.observable(false);

    self.SelectedEvent = ko.observable(null);
    self.SelectedEvent.subscribe(function (newValue) {
        self.LoadExternalRuns();
    });
    self.SelectedRun = ko.observable(null);

    self.CanFill = ko.computed(function () {
        return self.SelectedEvent() != null && self.SelectedRun() != null;
    }, self);

    self.LiveResultRun = ko.observable(new LiveResultRunModel(self));
    self.LiveResultCheckPoints = ko.observableArray([]);
    self.LiveResultStartZones = ko.observableArray([]);
    self.LiveResultMattTypes = ko.observableArray([]);
    self.TrainingTypesModel = ko.observable(null);

    self.SelectedCheckPoint = ko.observable(null);
    self.SelectedStartZone = ko.observable(null);

    self.LiveResultsUrl = ko.computed(function () {
        if (!self.LiveResultRun().Id()) return '';
        if (self.LiveResultRun().StringIdentifireDB()) {
            return ContextPath + "Live/" + self.LiveResultRun().StringIdentifireDB();
        } else {
            return ContextPath + "LiveResult/Index?periodId=" + self.LiveResultRun().PeriodId();
        }
    }, self);
    self.ExternalWaveAdminUrl = ko.computed(function () {
        if (!self.LiveResultRun().Id()) return '';
        return ContextPath + "LiveResult/ExternalAdminWavesEditor?periodId=" + self.LiveResultRun().PeriodId()
    }, self);
    self.InvalidReadsUrl = ko.computed(function () {
        if (!self.LiveResultRun().Id()) return '';
        return ContextPath + "LiveResult/InvalidReads?periodId=" + self.LiveResultRun().PeriodId()
    }, self);

    self.AddCheckPoint = function () {
        var checkpoint = new LiveResultCheckPointModel(self);
        checkpoint.LiveResultRunId(self.LiveResultRun().Id());
        checkpoint.TrainingType(self.TrainingTypesModel().List[0]);
        var len = self.LiveResultCheckPoints().length;
        if (len > 0) {
            checkpoint.Index(self.LiveResultCheckPoints()[len - 1].Index() + 1);
        }
        self.LiveResultCheckPoints.push(checkpoint);
        self.SelectedCheckPoint(checkpoint);
    };

    self.AddStartZone = function () {
        if (self.LiveResultCheckPoints().length == 0) {
            var modal = $('#emptyMsgModal');
            modal.find('#emptyMsgModalLabel').html(translations.CannotAdd);
            modal.find('.askMsg').html(translations.CannotAddStartPoint);
            modal.modal("show");
        } else {
            var zone = new LiveResultStartZoneModel(self);
            zone.LiveResultRunId(self.LiveResultRun().Id());
            self.LiveResultStartZones.push(zone);
            self.SelectedStartZone(zone);
        }
    };

    self.SelectableCheckPoints = ko.computed(function () {
        var result = [];
        var checkpoints = self.LiveResultCheckPoints();
        for (var i = 0, len = checkpoints.length; i < len; i++) {
            if (checkpoints[i].Id() > 0) {
                result.push(checkpoints[i]);
            }
        }
        return result;
    }, self);

    self.CheckValid = function () {

        var msg = '';
        $.each(self.LiveResultCheckPoints(), function (index, checkpoint) {
            var mattTypeId = checkpoint.LiveResultMattTypeId();
            if (!(typeof mattTypeId == 'number' && mattTypeId > 0)) {
                msg = translations.MattTypeIsMissing + ' ' + checkpoint.Name() + '.';
            }
        });

        if (msg.length > 0) {
            var modal = $('#emptyMsgModal');
            modal.find('#emptyMsgModalLabel').html(translations.ValidationFailed);
            modal.find('.askMsg').html(msg);
            modal.modal("show");
            return false;
        }
        return true;
    }

    self.Save = function () {

        if (!self.CheckValid()) {
            return;
        }

        var configuration = {};
        configuration.LiveResultRun = self.LiveResultRun().getClearData();
        configuration.LiveResultCheckPoints = [];
        for (var i = 0, len = self.LiveResultCheckPoints().length; i < len; i++) {
            configuration.LiveResultCheckPoints.push(self.LiveResultCheckPoints()[i].getClearData());
        }
        configuration.LiveResultStartZones = [];
        for (var i = 0, len = self.LiveResultStartZones().length; i < len; i++) {
            configuration.LiveResultStartZones.push(self.LiveResultStartZones()[i].getClearData());
        }

        if (self.SelectedStartZone()) {
            var selZnId = self.SelectedStartZone().Id();
            var selZnStart = self.SelectedStartZone().StartTime();
        }
        if (self.SelectedCheckPoint()) {
            var selChkId = self.SelectedCheckPoint().Id();
            var selChkDistance = self.SelectedCheckPoint().Distance();
        }

        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SaveExternalConfiguration",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(configuration)
        }).done(function (result) {
            $('body').css('cursor', 'default');
            self.initializeByModel(result, selChkId, selChkDistance, selZnId, selZnStart);

            ShowSavedAlert();
        });
    };

    self.SaveZones = function () {
        var configuration = {};

        configuration.LiveResultRun =
            {
                Id: self.LiveResultRun().Id(),
                PeriodId: self.LiveResultRun().PeriodId(),
                ReadsStartTime: self.LiveResultRun().ReadsStartTime()
            };

        configuration.LiveResultStartZones = [];
        for (var i = 0, len = self.LiveResultStartZones().length; i < len; i++) {
            configuration.LiveResultStartZones.push(self.LiveResultStartZones()[i].getClearData());
        }

        var selId = self.SelectedStartZone().Id();
        var selStart = self.SelectedStartZone().StartTime();

        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SaveZonesConfiguration",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(configuration)
        }).done(function (result) {
            $('body').css('cursor', 'default');

            self.initializeZonesByModel(result, selId, selStart);
            ShowSavedAlert();
        });
    };

    self.Cancel = function () {
        self.Initialize();
    };

    self.LoadExternalEvents = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/GetExternalEvents",
            data: { login: self.LiveResultRun().ExternalApiLogin(), password: self.LiveResultRun().ExternalApiPassword() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.AvailableEvents(result);
            self.IsConnected(true);
        });
    };

    self.LoadExternalRuns = function () {
        if (self.SelectedEvent() && self.SelectedEvent().RunIds) {
            $('body').css('cursor', 'wait');
            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/GetExternalRuns",
                data: JSON.stringify({
                    runIds: self.SelectedEvent().RunIds,
                    login: self.LiveResultRun().ExternalApiLogin(),
                    password: self.LiveResultRun().ExternalApiPassword()
                }),
                dataType: "json",
                contentType: "application/json; charset=utf-8"
            })
            .done(function (result) {
                $('body').css('cursor', 'default');
                self.AvailableRuns(result);
            });
        }
    };

    self.Fill = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/FillExternalConfiguration",
            data: {
                periodId: self.LiveResultRun().PeriodId(),
                login: self.LiveResultRun().ExternalApiLogin(),
                password: self.LiveResultRun().ExternalApiPassword(),
                externalEventId: self.SelectedEvent().Id,
                externalRunId: self.SelectedRun().Id
            }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.initializeByModel(result);
        });
    };

    self.EditConnectionString = function () {
        self.LiveResultRun().ReadsConnectionString().createCopy();
        $('#readsConnectionStringModal').modal('show');
        $('body').css('cursor', 'default');
    };

    self.Initialize = function (periodId) {
        if (!periodId) {
            periodId = GeneralModel.PeriodsModel.SelectdPeriod().Id();
        }
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/GetExternalConfiguration",
            data: { periodId: periodId }
        })
        .done(function (result) {
            self.initializeByModel(result);
        });
    };

    self.initializeByModel = function (result, selChkId, selChkDistance, selZnId, selZnStartTime) {
        self.LiveResultRun().initByModel(result.LiveResultRun);

        self.initializeCheckpointsByModel(result, selChkId, selChkDistance);
        self.initializeZonesByModel(result, selZnId, selZnStartTime);

        if (result.LiveResultMattTypes) {
            self.LiveResultMattTypes.removeAll();
            for (var i = 0; i < result.LiveResultMattTypes.length; i++) {
                var liveResultMattType = new LiveResultMattTypeModel(self);
                liveResultMattType.initByModel(result.LiveResultMattTypes[i]);
                self.LiveResultMattTypes.push(liveResultMattType);
            }
        }
        if (result.TrainingTypesModel) {
            self.TrainingTypesModel(result.TrainingTypesModel);
        }
    };

    self.initializeCheckpointsByModel = function (result, selId, selDistance) {
        if (result.LiveResultCheckPoints) {
            self.LiveResultCheckPoints.removeAll();
            for (var i = 0; i < result.LiveResultCheckPoints.length; i++) {
                var liveResultCheckPoint = new LiveResultCheckPointModel(self);
                liveResultCheckPoint.initByModel(result.LiveResultCheckPoints[i]);
                self.LiveResultCheckPoints.push(liveResultCheckPoint);
                if ((!selId && !selDistance && i == 0) || (selId && selId === liveResultCheckPoint.Id()) || (selDistance && selDistance == liveResultCheckPoint.Distance())) {
                    self.SelectedCheckPoint(liveResultCheckPoint);
                }
            }
        }
    }

    self.initializeZonesByModel = function (result, selId, selStartTime) {
        if (result.LiveResultStartZones) {
            self.LiveResultStartZones.removeAll();
            for (var i = 0; i < result.LiveResultStartZones.length; i++) {
                var liveResultStartZone = new LiveResultStartZoneModel(self);
                liveResultStartZone.initByModel(result.LiveResultStartZones[i]);
                self.LiveResultStartZones.push(liveResultStartZone);

                if ((!selId && !selStartTime && i == 0) || (selId && selId === liveResultStartZone.Id()) || (selStartTime && selStartTime == liveResultStartZone.StartTime())) {
                    self.SelectedStartZone(liveResultStartZone);
                }
            }
        }
    }

    self.runByConfirm = function (method, action) {
        modalAskModel.Show(
            translations.Confirm,
            translations.ConfirmMsg + " " + action.toLowerCase() + "?",
            function () { method(); },
            translations.Execute);
    };

    self.GetParticipants = function () {
        self.runByConfirm(self.coreGetParticipants, translations.UploadStartingList);
    };

    self.coreGetParticipants = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/FetchParticipants",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            if (result.ErrorMsg) {
                alert(result.ErrorMsg);
            } else {
                alert("Get " + result.NrOfPersons + " comperitors");
            }
        });
    };

    self.GetParticipantsResults = function () {
        self.runByConfirm(self.coreGetParticipantsResults, translations.UploadResults);
    };

    self.coreGetParticipantsResults = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/FetchParticipantsResults",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            if (result.ErrorMsg) {
                alert(result.ErrorMsg);
            } else {
                alert("Get " + result.NrOfResults + " comperitors results");
            }
        });
    };

    self.ResetCache = function () {
        self.runByConfirm(self.coreResetCache, translations.ResetCache);
    };

    self.coreResetCache = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/ResetCache",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
        });
    };

    self.AssignZonesByStartNumber = function () {
        self.runByConfirm(self.coreAssignZonesByStartNumber, translations.AssignStartWavesByStartNumbers);
    };

    self.coreAssignZonesByStartNumber = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/AssignZonesByStartNumber",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.Initialize();
        });
    };

    self.DeleteReads = function () {
        self.runByConfirm(self.coreDeleteReads, translations.DeleteReads);
    };

    self.coreDeleteReads = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/DeleteReads",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
        });
    };


    self.RebuildCategories = function () {
        self.runByConfirm(self.coreRebuildCategories, translations.RebuildCategories);
    };

    self.coreRebuildCategories = function () {
        $('body').css('cursor', 'wait');
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/RebuildCategories",
            data: { periodId: self.LiveResultRun().PeriodId() }
        })
        .done(function (result) {
            $('body').css('cursor', 'default');
            self.Initialize();
            /*
            if (_.isArray(result)) {
                koArrayCopyComplex(serverModel.Categories, self.Categories, false, function (e) { var p = new CategoryInfo(self); p.initByModel(e); return p; });
            }
            if (!self.LiveResultRun().Categories().length) { 
                self.LiveResultRun().Categories.push(new CategoryInfo(self));
            }
            */
        });
    };

    self.CopyCheckpoints = function () {
        var cps = [];
        var pcs = self.LiveResultCheckPoints();
        for (var i = 0, len = pcs.length; i < len; i++) {
            cps.push(pcs[i].getClearData(true));
        }

        var text = JSON.stringify(cps);
        copyTextToClipboard(text);
    }
    self.PasteCheckpoints = function () {
        var success = false;
        getClipboardText(function (txt) {
            if (txt) {
                try {
                    var obj = JSON.parse(txt);

                    for (var i = 0; i < obj.length; i++) {
                        var liveResultCheckPoint = new LiveResultCheckPointModel(self);
                        liveResultCheckPoint.initByModel(obj[i]);
                        self.LiveResultCheckPoints.push(liveResultCheckPoint);
                        success = true;
                    }
                } catch (err) {
                    console.log('Oops, unable to parse pasted text');
                    success = false;
                }
            }
            if (!success) {
                setTimeout(function () { ShowEmptyMsg(translations.NoValidStartZonesInClipboard, WarnMessageType); }, 500);
            }
        });
    };

    self.CopyWaves = function () {
        var cps = [];
        var wav = self.LiveResultStartZones();
        for (var i = 0, len = wav.length; i < len; i++) {
            cps.push(wav[i].getClearData(true));
        }

        var text = JSON.stringify(cps);
        copyTextToClipboard(text);
    }
    self.PasteWaves = function () {
        var success = false;
        getClipboardText(function (txt) {
            if (txt) {
                try {
                    var obj = JSON.parse(txt);

                    for (var i = 0; i < obj.length; i++) {
                        var liveResultStartZone = new LiveResultStartZoneModel(self);
                        liveResultStartZone.initByModel(obj[i]);
                        liveResultStartZone.LiveResultCheckPointId(self.LiveResultCheckPoints()[0].Id());
                        liveResultStartZone.LiveResultRunId(self.LiveResultRun().Id());
                        self.LiveResultStartZones.push(liveResultStartZone);
                        success = true;
                    }
                } catch (err) {
                    console.log('Oops, unable to parse pasted text');
                    success = false;
                }
            }
            if (!success) {
                setTimeout(function () { ShowEmptyMsg(translations.NoValidCheckpointsInClipboard, WarnMessageType); }, 500);
            }
        });
    };
}

var externalConfigurationModel = new ExternalConfigurationModel();
GeneralModel.addProperty("ExternalConfigurationModel", externalConfigurationModel);;ko.components.register('stat-value-ctrl', {
    viewModel: function (params) {
        var self = this;
        self.additionalClass = params && params.additionalClass || '';
        self.iconSrc = params && params.iconSrc || '';
        
        self.iconClass = params && params.iconClass || '';
        self.label = params && params.label || '';
        
        self.value = params && params.value || '';
        self.unit = params && params.unit || '';
        self.showEmpty = params && params.showEmpty || false;

        if (params && (params.roundValue || params.roundValue === 0)) {
            self.roundValue = params.roundValue;
        }
        else {
            self.roundValue = -1;
        }
        

        self.Show = ko.pureComputed(function () {
            var val = getFuncVal(self.value);            
            return self.showEmpty || (val && val != '0' && val != '00:00' && val != '00:00:00');
        });
        self.IconSrc = ko.pureComputed(function () {
            var val = getFuncVal(self.iconSrc);
            if (val && val.charAt(0) != '/') {
                val = ContextPath + 'Content/images/icons/' + val;
                if (val.indexOf('.') < 0) {
                    val += '.png';
                }
            }
            return val;
        });
        self.Value = ko.pureComputed(function () {
            var v = getFuncVal(self.value);
            var roundVal = getFuncVal(self.roundValue);
            if (v && roundVal >= 0) {
                v = parseFloat(v.replace(',','.'));
                if (roundVal == 0) {
                    v = Math.round(v);
                } else {
                    var prec = 10 ^ roundVal;
                    v = Math.round(v * prec) / prec;
                }
            }
            return v;
        });
        self.Unit = ko.pureComputed(function () {
            return getFuncVal(self.unit);
        });
        self.IconClass = ko.pureComputed(function () {
            return getFuncVal(self.iconClass);
        });
        self.Label = ko.pureComputed(function () {
            var val = getFuncVal(self.label);
            if (!val && params && params.transLabel) {
                val = translations[params.transLabel];
            }
            return val;
        });
    },
    template:
        '<div class="statControl withIcon koComp" data-bind="css: additionalClass, visible: Show">               ' +
        '    <div class="icon left">                                                                    ' +
        '        <img data-bind="attr: { src: IconSrc, alt: Label, title: Label }, visible: IconSrc" /> ' +
        '        <span data-bind="attr: { src: IconClass, title: Label }, visible: IconClass"></span>   ' +
        '    </div>                                                                                     ' +
        '    <div class="right">                                                                        ' +
        '        <div class="statValue">                                                                ' +
        '            <!-- ko text: Value --><!-- /ko -->                                                ' +
        '            <span class="unit" data-bind="html: Unit"></span>                                  ' +
        '        </div>                                                                                 ' +
        '        <div class="statLabel" data-bind="text: Label"></div>                                  ' +
        '    </div>                                                                                     ' +
        '</div>'
});;ko.components.register('stat-text-ctrl', {
    viewModel: function (params) {
        var self = this;
        self.additionalClass = params && params.additionalClass || '';       
        self.label = params && params.label || '';
        if (!self.label && params && params.transLabel) {
            self.label = translations[params.transLabel];
        }
        self.value = params && params.value || '';
//        self.unit = params && params.unit || '';
        self.showEmpty = params && params.showEmpty || false;
       
        self.show = ko.computed(function () {
            var val = self.value;
            if (val && _.isFunction(val)) {
                val = val();
            }
            return self.showEmpty || (val && val != '0' && val != '00:00' && val != '00:00:00');
        });
    },
    template:
        '<div class="statControl text koComp" data-bind="css: additionalClass, visible: show">               ' +
        '    <div class="statLabel" data-bind="text: label"></div>                                      ' +
        '    <div class="right">                                                                        ' +
        '        <div class="statValue">                                                                ' +
        '            <!-- ko text: value --><!-- /ko -->                                                ' +
//        '            <span class="unit" data-bind="html: unit"></span>                                  ' +
        '        </div>                                                                                 ' +       
        '    </div>                                                                                     ' +
        '</div>'
});;ko.components.register('priority-ctrl', {
    viewModel: function (params) {
        var self = this;
        
        self.Priority = params.Priority;
        self.Priorities = PriorityCtrlPriorities;

        self.AdditionaClass = params.AdditionaClass;

        self.PriorityName = ko.computed(function () {
            var s = _.findWhere(self.Priorities, { value: self.Priority() });
            if (s) return s.text;
            return '';
        });
        self.PriorityIcon = ko.computed(function () {
            var s = _.findWhere(self.Priorities, { value: self.Priority() });
            if (s) return '/Content/images/icons/' + s.icon + '.png';
            return '';
        });
    },
    template:
        '<span class="selectableStars priorityCtrl" data-bind="attr: {title: PriorityName, alt: PriorityName}, css: AdditionaClass">                                    ' +
        '    <img src="/Content/images/icons/prio_low_19.png" data-bind="css: {selected: Priority() != 0 && Priority() <= 3}, click: function () { Priority(3); }" />   '+
        '    <img src="/Content/images/icons/prio_low_19.png" data-bind="css: {selected: Priority() != 0 && Priority() <= 2}, click: function () { Priority(2); }" />   '+
        '    <img src="/Content/images/icons/prio_low_19.png" data-bind="css: {selected: Priority() != 0 && Priority() <= 1}, click: function () { Priority(1); }" />   '+
        '</span>                                                                                                                                                         '
});


var PriorityCtrlPriorities = [{ value: 1, text: translations.Priority_High, icon: 'prio_high_19' }
               , { value: 2, text: translations.Priority_Medium, icon: 'prio_med_19' }
               , { value: 3, text: translations.Priority_Low, icon: 'prio_low_19' }];;ko.components.register('excercise-ctrl', {
    viewModel: function (params) {
        var self = this;

        if (params.ActiveModel) {
            self.Data = params.ActiveModel;
        } else if (params.PlainModel) {
            self.Data = new TrainingExerciseModel(null);
            self.Data.initByModel(params.PlainModel);
        } else if (params.StringModel) {
            self.Data = new TrainingExerciseModel(null);
            self.Data.initByModel(JSON.parse(params.StringModel));
        } else {
            self.Data = new TrainingExerciseModel(null);
        }

        self.ShowTypeIcon = params.ShowTypeIcon;
        self.ShowShortcut = params.ShowShortcut;
        self.ShowName = params.ShowName;
        self.ShowDescription = params.ShowDescription;

        self.ShowSelectOnChart = params.ShowSelectOnChart || false;

        self.ShowDescriptInName = ko.pureComputed(function () {
            return self.ShowDescription && (!self.Data.TypeShortcut() || !self.ShowShortcut) && (!self.Name() || !self.ShowName);
        });
        self.ShowSecondDescript = ko.pureComputed(function () {
            return self.ShowDescription && false;
        });

        self.XMultip_1 = ko.pureComputed(function () {
            return false;// self.Data.Value() && self.Data.Repeats();
        });
        self.Value = ko.pureComputed(function () {
            return self.Data.Value() && self.Data.Value() != '0';
        });
        self.ValueUnit = ko.pureComputed(function () {
            return self.Value() && self.Data.Unit() && self.Data.Unit() != 'exe';// || self.Data.LoadChangeMode() != 'list');
        });
        self.ExeListMode = ko.pureComputed(function () {
            return self.Data.Unit() == 'exe' && self.Data.LoadChangeMode() == 'list';
        });
        self.ExeXMultip = ko.pureComputed(function () {
            return self.Data.Unit() == 'exe' && self.Data.LoadChangeMode() != 'list';
        });
        self.Load = ko.pureComputed(function () {
            return self.Data.Pace() && self.Data.Pace() != '0';
        });
        self.LoadUnit = ko.pureComputed(function () {
            return self.Load() && self.Data.LoadUnit();
        });
        self.LastSemicolon = ko.pureComputed(function () {
            return self.Data.Description() && (self.Data.Repeats() || self.Value() || self.Data.Series() || self.Load() || self.Data.Relax());
        });

        self.ComplexValue = ko.pureComputed(function () {
            var u = self.Data.Unit();
            return (u == 'mm:ss' || u == 'gg:mm:ss' || u == 'gg:mm' || u == 'RM' || u == 'achieve');
        });
        self.ComplexLoad = ko.pureComputed(function () {
            var u = self.Data.LoadUnit();
            return (u == 'strefaHR' || u == 'strefaFTP' || u == '%FTHR' || u == '%hr' || u == '%FTP' || u == 'paceZone' || u == 'RM');
        });
        self.Name = ko.pureComputed(function () {
            var result = self.Data.ExcerciseDetails().Name;
            if (result != translations.None) return result;
            return '';
        });
        self.NameDwukropek = ko.pureComputed(function () {
            return self.Data.Repeats() || self.Value() || self.Data.Series() || self.Load() || self.Data.Relax();
        });

        self.Cadence = ko.pureComputed(function () {
            var c = self.Data.Cadence();
            if (!c || c == '0') return '';
            var u = self.Data.LoadUnit();
            if (u != '%FTP' && u != 'strefaFTP') return '';
            return c + 'rpm';
        });

        self.Pause = ko.pureComputed(function () {
            var u = self.Data.Unit();
            if (u != 'exe') return '';
            return self.Data.Regeneration();// && !self.ExeXMultip();
        });
        self.ActivReg = ko.pureComputed(function () {
            var u = self.Data.Unit();
            if (u == 'exe') return '';
            return self.Data.Regeneration();// && !self.ExeXMultip();
        });
        self.RegenerationUnit = ko.pureComputed(function () {
            return self.Data.RegenerationUnit();
        });

        self.LoadChangeMode = ko.pureComputed(function () {
            var m = self.Data.LoadChangeMode();
            if (!m || m == 'fixed' || m == 'between' || m == 'series') return '';
            return m;
        });

        self.NameTooltip = ko.pureComputed(function () {
            var dets = self.Data.ExcerciseDetails();
            if (!dets) return '';
            var result = dets.Name;
            if (dets.Shortcut) result += ' (' + dets.Shortcut + ')';
            if (dets.Description) result += '\n' + dets.Description;
            return result;
        });
    },
    template:
                '<!-- ko if: ShowSelectOnChart -->                                                                                                              ' +
                '<div class="showSelectOnChart" data-bind="with: Data">                                                                                         ' +
                '<span class="smalChb" data-bind="css:{selected:SelectedOnChart }, click: ToggleSelectedOnChart"></span>             ' +
                '</div>                                                                                                                                         ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ShowTypeIcon -->                                                                                                                   ' +
                '<div class="activityType" data-bind="with: Data.TypeManager">                                                                                  ' +
                '    <!-- ko with: SelectedType -->                                                                                                             ' +
                '    <div class="disc_22" data-bind="css: IconCss, attr: { Title: Text }"></div>                                                                ' +
                '    <!-- /ko -->                                                                                                                               ' +
                '</div>                                                                                                                                         ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ShowShortcut -->                                                                                                                   ' +
                '<div class="shortcut" data-bind="text: Data.TypeShortcut, visible: Data.TypeShortcut, attr:{title: NameTooltip}, css: {nameDD: NameDwukropek}"></div>                        ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ShowName -->                                                                                                                       ' +
                '<div class="name" data-bind="text: Name, visible: Name() && (!Data.TypeShortcut() || !ShowShortcut), attr:{title: NameTooltip}, css: {nameDD: NameDwukropek}"></div>         ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ShowDescriptInName -->                                                                                                                       ' +
                '<div class="name" data-bind="brHtml: Data.Description, visible: Data.Description, css: {nameDD: NameDwukropek}"></div>         ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko ifnot: ExeListMode -->                                                                                                                 ' +
                '<div class="value" data-bind="text: Data.Value, visible: Value() && ExeXMultip()" title="' + translations.Value + '"></div>                    ' +
                '<div class="xMultip" data-bind="visible: ExeXMultip"></div>                                                                                    ' +
                '<div class="repeats" data-bind="text: Data.Repeats, visible: Data.Repeats" title="' + translations.NumberOfRepeats + '"></div>                 ' +
                '<div class="xMultip" data-bind="visible: XMultip_1"></div>                                                                                     ' +
                '<!-- ko ifnot: ComplexValue -->                                                                                                                ' +
                '<div class="value" data-bind="text: Data.Value, visible: Value() && !ExeXMultip()" title="' + translations.TimeOfWork + '"></div>              ' +
                '<div class="valueUnit" data-bind="text: Data.Unit, visible: ValueUnit" title="' + translations.TimeOfWork + '"></div>                          ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ComplexValue -->                                                                                                                   ' +
                '<div class="value" data-bind="text: Data.ValueWithUnit, visible: Value" title="' + translations.TimeOfWork + '"></div>                         ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ActivReg() -->                                                                                                                     ' +
                 '<div class="activReg">/</div>                                                                                                                 ' +
                 '<div class="value" data-bind="text: Data.ProperRegenerationValue" title="' + translations.AktywnaRegeneracja + '"></div>                      ' +
                 '<!-- /ko -->                                                                                                                                  ' +
                '<!-- ko ifnot: ComplexLoad -->                                                                                                                 ' +
                '<div class="pace" data-bind="css: Data.LoadIconCss, text: Data.Pace, visible: Load" title="' + translations.Intensivity + '"></div>            ' +
                '<div class="loadUnit" data-bind="text: Data.ProperLoadUnit, visible: LoadUnit" title="' + translations.Intensivity + '"></div>                 ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ComplexLoad -->                                                                                                                    ' +
                '<div class="pace" data-bind="css: Data.LoadIconCss, text: Data.LoadWithUnit, visible: Load, attr:{title: Data.LoadTooltip}"></div>             ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: LoadChangeMode -->                                                                                                                 ' +
                '<div class="loadChangeMode" data-bind="css: LoadChangeMode, attr: {title:  translations[\'exeChangeMode_\' + LoadChangeMode()] }"></div>       ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: Pause -->                                                                                                                       ' +
                '<div class="relax">&nbsp;</div>                                                                                                                ' +
                '<div class="value" data-bind="text: Data.ProperRegenerationValue" title="' + translations.SeriesRestTime + '"></div>                           ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- ko if: ExeListMode -->                                                                                                                    ' +
                '<div class="pace" data-bind="css: Data.LoadIconCss, visible: Load" title="' + translations.Trimp + '">&nbsp;</div>                             ' +
                '<div class="svl" data-bind="foreach: Data.SeriesValuesList">                                                                                   ' +
                '<span class="repeats" data-bind="text:Repeats"></span>x<span class="pace" data-bind="text:DisplayPace"></span>                                 ' +
                '</div>                                                                                                                                         ' +
                '<div class="loadUnit" data-bind="text: Data.ProperLoadUnit, visible: LoadUnit" title="' + translations.Intensivity + '"></div>                 ' +
                '<!-- ko if: Pause -->                                                                                                                       ' +
                '<div class="relax">&nbsp;</div>                                                                                                                ' +
                '<div class="value" data-bind="text: Data.ProperRegenerationValue" title="' + translations.SeriesRestTime + '"></div>                           ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<!-- /ko -->                                                                                                                                   ' +
                '<div class="cadence" data-bind="text: Cadence, visible: Cadence" title="' + translations.Cadence + '"></div>                                   ' +
                '<!-- ko if: ShowSecondDescript -->                                                                                                                ' +
                '<div class="" data-bind="visible: LastSemicolon">;</div>                                                                                       ' +
                '<div class="descriptions" data-bind="brHtml: Data.Description, visible: Data.Description" title="' + translations.Description + '"></div>      ' +
                '<!-- /ko -->                                                                                                                                   '
});
;ko.components.register('excercise-set-ctrl', {
    viewModel: function (params) {

        var Data;
        if (params.ActiveModel) {
            Data = params.ActiveModel;
        } else if (params.PlainModel) {
            Data = new TrainingExerciseModel(null);
            Data.initByModel(params.PlainModel);
        } else if (params.StringModel) {
            Data = new TrainingExerciseModel(null);
            Data.initByModel(JSON.parse(params.StringModel));
        } else {
            Data = new TrainingExerciseModel(null);
        }

        var self = _.extend(this, Data);

        self.ShowExeInfo = params.ShowExeInfo;
        self.ShowTypeIcon = params.ShowTypeIcon;
        self.ShowSelectOnChart = params.ShowSelectOnChart || false;

        self.DisplayExeInfo = ko.pureComputed(function () {
            return self.ShowExeInfo && self.ExcerciseDetails() && self.ExcerciseDetails().Id;
        });
        self.DisplayExeInfoIcon = ko.pureComputed(function () {
            return self.ExerciseTypeId() && self.ExerciseTypeId() != '0';
        });
        
    },
    template:
               
        '<div class="trainingExercise sortableItem dispMode" data-bind="css: {InSerie:InSerie,FirstInSerie:FirstInSerie,LastInSerie:LastInSerie, withInfo: DisplayExeInfoIcon  }">' +
        '    <!-- ko if: DisplayExeInfo -->                                                                                                         ' +
        '    <div class="line basic">                                                                                                               '+
        '        <div class="exerciseType">                                                                                                         '+
        '            <span class="name" data-bind="text: ExcerciseDetails().Name"></span>                                                           '+
        '        </div>                                                                                                                             '+
        '        <div class="exeInfoIcon info_25" data-bind="visible: DisplayExeInfoIcon, click: OpenExeInfo"></div>                                ' +
        '    </div>                                                                                                                                 '+
        '    <!-- /ko -->                                                                                                                           '+
        '                                                                                                                                           '+
        '    <!-- ko if: FirstInSerie -->                                                                                                           '+
        '    <div class="line serieParam dispMode">                                                                                                 '+
        '        <div class="xMultip" data-bind="visible: SeriesDisplay" title="' + translations.NrOfSeries + '"></div>                                                                            ' +
        '        <div class="series" data-bind="text: Series, visible: SeriesDisplay" title="' + translations.NrOfSeries + '"></div>                           ' +
        '        <div class="relax" data-bind="text: ProperRelax, visible: Relax, css: {notLeft: SeriesDisplay}" title="' + translations.Relax + '"></div> ' +
        '    </div>                                                                                                                                 '+
        '    <!-- /ko -->                                                                                                                           '+
        '                                                                                                                                           '+
        '    <div class="line exeParams dispMode">                                                                                                  '+
        '        <!-- ko component: {                                                                                                               '+
        '            name: "excercise-ctrl",                                                                                                        '+
        '            params: { ActiveModel: $data, ShowTypeIcon:ShowTypeIcon, ShowShortcut:true, ShowDescription:true, ShowName:true, ShowSelectOnChart:ShowSelectOnChart }                       ' +
        '        } -->                                                                                                                              '+
        '        <!-- /ko -->                                                                                                                       '+
        '    </div>                                                                                                                                 '+
        '                                                                                                                                           '+
        '    <div class="line descripts dispMode">                                                                                                  '+
        '        <div class="comments" data-bind="brHtml: Comments, visible: Comments" title="' + translations.Comment + '"></div>                  '+
        '    </div>                                                                                                                                 '+
        '                                                                                                                                           '+
        '    <div class="line serieEnd"></div>                                                                                                      '+
        '                                                                                                                                           '+
        '    <div class="clearfix"></div>                                                                                                           ' +
        '</div>                                                                                                                                     '


});

;ko.components.register('discipline-single-ctrl', {
    viewModel: function (params) {
        var self = this;

        self.TypeManager = params.typeManager;
        self.ClubId = self.TypeManager.ClubId;
        self.Disciplines = self.TypeManager.Disciplines;
        self.ShowSubTypes = params.ShowSubTypes;
        self.SelectSubTypes = params.SelectSubTypes;
        self.AdditionaClass = params.AdditionaClass;
        self.Placeholder = params.Placeholder || translations.choose;
        self.IconSize = params.IconSize || 25;
        self.DisplaySubParent = params.DisplaySubParent;
        self.SelectDomObj = null;
        self.NoneElem = params.noneElem;
        self.SelectedIconSize = params.SelectedIconSize || 30;

        self.Data = null;
        if (self.Disciplines) {
            if (self.ClubId) {
                self.Data = DiscDatas.ClubDisciplines.slice(0);
            } else {
                self.Data = DiscDatas.PsDisciplines.slice(0);
            }
        } else {
            if (self.ClubId) {
                self.Data = DiscDatas.ClubTrainingTypes.slice(0);
            } else {
                self.Data = DiscDatas.PsTrainingTypes.slice(0);
            }
        }
        if (self.NoneElem) {
            self.Data.unshift(self.NoneElem);
        } else {
            if (self.TypeManager.AllInstedNone) {
                self.Data.unshift({ id: 0, text: translations.AllDiscs.toLowerCase(), icon: 'all' });
            } else {
                self.Data.unshift({ id: 0, text: translations.Unspecified.toLowerCase(), icon: 'none' });
            }
        }

        /*= $.map(self.TypeManager.Types(), function (obj) {
            var newObj = {
                id: obj.Id
                , text: obj.Text
                , org: obj
                //, children: []
            };
            return newObj;
        });
        */

        /*
        self.SelectedTypeId = ko.observable(self.TypeManager.SelectedTypeId());
        self.SelectedTypeId.subscribe(function (newValue) {
            if (newValue && newValue.indexOf('s')>=0) {
                var idx = newValue.indexOf('s');
                var main = newValue.substring(0, idx);
                var sub = newValue.substring(idx + 1);
                self.TypeManager.SelectValue(main, sub);
            } else {
                self.TypeManager.SelectTypeById(newValue);
            }
        }, self);
        */

        self._ValToStore = null;

        self.SelectedTypeId = ko.pureComputed({
            read: function () {
                var val = '';
                if (self.SelectSubTypes && self.TypeManager.SelectedSubTypeId()) {
                    val = self.TypeManager.SelectedTypeId() + 's' + self.TypeManager.SelectedSubTypeId();
                    if (!_.findWhere(self.Data, { id: val })) {
                        val = self.TypeManager.SelectedTypeId();
                    }
                } else {
                    val = self.TypeManager.SelectedTypeId();
                }
                return val;
            },
            write: function (newValue) {
                if (newValue === 0 || newValue === '0') {
                    self._ValToStore = { main: 0, sub: null };
                } else if (_.findWhere(self.Data, { id: newValue })) {
                    if (newValue && newValue.indexOf('s') >= 0) {
                        var idx = newValue.indexOf('s');
                        var main = newValue.substring(0, idx);
                        var sub = newValue.substring(idx + 1);
                        //self.TypeManager.SelectValue(main, sub);
                        self._ValToStore = { main: main, sub: sub };
                    } else {
                        //self.TypeManager.SelectValue(newValue, null);
                        self._ValToStore = { main: newValue, sub: null };
                    }
                }
            },
            owner: self
        });

        self.SelectEvent = function (event) {
            self.TypeManager.SelectValue(self._ValToStore.main, self._ValToStore.sub, 0, null, true);
        }

        self.collapsedClass = 'cled';
        self.openFnc = function (event) {
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.removeClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().removeClass(self.collapsedClass);
            event.stopPropagation();
        }
        self.closeFnc = function (event) {
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.addClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().addClass(self.collapsedClass);
            event.stopPropagation();
        }
        self.toggleFnc = function (event) {
            //console.log("toggleFnc");
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.toggleClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().toggleClass(self.collapsedClass);
            event.stopPropagation();
            return false;
        }
        self.stopEvent = function (event) {
            event.stopPropagation();
            return false;
        }

        self.openDdl = function (data,event ) {
            if (!self.SelectDomObj) {
                self.SelectDomObj = $(event.target).parents('.disciplineCtrl').find('select');
            }
            self.SelectDomObj.select2("open");
        }

        self.FormatState = function (type, container, mode) {
            if (!type.id) { return type.text; }

            if (type.id != 0 && type.id!= '0' && self.TypeManager.AvailableIds) {
                //if (type.parentId) return null;
                var ai = self.TypeManager.AvailableIds();
                var eId =type.parentId?type.parentId: parseInt(type.id);
                if (!_.contains(ai, eId)) return null;
            }

            var iconSize = self.IconSize;
            if (type.icon) {
                var dom = $('<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.icon + '" title="' + type.text + '">&nbsp;</span><span class="name">' + type.text + '</span></span>');

                if (self.ShowSubTypes && type.subs && type.subs.length && (mode != 'query')) {
                    //var subsContainer = $('<span class="subs"></span>');
                    //dom.append(subsContainer);
                    var pierdolnik = $('<span class="dzyndz ' + self.collapsedClass + '" data-typeid=' + type.id + '></span>');
                    pierdolnik.click(self.stopEvent);
                    pierdolnik.mousedown(self.toggleFnc);
                    pierdolnik.mouseup(self.stopEvent);
                    //pierdolnik.mouseenter(self.openFnc);
                    //pierdolnik.mouseleave(self.closeFnc);
                    dom.append(pierdolnik);

                    /*
                    _.each(type.subs, function (sub) {
                        var subElem = $('<span class="subDisc">' + sub.text + '</span>');
                        subsContainer.append(subElem);
                    });
                    */
                }
                return dom;

            } else {
                if (mode == 'query') {
                    return $(
                        '<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.parentIcon + '" title="' + type.parent + '">&nbsp;</span><span class="name">' + type.parent + ' - ' + type.text + '</span></span>'
                        //+'<span class="discLi subDisc"><span class="name">' + type.text + '</span></span>'
                        );
                } else {
                    if (self.ShowSubTypes) {
                        $(container).addClass(self.collapsedClass);
                        return $('<span class="discLi subDisc" data-parentid="' + type.parentId + '"><span class="name">' + type.text + '</span></span>');
                    }
                }
            }
        };

        self.TemplateSelection = function (type, container) {

            var type2 = _.findWhere(self.Data, { id: self.SelectedTypeId() });
            if (type2) type = type2;

            if (!type.id) { return type.text; }
            iconSize = 15;
            if (type.icon) {
                //return $('<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.icon + '" title="' + type.text + '">&nbsp;</span><span>' + type.text + '</span></span>');
                //return $('<span class="discLi s' + iconSize + '"><span>' + type.text + '</span></span>');
                return type.text;
            } else {
                //return $('<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.parentIcon + '" title="' + type.parent + '">&nbsp;</span><span>' + type.parent + (self.DisplaySubParent ? (' - ' + type.text) : '') + '</span></span>');
                //return $('<span class="discLi s' + iconSize + '"><span>' + type.parent + (self.DisplaySubParent ? (' - ' + type.text) : '') + '</span></span>');
                return  type.parent + (self.DisplaySubParent ? (' - ' + type.text) : '');
            }
        }
    },
    template: '<div class="disciplineCtrl" data-bind="css: AdditionaClass"><span class="discIcon" data-bind="css: TypeManager.SelectedTypeIconCss() + (\' disc_\' + SelectedIconSize), click: openDdl, attr: {title:TypeManager.NameOfSelection }"></span><select data-bind="select2: {data:Data , placeholder: Placeholder, templateResult: FormatState, templateSelection:TemplateSelection, additionalDropdownClass: \'uniHeadBar single\', dropdownAutoWidth : true   },value:SelectedTypeId, event: {\'select2:select\':SelectEvent } "></select></div>'
});

ko.components.register('discipline-multi-ctrl', {
    viewModel: function (params) {
        var self = this;

        self.TypeManager = params.typeManager;
        self.ClubId = self.TypeManager.ClubId;
        self.Disciplines = self.TypeManager.Disciplines;
        self.ShowSubTypes = params.ShowSubTypes;
        self.SelectSubTypes = params.SelectSubTypes;
        self.AdditionaClass = params.AdditionaClass;
        self.Placeholder = params.Placeholder || (self.TypeManager.AllInstedNone ? translations.AllDiscs.toLowerCase() : translations.Unspecified.toLowerCase());
        self.IconSize = params.IconSize || 25;
        self.DisplaySubParent = params.DisplaySubParent;
        self.SelectedIconSize = params.SelectedIconSize || 30;

        self.Disabled = params.disabled;

        self.Data = null;
        if (self.Disciplines) {
            if (self.ClubId) {
                self.Data = DiscDatas.ClubDisciplines.slice(0);
            } else {
                self.Data = DiscDatas.PsDisciplines.slice(0);
            }
        } else {
            if (self.ClubId) {
                self.Data = DiscDatas.ClubTrainingTypes.slice(0);
            } else {
                self.Data = DiscDatas.PsTrainingTypes.slice(0);
            }
        }
        if (self.TypeManager.AllInstedNone) {
            self.Data.unshift({ id: 0, text: translations.AllDiscs.toLowerCase(), icon: 'all' });
        } else {
            self.Data.unshift({ id: 0, text: translations.Unspecified.toLowerCase(), icon: 'none' });
        }

        self._ValsToStore = null;
        self._updateTime = ko.observable(false);

        self.NoneIcon = self.TypeManager.AllInstedNone ? 'all' : 'none';
        self.NoneLabel = self.TypeManager.AllInstedNone ? translations.AllDisc : translations.Unspecified;

        self.SelectDomObj = null;
        self.ForceClear = function () {
            if (self.SelectDomObj) {
                self.SelectDomObj.val(null).trigger("change");
            }
        }

        self.SelectedTypeIds = ko.pureComputed({
            read: function () {
                if (self._updateTime()) return self._ValsToStore;
                var val = [];
                if (self.TypeManager.Multi) {
                    val = _.map(self.TypeManager.SelectedTypeIds(), function (elem) { return '' + elem; });
                }
                if (!val.length) self.ForceClear();
                return val;
            },
            write: function (newValue) {
                if (!self._updateTime()) {
                    var prevContains0 = self._ValsToStore && (_.contains(self._ValsToStore, 0) || _.contains(self._ValsToStore, "0"));
                    self._ValsToStore = [];
                    if (newValue && newValue.length > 0) {
                        if (!prevContains0 && (_.contains(newValue, 0) || _.contains(newValue, "0"))) return;
                        _.each(newValue, function (elem) {
                            if (_.findWhere(self.Data, { id: elem })) {
                                self._ValsToStore.push(elem);
                            }
                        });
                    }
                }
            },
            owner: self
        });

        self.SelectEvent = function (data, event) {
            self._updateTime(true);
            self.TypeManager.DeselectAll();
            _.each(self._ValsToStore, function (id) {
                if (id && id.indexOf('s') >= 0) {
                    var idx = id.indexOf('s');
                    var main = id.substring(0, idx);
                    var sub = id.substring(idx + 1);
                    self.TypeManager.SelectTypeById(main);
                    if (self.TypeManager.MultiSubs) {
                        var sub = id.substring(idx + 1);
                        self.TypeManager.SelectSubTypeById(sub);
                    }
                } else {
                    self.TypeManager.SelectTypeById(id);
                }                
            });
            if (!self.SelectDomObj) self.SelectDomObj = $(event.target);

            if ((!self._ValsToStore || !self._ValsToStore.length)) {
                self.ForceClear();
            }
            self._updateTime(false);
        }

        self.collapsedClass = 'cled';
        self.openFnc = function (event) {
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.removeClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().removeClass(self.collapsedClass);
            event.stopPropagation();
        }
        self.closeFnc = function (event) {
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.addClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().addClass(self.collapsedClass);
            event.stopPropagation();
        }
        self.toggleFnc = function (event) {
            //console.log("toggleFnc");
            var pierdolnik = $(event.target)
            var typeId = pierdolnik.data('typeid');
            pierdolnik.toggleClass(self.collapsedClass);
            pierdolnik.closest('.select2-results__options').find('[data-parentid=' + typeId + ']').parent().toggleClass(self.collapsedClass);
            event.stopPropagation();
            return false;
        }
        self.stopEvent = function (event) {
            event.stopPropagation();
            return false;
        }

        self.OpenSelect = function (data, event) {
            $(event.target).parents('.disciplineCtrl').find('select').select2("open");
        }

        self.FormatState = function (type, container, mode) {
            if (!type.id) { return type.text; }

            var iconSize = self.IconSize;
            if (type.icon) {
                var dom = $('<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.icon + '" title="' + type.text + '">&nbsp;</span><span class="name">' + type.text + '</span></span>');

                if (self.ShowSubTypes && type.subs && type.subs.length) {
                    var pierdolnik = $('<span class="dzyndz ' + self.collapsedClass + '" data-typeid=' + type.id + '></span>');
                    pierdolnik.click(self.stopEvent);
                    pierdolnik.mousedown(self.toggleFnc);
                    pierdolnik.mouseup(self.stopEvent);
                    dom.append(pierdolnik);
                }
                return dom;

            } else {
                if (mode == 'query') {
                    return $(
                        '<span class="discLi s' + iconSize + '"><span class="disc_' + iconSize + ' ' + type.parentIcon + '" title="' + type.parent + '">&nbsp;</span><span class="name">' + type.parent + ' - ' + type.text + '</span></span>'
                        );
                } else {
                    if (self.ShowSubTypes) {
                        $(container).addClass(self.collapsedClass);
                        return $('<span class="discLi subDisc" data-parentid="' + type.parentId + '"><span class="name">' + type.text + '</span></span>');
                    }
                }
            }
        };

        self.TemplateSelection = function (type, container) {

            /*
            var ids = self.SelectedTypeIds();
            if (ids.length == 1) {
                var type2 = _.find(self.Data, function (elem) { id: ids[0] });
                if (type2) type = type2;
            } else if (ids.length > 1) {
                return "many";
            }
            */

            container.hide();

            if (type.icon) {
                return $('<span class="discIcon disc_30 ' + type.icon + '" title="' + type.text + '"></span>');
            }

            if (!type.id) { return type.text; }
            iconSize = 15;
            if (type.icon) {
                return type.text;
            } else {
                return type.parent + (self.DisplaySubParent ? (' - ' + type.text) : '');
            }
        }
    },
    template: '<div class="disciplineCtrl multi" data-bind="css: AdditionaClass">'
             + '<span class="discIcons" data-bind="foreach: TypeManager.SelectedTypes, visible: TypeManager.SelectedTypes().length">'
             + '<span class="discIcon" data-bind="css: IconCss() + (\' disc_\' + $parent.SelectedIconSize), attr: {title: Name}, click: Select">&nbsp;</span></span>'
             + '<span class="discIcons" data-bind="visible: !TypeManager.SelectedTypes().length">'
             + '<span class="discIcon" data-bind="css: NoneIcon + (\' disc_\' + SelectedIconSize), attr: {title: NoneLabel}, click: OpenSelect">&nbsp;</span></span>'
             + '<select multiple="multiple" data-bind="select2: {data:Data , placeholder: Placeholder, templateResult: FormatState, templateSelection:TemplateSelection, additionalDropdownClass: \'uniHeadBar multi\', dropdownAutoWidth : true  },selectedOptions:SelectedTypeIds, event: {\'select2:select\':SelectEvent, \'select2:unselect\':SelectEvent }, disabled: Disabled "></select></div>'
});


;ko.components.register('user-hashtags-ctrl', {
    viewModel: function (params) {
        var self = this;

        self.ParentId = params.parentId;

        self.HashTags = params.hashTags;
        self._startTags = [];
        self.StartHashTags = ko.computed(function () {
            if (!self.selecting) {
                self._startTags = _.uniq(self.HashTags());
            }
            return self._startTags;
        });
        self.AdditionaClass = params.AdditionaClass;

        self.selecting = false;
        self.onSelect = function () {
            self.selecting = false;
        }
        self.onSelecting = function () {
            self.selecting = true;
        }

        self.FormatItem = function (item, container) {
            if (item.text !== undefined) {
                if (!item.id) { return item.text; }
                return $('<span>#' + item.text + '</span>');
            } else if (item) {
                return $('<span>#' + item + '</span>');
            }
        };

        self.TemplateSelection = function (item, container) {
            if (!item.id) { return item.text; }
            return '#' + item.text;
        }

        self.Selec2Params = {
            hashTags: self.HashTags,
            ajax: {
                url: ContextPath + "User/GetUserHashTags",
                type: 'POST',
                dataType: 'json',
                delay: 250,
                data: function (params) {
                    return {
                        pattern: params.term, // search term
                        page: params.page
                    };
                },
                processResults: function (data, params) {
                    params.page = params.page || 1;
                    return {
                        results: _.map(data.items, function (elem) { return {id: elem, text: elem}}),
                        pagination: {
                            more: (params.page * 30) < data.total_count
                        }
                    };
                },
                cache: false
            },
            templateResult: self.FormatItem,
            templateSelection: self.TemplateSelection,
            tags: true,
            tokenSeparators: [',', ' ', ';', '#']
        };

    },
    template: '<div class="userHashTagsCtrl" data-bind="css: AdditionaClass" ><select multiple="multiple" data-bind="select2:Selec2Params" tyle="width: 100%"></select></div>'
});
;var liveResults = extendNs("liveResults");

//#region config

liveResults.ConfigManager = function () {
    var self = this;

    self.initMode = true;

    self.activeTable = false;
    self.activeMultiMap = false;

    self.WithMap = false;

    self.elevationMgr = null;

    self.tableSelector = 0;
    self.periodId = 0;
    self.mat = ko.observable(0);
    self.discipline = ko.observable(0);
    self.category = ko.observable("");
    self.Mode = ko.observable('start');
    self.Male = ko.observable(null);
    self.Pro = ko.observable(null);
    self.TimeStatus = ko.observable('before');
    self.MapCountLimit = ko.observable(15);

    self.CheckPointsConfig = ko.observable(null);

    self.mat.subscribe(function (newValue) {
        if (!self.initMode) {
            self.initMode = true;
            if (newValue == -1) self.mat(0);
            self.discipline(0);
            self.initMode = false;
            self.reInitTable();
        }
    });
    self.discipline.subscribe(function (newValue) {
        if (!self.initMode) {
            self.initMode = true;
            self.mat(0);
            self.initMode = false;
            self.reInitTable();
        }
    });
    self.category.subscribe(function (newValue) { if (!self.initMode) { self.reInitTable(); } });
    self.Mode.subscribe(function (newValue) {
        if (!self.initMode) {
            self.reInitTable();
        }
    });
    self.Male.subscribe(function (newValue) {
        if (!self.initMode) {
            self.reInitTable();
        }
    });
    self.Pro.subscribe(function (newValue) {
        if (!self.initMode) {
            self.reInitTable();
        }
    });
    self.MapCountLimit.subscribe(function (newValue) {
        if (!self.initMode) {
            self.reInitTable();
        }
    });

    self.listInitiated = false;
    self.listReady = false;
    //self.matList = ko.observableArray([]);
    //self.disciplineList = ko.observableArray([]);
    self.categoryList = ko.observableArray([]).extend({ rateLimit: 50 });
    self.wavesList = ko.observableArray([]).extend({ rateLimit: 50 });

    self.matList = ko.computed(function () {
        var cpc = self.CheckPointsConfig();
        if (!cpc) return [];
        return cpc.All;
    });
    self.disciplineList = ko.computed(function () {
        var cpc = self.CheckPointsConfig();
        if (!cpc || !cpc.Meta) return [];
        var metaId = cpc.Meta.Id;
        return _(cpc.All).filter(function (matt) { return matt.StageEnd || matt.Id == metaId; });
    });

    self.SexList = [
        { value: null, text: translations.All },
        { value: true, text: translations.Males },
        { value: false, text: translations.Females },
    ];
    self.ProsList = [
        { value: null, text: translations.All },
        { value: true, text: translations.Pro },
        { value: false, text: translations.NonPro },
    ];
    self.filteredCategoryList = ko.computed(function () {
        var list = self.categoryList();
        var male = self.Male();
        var pro = self.Pro();

        if (male != null) {
            list = _(list).filter(function (cat) { return cat.Male == null || cat.Male == male; });
        }
        if (pro != null) {
            list = _(list).filter(function (cat) { return cat.Pro == null || cat.Pro == pro; });
        }

        return list;
    });


    if (IsAdmin) {
        self.mattsCtrl = ko.computed(function () {
            var cpc = self.CheckPointsConfig();
            if (!cpc || !cpc.Meta) return [];
            var metaId = cpc.Meta.Id;

            var candis = _(cpc.All).filter(function (matt) { return matt.StageEnd || matt.Id == metaId || matt.DisciplineChange; });
            candis = _(candis).sortBy(function (m) { return m.Index; });

            var result = [];
            var lastIndex = 0;
            for (var c = 0, lenc = candis.length; c < lenc; c++) {
                var candi = candis[c];
                candi.matList = [];
                for (var i = lastIndex, len = cpc.All.length; i < len && i <= candi.Index; i++) {
                    var subCandi = cpc.All[i];
                    if (subCandi.TypeId != 1) {
                        if (subCandi.Index <= candi.Index) {
                            candi.matList.push(subCandi);
                            lastIndex = i;
                        }
                    }
                }
                candi.singleMatt = candi.matList.length <= 1;
                candi.firstMatt = candi.matList.length ? candi.matList[0] : null;

                candi.mat = ko.pureComputed({
                    read: function () {
                        var selId = self.mat();
                        if (_(this.matList).findWhere({ Id: selId })) {
                            return selId;
                        } else {
                            return '-1';
                        }
                    },
                    write: function (newValue) {
                        if (newValue != '-1') {
                            self.mat(newValue);
                        }
                    },
                    owner: candi
                });
                lastIndex++;
                result.push(candi);
            }

            return result;
        });
    }

    self.initCfg = function (mat, discipline, category, periodId, tableSelector, withMap) {
        self.initMode = true;

        self.WithMap = withMap;

        if (periodId) self.periodId = periodId;
        if (tableSelector) self.tableSelector = tableSelector;

        self.mat(mat ? mat : 0);
        self.discipline(discipline ? discipline : 0);
        self.category(category ? category : '');

        self.initMode = false;

        setTimeout(self.initLists, 300);
    }

    self.intListsCallback = null;
    self.initLists = function () {
        if (!self.listInitiated) {
            self.listInitiated = true;

            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/GetConfigs",
                data: { periodId: self.periodId }
            })
            .done(function (serverModel) {
                if (serverModel) {
                    self.initMode = true;
                    var cfg = serverModel.checkPointsConfig;
                    if (cfg) {
                        for (var i = 0, len = cfg.All.length; i < len; i++) {
                            cfg.All[i].ParticipCount = ko.observable(0);
                            cfg.All[i].ParticipAcuCount = ko.observable(0);
                        }
                        self.CheckPointsConfig(cfg);
                    }
                    koArrayCopy(serverModel.categories, self.categoryList, false, false);
                    self.categoryList.unshift({ Name: translations.All, Male: null, Pro: null });
                    setTimeout(function () { self.initMode = false; }, 100);
                    self.listReady = true;
                    if (self.intListsCallback) self.intListsCallback();
                }
            });
            self.GetAvailableWaves();
        }
    }

    self.reInitTable = function () {
        if (self.activeTable) {
            liveResults.reInitTable();
        }
        if (self.activeMultiMap) {
            liveResults.showParticipantsRoutePoints();
        }
    }

    self.GetCheckPointsCountsTimeOut = null;
    self.GetCheckPointsCounts = function (category) {
        if (self.GetCheckPointsCountsTimeOut) clearTimeout(self.GetCheckPointsCountsTimeOut);

        var cfg = self.CheckPointsConfig();
        if (cfg) {
            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/GetCheckPointsCounts",
                data: { periodId: self.periodId, category: category }
            })
            .done(function (serverModel) {
                if (serverModel) {

                    var _model = _(serverModel);
                    for (var i = 0, len = cfg.All.length; i < len; i++) {
                        var cp = cfg.All[i];
                        var cpCount = _model.findWhere({ key: cp.Id });
                        cp.ParticipCount(cpCount ? cpCount.value : 0);
                        cp.ParticipAcuCount(cpCount ? cpCount.accuVal : 0);
                    }
                }
            });
        } else {
            self.GetCheckPointsCountsTimeOut = setTimeout(function () { self.GetCheckPointsCounts(category); }, 200);
        }
    }

    self.GetAvailableWaves = function () {
        var self = this;
        var pId = self.periodId || liveResults.periodId;
        if (pId) {
            if (IsAdmin) {
                $.ajax({
                    type: "POST",
                    url: ContextPath + "LiveResult/GetAvailableWaves",
                    data: {
                        periodId: pId
                    }
                })
                .done(function (serverModel) {
                    if (serverModel) {
                        koArrayCopy(serverModel, self.wavesList, false, false);
                    }
                });
            }
        }
    }
}

liveResults.cfgMgr = new liveResults.ConfigManager();


liveResults.initConfig = function (periodId, discipline, category, timeStatus, mode) {
    if (timeStatus) {
        liveResults.cfgMgr.TimeStatus(timeStatus);
    }
    liveResults.cfgMgr.initCfg(0, discipline, category, periodId, null, true);
}

//#endregion

//#region Map

liveResults.mapLoaded = false;

liveResults.routes = null;


liveResults.getMapParams = function () {
    var mapParent = $('#map-canvas').parent();
    return getMapParams(mapParent);
}

liveResults.setMapParams = function (mapParams) {
    var mapParent = $('#map-canvas').parent();
    setMapParams(mapParent, mapParams);
}


liveResults.initMap = function (periodId, mapParams, participDetails) {
    //if (!self.mapLoaded)
    {
        var mapParent = $('#map-canvas').parent();

        MapInitialize(mapParent);

        //liveResults.mapLoaded = true;
        var participList = [];
        var particip = participDetails || liveResults.ParticipDetails();
        if (particip != null) {
            if (particip.CheckPointsReads != null) {
                var lastReadCheckPoint = particip.CheckPointsReads;
                var orientPlaceInts = lastReadCheckPoint.OrientPlaceInts;
                if (orientPlaceInts != null) {
                    participList.push(particip);
                }
            }
        }

        var drawParticipantCallback = function (routes, periodId, mapParent, mapParams) {
            liveResults.drawParticipantDistancePoints(mapParent, routes, participList, null, false);
        };
        liveResults.GetPeriodRoutePoints(periodId, mapParams, drawParticipantCallback);
    }
}


liveResults.showParticipantsRoutePoints = function (routes, periodId) {
    if (!routes) routes = liveResults.routes;
    var cfg = liveResults.cfgMgr;
    var discipline = cfg.discipline(); if (!discipline) discipline = 0;;
    var category = cfg.category();
    if (!category || category == translations.All) category = '';
    var mode = cfg.Mode();
    var male = cfg.Male();
    var pro = cfg.Pro();
    var countLimit = cfg.MapCountLimit();
    if (!periodId) periodId = cfg.periodId;
    liveResults.getParticipantsRoutePoints(routes, periodId, discipline, category, mode, male, pro, countLimit);
}

liveResults.drawParticipantDistancePoints = function (mapParent, routes, participList, buildInfo, singlePoint) {
    if (participList != null) {
        clearRouteParticipants(mapParent);
        for (var i = 0, len = routes.length; i < len; i++) {
            var route = routes[i];
            route.ParticipPoints = null;
            liveResults.calculateRouteDistancePoints(route, participList, buildInfo, singlePoint);
            if (route.ParticipPoints != null && route.ParticipPoints.length > 0) {
                drawRouteParticipants(mapParent, route.ParticipPoints);
            }
        }
        
        liveResults.elevationMgr.setMarkers(getParticipantsMarkers(mapParent));
    }
}

liveResults.loadPersonMapInfoDetails = function (participId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "LiveResult/GetParticipDetails",
        data: { periodId: liveResults.cfgMgr.periodId, participId: participId }
    })
      .done(function (result) {
          liveResults.ParticipDetails(new ParticipDetails(result));
          $.scrollTo($('#personMapInfoDetailsContainer'), { duration: 1000, easing: 'swing' });
      });
}

liveResults.buildParticipMapInfo = function (particip) {
    var distF = Math.round((particip.OrientPlaceInts.DistanceFrom) / 100) / 10;
    var distT = Math.round((particip.OrientPlaceInts.DistanceTo) / 100) / 10;
    var dist = distF;
    if (distF != distT) dist = distF + '-' + distT;

    var info = '<div class="personMapInfo"><a class="detailsLink" onclick="liveResults.loadPersonMapInfoDetails(' + particip.Id + '); return false;"><span>' + particip.Number + ' ' + particip.FirstName + ' ' + particip.LastName + ' - ' + dist + ' km</span></a></div>';
    return info;
}

liveResults.getRouteParticipList = function (route, participList, buildInfo, singlePoint) {
    var result = [];
    for (var y = 0, partLen = participList.length; y < partLen; y++) {
        var particip = participList[y];
        var distanceFrom = 0;
        var distanceTo = 0;
        var trainingTypeId = 0;
        var info = '';
        var tooltip = '';

        if (buildInfo) {
            if (particip.FirstName != null && particip.LastName != null) {
                tooltip = particip.FirstName + ' ' + particip.LastName;
                info = liveResults.buildParticipMapInfo(particip);
            }
        }

        var orientPlaceInts = null;
        if (particip.OrientPlaceInts != null) {
            orientPlaceInts = particip.OrientPlaceInts;

        } else if (particip.CheckPointsReads != null) {
            var lastReadCheckPoint = particip.CheckPointsReads;
            orientPlaceInts = lastReadCheckPoint.OrientPlaceInts;

        }

        if (orientPlaceInts != null && route.Type == orientPlaceInts.TrainingTypeId) {
            result.push(new Object({
                DistanceFrom: orientPlaceInts.DistanceFrom / 1000,
                DistanceTo: orientPlaceInts.DistanceTo / 1000,
                TrainingTypeId: orientPlaceInts.TrainingTypeId,
                Tooltip: tooltip,
                Info: info,
                Number: particip.Number
            }));
        }
    }
    return liveResults.getClusteredRouteParticipList(result, singlePoint);
}

liveResults.getClusteredRouteParticipList = function (participList, singlePoint) {
    var result = [];

    var cache = new Object();


    for (var y = 0, partLen = participList.length; y < partLen; y++) {
        var routeParticipInfo = participList[y];

        var key = routeParticipInfo.DistanceFrom + ":" + routeParticipInfo.DistanceTo;

        var clusteredrouteParticipInfo = cache[key];
        if (clusteredrouteParticipInfo == null) {
            cache[key] = routeParticipInfo;
            result.push(routeParticipInfo);

            if (singlePoint && routeParticipInfo.DistanceFrom != null && routeParticipInfo.DistanceTo != null && routeParticipInfo.DistanceTo > 0) {
                routeParticipInfo.DistanceFrom = (routeParticipInfo.DistanceFrom + routeParticipInfo.DistanceTo) / 2;
                routeParticipInfo.DistanceTo = routeParticipInfo.DistanceFrom;
            }

        } else {

            var multi = clusteredrouteParticipInfo.Multi;
            if (multi != null) {
                multi++;
            } else {
                multi = 2;
            }
            clusteredrouteParticipInfo.Multi = multi;

            var info = '';
            if (clusteredrouteParticipInfo.Info != null) {
                info = clusteredrouteParticipInfo.Info;

            }
            if (routeParticipInfo.Info != null) {
                //if (info.length > 0) {
                //    info = info + '<BR />';
                //}
                info = info + routeParticipInfo.Info;
            }
            clusteredrouteParticipInfo.Info = info;

            var tooltip = '';

            if (clusteredrouteParticipInfo.Tooltip != null) {
                tooltip = clusteredrouteParticipInfo.Tooltip;
            }

            var maxMulti = 3;

            if (multi <= maxMulti) {
                if (routeParticipInfo.Tooltip != null) {
                    if (tooltip.length > 0) {
                        tooltip = tooltip + ', ';
                    }
                    tooltip = tooltip + routeParticipInfo.Tooltip;
                }
                clusteredrouteParticipInfo.Tooltip = tooltip;

                if (multi == maxMulti) {
                    clusteredrouteParticipInfo.Tooltip = tooltip + ' ' + translations.LiveResultOthers;
                }
            }

            if (singlePoint && multi == 2 && clusteredrouteParticipInfo.DistanceFrom != null && clusteredrouteParticipInfo.DistanceTo != null
                && clusteredrouteParticipInfo.DistanceTo > 0) {
                clusteredrouteParticipInfo.DistanceFrom = (clusteredrouteParticipInfo.DistanceFrom + clusteredrouteParticipInfo.DistanceTo) / 2;
                clusteredrouteParticipInfo.DistanceTo = clusteredrouteParticipInfo.DistanceFrom;
            }
        }
    }
    return result;

}

liveResults.drawElevationDistancePoint = function (routes, trainingType, elevationDistanceInKm) {
    if (routes != null) {
        var elevationDistancePoint = null;
        if (elevationDistanceInKm != null && elevationDistanceInKm >= 0) {
            for (var i = 0, len = routes.length; i < len; i++) {
                var route = routes[i];
                if (route.Type === trainingType) {
                    elevationDistancePoint = liveResults.calculateElevationDistancePoint(route, elevationDistanceInKm);
                }
            }
        }
        var mapParent = $('#map-canvas').parent();
        drawElevationPoint(mapParent, elevationDistancePoint);
    }
}

liveResults.calculateElevationDistancePoint = function (route, elevationDistance) {
    var points = route.Points;
    var lastPoint = null;
    var distance = 0;
    var elevationDistancePoint = null;
    for (var i = 0, length = points.length; i < length; i++) {
        if (lastPoint != null) {
            var lat = points[i][0];
            var long = points[i][1];

            var prevDistance = distance;
            var pointDistance = calculateLineDistance(lastPoint[0], lastPoint[1], lat, long, false);

            distance += pointDistance;

            if (elevationDistance >= prevDistance && elevationDistance <= distance) {
                elevationDistancePoint = lastPoint;
                return elevationDistancePoint;
            }
        }
        lastPoint = points[i];
    }
    return elevationDistancePoint;
}

liveResults.calculateRouteDistancePoints = function (route, participList, buildInfo, singlePoint) {
    var points = route.Points;
    var lastPoint = null;
    var distance = 0;
    var routeParticipList = liveResults.getRouteParticipList(route, participList, buildInfo, singlePoint);

    if (routeParticipList != null && routeParticipList.length > 0) {
        var maxDistanceDifference = 0.2;
        for (var i = 0, length = points.length; i < length; i++) {
            if (lastPoint != null) {
                var lat = points[i][0];
                var long = points[i][1];

                var prevDistance = distance;
                var pointDistance = calculateLineDistance(lastPoint[0], lastPoint[1], lat, long, false);

                distance += pointDistance;

                for (var y = 0, partLen = routeParticipList.length; y < partLen; y++) {
                    routeParticip = routeParticipList[y];


                    if (routeParticip.MarkerPoint == null && routeParticip.DistanceFrom != null && routeParticip.DistanceFrom >= 0
                        && routeParticip.DistanceTo != null && routeParticip.DistanceTo > 0 && routeParticip.DistanceTo != routeParticip.DistanceFrom) {

                        var markerDistance = (routeParticip.DistanceFrom + routeParticip.DistanceTo) / 2;

                        if (markerDistance >= prevDistance && markerDistance <= distance) {
                            routeParticip.MarkerPoint = lastPoint;
                            routeParticip.Bearing = calculateBearing(lastPoint[0], lastPoint[1], lat, long);

                            var distanceToPoint = markerDistance - prevDistance;
                            if (distanceToPoint > maxDistanceDifference) {
                                routeParticip.MarkerPoint = calculateBearingPoint(lastPoint[0], lastPoint[1], distanceToPoint, routeParticip.Bearing);
                            }

                        }
                    }

                    if (routeParticip.DistanceFrom >= prevDistance && routeParticip.DistanceFrom <= distance) {
                        routeParticip.StartPoint = lastPoint;

                        if (routeParticip.PositionPoints == null) {
                            routeParticip.PositionPoints = [];
                        }
                        routeParticip.Bearing = calculateBearing(lastPoint[0], lastPoint[1], lat, long);

                        var distanceToPoint = routeParticip.DistanceFrom - prevDistance;
                        if (distanceToPoint > maxDistanceDifference) {
                            routeParticip.StartPoint = calculateBearingPoint(lastPoint[0], lastPoint[1], distanceToPoint, routeParticip.Bearing);
                        }

                        routeParticip.PositionPoints.push(routeParticip.StartPoint);
                    }

                    if (routeParticip.StartPoint != null && routeParticip.EndPoint == null
                        && routeParticip.DistanceTo != null && routeParticip.DistanceTo != routeParticip.DistanceFrom
                        && routeParticip.DistanceTo > 0) {

                        if (routeParticip.DistanceTo >= prevDistance && routeParticip.DistanceTo <= distance) {
                            routeParticip.EndPoint = lastPoint;

                            var distanceToPoint = routeParticip.DistanceTo - prevDistance;
                            if (distanceToPoint > maxDistanceDifference) {
                                var bearing = calculateBearing(lastPoint[0], lastPoint[1], lat, long);
                                routeParticip.EndPoint = calculateBearingPoint(lastPoint[0], lastPoint[1], distanceToPoint, bearing);
                            }
                            routeParticip.PositionPoints.push(routeParticip.EndPoint);

                        } else {
                            routeParticip.PositionPoints.push(lastPoint);
                        }
                    }

                }
            }
            lastPoint = points[i];
        }

        if (distance > 0 && lastPoint != null) {
            for (var y = 0, partLen = routeParticipList.length; y < partLen; y++) {
                routeParticip = routeParticipList[y];

                if (routeParticip.StartPoint == null && routeParticip.DistanceFrom >= distance) {
                    routeParticip.StartPoint = lastPoint;
                    if (routeParticip.PositionPoints == null) {
                        routeParticip.PositionPoints = [];
                    }
                    routeParticip.PositionPoints.push(lastPoint);

                } else if (routeParticip.StartPoint != null && routeParticip.EndPoint == null
                    && routeParticip.DistanceTo > 0
                    && routeParticip.DistanceTo >= distance) {
                    routeParticip.EndPoint = lastPoint;
                    routeParticip.PositionPoints.push(lastPoint);
                }
            }
        }
        route.ParticipPoints = routeParticipList;
    }
}


liveResults.getParticipantsRoutePoints = function (routes, periodId, discipline, category, mode, male, pro, countLimit) {
    $.ajax({
        type: "POST",
        url: ContextPath + "LiveResult/GetParticipantsRoutPoints",
        data: {
            periodId: periodId,
            discipline: discipline,
            category: category,
            mode: mode,
            male: male,
            pro: pro,
            countLimit: countLimit
        }
    })
    .done(function (result) {
        if (result != null && routes != null) {
            var mapParams = liveResults.getMapParams();
            var mapParent = $('#map-canvas').parent();
            var bounds = new google.maps.LatLngBounds()
            liveResults.drawParticipantDistancePoints(mapParent, routes, result, true, true);
            if (mapParams != null) {
                liveResults.setMapParams(mapParams);
            }
        }
    });
}

liveResults.InitMapWithPeriodRoutePoints = function (periodId) {
    var mapParent = $('#map-canvas').parent();
    var mapParams = liveResults.getMapParams();
    MapInitialize(mapParent);
    ShowMapCooridinates(mapParent);
    
    var drawParticipantCallback = function (routes, periodId, mapParent, mapParams) {
        if (liveResults.cfgMgr.TimeStatus() === 'during') {
            liveResults.showParticipantsRoutePoints(routes, periodId);
        }
    };
    liveResults.GetPeriodRoutePoints(periodId, mapParams, drawParticipantCallback);
}


liveResults.GetPeriodRoutePoints = function (periodId, mapParams, drawParticipantCallback) {
    $.ajax({
        type: "POST",
        url: ContextPath + "LiveResult/GetPeriodRoutePoints",
        data: {
            periodId: periodId
        }
    })
    .done(function (result) {
        if (result) {
            liveResults.routes = result;
            var mapParent = $('#map-canvas').parent();
            var bounds = new google.maps.LatLngBounds()
            for (var i = 0, len = result.length; i < len; i++) {
                var route = result[i];
                addMultiMapPoint(mapParent, route.Points, route.SpecialPoints, null, null, null, i, bounds, route.Type);
            }

            if (drawParticipantCallback != null) {
                drawParticipantCallback(liveResults.routes, periodId, mapParent, mapParams);
                //liveResults.showParticipantsRoutePoints(periodId);
            }
            if (mapParams != null) {
                liveResults.setMapParams(mapParams);
            }

            liveResults.initElevationMgr(result);
        }
    });
}

//#endregion

//#region elevation chart

liveResults.ElevationChartManager = function (divSelector) {
    var self = this;

    self.divSelector = divSelector || '#elevationChart';
    self.container = $(self.divSelector);

    //self.elevationData =  liveResults.routes.elevationsPoints;

    self.chartConfig = null;
    self.chartData = null;

    self.Chart = null;

    self.RawRoutes = null;
    self.AvailableDataModels = ko.observableArray([]);
    self.SelectedDataModel = ko.observable(null);
    self.SelectedDataModel.subscribe(function (newValue) {
        if (newValue) {
            self.draw();
        }
    });

    self.Markers = [];

    self.setDataModels = function (routesModel) {

        self.container = $(self.divSelector);

        self.RawRoutes = routesModel;

        if (self.container.length) {

            if (liveResults.cfgMgr.listReady) {
                self.coreSetDataModels();
            } else {
                liveResults.cfgMgr.intListsCallback = self.coreSetDataModels;
            }
            
        }
    }
    self.coreSetDataModels = function () {

        var _routesModels = _(self.RawRoutes);
            var disc = liveResults.cfgMgr.disciplineList();
            if (!disc || !disc.length) disc = [{ TrainingTypeId: 3 }, { TrainingTypeId: 1 }, { TrainingTypeId: 2 }];

            koArrayCopyComplex(disc, self.AvailableDataModels, false, function (disc) {
                var route = _routesModels.find(function (elem) { return elem.Type == disc.TrainingTypeId });
                if (route) {
                    if (!route.ElevationPoints || !route.ElevationPoints.length) return undefined;
                    route.Selected = ko.computed(function () {
                        return self.SelectedDataModel() == this;
                    }, route);
                    route.Css = ko.computed(function () {
                        return this.TrainingTypeIcon + ' ' + (this.Selected() ? 'cze' : '');
                    }, route);
                    return route;
                }
            });

            if (self.RawRoutes && self.RawRoutes.length && self.AvailableDataModels().length) {
                self.SelectedDataModel(self.AvailableDataModels()[0]);
                self.addPersonMarks();
            }        
    }

    self.setMarkers = function (markers) {
        self.Markers = markers.slice(0);
        self.Markers.reverse();
        self.addPersonMarks();
    }

    self.setConfig = function () {
        if (!self.chartConfig) {
            var properChartColors = getChartColors(false);
            self.chartConfig = {
                xaxis: getDistanceXaxis(),//self.ServerModel.MaxXDistance),
                yaxes: [],
                grid: {
                    backgroundColor: '#ffffff',
                    // borderColor: { 'top': 'rgb(57, 57, 57)', 'right': 'rgb(57, 57, 57)', 'bottom': 'rgb(57, 57, 57)', 'left': '#ffffff' },
                    borderWidth: 0,
                    markings: [],
                    hoverable: true
                },
                legend: {
                    show: false //noColumns: result.zoneData.length, position: "se"
                },
                crosshair: {
                    mode: "x"//"y"
                },
                colors: properChartColors,
                series: {
                    shadowSize: 0
                }
            };
            self.chartConfig.xaxis.color = 'rgb(57, 57, 57)';

            self.chartConfig.xaxis.tickFormatter = function (v) {
                if (v < 0) return '';
                return Number((v).toFixed(2).toString()) + '&nbsp;km';
            };

            var right = getCustomValueYaxis("right", 'm', null, undefined, true);
            //right.max = selfDataType.MaxVal * 1.1;
            //right.labelWidth = self.labelWidth;
            right.color = 'rgb(57, 57, 57)';
            right.font = { color: 'rgb(57, 57, 57)' };
            right.autoscaleMargin = 0.2;
            self.chartConfig.yaxes.push(right);

        }
    }

    self.setData = function () {
        self.chartData = null;
        var model = self.SelectedDataModel();
        if (model) {
            self.chartData = [];
            var flotSerie = {
                data: model.ElevationPoints,
                //yaxis: 1,
                lines: { show: true, lineWidth: 2 }
            };
            self.chartData.push(flotSerie);
        }
    }

    self.draw = function () {

        self.setConfig();
        self.setData()

        self.Chart = $.plot(self.container, self.chartData, self.chartConfig);

        self.bindInteractions();

        setTimeout( self.addMatts, 500);

        setTimeout(self.addPersonMarks, 500);
    }

    self.mapTooltipHide = function () {
        liveResults.drawElevationDistancePoint(self.RawRoutes, self.SelectedDataModel().Type, null);
    }
    self.mapTooltipHideTimeout = null;
    self.bindInteractions = function () {

        self.container.bind("plothover", function (event, pos, item) {
            if (self.mapTooltipHideTimeout) clearTimeout(self.mapTooltipHideTimeout);
            liveResults.drawElevationDistancePoint(self.RawRoutes, self.SelectedDataModel().Type, pos.x);
            if (item) {
                var x = item.datapoint[0].toFixed(2),
                    y = item.datapoint[1].toFixed(2);

                $("#tooltip").html(translations.Distance + ": " + x + " km, " + translations.Elevation + ": " + y + " m")
                    .css({ top: item.pageY + 5, left: item.pageX + 5 })
                    .fadeIn(200);

            } else {
                $("#tooltip").hide();

                self.mapTooltipHideTimeout = setTimeout(self.mapTooltipHide, 1000);
            }
        });
    }

    self.addMatts = function () {
        var matts = liveResults.cfgMgr.matList();
        if (!matts || !matts.length) {
            liveResults.cfgMgr.intListsCallback = function () {
                self.coreAddMatts(liveResults.cfgMgr.matList());
                liveResults.cfgMgr.intListsCallback = null;
            };
        } else {
            self.coreAddMatts(matts);
        }
    }
    self.coreAddMatts = function (matts) {
        if (self.container && self.SelectedDataModel()) {
            var selectedDisc = self.SelectedDataModel().Type;
            _(matts).each(function (matt) {
                if (matt.TrainingTypeId == selectedDisc) {
                    var o = self.Chart.pointOffset({ x: matt.Distance / 1000, y: 0 });
                    self.container.append("<img class='matt' style='position:absolute;left:" + (o.left - 6) + "px;bottom:15px;' src='" + ICON_CHECKPOINT_MARKER + "' title='" + matt.Name + "' />");
                }
            });
        }
    }
    self.addPersonMarks = function () {
        if (self.container && self.SelectedDataModel()) {
            self.container.find('.marker').remove();
            var selectedDisc = self.SelectedDataModel().Type;
            var markers = self.Markers;
            _(markers).each(function (marker) {
                var participPoint = marker.participPoint;
                if (participPoint.TrainingTypeId == selectedDisc) {
                    var midDist = (participPoint.DistanceFrom + participPoint.DistanceTo) / 2;
                    var o = self.Chart.pointOffset({ x: midDist, y: 0 });
                    var imgUrl = ICON_POSITION_MARKER_WEST;
                    var nr = participPoint.Number;
                    var clss='';
                    if (participPoint.Multi) {
                        imgUrl = ICON_POSITION_MARKER_GROUP_WEST;
                        nr = participPoint.Multi;
                        clss='g';
                    }
                    var img = $("<span class='marker "+clss+"' style='position:absolute;left:" + (o.left - 22) + "px;bottom:21px;'><span class='nr'>" + nr + "</span><img src='" + imgUrl + "' title='" + participPoint.Tooltip + "' /></span>");
                    img.click(function () {
                        google.maps.event.trigger(marker, 'click');
                    });
                    self.container.append(img);
                }
            });
        }
    }
}
liveResults.elevationMgr = new liveResults.ElevationChartManager();
liveResults.cfgMgr.elevationMgr = liveResults.elevationMgr;
liveResults.initElevationMgr = function (routesModel) {
    liveResults.elevationMgr.setDataModels(routesModel);
}


//#endregion


//#region tables

liveResults.DetailsTemplate = null;

liveResults.LoadParticipDetails = function (nTd, periodId, participId) {
    $.ajax({
        type: "POST",
        url: ContextPath + "LiveResult/GetParticipDetails",
        data: {
            periodId: periodId, participId: participId
        }
    })
    .done(function (result) {
        nTd.html(liveResults.DetailsTemplate);
        ko.cleanNode(nTd[0]);
        var data = new ParticipDetails(result);
        nTd.data('participDetails', data);
        ko.applyBindings(data, nTd[0]);

        if (data.WithMap) {
            liveResults.initMap(periodId, liveResults.getMapParams(), data);
        }

        liveResults.ParticipDetailsShare(new ParticipDetails(result));
    });
}

liveResults.setParticipantsDataTable = function (tableSelector, periodId, mat, discipline, category, timeStatus, mode) {
    liveResults.periodId = periodId;
    liveResults.cfgMgr.activeTable = true;
    if (timeStatus) {
        liveResults.cfgMgr.TimeStatus(timeStatus);
        switch (timeStatus) {
            case 'before': liveResults.cfgMgr.Mode('start'); break;
            case 'after': liveResults.cfgMgr.Mode('meta'); break;
            case 'during': liveResults.cfgMgr.Mode('live'); break;
        }
    }
    if (mode) liveResults.cfgMgr.Mode(mode);
    liveResults.cfgMgr.initCfg(mat, discipline, category, periodId, tableSelector, true);
    liveResults.initParticipantsDataTable();
}

liveResults.initParticipantsDataTable = function (searchVal, length) {

    var cfg = liveResults.cfgMgr;

    var table = $(cfg.tableSelector);

    var container = table.closest('.tableContainer');
    var width = container.width();

    liveResults.widthSpace = width;

    var mat = cfg.mat(); if (!mat) mat = 0;
    var disc = cfg.discipline(); if (!disc) disc = 0;

    var first = liveResults.participantsTable == null;

    var mode = cfg.Mode(); //start, live, meta, disc
    var sortCol = 1;
    switch (mode) {
        case 'start': sortCol = 1; break;
        case 'live': sortCol = 18; break;
        case 'fan': sortCol = 18; break;
        case 'meta': sortCol = 17; break;
        case 'disc': sortCol = 17; break;
        case 'matt': sortCol = 18; break;
    }


    liveResults.participantsTable = table.dataTable({
        "oLanguage": dataTableLanguageOptions,
        "aoColumns": [
            { "bVisible": false },                                        //0 - Id
            { "bVisible": true, "sWidth": "55px", "sClass": "center" },   //1 - StartingNumber
            { "bVisible": true, "sClass": "upper" },                                         //2 - FirstName
            { "bVisible": true, "sClass": "upper" },                                         //3 - LastName
            { "bVisible": mode == 'start', "sWidth": "50px", "sClass": "center" },   //4 - BirthYearShort
            { "bVisible": mode == 'start', "sWidth": "30px", "sClass": "center" },   //5 - Sex
            { "bVisible": true, "sClass": "upper" },                                         //6 - Country
            { "bVisible": mode == 'start', "sClass": "upper" },                              //7 - City
            { "bVisible": mode == 'start' },                              //8 - Club
            { "bVisible": true },                                                             //9 - Category
            { "bVisible": mode == 'meta' || mode == 'disc', "sClass": "center" },             //10 - CzasPlywanie
            { "bVisible": mode == 'disc', "sClass": "center" },                               //11 - MiejscePlywanie
            { "bVisible": mode == 'disc', "sClass": "center" },                               //12 - CzasRower
            { "bVisible": mode == 'disc', "sClass": "center" },                               //13 - MiejsceRower
            { "bVisible": mode == 'meta', "sClass": "center" },                               //14 - CzasPoRowerze
            { "bVisible": mode == 'disc', "sClass": "center" },                               //15 - CzasBieg
            { "bVisible": mode == 'disc', "sClass": "center" },                               //16 - MiejsceBieg
            { "bVisible": mode == 'meta' || mode == 'disc', "sClass": "center" },             //17 - TimeMeta
            { "bVisible": mode == 'live' || mode == 'fan' || mode == 'matt', "sClass": "center" },                               //18 - TimeLive
            { "bVisible": mode == 'live' || mode == 'fan', "sClass": "center" },                               //19 - MeasurePlace
            { "bVisible": mode == 'live' || mode == 'fan', "sClass": "center" },                               //20 - StartaOpen
            { "bVisible": mode == 'live' || mode == 'fan', "sClass": "center" },                               //21 - StrataKat
            { "bVisible": mode == 'live' || mode == 'fan' || mode == 'matt', "sClass": "center" },                               //22 - MiejsceOpen live
            { "bVisible": mode == 'meta', "sClass": "center" },                               //23 - MiejsceOpen
            { "bVisible": mode == 'live' || mode == 'fan' || mode == 'matt', "sClass": "center" },                               //24 - MiejsceKat live
            { "bVisible": mode == 'meta', "sClass": "center" },                               //25 - MiejsceKat
            /*
            { "bVisible": mat == 0 && disc == 0 },    //TimeBrutto
            { "bVisible": false },    //timeNetto
            { "bVisible": mat > 0 },    //StageTime
            { "bVisible": false },    //miejsce w kategorii
            { "bVisible": disc > 0 },    //stage time
*/
        ],
        "aaSorting": [[sortCol, 'asc']],
        "iDisplayLength": length ? length : 50,
        "oSearch": { "sSearch": searchVal },
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": ContextPath + "LiveResult/GetParticipants",
        "bServerSide": true,
        "fnServerData": function (sSource, aoData, fnCallback, oSettings) {

            var mode = cfg.Mode();
            var m = cfg.mat(); if (!m) m = 0;
            var d = cfg.discipline(); if (!d) d = 0;
            var c = cfg.category(); if (!c || c == translations.All) c = '';

            var male = cfg.Male();
            var pro = cfg.Pro();

            cfg.GetCheckPointsCounts(c);

            aoData.push({ name: "mode", value: mode });
            aoData.push({ name: "periodId", value: liveResults.periodId });
            aoData.push({ name: "mat", value: m });
            aoData.push({ name: "discipline", value: d });
            aoData.push({ name: "category", value: c });
            aoData.push({ name: "male", value: male });
            aoData.push({ name: "pro", value: pro });
            oSettings.jqXHR = $.ajax({
                "dataType": 'json',
                "type": "POST",
                "url": sSource,
                "data": aoData,
                "success": fnCallback
            });
        },
        "sServerMethod": "POST"
    });

    if (first) {
        table.on('click', 'tr', function () {
            var position = liveResults.participantsTable.fnGetPosition(this);

            var data = liveResults.participantsTable.fnGetData(position);
            if (data) {
                var id = data[0];

                var thisRow = $(this);

                var thisIsOpen = thisRow.data('open');

                //ukryj inne
                table.find('tr.details').remove();
                table.find('tr').data('open', false);

                if (!thisIsOpen) {
                    thisRow.data('open', true);
                    //stworz nową linie
                    var tds = thisRow.find('td');
                    var nTr = $('<tr></tr>');
                    nTr.addClass('details');
                    thisRow.after(nTr);
                    var nTd = $('<td></td>');
                    nTd.attr('colspan', tds.length);
                    nTr.append(nTd);
                    var nDiv = $('<div class=""></div>');
                    nTd.html('<img src="' + ContextPath + 'Content/images/ajax-loader.gif" alt="' + translations.sLoadingRecords + '" title="' + translations.sLoadingRecords + '" />');

                    if (!liveResults.DetailsTemplate) {
                        $.ajax({
                            type: "POST",
                            url: ContextPath + "LiveResult/ParticipTableDetailsTemplate",
                            data: {
                                periodId: cfg.periodId
                            }
                        })
                     .done(function (result) {
                         liveResults.DetailsTemplate = result;
                         liveResults.LoadParticipDetails(nTd, cfg.periodId, id);
                     });

                    } else {
                        liveResults.LoadParticipDetails(nTd, cfg.periodId, id);
                    }
                }
            }
        });

        // $(window).off("resize").on('resize', liveResults.resizeWindow);
    }
};

liveResults.resizeWindow = function () {
    var container = liveResults.participantsTable.closest('.tableContainer');
    var newWidth = container.width();
    var diff = newWidth - liveResults.widthSpace;

    if (diff > 50 || diff < -50) {
        liveResults.reInitTable();
    }
}

liveResults.reInitTable = function () {
    var searchVal = '';
    var length = 50;
    if (liveResults.participantsTable) {
        searchVal = $('.dataTables_filter input').val();
        length = parseInt($('.dataTables_length select').val());
        liveResults.participantsTable.fnDestroy();
    }
    liveResults.initParticipantsDataTable(searchVal, length);
}

liveResults.participantsDataTableReload = function () {
    if (liveResults.participantsTable)
        liveResults.participantsTable.fnDraw();
}

//#endregion

//#region single participant

liveResults.initParticipant = function (periodId, participId, callback) {
    liveResults.cfgMgr.initCfg(0, 0, '', periodId, null, true);
    $.ajax({
        type: "POST",
        url: ContextPath + "LiveResult/GetParticipDetails",
        data: { periodId: periodId, participId: participId }
    })
     .done(function (result) {
         var mapParams = liveResults.getMapParams();
         liveResults.ParticipDetails(new ParticipDetails(result));
         liveResults.ParticipDetails().FullPage(true);
         liveResults.ParticipDetailsShare(new ParticipDetails(result));
         if (liveResults.ParticipDetails().WithMap) {
             liveResults.initMap(periodId, mapParams);
         }
         if (callback) callback();
     });
}
liveResults.reloadParticipant = function (expandDetails) {
    var ex = liveResults.ParticipDetails();
    if (ex) {
        liveResults.initParticipant(ex.PeriodId, ex.Id, function () {
            if (expandDetails) {
                $('a[href="#detailsContainer"]').click();
            }
        });
    }
}

liveResults.ParticipDetails = ko.observable(null);
liveResults.ParticipDetailsShare = ko.observable(null);

GeneralModel.addProperty("LiveResults", liveResults);

ParticipDetails = function (serverModel, afterMode) {
    var self = $.extend(this, serverModel);
    self.initMode = false;

    self.cfgMgr = afterMode ? new liveResults.ConfigManager() : liveResults.cfgMgr;

    self.CompetitorUrl = self.getParticipUrl(self);

    self.NextParicipInCategoryUrl = self.getParticipUrl(self.NextParicipInCategory);
    self.NextParicipantUrl = self.getParticipUrl(self.NextParicipant);
    self.PrevParicipInCategoryUrl = self.getParticipUrl(self.PrevParicipInCategory);
    self.PrevParicipantUrl = self.getParticipUrl(self.PrevParicipant);

    self.Notes = ko.observable(self.PublicNotes);
    self.WaveId = ko.observable(self.WaveId);
    self.NrOfFans = ko.observable(self.NrOfFans);

    self.AvailableWaves = self.cfgMgr.wavesList;
    if (!self.AvailableWaves().length) {
        self.GetAvailableWaves(self.WaveId());
    }
    self.WaveId(serverModel.WaveId);

    self.WithMap = afterMode || self.cfgMgr.WithMap;// && !self.CheckPointsReads.Finished;
    self.FullPage = ko.observable(afterMode);

    self.CheckPointsReads.ReverseCheckPointsSequence = self.CheckPointsReads.CheckPointsSequence.slice(0);
    self.CheckPointsReads.ReverseCheckPointsSequence.reverse();

    //setTimeout(self.mergeReadsToConfig, 100);

    self.mat = ko.observable(0);
    self.discipline = ko.observable(0);
    self.mat.subscribe(function (newValue) {
        if (!self.initMode) {
            self.initMode = true;
            if (newValue == -1) self.mat(0);
            self.discipline(0);
            self.initMode = false;
        }
    });
    self.discipline.subscribe(function (newValue) {
        if (!self.initMode) {
            self.initMode = true;
            self.mat(0);
            self.initMode = false;
        }
    });
    if (IsAdmin) {
        self.competitorMattsCtrl = ko.computed(function () {
            var cps = self.CheckPointsReads.CheckPointsSequence;
            var last = self.CheckPointsReads.LastReadCheckPoint;
            var lastId = last && last.BruttoRank ? last.CheckPoint.Id : 0;
            if (lastId) self.mat(lastId);

            var _cps = _(cps);

            var result = self.cfgMgr.mattsCtrl();

            if (result.length > 0 && cps.length > 0) {
                for (var i_disc = 0; i_disc < result.length; i_disc++) {
                    var disc = result[i_disc];
                    for (var i_mat = 0; i_mat < disc.matList.length; i_mat++) {
                        var mat = disc.matList[i_mat];
                        mat.reads = _cps.find(function (elem) { return elem.CheckPoint.Id == mat.Id });
                        mat.isLast = (mat.Id == lastId);
                    }
                }
            }

            return result;
        });
    }

    self.FinishFail = ko.observable(self.FinishFail);
    self.FinishFail.subscribe(function (newValue) {
        self.setFinishFail(newValue);
    });

    _.bindAll(self,
        'openInvalidReads',
        'DeleteParticipant',
        'SetParticipantWave',
        'SetParticipantNotes',
        //'SetParticipChpReadValid',
        //'SetParticipReadValid',
        'coreDeleteParticipant',
        'beFan',
        'openReads',
        'setFinishFail'
        );
}

ParticipDetails.prototype = {
    openInvalidReads: function () {
        var self = this;
        var content = '<div class="invalidReads">';

        var ir = self.InvalidReadsStr;
        if (ir) {
            for (var i = 0, len = ir.length; i < len; i++) {
                var irr = ir[i];
                content += '<div><span>' + irr[0] + '</span> <span>' + irr[1] + '</span> <span>' + irr[2] + '</span></div>';
            }
        }

        content += '</div>';

        ShowEmptyMsg(content, translations.Info);
    },
    DeleteParticipant: function () {
        modalAskModel.Show(
            translations.Confirm,
            translations.DeleteLiveParticipMsg,
            this.coreDeleteParticipant,
            translations.Delete);
    },
    coreDeleteParticipant: function () {
        var self = this;

        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/DeleteParticipant",
            data: {
                periodId: self.PeriodId, participId: self.Id
            }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                window.location = ContextPath + "LiveResult/Index?periodId=" + self.PeriodId;
            } else {
                ShowEmptyMsg(translations.DeleteParticipantFailed, translations.Error);
            }
        });
    },
    SetParticipantWave: function () {
        var self = this;
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SetParticipantWave",
            data: {
                periodId: self.PeriodId, participId: self.Id, waveId: self.WaveId()
            }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                liveResults.reloadParticipant();
            } else {
                ShowEmptyMsg(translations.Error, translations.Error);
            }
        });
    },
    SetParticipantNotes: function (data, event) {
        var self = this;
        var infoContainer = event ? $(event.target) : null;
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SetParticipantNotes",
            data: {
                periodId: self.PeriodId, participId: self.Id, notes: self.Notes()
            }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                ShowSavedAlert(infoContainer);
            } else {
                ShowEmptyMsg(translations.Error, translations.Error);
            }
        });
    },
    /*
    SetParticipChpReadValid: function (checkPointId, inValid) {
        var self = this;

        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SetParticipChpReadValid",
            data: {
                periodId: self.PeriodId, participId: self.Id, checkPointId: checkPointId, inValid: inValid
            }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                liveResults.reloadParticipant();
            } else {
                ShowEmptyMsg(translations.Error, translations.Error);
            }
        });
    },
    SetParticipReadValid: function (readId, inValid) {
        var self = this;
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SetParticipChpReadValid",
            data: {
                periodId: self.PeriodId, participId: self.Id, readId: readId, inValid: inValid
            }
        })
        .done(function (result) {
            if (result && result == 'ok') {
                liveResults.reloadParticipant();
            } else {
                ShowEmptyMsg(translations.Error, translations.Error);
            }
        });
    },*/
    GetAvailableWaves: function (selId) {
        var self = this;
        if (IsAdmin) {
            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/GetAvailableWaves",
                data: {
                    periodId: self.PeriodId
                }
            })
            .done(function (serverModel) {
                if (serverModel) {
                    koArrayCopy(serverModel, self.AvailableWaves, false, false);
                    if (selId) self.WaveId(selId);
                }
            });
        }
    },
    getParticipUrl: function (particip) {
        if (!particip) return '';
        return ContextPath + "LiveResult/Competitor?periodId=" + particip.PeriodId + "&participId=" + particip.Id;
    },
    beFan: function () {
        var self = this;
        //BeFan(int periodId, int participId)
        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/BeFan",
            data: {
                periodId: self.PeriodId, participId: self.Id
            }
        })
            .done(function (serverModel) {
                if (!isNaN(serverModel)) {
                    self.NrOfFans(serverModel)
                }
            });
    },
    openReads: function (data, event) {
        var self = this;
        liveResults.LiveResultReadsManager().LoadReads(self.Id, data.CheckPoint.Id)
    },
    setFinishFail: function (fail) {
        var self = this;

        $.ajax({
            type: "POST",
            url: ContextPath + "LiveResult/SetFinishFail",
            data: {
                periodId: self.PeriodId, participId: self.Id, fail: fail
            }
        })
            .done(function (serverModel) {

            });
    }
}

//#endregion

//#region entry edit
if (IsAdmin) {

    LiveResultReadsManager = function () {
        var self = this;
        self.Reads = ko.observableArray([]);
        self.Editable = ko.observable(null);

        self.LoadReads = function (participId, mattId) {
            self.Reads.removeAll();
            self.Editable(null);
            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/GetParticipantReads",
                data: {
                    periodId: liveResults.cfgMgr.periodId, participId: participId, mattId: mattId
                }
            })
           .done(function (serverModel) {
               if (serverModel) {
                   var first = null;
                   if (serverModel.length) {
                       first = serverModel[0];
                       if (first.Manual) {
                           var f = new LiveResultReadModel(self);
                           f.initByModel(first);
                           self.Editable(f);
                       } else {
                           first = null;
                       }
                   }
                   koArrayCopyComplex(serverModel, self.Reads, false, function (e) { if (e != first) { var p = new LiveResultReadModel(self); p.initByModel(e); return p; } });
               }
           });
            loadAndBindTemplate({
                simpleContainer: 'readsEditModalContainer',
                existingElem: '#readsEditModal',
                ajaxPath: 'LiveResult/ReadsEditor',
                callback: function () {
                    $('#readsEditModal').modal('show');
                }
            });
        }


        self.save = function (data, event) {
            var self = this;

            var data = [];
            var edit = self.Editable();
            var eTime = edit.BruttoTime();
            if (eTime && eTime.TotalMilliseconds) {
                data.push(edit.getClearData())
            }
            var rs = self.Reads();
            for (var i = 0; i < rs.length; i++) {
                var re = rs[i];
                data.push(re.getClearData());
            }

            $.ajax({
                type: "POST",
                url: ContextPath + "LiveResult/SetParticipantReads",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: JSON.stringify(data)
            })
             .done(function (result) {
                 $('#readsEditModal').modal('hide');
                 liveResults.reloadParticipant(true);
             });

        }
    }
    liveResults.LiveResultReadsManager = ko.observable(new LiveResultReadsManager());

    LiveResultReadModel = function (parent) {
        var self = this;
        self.Parent = parent;
        self.BaseObj = null;

        self.DbReadId = ko.observable(0);
        self.MattId = ko.observable(0);
        self.RunId = ko.observable(0);
        self.StartId = ko.observable(0);
        self.Invalid = ko.observable(false);
        self.Manual = ko.observable(false);
        self.BruttoTime = ko.observable(null);
        self.ClockTime = ko.observable(null);
        self.RawTime = ko.observable(null);
        self.RawTimeMiliSecs = ko.observable(0);
        self.StartTime = ko.observable(null);
        self.ZeroClockTime = ko.observable(null);
        self.Info = ko.observable('');

        self.BruttoTimeStr = ko.pureComputed({
            read: function () {
                var val = self.BruttoTime();
                if (val != null && _.isNumber(val.TotalMilliseconds)) {
                    return MsToString(val.TotalMilliseconds);
                } else {
                    return '';
                }
            },
            write: function (value) {
                var secs = stringToSeconds(value);
                var d = secs * 1000;
                self.BruttoTime({ TotalMilliseconds: d });

                var sT = self.StartTime();
                if (sT != null) {
                    self.RawTime({ TotalMilliseconds: d + sT.TotalMilliseconds });
                }

                var sT2 = self.ZeroClockTime();
                if (sT != null) {
                    self.ClockTime({ TotalMilliseconds: d + sT2.TotalMilliseconds + sT.TotalMilliseconds });
                }
            }
        });
        self.ClockTimeStr = ko.pureComputed({
            read: function () {
                var val = self.ClockTime();
                if (val != null && _.isNumber(val.TotalMilliseconds)) {
                    return MsToString(val.TotalMilliseconds);
                } else {
                    return '';
                }
            },
            write: function (value) {
                var secs = stringToSeconds(value);
                var d = secs * 1000;
                self.ClockTime({ TotalMilliseconds: d });

                var sT = self.ZeroClockTime();
                if (sT != null) {
                    self.BruttoTime({ TotalMilliseconds: d - sT.TotalMilliseconds });
                }

                sT = self.StartTime();
                if (sT != null) {
                    self.RawTime({ TotalMilliseconds: d + sT.TotalMilliseconds });
                }
            }
        });
        self.RawTimeStr = ko.pureComputed({
            read: function () {
                var val = self.RawTime();
                if (val != null && _.isNumber(val.TotalMilliseconds) && val.TotalMilliseconds) {
                    return MsToString(val.TotalMilliseconds);
                } else {
                    return '';
                }
            },
            write: function (value) {
                var secs = stringToSeconds(value);
                var d = secs * 1000;
                self.RawTime({ TotalMilliseconds: d });

                var sT = self.StartTime();
                if (sT != null) {
                    self.BruttoTime({ TotalMilliseconds: d - sT.TotalMilliseconds });
                }

                sT = self.ZeroClockTime();
                if (sT != null) {
                    self.ClockTime({ TotalMilliseconds: d + sT.TotalMilliseconds });
                }

            }
        });
    }

    LiveResultReadModel.prototype = {
        init: function () {
            var self = this;
            self.DbReadId(0);
            self.MattId(0);
            self.RunId(0);
            self.StartId(0);
            self.Invalid(false);
            self.Manual(false);
            self.BruttoTime(null);
            self.ClockTime(null);
            self.RawTime(null);
            self.RawTimeMiliSecs(0);
            self.StartTime(null);
            self.ZeroClockTime(null);
            self.Info('');
        },
        CancelChanges: function () {
            var self = this;
            if (self.BaseObj) {
                self.initByModel(self.BaseObj);
            }
        },
        initByModel: function (serverModel) {
            var self = this;
            if (serverModel) {
                self.BaseObj = serverModel;
                self.DbReadId(serverModel.DbReadId);
                self.MattId(serverModel.MattId);
                self.RunId(serverModel.RunId);
                self.StartId(serverModel.StartId);
                self.Invalid(serverModel.Invalid);
                self.Manual(serverModel.Manual);
                self.BruttoTime(serverModel.BruttoTime);
                self.ClockTime(serverModel.ClockTime);
                self.RawTime(serverModel.RawTime);
                self.RawTimeMiliSecs(serverModel.RawTimeMiliSecs);
                self.StartTime(serverModel.StartTime);
                self.ZeroClockTime(serverModel.ZeroClockTime);
                self.Info(serverModel.Info);
            }
        },
        getClearData: function () {
            var self = this;
            var data = {
                DbReadId: self.DbReadId(),
                MattId: self.MattId(),
                RunId: self.RunId(),
                StartId: self.StartId(),
                Invalid: self.Invalid(),
                Manual: self.Manual(),
                RawTimeMiliSecs: self.RawTime() ? self.RawTime().TotalMilliseconds : 0
            }
            return data;
        }
    }


}
//#endregion

//#region ko bindings/ctrls

ko.bindingHandlers.rankCtrl = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();


        var html = '<span class="rankCtrl">';
        if (!valueUnwrapped) {
            html += '-';
        } else {
            html += '<span class="rank main">' + valueUnwrapped + '</span>';
            var prevRank = allBindings.prevRank; if (!prevRank) prevRank = 0;

            var stagRank = allBindings.stageRank;
            var showStage = typeof (stagRank) != 'undefined';
            if (!stagRank) stagRank = 0;

            if (prevRank != valueUnwrapped) {
                if (prevRank == 0) {
                    html += ' (<span title="' + translations.ChangeToPrevious + '" class="rank">-</span>';
                } else {
                    html += ' (<span title="' + translations.ChangeToPrevious + '" class="rank ' + (prevRank < 0 ? 'up' : (prevRank > 0 ? 'down' : '')) + '">' + (prevRank < 0 ? -prevRank : prevRank) + '</span>';
                }
                if (showStage && prevRank != stagRank) {
                    if (stagRank == 0) {
                        html += ' / <span title="' + translations.ChangeToSatge + '" class="rank">-</span>';
                    } else {
                        html += ' / <span title="' + translations.ChangeToSatge + '" class="rank ' + (stagRank < 0 ? 'up' : (stagRank > 0 ? 'down' : '')) + '">' + (stagRank < 0 ? (-stagRank) : stagRank) + '</span>';
                    }
                }
                html += ')';
            }
        }
        html += '</span>';

        $(element).html(html);
    }
};


ko.bindingHandlers.mToKm = {
    update: function (element, valueAccessor, allBindingsAccesor) {
        // First get the latest data that we're bound to
        var value = valueAccessor();
        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.unwrap(value);

        var allBindings = allBindingsAccesor();

        var zero = allBindings.zero;

        var html = '';
        if (!valueUnwrapped || isNaN(valueUnwrapped)) {
            html += '-';
        } else {
            if (zero && valueUnwrapped < 0) { valueUnwrapped = 0; }
            if (valueUnwrapped < 1000 && valueUnwrapped > 0) {
                html += Math.round(valueUnwrapped) + ' m';
            } else {
                html += (Math.round(valueUnwrapped / 100) / 10) + ' km';
            }
        }
        html += '';

        $(element).html(html);
    }
};

//#endregion
;var isGoogleChartLoaded = false;
function loadGoogleChart() {
    if (!isGoogleChartLoaded) {
        google.charts.load('current', { 'packages': ['corechart'], 'language': translations.lang });
    }
};// http://spin.js.org/#v2.3.2
!function(a,b){"object"==typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return m[e]||(k.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",k.cssRules.length),m[e]=1),e}function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice(1),void 0!==e[b])return b;for(d=0;d<l.length;d++)if(c=l[d]+b,void 0!==e[c])return c}function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)void 0===a[d]&&(a[d]=c[d])}return a}function g(a,b){return"string"==typeof a?a:a[b%a.length]}function h(a){this.opts=f(a||{},h.defaults,n)}function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',c)}k.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.scale*d.width,left:d.scale*d.radius,top:-d.scale*d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.scale*(d.length+d.width),k=2*d.scale*j,l=-(d.width+d.length)*d.scale*2+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}}var j,k,l=["webkit","Moz","ms","O"],m={},n={lines:12,length:7,width:5,radius:10,scale:1,corners:1,color:"#000",opacity:.25,rotate:0,direction:1,speed:1,trail:100,fps:20,zIndex:2e9,className:"spinner",top:"50%",left:"50%",shadow:!1,hwaccel:!1,position:"absolute"};if(h.defaults={},f(h.prototype,{spin:function(b){this.stop();var c=this,d=c.opts,f=c.el=a(null,{className:d.className});if(e(f,{position:d.position,width:0,zIndex:d.zIndex,left:d.left,top:d.top}),b&&b.insertBefore(f,b.firstChild||null),f.setAttribute("role","progressbar"),c.lines(f,c.opts),!j){var g,h=0,i=(d.lines-1)*(1-d.direction)/2,k=d.fps,l=k/d.speed,m=(1-d.opacity)/(l*d.trail/100),n=l/d.lines;!function o(){h++;for(var a=0;a<d.lines;a++)g=Math.max(1-(h+(d.lines-a)*n)%l*m,d.opacity),c.opacity(f,a*d.direction+i,g,d);c.timeout=c.el&&setTimeout(o,~~(1e3/k))}()}return c},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=void 0),this},lines:function(d,f){function h(b,c){return e(a(),{position:"absolute",width:f.scale*(f.length+f.width)+"px",height:f.scale*f.width+"px",background:b,boxShadow:c,transformOrigin:"left",transform:"rotate("+~~(360/f.lines*k+f.rotate)+"deg) translate("+f.scale*f.radius+"px,0)",borderRadius:(f.corners*f.scale*f.width>>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k<f.lines;k++)i=e(a(),{position:"absolute",top:1+~(f.scale*f.width/2)+"px",transform:f.hwaccel?"translate3d(0,0,0)":"",opacity:f.opacity,animation:j&&c(f.opacity,f.trail,l+k*f.direction,f.lines)+" "+1/f.speed+"s linear infinite"}),f.shadow&&b(i,e(h("#000","0 0 4px #000"),{top:"2px"})),b(d,b(i,h(g(f.color,k),"0 0 1px rgba(0,0,0,.1)")));return d},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}}),"undefined"!=typeof document){k=function(){var c=a("style",{type:"text/css"});return b(document.getElementsByTagName("head")[0],c),c.sheet||c.styleSheet}();var o=e(a("group"),{behavior:"url(#default#VML)"});!d(o,"transform")&&o.adj?i():j=d(o,"animation")}return h});;/**
 * Copyright (c) 2011-2014 Felix Gnass
 * Licensed under the MIT license
 * http://spin.js.org/
 */

/*

Basic Usage:
============

$('#el').spin() // Creates a default Spinner using the text color of #el.
$('#el').spin({ ... }) // Creates a Spinner using the provided options.

$('#el').spin(false) // Stops and removes the spinner.

Using Presets:
==============

$('#el').spin('small') // Creates a 'small' Spinner using the text color of #el.
$('#el').spin('large', '#fff') // Creates a 'large' white Spinner.

Adding a custom preset:
=======================

$.fn.spin.presets.flower = {
  lines:   9
, length: 10
, width:  20
, radius:  0
}

$('#el').spin('flower', 'red')

*/

;(function(factory) {

  if (typeof exports == 'object') {
    // CommonJS
    factory(require('jquery'), require('spin.js'))
  } else if (typeof define == 'function' && define.amd) {
    // AMD, register as anonymous module
    define(['jquery', 'spin'], factory)
  } else {
    // Browser globals
    if (!window.Spinner) throw new Error('Spin.js not present')
    factory(window.jQuery, window.Spinner)
  }

}(function($, Spinner) {

  $.fn.spin = function(opts, color) {

    return this.each(function() {
      var $this = $(this)
        , data = $this.data()

      if (data.spinner) {
        data.spinner.stop()
        delete data.spinner
      }
      if (opts !== false) {
        opts = $.extend(
          { color: color || $this.css('color') }
        , $.fn.spin.presets[opts] || opts
        )
        data.spinner = new Spinner(opts).spin(this)
      }
    })
  }

  $.fn.spin.presets = {
    tiny:  { lines:  8, length: 2, width: 2, radius: 3 }
  , small: { lines:  8, length: 4, width: 3, radius: 5 }
  , large: { lines: 10, length: 8, width: 4, radius: 8 }
  }

}));
;