<commit_before>/*!
 * Copyright 2006 - 2015 TubePress LLC (http://tubepress.com).
 * This file is part of TubePress (http://tubepress.com).
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*global jQuery, console */
/*jslint devel: true, browser: true, sloppy: false, white: true, maxerr: 50, indent: 4 */

/**
 * This file serves as the core of TubePress's JavaScript presence. It contains
 *
 *  1. Utility functions, mostly to help with language and runtime tasks.
 *  2. An event bus.
 *  3. Functions to asychronously load other TubePress scripts as needed.
 */

/**
 * IE8 and below forces us to declare these now.
 *
 * http://tobyho.com/2013/03/13/window-prop-vs-global-var/
 */
var tubePressDomInjector,
    tubePressBeacon,

    TubePress = (function (jquery, win) {

    /** http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ */
    'use strict';

    /**
     * Let's start with some variable declarations to help us with compression.
     */
    var text_tubepress      = 'tubepress',
        text_ajax           = 'ajax',
        text_base           = 'base',
        text_css            = 'css',
        text_gallery        = 'gallery',
        text_head           = 'head',
        text_http           = 'http',
        text_js             = 'js',
        text_php            = 'php',
        text_script         = 'script',
        text_src            = 'src',
        text_sys            = 'sys',
        text_text           = 'text',
        text_urls           = 'urls',
        text_web            = 'web',
        text_dot            = '.',
        text_path_separator = '/',
        text_empty          = '',
        windowLocation      = win.location,
        dokument            = win.document,
        troo                = true,
        fawlse              = false,

        /**
         * Random language utilities.
         */
        langUtils = (function () {

            var isDefined = function (obj) {

                return typeof obj !== 'undefined';
            },

                getParameterByName = function (name) {

                    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");

                    var regexS  = "[\\?&]" + name + "=([^&#]*)",
                        regex   = new RegExp(regexS),
                        results = regex.exec(windowLocation.search);

                    return results === null ? text_empty : decodeURIComponent(results[1].replace(/\+/g, ' '));
                },

                parseIntOrZero = function (candidate) {

                    var firstPass = parseInt(candidate, 10);

                    return isNaN(firstPass) ? 0 : firstPass;
                },

                /**
                 * Utility to wait for test() to be true, then call callback()
                 */
                callWhenTrue = function (callback, test, delay) {

                    /** It's ready... */
                    if (test() === troo) {

                        callback();

                        return;
                    }

                    /** Set up a timeout callback. */
                    var func = function () {

                        callWhenTrue(callback, test, delay);
                    };

                    /** Keep waiting... */
                    setTimeout(func, delay);
                },

                //http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key
                hasOwnNestedProperty = function (o) {

                    if (!o) {

                        return fawlse;
                    }

                    var args = Array.prototype.slice.call(arguments),
                        obj  = args.shift(),
                        i;

                    for (i = 0; i < args.length; i += 1) {

                        if (!obj.hasOwnProperty(args[i])) {

                            return fawlse;
                        }

                        obj = obj[args[i]];
                    }

                    return true;
                },

                trimSlashes = function (haystack, leading) {

                    var search;

                    if (leading) {

                        search = /^\/+/;

                    } else {

                        search = /\/+$/;
                    }

                    return haystack.replace(search, text_empty);
                };

            return {

                isDefined            : isDefined,
                getParameterByName   : getParameterByName,
                parseIntOrZero       : parseIntOrZero,
                callWhenTrue         : callWhenTrue,
                hasOwnNestedProperty : hasOwnNestedProperty,
                trimSlashes          : trimSlashes
            };
        }()),

        /**
         * Logger!
         */
        logger = (function () {

            /**
             * Is the log on?
             */
            var isLoggingRequested = windowLocation.search.indexOf(text_tubepress + '_debug=true') !== -1,
                windowConsole      = win.console,
                isLoggingAvailable = langUtils.isDefined(windowConsole),

                /**
                 * The log is on if it's been enabled and requested.
                 */
                isLoggingOn = function () {

                    return isLoggingRequested && isLoggingAvailable;
                },

                /**
                 * Output a message.
                 */
                log = function (msg) {

                    windowConsole.log(text_tubepress + ': ' + msg);
                },

                dir = function (obj) {

                    windowConsole.dir(obj);
                };

            return {

                on  : isLoggingOn,
                log : log,
                dir : dir
            };
        }()),

        /**
         * Details about our environment.
         */
        environment = (function () {

            var alreadyCalculatedBaseUrl = fawlse,
                text_usr                 = 'usr',
                cachedBaseUrl,

                getJsConfig = function () {

                    return win.TubePressJsConfig;
                },

                getBaseUrl = function () {

                    var tubePressJsConfig = getJsConfig(),
                        scripts, x, scriptSrc;

                    if (!alreadyCalculatedBaseUrl) {

                        if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_base)) {

                            cachedBaseUrl = langUtils.trimSlashes(tubePressJsConfig[text_urls][text_base], fawlse);

                        } else {

                            //http://stackoverflow.com/questions/2161159/get-script-path
                            dokument.getElementsByTagName(text_script);
                            x       = 0;

                            for (x; x < scripts.length; x += 1) {

                                scriptSrc = scripts[x][text_src];

                                if (scriptSrc.indexOf(text_path_separator + text_tubepress + text_dot + text_js) !== -1) {

                                    cachedBaseUrl = langUtils.trimSlashes(
                                        scriptSrc.substr(0, scriptSrc.lastIndexOf(text_path_separator))
                                            .split('?')[0]
                                            .replace(text_web + text_path_separator + text_js, text_empty)
                                        , fawlse);
                                    break;
                                }
                            }
                        }

                        alreadyCalculatedBaseUrl = troo;
                    }

                    return cachedBaseUrl;
                },

                getAjaxEndpointUrl = function () {

                    var text_ajaxEndpoint = text_ajax + 'Endpoint',
                        tubePressJsConfig = getJsConfig();

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_ajax)) {

                        return tubePressJsConfig[text_urls][text_ajax];

                    }

                    return getBaseUrl() + text_path_separator + text_web + text_path_separator + text_php +
                        text_path_separator + text_ajaxEndpoint + text_dot + text_php;
                },

                getUserContentUrl = function () {

                    var tubePressJsConfig = getJsConfig();

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_usr)) {

                        return tubePressJsConfig[text_urls][text_usr];
                    }

                    return getBaseUrl() + text_path_separator + text_tubepress + '-content';
                };

            return {

                getBaseUrl         : getBaseUrl,
                getAjaxEndpointUrl : getAjaxEndpointUrl,
                getUserContentUrl  : getUserContentUrl
            };
        }()),

        /**
         * Lightweight event bus for TubePress.
         *
         * https://gist.github.com/cowboy/661855
         *
         * jQuery Tiny Pub/Sub - v0.3 - 11/4/2010
         * http://benalman.com/
         *
         * Copyright (c) 2010 "Cowboy" Ben Alman
         * Dual licensed under the MIT and GPL licenses.
         * http://benalman.com/about/license/
         */
        beacon = (function () {

            var bus = jquery({}),

                subscribe = function () {

                    bus.on.apply(bus, arguments);
                },

                unsubscribe = function () {

                    bus.off.apply(bus, arguments);
                },

                publish = function () {

                    if (logger.on()) {

                        var args = arguments;

                        logger.log('firing event ' + args[0]);

                        if (args.length > 1) {

                            logger.dir(args[1]);
                        }
                    }

                    bus.trigger.apply(bus, arguments);
                };

            return {

                subscribe   : subscribe,
                unsubscribe : unsubscribe,
                publish     : publish
            };
        }()),

        /**
         * Injects JS and CSS into the DOM.
         */
        domInjector = (function () {

            var filesAlreadyLoaded = [],

                alreadyLoaded = function (path) {

                    return filesAlreadyLoaded[path] === troo;
                },

                convertToAbsoluteUrl = function (url, isSystem) {

                    if (url.indexOf(text_path_separator) === 0 || url.indexOf(text_http) === 0) {

                        //already absolute
                        return url;
                    }

                    var prefix;

                    if (isSystem) {

                        prefix = environment.getBaseUrl();

                    } else {

                        prefix = environment.getUserContentUrl();
                    }

                    return prefix + text_path_separator + langUtils.trimSlashes(url, troo);
                },

                doLog = function (path, type) {

                    if (logger.on()) {

                        logger.log('Injecting ' + type + ': ' + path);
                    }
                },

                appendToHead = function (element) {

                    dokument.getElementsByTagName(text_head)[0].appendChild(element);
                },

                loadCss = function (path, isSystem) {

                    isSystem = langUtils.isDefined(isSystem) ? isSystem : troo;

                    path = convertToAbsoluteUrl(path, isSystem);

                    if (alreadyLoaded(path)) {

                        return;
                    }

                    filesAlreadyLoaded[path] = troo;

                    var fileref   = dokument.createElement('link');

                    fileref.rel  = 'stylesheet';
                    fileref.type = text_text + text_path_separator + text_css;
                    fileref.href = path;

                    doLog(path, text_css);

                    appendToHead(fileref);
                },

                loadJs = function (path, isSystem) {

                    isSystem = langUtils.isDefined(isSystem) ? isSystem : troo;

                    path = convertToAbsoluteUrl(path, isSystem);

                    if (alreadyLoaded(path)) {

                        return;
                    }

                    filesAlreadyLoaded[path] = troo;

                    doLog(path, text_js);

                    var script = dokument.createElement(text_script);

                    script.type      = text_text + '/java' + text_script;
                    script[text_src] = path;
                    script.async     = troo;

                    dokument.getElementsByTagName(text_head)[0].appendChild(script);
                },

                loadSystemScript = function (scriptName) {

                    var tubePressJsConfig = win.TubePressJsConfig;

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_js, text_sys, scriptName)) {

                        loadJs(tubePressJsConfig[text_urls][text_js][text_sys][scriptName]);

                    } else {

                        loadJs(text_web + text_path_separator + text_js + text_path_separator + scriptName + text_dot + text_js);
                    }
                },

                loadGalleryJs = function () {

                    loadSystemScript(text_gallery);
                },

                loadPlayerApiJs = function () {

                    loadSystemScript('playerApi');
                },

                loadAjaxSearchJs = function () {

                    loadSystemScript(text_ajax + 'Search');
                };

            return {

                loadJs           : loadJs,
                loadCss          : loadCss,
                loadGalleryJs    : loadGalleryJs,
                loadPlayerApiJs  : loadPlayerApiJs,
                loadAjaxSearchJs : loadAjaxSearchJs
            };
        }()),

        /**
         * Core of our asychronous APIs.
         */
        asyncConverter = (function () {

            //http://tmxcredit.com/tech-blog/understanding-javascript-asynchronous-apis/
            var convertQueueToFunctionCalls = function (asyncObjectName, delegate) {

                var loggerOn = logger.on(),
                    queueLength,

                    queue = win[asyncObjectName],

                    queueCall = function (callArray) {

                        var method = callArray[0],
                            args   = callArray.slice(1);

                        delegate[method].apply(this, args);
                    };

                if (langUtils.isDefined(queue) && jquery.isArray(queue)) {

                    queueLength = queue.length;

                    if (loggerOn) {

                        logger.log('Running ' + queueLength + ' queue items for ' + asyncObjectName);
                    }

                    // loop through our existing queue, calling methods in order
                    queue.reverse();

                    while (queue.length) {

                        queueCall(queue.pop());
                    }
                }

                if (loggerOn) {

                    logger.log(asyncObjectName + ' is now connected');
                }

                // over write the sampleQueue, replacing the push method with 'queueCall'
                // this creates a globally accessible interface to your API through sampleQueue.push()
                win[asyncObjectName] = {

                    push : queueCall
                };
            };

            return {

                processQueueCalls : convertQueueToFunctionCalls
            };
        }()),

        /**
         * Handles styling DOM elements before and after loads.
         */
        loadStyler = (function () {

            /**
             * Fade to "white".
             */
            var apply = function (selector, finalOpacity) {

                    jquery(selector).fadeTo(0, finalOpacity);
                },

                applyLoadingStyle = function (selector) {

                    apply(selector, 0.3);
                },

                /**
                 * Fade back to full opacity.
                 */
                removeLoadingStyle = function (selector ) {

                    apply(selector, 1);
                };

            return {

                applyLoadingStyle  : applyLoadingStyle,
                removeLoadingStyle : removeLoadingStyle
            };
        }());

    /**
     * Handle window resize events.
     */
    (function () {

        var timeout,

            publishResizeEvent = function () {

                beacon.publish(text_tubepress + '.window.resize');
            },

            onBrowserResize = function () {

                clearTimeout(timeout);

                timeout = setTimeout(publishResizeEvent, 150);
            },

            init = function () {

                jquery(win).resize(onBrowserResize);
            };

        jquery(init);

    }());

    /**
     * Convert any queued calls to their real counterparts.
     */
    (function () {

        var textCamelCaseTubePress = 'tubePress';

        asyncConverter.processQueueCalls(textCamelCaseTubePress + 'DomInjector', domInjector);
        asyncConverter.processQueueCalls(textCamelCaseTubePress + 'Beacon', beacon);

    }());

    /**
     * Stuff we expose to everyone else.
     */
    return {

        Ajax : {
            LoadStyler : loadStyler
        },

        AsyncUtil   : asyncConverter,
        Beacon      : beacon,
        DomInjector : domInjector,
        Environment : environment,

        Lang : {

            Utils : langUtils
        },

        Logger : logger
    };

}(jQuery, window));
<commit_msg>Fixed possibility of TubePress not being able to detect its installation path via JavaScript in rare circumstances<commit_after>/*!
 * Copyright 2006 - 2015 TubePress LLC (http://tubepress.com).
 * This file is part of TubePress (http://tubepress.com).
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*global jQuery, console */
/*jslint devel: true, browser: true, sloppy: false, white: true, maxerr: 50, indent: 4 */

/**
 * This file serves as the core of TubePress's JavaScript presence. It contains
 *
 *  1. Utility functions, mostly to help with language and runtime tasks.
 *  2. An event bus.
 *  3. Functions to asychronously load other TubePress scripts as needed.
 */

/**
 * IE8 and below forces us to declare these now.
 *
 * http://tobyho.com/2013/03/13/window-prop-vs-global-var/
 */
var tubePressDomInjector,
    tubePressBeacon,

    TubePress = (function (jquery, win) {

    /** http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ */
    'use strict';

    /**
     * Let's start with some variable declarations to help us with compression.
     */
    var text_tubepress      = 'tubepress',
        text_ajax           = 'ajax',
        text_base           = 'base',
        text_css            = 'css',
        text_gallery        = 'gallery',
        text_head           = 'head',
        text_http           = 'http',
        text_js             = 'js',
        text_php            = 'php',
        text_script         = 'script',
        text_src            = 'src',
        text_sys            = 'sys',
        text_text           = 'text',
        text_urls           = 'urls',
        text_web            = 'web',
        text_dot            = '.',
        text_path_separator = '/',
        text_empty          = '',
        windowLocation      = win.location,
        dokument            = win.document,
        troo                = true,
        fawlse              = false,

        /**
         * Random language utilities.
         */
        langUtils = (function () {

            var isDefined = function (obj) {

                return typeof obj !== 'undefined';
            },

                getParameterByName = function (name) {

                    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");

                    var regexS  = "[\\?&]" + name + "=([^&#]*)",
                        regex   = new RegExp(regexS),
                        results = regex.exec(windowLocation.search);

                    return results === null ? text_empty : decodeURIComponent(results[1].replace(/\+/g, ' '));
                },

                parseIntOrZero = function (candidate) {

                    var firstPass = parseInt(candidate, 10);

                    return isNaN(firstPass) ? 0 : firstPass;
                },

                /**
                 * Utility to wait for test() to be true, then call callback()
                 */
                callWhenTrue = function (callback, test, delay) {

                    /** It's ready... */
                    if (test() === troo) {

                        callback();

                        return;
                    }

                    /** Set up a timeout callback. */
                    var func = function () {

                        callWhenTrue(callback, test, delay);
                    };

                    /** Keep waiting... */
                    setTimeout(func, delay);
                },

                //http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key
                hasOwnNestedProperty = function (o) {

                    if (!o) {

                        return fawlse;
                    }

                    var args = Array.prototype.slice.call(arguments),
                        obj  = args.shift(),
                        i;

                    for (i = 0; i < args.length; i += 1) {

                        if (!obj.hasOwnProperty(args[i])) {

                            return fawlse;
                        }

                        obj = obj[args[i]];
                    }

                    return true;
                },

                trimSlashes = function (haystack, leading) {

                    var search;

                    if (leading) {

                        search = /^\/+/;

                    } else {

                        search = /\/+$/;
                    }

                    return haystack.replace(search, text_empty);
                };

            return {

                isDefined            : isDefined,
                getParameterByName   : getParameterByName,
                parseIntOrZero       : parseIntOrZero,
                callWhenTrue         : callWhenTrue,
                hasOwnNestedProperty : hasOwnNestedProperty,
                trimSlashes          : trimSlashes
            };
        }()),

        /**
         * Logger!
         */
        logger = (function () {

            /**
             * Is the log on?
             */
            var isLoggingRequested = windowLocation.search.indexOf(text_tubepress + '_debug=true') !== -1,
                windowConsole      = win.console,
                isLoggingAvailable = langUtils.isDefined(windowConsole),

                /**
                 * The log is on if it's been enabled and requested.
                 */
                isLoggingOn = function () {

                    return isLoggingRequested && isLoggingAvailable;
                },

                /**
                 * Output a message.
                 */
                log = function (msg) {

                    windowConsole.log(text_tubepress + ': ' + msg);
                },

                dir = function (obj) {

                    windowConsole.dir(obj);
                };

            return {

                on  : isLoggingOn,
                log : log,
                dir : dir
            };
        }()),

        /**
         * Details about our environment.
         */
        environment = (function () {

            var alreadyCalculatedBaseUrl = fawlse,
                text_usr                 = 'usr',
                cachedBaseUrl,

                getJsConfig = function () {

                    return win.TubePressJsConfig;
                },

                getBaseUrl = function () {

                    var tubePressJsConfig = getJsConfig(),
                        scripts, x, scriptSrc;

                    if (!alreadyCalculatedBaseUrl) {

                        if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_base)) {

                            cachedBaseUrl = langUtils.trimSlashes(tubePressJsConfig[text_urls][text_base], fawlse);

                        } else {

                            //http://stackoverflow.com/questions/2161159/get-script-path
                            scripts = dokument.getElementsByTagName(text_script);
                            x       = 0;

                            for (x; x < scripts.length; x += 1) {

                                scriptSrc = scripts[x][text_src];

                                if (scriptSrc.indexOf(text_path_separator + text_tubepress + text_dot + text_js) !== -1) {

                                    cachedBaseUrl = langUtils.trimSlashes(
                                        scriptSrc.substr(0, scriptSrc.lastIndexOf(text_path_separator))
                                            .split('?')[0]
                                            .replace(text_web + text_path_separator + text_js, text_empty)
                                        , fawlse);
                                    break;
                                }
                            }
                        }

                        alreadyCalculatedBaseUrl = troo;
                    }

                    return cachedBaseUrl;
                },

                getAjaxEndpointUrl = function () {

                    var text_ajaxEndpoint = text_ajax + 'Endpoint',
                        tubePressJsConfig = getJsConfig();

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_ajax)) {

                        return tubePressJsConfig[text_urls][text_ajax];

                    }

                    return getBaseUrl() + text_path_separator + text_web + text_path_separator + text_php +
                        text_path_separator + text_ajaxEndpoint + text_dot + text_php;
                },

                getUserContentUrl = function () {

                    var tubePressJsConfig = getJsConfig();

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_usr)) {

                        return tubePressJsConfig[text_urls][text_usr];
                    }

                    return getBaseUrl() + text_path_separator + text_tubepress + '-content';
                };

            return {

                getBaseUrl         : getBaseUrl,
                getAjaxEndpointUrl : getAjaxEndpointUrl,
                getUserContentUrl  : getUserContentUrl
            };
        }()),

        /**
         * Lightweight event bus for TubePress.
         *
         * https://gist.github.com/cowboy/661855
         *
         * jQuery Tiny Pub/Sub - v0.3 - 11/4/2010
         * http://benalman.com/
         *
         * Copyright (c) 2010 "Cowboy" Ben Alman
         * Dual licensed under the MIT and GPL licenses.
         * http://benalman.com/about/license/
         */
        beacon = (function () {

            var bus = jquery({}),

                subscribe = function () {

                    bus.on.apply(bus, arguments);
                },

                unsubscribe = function () {

                    bus.off.apply(bus, arguments);
                },

                publish = function () {

                    if (logger.on()) {

                        var args = arguments;

                        logger.log('firing event ' + args[0]);

                        if (args.length > 1) {

                            logger.dir(args[1]);
                        }
                    }

                    bus.trigger.apply(bus, arguments);
                };

            return {

                subscribe   : subscribe,
                unsubscribe : unsubscribe,
                publish     : publish
            };
        }()),

        /**
         * Injects JS and CSS into the DOM.
         */
        domInjector = (function () {

            var filesAlreadyLoaded = [],

                alreadyLoaded = function (path) {

                    return filesAlreadyLoaded[path] === troo;
                },

                convertToAbsoluteUrl = function (url, isSystem) {

                    if (url.indexOf(text_path_separator) === 0 || url.indexOf(text_http) === 0) {

                        //already absolute
                        return url;
                    }

                    var prefix;

                    if (isSystem) {

                        prefix = environment.getBaseUrl();

                    } else {

                        prefix = environment.getUserContentUrl();
                    }

                    return prefix + text_path_separator + langUtils.trimSlashes(url, troo);
                },

                doLog = function (path, type) {

                    if (logger.on()) {

                        logger.log('Injecting ' + type + ': ' + path);
                    }
                },

                appendToHead = function (element) {

                    dokument.getElementsByTagName(text_head)[0].appendChild(element);
                },

                loadCss = function (path, isSystem) {

                    isSystem = langUtils.isDefined(isSystem) ? isSystem : troo;

                    path = convertToAbsoluteUrl(path, isSystem);

                    if (alreadyLoaded(path)) {

                        return;
                    }

                    filesAlreadyLoaded[path] = troo;

                    var fileref   = dokument.createElement('link');

                    fileref.rel  = 'stylesheet';
                    fileref.type = text_text + text_path_separator + text_css;
                    fileref.href = path;

                    doLog(path, text_css);

                    appendToHead(fileref);
                },

                loadJs = function (path, isSystem) {

                    isSystem = langUtils.isDefined(isSystem) ? isSystem : troo;

                    path = convertToAbsoluteUrl(path, isSystem);

                    if (alreadyLoaded(path)) {

                        return;
                    }

                    filesAlreadyLoaded[path] = troo;

                    doLog(path, text_js);

                    var script = dokument.createElement(text_script);

                    script.type      = text_text + '/java' + text_script;
                    script[text_src] = path;
                    script.async     = troo;

                    dokument.getElementsByTagName(text_head)[0].appendChild(script);
                },

                loadSystemScript = function (scriptName) {

                    var tubePressJsConfig = win.TubePressJsConfig;

                    if (langUtils.hasOwnNestedProperty(tubePressJsConfig, text_urls, text_js, text_sys, scriptName)) {

                        loadJs(tubePressJsConfig[text_urls][text_js][text_sys][scriptName]);

                    } else {

                        loadJs(text_web + text_path_separator + text_js + text_path_separator + scriptName + text_dot + text_js);
                    }
                },

                loadGalleryJs = function () {

                    loadSystemScript(text_gallery);
                },

                loadPlayerApiJs = function () {

                    loadSystemScript('playerApi');
                },

                loadAjaxSearchJs = function () {

                    loadSystemScript(text_ajax + 'Search');
                };

            return {

                loadJs           : loadJs,
                loadCss          : loadCss,
                loadGalleryJs    : loadGalleryJs,
                loadPlayerApiJs  : loadPlayerApiJs,
                loadAjaxSearchJs : loadAjaxSearchJs
            };
        }()),

        /**
         * Core of our asychronous APIs.
         */
        asyncConverter = (function () {

            //http://tmxcredit.com/tech-blog/understanding-javascript-asynchronous-apis/
            var convertQueueToFunctionCalls = function (asyncObjectName, delegate) {

                var loggerOn = logger.on(),
                    queueLength,

                    queue = win[asyncObjectName],

                    queueCall = function (callArray) {

                        var method = callArray[0],
                            args   = callArray.slice(1);

                        delegate[method].apply(this, args);
                    };

                if (langUtils.isDefined(queue) && jquery.isArray(queue)) {

                    queueLength = queue.length;

                    if (loggerOn) {

                        logger.log('Running ' + queueLength + ' queue items for ' + asyncObjectName);
                    }

                    // loop through our existing queue, calling methods in order
                    queue.reverse();

                    while (queue.length) {

                        queueCall(queue.pop());
                    }
                }

                if (loggerOn) {

                    logger.log(asyncObjectName + ' is now connected');
                }

                // over write the sampleQueue, replacing the push method with 'queueCall'
                // this creates a globally accessible interface to your API through sampleQueue.push()
                win[asyncObjectName] = {

                    push : queueCall
                };
            };

            return {

                processQueueCalls : convertQueueToFunctionCalls
            };
        }()),

        /**
         * Handles styling DOM elements before and after loads.
         */
        loadStyler = (function () {

            /**
             * Fade to "white".
             */
            var apply = function (selector, finalOpacity) {

                    jquery(selector).fadeTo(0, finalOpacity);
                },

                applyLoadingStyle = function (selector) {

                    apply(selector, 0.3);
                },

                /**
                 * Fade back to full opacity.
                 */
                removeLoadingStyle = function (selector ) {

                    apply(selector, 1);
                };

            return {

                applyLoadingStyle  : applyLoadingStyle,
                removeLoadingStyle : removeLoadingStyle
            };
        }());

    /**
     * Handle window resize events.
     */
    (function () {

        var timeout,

            publishResizeEvent = function () {

                beacon.publish(text_tubepress + '.window.resize');
            },

            onBrowserResize = function () {

                clearTimeout(timeout);

                timeout = setTimeout(publishResizeEvent, 150);
            },

            init = function () {

                jquery(win).resize(onBrowserResize);
            };

        jquery(init);

    }());

    /**
     * Convert any queued calls to their real counterparts.
     */
    (function () {

        var textCamelCaseTubePress = 'tubePress';

        asyncConverter.processQueueCalls(textCamelCaseTubePress + 'DomInjector', domInjector);
        asyncConverter.processQueueCalls(textCamelCaseTubePress + 'Beacon', beacon);

    }());

    /**
     * Stuff we expose to everyone else.
     */
    return {

        Ajax : {
            LoadStyler : loadStyler
        },

        AsyncUtil   : asyncConverter,
        Beacon      : beacon,
        DomInjector : domInjector,
        Environment : environment,

        Lang : {

            Utils : langUtils
        },

        Logger : logger
    };

}(jQuery, window));
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {
    ScrollView,
    StyleSheet,
    Image,
    View,
    Text,
} from 'react-native';

import OneRMDebugScreen from './debug/OneRMDebugScreen';
import OneRMCalculateScreen from './calculate/OneRMCalculateScreen';
import OneRMResultsScreen from './results/OneRMResultsScreen';
import OneRMLoggedOutView from './logged_out/OneRMLoggedOutView';
import OneRMProtocolView from './protocol/OneRMProtocolView';
import { SETTINGS_PANEL_STYLES } from 'app/appearance/styles/GlobalStyles';

// Hack for svg bug with scrolling in victory charts on Android
// https://github.com/FormidableLabs/victory-native/issues/96
class AnalysisTab extends Component {
    // separated Chart and Screen to ensure android hack works along with calculate button
    constructor(props) {
        super(props);

        this.state = { lastScroll: false };
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.scroll !== this.state.lastScroll) {
            // save new props
            this.setState({
                lastScroll: nextProps.scroll
            });

            // scroll
            if (this.results && this.results.measure) {
                this.results.measure( (fx, fy, width, height, px, py) => {
                    this.scrollView.scrollTo({x: 0, y: fy, animated: true});
                });
            }
        }
    }

    render() {
        if (this.props.isLoggedIn) {
            // TODO: test the hack still works on Android
            return (
                <ScrollView style={{flex: 1}} ref={(ref) => { this.scrollView = ref}}>
                    <OneRMDebugScreen />
                    <OneRMCalculateScreen />
                    <Image style={styles.pseudoScrollView} source={require('app/appearance/images/blank.png')} />
                    <View ref={(ref) => { this.results = ref }}>
                    <OneRMResultsScreen />
                    </View>
                    <OneRMProtocolView />
                </ScrollView>
            );
        } else {
            return <OneRMLoggedOutView />;
        }
    }
}

const styles = StyleSheet.create({
    pseudoScrollView: {
        opacity: 0,
        position: 'absolute',
        top: 0,
        left: 0,
        width:"100%",
        height:"100%",
    },
});

export default AnalysisTab;
<commit_msg>Ensure that all elements tappable on android<commit_after>import React, {Component} from 'react';
import {
    ScrollView,
    StyleSheet,
    Image,
    View,
    Text,
} from 'react-native';

import OneRMDebugScreen from './debug/OneRMDebugScreen';
import OneRMCalculateScreen from './calculate/OneRMCalculateScreen';
import OneRMResultsScreen from './results/OneRMResultsScreen';
import OneRMLoggedOutView from './logged_out/OneRMLoggedOutView';
import OneRMProtocolView from './protocol/OneRMProtocolView';
import { SETTINGS_PANEL_STYLES } from 'app/appearance/styles/GlobalStyles';

// Hack for svg bug with scrolling in victory charts on Android
// https://github.com/FormidableLabs/victory-native/issues/96
class AnalysisTab extends Component {
    // separated Chart and Screen to ensure android hack works along with calculate button
    constructor(props) {
        super(props);

        this.state = { lastScroll: false };
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.scroll !== this.state.lastScroll) {
            // save new props
            this.setState({
                lastScroll: nextProps.scroll
            });

            // scroll
            if (this.results && this.results.measure) {
                this.results.measure( (fx, fy, width, height, px, py) => {
                    this.scrollView.scrollTo({x: 0, y: fy, animated: true});
                });
            }
        }
    }

    render() {
        if (this.props.isLoggedIn) {
            // TODO: test the hack still works on Android
            return (
                <ScrollView style={{flex: 1}} ref={(ref) => { this.scrollView = ref}}>
                    <Image style={styles.pseudoScrollView} source={require('app/appearance/images/blank.png')} />
                    <OneRMDebugScreen />
                    <OneRMCalculateScreen />
                    <View ref={(ref) => { this.results = ref }}>
                        <OneRMResultsScreen />
                    </View>
                    <OneRMProtocolView />
                </ScrollView>
            );
        } else {
            return <OneRMLoggedOutView />;
        }
    }
}

const styles = StyleSheet.create({
    pseudoScrollView: {
        opacity: 0,
        position: 'absolute',
        top: 0,
        left: 0,
        width:"100%",
        height:"100%",
    },
});

export default AnalysisTab;
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Ian Davies
 *
 * 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.
 */
define([], function() {

	return ["api", function(api) {

		return {
			scope: true,

			restrict: "A",

			templateUrl: "/partials/pod_carousel.html",

			link: function(scope, element, attrs) {
				scope.startPod = 0;
				scope.pods = api.pods.get();
			},
		};
	}];
});<commit_msg>Sort and auto-rotate pods.<commit_after>/**
 * Copyright 2015 Ian Davies
 *
 * 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.
 */
define([], function() {

	return ["api", "$timeout", function(api, $timeout) {

		return {
			scope: true,

			restrict: "A",

			templateUrl: "/partials/pod_carousel.html",

			link: function(scope, element, attrs) {
				scope.startPod = 0;
				scope.pods = api.pods.get();

				scope.pods.$promise.then(function(){
					scope.pods.sort(function(a,b) { return a.id > b.id; });
				});

				$timeout(function() {
					scope.startPod++;
				}, 10000);
			},
		};
	}];
});<|endoftext|>


<commit_before>/*
Copyright (c) 2015 SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
ALL RIGHTS RESERVED.
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.
Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
This work has been performed in the framework of the SONATA project,
funded by the European Commission under Grant number 671517 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.sonata-nfv.eu).
*/

SonataApp.controller('MainController',['$rootScope','$scope','$routeParams', '$location', '$http',function($rootScope,$scope, $routeParams, $location, $http) {
   $scope.debug=(window.location.origin=="http://localhost"?true:false);

    $scope.todos = new Array();
    (function(w){w = w || window; var i = w.setInterval(function(){},100000); while(i>=0) { w.clearInterval(i--); }})(/*window*/);

    $rootScope.setStorage = function(valuename,valuevalue){
      window.localStorage.setItem(valuename,valuevalue);
    }

    $rootScope.getStorage = function(valuename){
      return window.localStorage.getItem(valuename);
    }

    $rootScope.getToken = function(){

        return $rootScope.getStorage('sonata-token');

    }

    $rootScope.getUserName = function(){
      return $rootScope.getStorage('sonata-username');
    }
    $rootScope.username=$rootScope.getUserName();

    $rootScope.checkIfValid = function(token){

      return true;

    }

    $rootScope.checkIfNull = function(val){
      return angular.isUndefined(val) || val === null
    }

   $rootScope.getIndexOf=function(arr, val, prop) {
      var l = arr.length,
        k = 0;
      for (k = 0; k < l; k = k + 1) {
        if (arr[k][prop] === val) {
          return k;
        }
      }
      return -1;
    }

     $scope.getServices = function(){

                if($scope.debug==false){
                  var protocol  = window.location.protocol;
                  var host      = window.location.hostname;

                  var gk_url = protocol+'//'+host+'/api/v2';
                  var mon_url = protocol+'//'+host+'/monitoring';
                  var vims_url = protocol+'//'+host+'/api/v2';
                  var logs_url = protocol+'//'+host+'/logs';

                }else{

                  var gk_url = 'http://sp.int3.sonata-nfv.eu:32001/api/v2';
                  var mon_url = 'http://sp.int3.sonata-nfv.eu:8000';
                  var vims_url = 'http://sp.int3.sonata-nfv.eu:32001/api/v2';
                  var logs_url = 'http://logs.sonata-nfv.eu:12900/logs';
                }
                  console.log("protocol: "+protocol);
                  console.log("host: "+host);

                  $scope.configuration = {
                    'logs_range':'86400' //time range (minutes before)
                  }

                  $scope.apis = {
                        'monitoring':mon_url+'/api/v1/prometheus/metrics/data',
                        'monitoring_list':mon_url+'/api/v1/prometheus/metrics/list',
                        'logs':logs_url+'/search/universal/relative?',
                        'vims':vims_url+'/vims',
                        'wims':vims_url+'/wims',
                        'monitoring_data':gk_url+'/kpis/collected',
                        'gatekeeper':{
                          'services' :gk_url+'/services',
                          'packages' :gk_url+'/packages',
                          'functions':gk_url+'/functions',
                          'requests' :gk_url+'/requests',
                          'kpis'     :gk_url+'/kpis',
                          'users'    :gk_url+'/users',
                          'user_sessions':gk_url+'/sessions',
                        }
                      };

              $rootScope.apis = $scope.apis;
              $rootScope.gk_headers = {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer '+$rootScope.getStorage('sonata-token')
              };
                     $rootScope.checkTokenValidity();
           }

    $rootScope.getGKHeaders = function(){
      var header = {};
      header = {'Content-Type': 'application/json',
                    'Authorization': 'Bearer '+$rootScope.getStorage('sonata-token')};
                    return header;
    }

    $rootScope.logout = function(){
      $rootScope.is_user_logged_in = false;
      $rootScope.gk_headers = {};
      $rootScope.token = '';
      delete window.localStorage['sonata-token'];
      delete window.localStorage['sonata-username'];

      location.hash='/login';

    }

$rootScope.checkTokenValidity = function(){

          $http({
              method  : 'GET',
                url: $scope.apis.gatekeeper.users,
                headers : $rootScope.getGKHeaders()
          }).then(function successCallback(response) {
            console.log("TOKEN is Valid");
          }, function errorCallback(response) {
            console.log("The token is not valid");
            console.log(response);
            $rootScope.logout();
          });
      }

    $scope.checkAuthorization = function(){

      if($rootScope.is_user_logged_in==false){
        $rootScope.logout();
      }
      if($location.url()!='/signup' && $location.url()!='/login'){

            var localStorageToken = $rootScope.getStorage('sonata-token');

            if($rootScope.checkIfNull(localStorageToken)){

              $rootScope.is_user_logged_in = false;
              delete window.localStorage['sonata-token'];
              location.hash='/login';
            }
            else{
              if($rootScope.checkIfValid(localStorageToken)){
                $rootScope.is_user_logged_in = true;
              }
            }
      }
    }
    $scope.checkAuthorization();

$scope.alerts_visibility = 0;

$rootScope.FixTimestamp = function(timestamp){

    timestamp = timestamp.toString();
    timestamp = timestamp.replace('.','');

    if(timestamp.length==12)
        timestamp=timestamp+'0';
    else if(timestamp.length==11)
      timestamp = timestamp+'00';
    else if(timestamp.length==10)
      timestamp = timestamp+'000';
    else if(timestamp.length==9)
      timestamp = timestamp+'0000';

    timestamp = parseInt(timestamp);
    return timestamp;
}

    $scope.changeHash = function(newHash){
      location.hash = newHash;
    }

  $rootScope.checkIfFilesAreThere = function(){

    return 1;
  }

    }]);<commit_msg>fixes<commit_after>/*
Copyright (c) 2015 SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
ALL RIGHTS RESERVED.
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.
Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
This work has been performed in the framework of the SONATA project,
funded by the European Commission under Grant number 671517 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.sonata-nfv.eu).
*/

SonataApp.controller('MainController',['$rootScope','$scope','$routeParams', '$location', '$http',function($rootScope,$scope, $routeParams, $location, $http) {
   $scope.debug=(window.location.origin=="http://localhost"?true:false);

    $scope.todos = new Array();
    (function(w){w = w || window; var i = w.setInterval(function(){},100000); while(i>=0) { w.clearInterval(i--); }})(/*window*/);

    $rootScope.setStorage = function(valuename,valuevalue){
      window.localStorage.setItem(valuename,valuevalue);
    }

    $rootScope.getStorage = function(valuename){
      return window.localStorage.getItem(valuename);
    }

    $rootScope.getToken = function(){

        return $rootScope.getStorage('sonata-token');

    }

    $rootScope.getUserName = function(){
      return $rootScope.getStorage('sonata-username');
    }
    $rootScope.username=$rootScope.getUserName();

    $rootScope.checkIfValid = function(token){

      return true;

    }

    $rootScope.checkIfNull = function(val){
      return angular.isUndefined(val) || val === null
    }

   $rootScope.getIndexOf=function(arr, val, prop) {
      var l = arr.length,
        k = 0;
      for (k = 0; k < l; k = k + 1) {
        if (arr[k][prop] === val) {
          return k;
        }
      }
      return -1;
    }

     $scope.getServices = function(){

                if($scope.debug==false){
                  var protocol  = window.location.protocol;
                  var host      = window.location.hostname;

                  var gk_url = protocol+'//'+host+'/api/v2';
                  var mon_url = protocol+'//'+host+'/monitoring';
                  var vims_url = protocol+'//'+host+'/api/v2';
                  var logs_url = protocol+'//'+host+'/logs';

                }else{

                  var gk_url = 'https://sp.int3.sonata-nfv.eu/api/v2';
                  //https://sp.int3.sonata-nfv.eu/api/v2/packages
                  var mon_url = 'http://sp.int3.sonata-nfv.eu:8000';
                  var vims_url = 'http://sp.int3.sonata-nfv.eu:32001/api/v2';
                  var logs_url = 'http://logs.sonata-nfv.eu:12900/logs';
                }
                  console.log("protocol: "+protocol);
                  console.log("host: "+host);

                  $scope.configuration = {
                    'logs_range':'86400' //time range (minutes before)
                  }

                  $scope.apis = {
                        'monitoring':mon_url+'/api/v1/prometheus/metrics/data',
                        'monitoring_list':mon_url+'/api/v1/prometheus/metrics/list',
                        'logs':logs_url+'/search/universal/relative?',
                        'vims':vims_url+'/vims',
                        'wims':vims_url+'/wims',
                        'monitoring_data':gk_url+'/kpis/collected',
                        'gatekeeper':{
                          'services' :gk_url+'/services',
                          'packages' :gk_url+'/packages',
                          'functions':gk_url+'/functions',
                          'requests' :gk_url+'/requests',
                          'kpis'     :gk_url+'/kpis',
                          'users'    :gk_url+'/users',
                          'user_sessions':gk_url+'/sessions',
                        }
                      };

              $rootScope.apis = $scope.apis;
              $rootScope.gk_headers = {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer '+$rootScope.getStorage('sonata-token')
              };
                     $rootScope.checkTokenValidity();
           }

    $rootScope.getGKHeaders = function(){
      var header = {};
      header = {'Content-Type': 'application/json',
                    'Authorization': 'Bearer '+$rootScope.getStorage('sonata-token')};
                    return header;
    }

    $rootScope.logout = function(){
      $rootScope.is_user_logged_in = false;
      $rootScope.gk_headers = {};
      $rootScope.token = '';
      delete window.localStorage['sonata-token'];
      delete window.localStorage['sonata-username'];

      location.hash='/login';

    }

$rootScope.checkTokenValidity = function(){

          $http({
              method  : 'GET',
                url: $scope.apis.gatekeeper.users,
                headers : $rootScope.getGKHeaders()
          }).then(function successCallback(response) {
            console.log("TOKEN is Valid");
          }, function errorCallback(response) {
            console.log("The token is not valid");
            console.log(response);
            $rootScope.logout();
          });
      }

    $scope.checkAuthorization = function(){

      if($rootScope.is_user_logged_in==false){
        $rootScope.logout();
      }
      if($location.url()!='/signup' && $location.url()!='/login'){

            var localStorageToken = $rootScope.getStorage('sonata-token');

            if($rootScope.checkIfNull(localStorageToken)){

              $rootScope.is_user_logged_in = false;
              delete window.localStorage['sonata-token'];
              location.hash='/login';
            }
            else{
              if($rootScope.checkIfValid(localStorageToken)){
                $rootScope.is_user_logged_in = true;
              }
            }
      }
    }
    $scope.checkAuthorization();

$scope.alerts_visibility = 0;

$rootScope.FixTimestamp = function(timestamp){

    timestamp = timestamp.toString();
    timestamp = timestamp.replace('.','');

    if(timestamp.length==12)
        timestamp=timestamp+'0';
    else if(timestamp.length==11)
      timestamp = timestamp+'00';
    else if(timestamp.length==10)
      timestamp = timestamp+'000';
    else if(timestamp.length==9)
      timestamp = timestamp+'0000';

    timestamp = parseInt(timestamp);
    return timestamp;
}

    $scope.changeHash = function(newHash){
      location.hash = newHash;
    }

  $rootScope.checkIfFilesAreThere = function(){

    return 1;
  }

    }]);<|endoftext|>


<commit_before>(function() {
  'use strict';

  describe('camel case keys', function() {
    beforeEach(module('radar'));

    var camelCaseKeys;

    beforeEach(inject(function(_camelCaseKeys_){
      camelCaseKeys = _camelCaseKeys_;
    }));

    it('handles an empty object', function() {
      expect(camelCaseKeys({})).toEqual({});
    });

    it('handles a flat object', function() {
      expect(camelCaseKeys({
        key_one: 'key_one',
        key_two: 'key_two'
      })).toEqual({
        keyOne: 'key_one',
        keyTwo: 'key_two'
      });
    });

    it('handles nested objects', function() {
      expect(camelCaseKeys({
        key_one: {
          key_one_one: 'value_one_one',
          key_one_two: 'value_one_two'
        },
        key_two: {
          key_two_one: 'value_two_one',
          key_two_two: 'value_two_two'
        }
      })).toEqual({
        keyOne: {
          keyOneOne: 'value_one_one',
          keyOneTwo: 'value_one_two'
        },
        keyTwo: {
          keyTwoOne: 'value_two_one',
          keyTwoTwo: 'value_two_two'
        }
      });
    });

    it('handles a list of objects', function() {
      expect(camelCaseKeys([
        {
          item_one_key_one: 'item_one_value_one',
          item_one_key_two: 'item_one_value_two'
        },
        {
          item_two_key_one: 'item_two_value_one',
          item_two_key_two: 'item_two_value_two'
        }
      ])).toEqual([
        {
          itemOneKeyOne: 'item_one_value_one',
          itemOneKeyTwo: 'item_one_value_two'
        },
        {
          itemTwoKeyOne: 'item_two_value_one',
          itemTwoKeyTwo: 'item_two_value_two'
        }
      ]);
    });

    it('skips upper cased keys', function() {
      expect(camelCaseKeys({
        KEY_1: 'value_1',
        KEY_2: 'value_2'
      })).toEqual({
        KEY_1: 'value_1',
        KEY_2: 'value_2'
      });
    });
  });

  describe('snake case keys', function() {
    beforeEach(module('radar'));

    var snakeCaseKeys;

    beforeEach(inject(function(_snakeCaseKeys_){
      snakeCaseKeys = _snakeCaseKeys_;
    }));

    it('handles an empty object', function() {
      expect(snakeCaseKeys({})).toEqual({});
    });

    it('handles a flat object', function() {
      expect(snakeCaseKeys({
        keyOne: 'keyOne',
        keyTwo: 'keyTwo'
      })).toEqual({
        key_one: 'keyOne',
        key_two: 'keyTwo'
      });
    });

    it('handles nested objects', function() {
      expect(snakeCaseKeys({
        keyOne: {
          keyOneOne: 'valueOneOne',
          keyOneTwo: 'valueOneTwo'
        },
        keyTwo: {
          keyTwoOne: 'valueTwoOne',
          keyTwoTwo: 'valueTwoTwo'
        }
      })).toEqual({
        key_one: {
          key_one_one: 'valueOneOne',
          key_one_two: 'valueOneTwo'
        },
        key_two: {
          key_two_one: 'valueTwoOne',
          key_two_two: 'valueTwoTwo'
        }
      });
    });

    it('handles a list of objects', function() {
      expect(snakeCaseKeys([
        {
          itemOneKeyOne: 'itemOneValueOne',
          itemOneKeyTwo: 'itemOneValueTwo'
        },
        {
          itemTwoKeyOne: 'itemTwoValueOne',
          itemTwoKeyTwo: 'itemTwoValueTwo'
        }
      ])).toEqual([
        {
          item_one_key_one: 'itemOneValueOne',
          item_one_key_two: 'itemOneValueTwo'
        },
        {
          item_two_key_one: 'itemTwoValueOne',
          item_two_key_two: 'itemTwoValueTwo'
        }
      ]);
    });
  });
})();
<commit_msg>Add test for snake case constants<commit_after>(function() {
  'use strict';

  describe('camel case keys', function() {
    beforeEach(module('radar'));

    var camelCaseKeys;

    beforeEach(inject(function(_camelCaseKeys_){
      camelCaseKeys = _camelCaseKeys_;
    }));

    it('handles an empty object', function() {
      expect(camelCaseKeys({})).toEqual({});
    });

    it('handles a flat object', function() {
      expect(camelCaseKeys({
        key_one: 'key_one',
        key_two: 'key_two'
      })).toEqual({
        keyOne: 'key_one',
        keyTwo: 'key_two'
      });
    });

    it('handles nested objects', function() {
      expect(camelCaseKeys({
        key_one: {
          key_one_one: 'value_one_one',
          key_one_two: 'value_one_two'
        },
        key_two: {
          key_two_one: 'value_two_one',
          key_two_two: 'value_two_two'
        }
      })).toEqual({
        keyOne: {
          keyOneOne: 'value_one_one',
          keyOneTwo: 'value_one_two'
        },
        keyTwo: {
          keyTwoOne: 'value_two_one',
          keyTwoTwo: 'value_two_two'
        }
      });
    });

    it('handles a list of objects', function() {
      expect(camelCaseKeys([
        {
          item_one_key_one: 'item_one_value_one',
          item_one_key_two: 'item_one_value_two'
        },
        {
          item_two_key_one: 'item_two_value_one',
          item_two_key_two: 'item_two_value_two'
        }
      ])).toEqual([
        {
          itemOneKeyOne: 'item_one_value_one',
          itemOneKeyTwo: 'item_one_value_two'
        },
        {
          itemTwoKeyOne: 'item_two_value_one',
          itemTwoKeyTwo: 'item_two_value_two'
        }
      ]);
    });

    it('skips constants', function() {
      expect(camelCaseKeys({
        KEY_1: 'value_1',
        KEY_2: 'value_2'
      })).toEqual({
        KEY_1: 'value_1',
        KEY_2: 'value_2'
      });
    });
  });

  describe('snake case keys', function() {
    beforeEach(module('radar'));

    var snakeCaseKeys;

    beforeEach(inject(function(_snakeCaseKeys_){
      snakeCaseKeys = _snakeCaseKeys_;
    }));

    it('handles an empty object', function() {
      expect(snakeCaseKeys({})).toEqual({});
    });

    it('handles a flat object', function() {
      expect(snakeCaseKeys({
        keyOne: 'keyOne',
        keyTwo: 'keyTwo'
      })).toEqual({
        key_one: 'keyOne',
        key_two: 'keyTwo'
      });
    });

    it('handles nested objects', function() {
      expect(snakeCaseKeys({
        keyOne: {
          keyOneOne: 'valueOneOne',
          keyOneTwo: 'valueOneTwo'
        },
        keyTwo: {
          keyTwoOne: 'valueTwoOne',
          keyTwoTwo: 'valueTwoTwo'
        }
      })).toEqual({
        key_one: {
          key_one_one: 'valueOneOne',
          key_one_two: 'valueOneTwo'
        },
        key_two: {
          key_two_one: 'valueTwoOne',
          key_two_two: 'valueTwoTwo'
        }
      });
    });

    it('handles a list of objects', function() {
      expect(snakeCaseKeys([
        {
          itemOneKeyOne: 'itemOneValueOne',
          itemOneKeyTwo: 'itemOneValueTwo'
        },
        {
          itemTwoKeyOne: 'itemTwoValueOne',
          itemTwoKeyTwo: 'itemTwoValueTwo'
        }
      ])).toEqual([
        {
          item_one_key_one: 'itemOneValueOne',
          item_one_key_two: 'itemOneValueTwo'
        },
        {
          item_two_key_one: 'itemTwoValueOne',
          item_two_key_two: 'itemTwoValueTwo'
        }
      ]);
    });

    it('skips constants', function() {
      expect(snakeCaseKeys({
        KEY1: 'value1',
        KEY2: 'value2'
      })).toEqual({
        KEY1: 'value1',
        KEY2: 'value2'
      });
    });
  });
})();
<|endoftext|>


<commit_before>'use strict';

/* Controllers */

angular.module('KMCModule').controller('PlayerEditCtrl',
	['$scope', 'PlayerData', '$routeParams', '$filter', 'menuSvc', 'PlayerService', 'apiService', 'localStorageService', 'userEntries', '$timeout','$modal',
		function($scope, PlayerData, $routeParams, $filter, menuSvc, PlayerService, apiService, localStorageService, userEntries, $timeout, $modal) {
			$scope.ks = localStorageService.get('ks');
			$scope.playerId = PlayerData.id;
			$scope.newPlayer = !$routeParams.id;
			$scope.title = ($routeParams.id) ? $filter('i18n')('Edit player') : $filter('i18n')('New  player');
			$scope.data = PlayerData;
			$scope.data.config = angular.fromJson($scope.data.config);
			$scope.masterData = angular.copy($scope.data);
			$scope.userEntriesList = [];
			$scope.userEntries = userEntries;
			$scope.settings = {};
			window.scope = $scope;
// set tags
			$scope.tags = [];
// all of the next block is just to show how to push into the tags autocomplete/dropdown the list of available tags should be loaded this way instead,
// the model tags of the player are actually set properly from the ng-model of the tags directive and are not needed here
			if (typeof $scope.data.tags != "undefined" && $scope.data.tags) { //can also be null
				var tags = typeof $scope.data.tags == "string" ? $scope.data.tags.split(",") : $scope.data.tags;
				for (var i = 0; i < tags.length; i++)
					$scope.tags.push({id: tags[i], text: tags[i]});
			}
//registers the tags to be available to the directive
			menuSvc.registerAction('getTags', function() {
				return $scope.tags;
			});
			angular.forEach($scope.userEntries.objects, function(value) {
				$scope.userEntriesList.push({'id': value.id, 'text': value.name});
			});
			menuSvc.registerAction('listEntries', function() { // those should be the first 20...
				return $scope.userEntriesList;
			});
			menuSvc.registerAction('queryEntries', function(query) {
				var data = {results: []};
				console.log(query.term);
				if (query.term) {
// here you should do some AJAX API call with the query term and then()...
					angular.forEach($scope.userEntriesList, function(item, key) {
						if (query.term.toUpperCase() === item.text.substring(0, query.term.length).toUpperCase()) {
							data.results.push(item);
						}
					});
					return query.callback(data);
				}
				else
					return query.callback({results: $scope.userEntriesList});

			});

			if (parseFloat($scope.data.version) < PlayerService.getRequiredVersion()) {
				menuSvc.registerAction('update', function() {
					PlayerService.playerUpdate($scope.data);
				});
			}
			$scope.settings.previewEntry = ( PlayerService.getPreviewEntry()) ? PlayerService.getPreviewEntry() : $scope.userEntriesList[0]; //default entry
			$scope.$watch('settings.previewEntry.id', function(newVal, oldVal) {
				if (newVal != oldVal) {
					PlayerService.setPreviewEntry($scope.settings.previewEntry);
					PlayerService.renderPlayer();
				}
			});
			$(document).ready(function() {
				$scope.masterData = angular.copy($scope.data);
// get the preview entry
				PlayerService.setPreviewEntry($scope.settings.previewEntry);
				PlayerService.renderPlayer();
			});
			$scope.save = function() {
				var request = {
					'service': 'uiConf',
					'action': 'update',
					'id': $scope.playerId,
					'uiConf:name': $scope.data.name,
					'uiConf:tags': $scope.data.tags,
					'uiConf:description': $scope.data.description ? $scope.data.description : '',
					'uiConf:config': angular.toJson($scope.data.config)
				};
				apiService.doRequest(request).then(function(result) {
// cleanup
						menuSvc.menuScope.playerEdit.$dirty = false;
						$scope.masterData = angular.copy($scope.data);
// if this is a new player - add it to the players list
						if ($scope.newPlayer){
// TODO: add player to cache or set flag to re-fetch the list from the server on the next list view
//PlayerService.cachePlayers(result);
						}
// TODO: replace with floating success message that will disappear after few seconds
						$modal.open({ templateUrl: 'template/dialog/message.html',
							controller: 'ModalInstanceCtrl',
							resolve: {
								settings: function() {
									return {
										'title': 'Save Player Settings',
										'message': 'Player Saved Successfully'
									};
								}
							}
						});
					}, function(msg) {
						$modal.open({ templateUrl: 'template/dialog/message.html',
							controller: 'ModalInstanceCtrl',
							resolve: {
								settings: function() {
									return {
										'title': 'Player save failure',
										'message': msg
									};
								}
							}
						});
					}
				);

			};
			$scope.$watch(function() {
				if (typeof menuSvc.menuScope.playerEdit != 'undefined') {
					if (menuSvc.menuScope.playerEdit.$error) {
						return menuSvc.menuScope.playerEdit.$error;

					}
				}
			}, function(obj, oldVal) {
				if (obj != oldVal) {
					$scope.validationObject = obj;
				}

			});
			$scope.saveEnabled = function() {
//instead of using the form dirty state we compare to the master copy.
				if (typeof menuSvc.menuScope.playerEdit != 'undefined') {
					if (menuSvc.menuScope.playerEdit.$valid)
						return !angular.equals($scope.data, $scope.masterData);
					else
						return false;
				}
			};
		}
	]);

angular.module('KMCModule').controller('editPageDataCntrl', ['$scope', function($scope) {

}]);<commit_msg>removed redundant renderPlayer()<commit_after>'use strict';

/* Controllers */

angular.module('KMCModule').controller('PlayerEditCtrl',
	['$scope', 'PlayerData', '$routeParams', '$filter', 'menuSvc', 'PlayerService', 'apiService', 'localStorageService', 'userEntries', '$timeout','$modal',
		function($scope, PlayerData, $routeParams, $filter, menuSvc, PlayerService, apiService, localStorageService, userEntries, $timeout, $modal) {
			$scope.ks = localStorageService.get('ks');
			$scope.playerId = PlayerData.id;
			$scope.newPlayer = !$routeParams.id;
			$scope.title = ($routeParams.id) ? $filter('i18n')('Edit player') : $filter('i18n')('New  player');
			$scope.data = PlayerData;
			$scope.data.config = angular.fromJson($scope.data.config);
			$scope.masterData = angular.copy($scope.data);
			$scope.userEntriesList = [];
			$scope.userEntries = userEntries;
			$scope.settings = {};
			window.scope = $scope;
// set tags
			$scope.tags = [];
// all of the next block is just to show how to push into the tags autocomplete/dropdown the list of available tags should be loaded this way instead,
// the model tags of the player are actually set properly from the ng-model of the tags directive and are not needed here
			if (typeof $scope.data.tags != "undefined" && $scope.data.tags) { //can also be null
				var tags = typeof $scope.data.tags == "string" ? $scope.data.tags.split(",") : $scope.data.tags;
				for (var i = 0; i < tags.length; i++)
					$scope.tags.push({id: tags[i], text: tags[i]});
			}
//registers the tags to be available to the directive
			menuSvc.registerAction('getTags', function() {
				return $scope.tags;
			});
			angular.forEach($scope.userEntries.objects, function(value) {
				$scope.userEntriesList.push({'id': value.id, 'text': value.name});
			});
			menuSvc.registerAction('listEntries', function() { // those should be the first 20...
				return $scope.userEntriesList;
			});
			menuSvc.registerAction('queryEntries', function(query) {
				var data = {results: []};
				console.log(query.term);
				if (query.term) {
// here you should do some AJAX API call with the query term and then()...
					angular.forEach($scope.userEntriesList, function(item, key) {
						if (query.term.toUpperCase() === item.text.substring(0, query.term.length).toUpperCase()) {
							data.results.push(item);
						}
					});
					return query.callback(data);
				}
				else
					return query.callback({results: $scope.userEntriesList});

			});

			if (parseFloat($scope.data.version) < PlayerService.getRequiredVersion()) {
				menuSvc.registerAction('update', function() {
					PlayerService.playerUpdate($scope.data);
				});
			}
			$scope.settings.previewEntry = ( PlayerService.getPreviewEntry()) ? PlayerService.getPreviewEntry() : $scope.userEntriesList[0]; //default entry
			$scope.$watch('settings.previewEntry.id', function(newVal, oldVal) {
				if (newVal != oldVal) {
					PlayerService.setPreviewEntry($scope.settings.previewEntry);
					//PlayerService.renderPlayer();
				}
			});
			$(document).ready(function() {
				$scope.masterData = angular.copy($scope.data);
// get the preview entry
				PlayerService.setPreviewEntry($scope.settings.previewEntry);
				PlayerService.renderPlayer();
			});
			$scope.save = function() {
				var request = {
					'service': 'uiConf',
					'action': 'update',
					'id': $scope.playerId,
					'uiConf:name': $scope.data.name,
					'uiConf:tags': $scope.data.tags,
					'uiConf:description': $scope.data.description ? $scope.data.description : '',
					'uiConf:config': angular.toJson($scope.data.config)
				};
				apiService.doRequest(request).then(function(result) {
// cleanup
						menuSvc.menuScope.playerEdit.$dirty = false;
						$scope.masterData = angular.copy($scope.data);
// if this is a new player - add it to the players list
						if ($scope.newPlayer){
// TODO: add player to cache or set flag to re-fetch the list from the server on the next list view
//PlayerService.cachePlayers(result);
						}
// TODO: replace with floating success message that will disappear after few seconds
						$modal.open({ templateUrl: 'template/dialog/message.html',
							controller: 'ModalInstanceCtrl',
							resolve: {
								settings: function() {
									return {
										'title': 'Save Player Settings',
										'message': 'Player Saved Successfully'
									};
								}
							}
						});
					}, function(msg) {
						$modal.open({ templateUrl: 'template/dialog/message.html',
							controller: 'ModalInstanceCtrl',
							resolve: {
								settings: function() {
									return {
										'title': 'Player save failure',
										'message': msg
									};
								}
							}
						});
					}
				);

			};
			$scope.$watch(function() {
				if (typeof menuSvc.menuScope.playerEdit != 'undefined') {
					if (menuSvc.menuScope.playerEdit.$error) {
						return menuSvc.menuScope.playerEdit.$error;

					}
				}
			}, function(obj, oldVal) {
				if (obj != oldVal) {
					$scope.validationObject = obj;
				}

			});
			$scope.saveEnabled = function() {
//instead of using the form dirty state we compare to the master copy.
				if (typeof menuSvc.menuScope.playerEdit != 'undefined') {
					if (menuSvc.menuScope.playerEdit.$valid)
						return !angular.equals($scope.data, $scope.masterData);
					else
						return false;
				}
			};
		}
	]);

angular.module('KMCModule').controller('editPageDataCntrl', ['$scope', function($scope) {

}]);<|endoftext|>


<commit_before>/**
 * Select2 <Language> translation.
 *
 * Author: bigmihail <bigmihail@bigmir.net>
 */
(function ($) {
    "use strict";

    $.extend($.fn.select2.defaults, {
        formatNoMatches: function () { return "Нічого не знайдено"; },
        formatInputTooShort: function (input, min) { var n = min - input.length, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка ще " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ]; },
        formatInputTooLong: function (input, max) { var n = input.length - max, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка на " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ] + " менше"; },
        formatSelectionTooBig: function (limit) {var s = ["", "и", "ів"], p = [2,0,1,1,1,2];  return "Ви можете вибрати лише " + limit + " елемент" + s[ (limit%100>4 && limit%100<=20)? 2 : p[Math.min(limit%10, 5)] ]; },
        formatLoadMore: function (pageNumber) { return "Завантаження даних..."; },
        formatSearching: function () { return "Пошук..."; }
    });
})(jQuery);
<commit_msg>Ukrainian l10n improved<commit_after>/**
 * Select2 Ukrainian translation.
 *
 * @author  bigmihail <bigmihail@bigmir.net>
 * @author  Uriy Efremochkin <efremochkin@uriy.me>
 */
(function ($) {
    "use strict";

    $.extend($.fn.select2.defaults, {
        formatNoMatches: function () { return "Нічого не знайдено"; },
        formatInputTooShort: function (input, min) { return "Введіть буль ласка ще" + character(min - input.length, "символ"); },
        formatInputTooLong: function (input, max) { return "Введіть буль ласка на" + character(input.length - max, "символ") + " менше"; },
        formatSelectionTooBig: function (limit) { return "Ви можете вибрати лише" + character(limit, "елемент"); },
        formatLoadMore: function (pageNumber) { return "Завантаження даних..."; },
        formatSearching: function () { return "Пошук..."; }
    });

    function character (n, word) {
        return " " + n + " " + word + (n%10 < 5 && n%10 > 0 && (n%100 < 5 || n%100 > 19) ? n%10 > 1 ? "и" : "" : "ів");
    }
})(jQuery);
<|endoftext|>


<commit_before>(function () {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.recyclebin.RecycleBinApp', {
        extend: 'Rally.app.GridBoardApp',

        columnNames: ['ID','Name', 'Type','DeletedBy','DeletionDate'],
        modelNames: ['RecycleBinEntry'],
        statePrefix: 'recyclebin',
        enableOwnerFilter: false,
        enableAddNew: false,
        enablePrint: false,
        enableCsvExport: false,

        getGridConfig: function () {
            return _.merge(this.callParent(arguments), {
                enableRanking: false,
                enableBulkEdit: false,
                noDataItemName: 'deleted item'
            });
        },

        getFilterControlConfig: function () {
            return {
                margin: '3 9 3 0',
                blackListFields: ['ObjectID']
            };
        },

        getFieldPickerConfig: function() {
            return {
                margin: '3 9 3 -20'
            };
        }
    });
})();<commit_msg>Fixing recycle bin icon styles after S81704 messed them up<commit_after>(function () {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.recyclebin.RecycleBinApp', {
        extend: 'Rally.app.GridBoardApp',

        columnNames: ['ID','Name', 'Type','DeletedBy','DeletionDate'],
        modelNames: ['RecycleBinEntry'],
        statePrefix: 'recyclebin',
        enableOwnerFilter: false,
        enableAddNew: false,
        enablePrint: false,
        enableCsvExport: false,

        getGridConfig: function () {
            return _.merge(this.callParent(arguments), {
                enableRanking: false,
                enableBulkEdit: false,
                noDataItemName: 'deleted item'
            });
        },

        getFilterControlConfig: function () {
            return {
                blackListFields: ['ObjectID']
            };
        }
    });
})();<|endoftext|>


<commit_before>frappe.ui.form.set_user_image = function(frm) {

	var image_section = frm.sidebar.image_section;
	var image_field = frm.meta.image_field;
	var image = frm.doc[image_field];
	var title_image = frm.page.$title_area.find('.title-image');
	var image_actions = frm.sidebar.image_wrapper.find('.sidebar-image-actions');

	image_section.toggleClass('hide', image_field ? false : true);
	title_image.toggleClass('hide', image_field ? false : true);

	if(!image_field) {
		return;
	}

	// if image field has value
	if (image) {
		image = (window.cordova && image.indexOf('http')===-1) ?
			frappe.base_url + image : image;

		image_section
			.find(".sidebar-image")
			.attr("src", image)
			.removeClass("hide");

		image_section
			.find('.sidebar-standard-image')
			.addClass('hide');

		title_image
			.css("background-image", `url("${image}")`)
			.html('');

		image_actions.find('.sidebar-image-change, .sidebar-image-remove').show();

	} else {
		image_section
			.find(".sidebar-image")
			.attr("src", null)
			.addClass("hide");

		var title = frm.get_title();

		image_section
			.find('.sidebar-standard-image')
			.removeClass('hide')
			.find('.standard-image')
			.html(frappe.get_abbr(title));

		title_image.css("background-image", '')
			.html(frappe.get_abbr(title));

		image_actions.find('.sidebar-image-change').show();
		image_actions.find('.sidebar-image-remove').hide();
	}

}

frappe.ui.form.setup_user_image_event = function(frm) {
	// re-draw image on change of user image
	if(frm.meta.image_field) {
		frappe.ui.form.on(frm.doctype, frm.meta.image_field, function(frm) {
			frappe.ui.form.set_user_image(frm);
		});
	}

	frm.sidebar.image_wrapper.on('click', ':not(.sidebar-image-actions)', (e) => {
		let $target = $(e.currentTarget);
		if ($target.is('a.dropdown-toggle, .dropdown')) {
			return;
		}
		let dropdown = frm.sidebar.image_wrapper.find('.sidebar-image-actions .dropdown');
		dropdown.toggleClass('open');
		e.stopPropagation();
	});

	// bind click on image_wrapper
	frm.sidebar.image_wrapper.on('click', '.sidebar-image-change, .sidebar-image-remove', function(e) {
		let $target = $(e.currentTarget);
		var field = frm.get_field(frm.meta.image_field);
		if ($target.is('.sidebar-image-change')) {
			if(!field.$input) {
				field.make_input();
			}
			field.$input.trigger('attach_doc_image');
		} else {
			/// on remove event for a sidebar image wrapper remove attach file.
			frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field], function() {
				field.set_value('').then(() => frm.save());
			});
		}
	});
}
<commit_msg>fix: no add/change image-field if user is not allowed<commit_after>frappe.ui.form.set_user_image = function(frm) {

	var image_section = frm.sidebar.image_section;
	var image_field = frm.meta.image_field;
	var image = frm.doc[image_field];
	var title_image = frm.page.$title_area.find('.title-image');
	var image_actions = frm.sidebar.image_wrapper.find('.sidebar-image-actions');

	image_section.toggleClass('hide', image_field ? false : true);
	title_image.toggleClass('hide', image_field ? false : true);

	if(!image_field) {
		return;
	}

	// if image field has value
	if (image) {
		image = (window.cordova && image.indexOf('http')===-1) ?
			frappe.base_url + image : image;

		image_section
			.find(".sidebar-image")
			.attr("src", image)
			.removeClass("hide");

		image_section
			.find('.sidebar-standard-image')
			.addClass('hide');

		title_image
			.css("background-image", `url("${image}")`)
			.html('');

		image_actions.find('.sidebar-image-change, .sidebar-image-remove').show();

	} else {
		image_section
			.find(".sidebar-image")
			.attr("src", null)
			.addClass("hide");

		var title = frm.get_title();

		image_section
			.find('.sidebar-standard-image')
			.removeClass('hide')
			.find('.standard-image')
			.html(frappe.get_abbr(title));

		title_image.css("background-image", '')
			.html(frappe.get_abbr(title));

		image_actions.find('.sidebar-image-change').show();
		image_actions.find('.sidebar-image-remove').hide();
	}

}

frappe.ui.form.setup_user_image_event = function(frm) {
	// re-draw image on change of user image
	if(frm.meta.image_field) {
		frappe.ui.form.on(frm.doctype, frm.meta.image_field, function(frm) {
			frappe.ui.form.set_user_image(frm);
		});
	}

	if (frm.fields_dict[frm.meta.image_field].df.read_only == 0) {
	   	frm.sidebar.image_wrapper.on('click', ':not(.sidebar-image-actions)', (e) => {
			let $target = $(e.currentTarget);
			if ($target.is('a.dropdown-toggle, .dropdown')) {
				return;
			}
			let dropdown = frm.sidebar.image_wrapper.find('.sidebar-image-actions .dropdown');
			dropdown.toggleClass('open');
			e.stopPropagation();
		});
	}

	// bind click on image_wrapper
	frm.sidebar.image_wrapper.on('click', '.sidebar-image-change, .sidebar-image-remove', function(e) {
		let $target = $(e.currentTarget);
		var field = frm.get_field(frm.meta.image_field);
		if ($target.is('.sidebar-image-change')) {
			if(!field.$input) {
				field.make_input();
			}
			field.$input.trigger('attach_doc_image');
		} else {
			/// on remove event for a sidebar image wrapper remove attach file.
			frm.attachments.remove_attachment_by_filename(frm.doc[frm.meta.image_field], function() {
				field.set_value('').then(() => frm.save());
			});
		}
	});
}
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const SSDPClient = require('node-ssdp').Client

const request = require('request')

const parseString = require('xml2js').parseString;

const l2 = require('../util/Layer2.js');

const URL = require('url').URL;
const sm = require('../net2/SysManager.js');
const sclient = require('../util/redis_manager.js').getSubscriptionClient();
const Message = require('../net2/Message.js');

class SSDPSensor extends Sensor {

  onResponse(headers, statusCode, rinfo) {
    // only support ipv4 yet
    if(rinfo.family === 'IPv4' && statusCode === 200) {
      let ip = rinfo.address;
      let location = headers.LOCATION;

      try {
        let url = new URL(location);

        if (!sm.inMySubnets4(url.hostname)) {
          log.warn(`SSDP Location outside of v4 subnets: ${location} via ${ip}:${rinfo.port}`);
          this.locationCache[ip] = 0;
          return;
        }
      } catch(e) {
        log.error("Invalid SSDP location", headers, statusCode, rinfo, e);
        return;
      }

      let lastFoundTimestamp = this.locationCache[ip];
      if(!lastFoundTimestamp || lastFoundTimestamp < new Date() / 1000 - this.CACHE_INTERVAL) {
        this.locationCache[ip] = new Date() / 1000;
        this.parseURL(ip, location, (err) => {
          if(err) {
            this.locationCache[ip] = 0;
          }
        })
      }
    } else if (statusCode !== 200) {
      log.debug("Got an error ssdp response: ", headers, statusCode, rinfo)
    } else {
      log.warn("Unsupported ssdp response: ", headers, statusCode, rinfo)
    }
  }

  notify(ip, ssdpResult) {
    l2.getMAC(ip, (err, mac) => {

      if(err) {
        // not found, ignore this host
        log.error("Not able to found mac address for host:", ip, mac, err);
        return;
      }

      let host = {
        ipv4: ip,
        ipv4Addr: ip,
        mac: mac,
        bname: ssdpResult.deviceName,
        modelName: ssdpResult.modelName,
        manufacturer: ssdpResult.manufacturer,
        from: "ssdp"
      }

      log.info(`Found a device via ssdp: ${host.bname} (${ip} - ${host.mac})`)

      sem.emitEvent({
        type: "DeviceUpdate",
        message: `Found a device via ssdp ${ip} ${mac}`,
        host: host
      })

    });
  }

  parseURL(ip, location, callback) {
    let options = {
      uri: location,
      method: 'GET'
    }
    request(options, (err, response, body) => {
      if(err) {
        log.error("Failed to GET", location, "err:", err)
        callback(err)
        return
      }

      parseString(body, (err, result) => {
        if(err) {
          log.error(`Invalid SSDP XML for location ${location}, err: ${err}`)
          callback(err)
          return
        }

        const rr = this.parseContent(result)

        if(rr && rr.deviceName) {
          this.notify(ip, rr)
        }

        callback(null)
      })
    });
  }

  getElement(object, element) {
    let array = object[element]

    return array && (array.constructor.name === 'Array') && array.length > 0 && array[0]
  }

  parseContent(content) {
    let root = content && content.root

    if(!root) {
      return
    }

    let firstDevice = this.getElement(root, "device")

    if(!firstDevice)
      return

    let deviceName = this.getElement(firstDevice, "friendlyName")

    let manufacturer = this.getElement(firstDevice, "manufacturer")

    let modelName = this.getElement(firstDevice, "modelName")

    return {
      deviceName: deviceName,
      manufacturer: manufacturer,
      modelName: modelName
    }
  }

  scheduleReload() {
    if (this.reloadTask)
      clearTimeout(this.reloadTask);
    this.reloadTask = setTimeout(() => {
      if (this.ssdpClient)
        this.ssdpClient.stop();
      this.ssdpClient = null;
      const monitoringInterfaces = sm.getMonitoringInterfaces();
      const ifaces = monitoringInterfaces.filter(i => i.name).map(i => i.name);
      this.ssdpClient = new SSDPClient({interfaces: ifaces});
      this.ssdpClient.on('response', (header, statusCode, rinfo) => {
        this.onResponse(header, statusCode, rinfo)
      });
      process.nextTick(() => {
        this.ssdpClient.search('ssdp:all');
      });
    }, 5000);
  }

  run() {
    this.locationCache = {};
    this.CACHE_INTERVAL = this.config.cacheTTL || 3600; // one hour

    sem.once('IPTABLES_READY', () => {
      this.scheduleReload();

      sclient.on("message", (channel, message) => {
        if (channel === Message.MSG_SYS_NETWORK_INFO_RELOADED) {
          log.info("Schedule reload SSDPSensor since network info is reloaded");
          this.scheduleReload();
        }
      });
      sclient.subscribe(Message.MSG_SYS_NETWORK_INFO_RELOADED);

      setInterval(() => {
        if (this.ssdpClient)
          this.ssdpClient.search('ssdp:all');
      }, this.config.interval * 1000 || 10 * 60 * 1000); // every 10 minutes
    });
  }
}

module.exports = SSDPSensor
<commit_msg>filter interface alias in SSDPSensor<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const Sensor = require('./Sensor.js').Sensor;

const sem = require('../sensor/SensorEventManager.js').getInstance();

const SSDPClient = require('node-ssdp').Client

const request = require('request')

const parseString = require('xml2js').parseString;

const l2 = require('../util/Layer2.js');

const URL = require('url').URL;
const sm = require('../net2/SysManager.js');
const sclient = require('../util/redis_manager.js').getSubscriptionClient();
const Message = require('../net2/Message.js');

class SSDPSensor extends Sensor {

  onResponse(headers, statusCode, rinfo) {
    // only support ipv4 yet
    if(rinfo.family === 'IPv4' && statusCode === 200) {
      let ip = rinfo.address;
      let location = headers.LOCATION;

      try {
        let url = new URL(location);

        if (!sm.inMySubnets4(url.hostname)) {
          log.warn(`SSDP Location outside of v4 subnets: ${location} via ${ip}:${rinfo.port}`);
          this.locationCache[ip] = 0;
          return;
        }
      } catch(e) {
        log.error("Invalid SSDP location", headers, statusCode, rinfo, e);
        return;
      }

      let lastFoundTimestamp = this.locationCache[ip];
      if(!lastFoundTimestamp || lastFoundTimestamp < new Date() / 1000 - this.CACHE_INTERVAL) {
        this.locationCache[ip] = new Date() / 1000;
        this.parseURL(ip, location, (err) => {
          if(err) {
            this.locationCache[ip] = 0;
          }
        })
      }
    } else if (statusCode !== 200) {
      log.debug("Got an error ssdp response: ", headers, statusCode, rinfo)
    } else {
      log.warn("Unsupported ssdp response: ", headers, statusCode, rinfo)
    }
  }

  notify(ip, ssdpResult) {
    l2.getMAC(ip, (err, mac) => {

      if(err) {
        // not found, ignore this host
        log.error("Not able to found mac address for host:", ip, mac, err);
        return;
      }

      let host = {
        ipv4: ip,
        ipv4Addr: ip,
        mac: mac,
        bname: ssdpResult.deviceName,
        modelName: ssdpResult.modelName,
        manufacturer: ssdpResult.manufacturer,
        from: "ssdp"
      }

      log.info(`Found a device via ssdp: ${host.bname} (${ip} - ${host.mac})`)

      sem.emitEvent({
        type: "DeviceUpdate",
        message: `Found a device via ssdp ${ip} ${mac}`,
        host: host
      })

    });
  }

  parseURL(ip, location, callback) {
    let options = {
      uri: location,
      method: 'GET'
    }
    request(options, (err, response, body) => {
      if(err) {
        log.error("Failed to GET", location, "err:", err)
        callback(err)
        return
      }

      parseString(body, (err, result) => {
        if(err) {
          log.error(`Invalid SSDP XML for location ${location}, err: ${err}`)
          callback(err)
          return
        }

        const rr = this.parseContent(result)

        if(rr && rr.deviceName) {
          this.notify(ip, rr)
        }

        callback(null)
      })
    });
  }

  getElement(object, element) {
    let array = object[element]

    return array && (array.constructor.name === 'Array') && array.length > 0 && array[0]
  }

  parseContent(content) {
    let root = content && content.root

    if(!root) {
      return
    }

    let firstDevice = this.getElement(root, "device")

    if(!firstDevice)
      return

    let deviceName = this.getElement(firstDevice, "friendlyName")

    let manufacturer = this.getElement(firstDevice, "manufacturer")

    let modelName = this.getElement(firstDevice, "modelName")

    return {
      deviceName: deviceName,
      manufacturer: manufacturer,
      modelName: modelName
    }
  }

  scheduleReload() {
    if (this.reloadTask)
      clearTimeout(this.reloadTask);
    this.reloadTask = setTimeout(() => {
      if (this.ssdpClient)
        this.ssdpClient.stop();
      this.ssdpClient = null;
      const monitoringInterfaces = sm.getMonitoringInterfaces();
      const ifaces = monitoringInterfaces.filter(i => i.name && !i.name.endsWith(":0")).map(i => i.name);
      this.ssdpClient = new SSDPClient({interfaces: ifaces});
      this.ssdpClient.on('response', (header, statusCode, rinfo) => {
        this.onResponse(header, statusCode, rinfo)
      });
      process.nextTick(() => {
        this.ssdpClient.search('ssdp:all');
      });
    }, 5000);
  }

  run() {
    this.locationCache = {};
    this.CACHE_INTERVAL = this.config.cacheTTL || 3600; // one hour

    sem.once('IPTABLES_READY', () => {
      this.scheduleReload();

      sclient.on("message", (channel, message) => {
        if (channel === Message.MSG_SYS_NETWORK_INFO_RELOADED) {
          log.info("Schedule reload SSDPSensor since network info is reloaded");
          this.scheduleReload();
        }
      });
      sclient.subscribe(Message.MSG_SYS_NETWORK_INFO_RELOADED);

      setInterval(() => {
        if (this.ssdpClient)
          this.ssdpClient.search('ssdp:all');
      }, this.config.interval * 1000 || 10 * 60 * 1000); // every 10 minutes
    });
  }
}

module.exports = SSDPSensor
<|endoftext|>