<commit_before>/*
 * Copyright (c) 2002-2010 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * 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/>.
 */

/**
 * JMX exploration page module for the monitor component.
 */
wa.components.jmx.Jmx = (function($) {

    var me = {};

    me.basePage = $("

");
    me.ui = {};

    me.uiLoaded  = false;

    me.jmxData = null;
    me.currentBean = null;

    me.visible = false;

    //
    // PUBLIC
    //

    me.api = {

            getPage :  function() {
                return me.basePage;
            },

            /**
             * Get bean data, given a bean name. This takes a callback since it
             * is not for sure that data has been loaded yet.
             *
             * @param name
             *            is the bean name to find
             * @param cb
             *            is a callback that will be called with an array of
             *            matching beans (usually just one, but could be several
             *            if you use wildcard markers in the name)
             */
            findBean : function(name, cb) {

                var beanInfo = me.api.parseBeanName(name);

                // The requested bean is not available locally, check if there
                // is a server connection

                var server = wa.Servers.getCurrentServer();

                if( ! server ) { // Nope
                    cb([]);
                    return;
                }

                // Server available
                server.manage.jmx.getBean(beanInfo.domain, beanInfo.name, (function(cb) {
                    return function(data) {
                        cb(data);
                    };
                })(cb));

            },

            /**
             * Extract data from a bean name
             */
            parseBeanName : function(beanName) {

                var parts = beanName.split(":");

                return {domain:parts[0], name:parts[1]};

            },

            pageChanged : function(ev) {

                if(ev.data === "jmx") {
                    me.visible = true;

                    if( me.uiLoaded === false ) {
                        me.basePage.setTemplateURL("templates/components/monitor/jmx.tp");
                    }

                    var server = wa.Servers.getCurrentServer();

                    // If jmx data has not been loaded for the current server
                    if( server ) {

                        me.loadJMXDomains(me.server);

                    }

                } else {
                    me.visible = false;
                }
            },

            init : function() {
                $( window ).bind( "hashchange", me.hashchange );
                me.hashchange();
            }

    };

    //
    // PRIVATE
    //

    me.loadJMXDomains = function() {

        var server = wa.Servers.getCurrentServer();

        if( server ) {
            server.manage.jmx.query( ["*:*"], function(data) {

                me.jmxData = [];

                for( var i=0, l=data.length; i<l; i++ ) {
                    var bean = data[i];
                    me.getDomain(bean.domain).beans.push(bean);
                }

                me.render();
            });
        }
    };

    me.getDomain = function(domain) {
        for( var index in me.jmxData ) {
            if(me.jmxData[index].name === domain) {
                return me.jmxData[index];
            }
        }

        var domainObject = { name: domain, beans:[] };
        me.jmxData.push(domainObject);

        return domainObject;
    };

    /**
     * Triggered when the URL hash state changes.
     */
    me.hashchange = function(ev) {
        var beanName = $.bbq.getState( "jmxbean" );

        if( typeof(beanName) !== "undefined" && (me.currentBean === null || beanName !== me.currentBean.name)) {
            me.api.findBean(beanName, function(bean) {
                if(bean) {
                    me.currentBean = bean;
                    me.render();
                }

            });

        }
    };

    me.render = function() {
        if(me.visible) {
            me.basePage.processTemplate({
                jmx : (me.jmxData === null) ? [] : me.jmxData,
                server : me.server,
                bean : me.currentBean
            });
        }

    };

    //
    // CONSTRUCT
    //

    $('.mor_monitor_jmxbean_button').live('click', function(ev) {

        setTimeout((function(ev){
            return function() {
                $.bbq.pushState({
                    jmxbean : $('.bean-name',ev.target.parentNode).val()
                });
            };
        })(ev),0);

        ev.preventDefault();
    });

    wa.bind("servers.current.changed", function() {
        me.jmxData = null;

        // If the monitor page is currently visible, load jmx stuff
        if( me.visible === true ) {
            me.loadJMXDomains();
        }
    });

    return me.api;

})(jQuery);

//
// REGISTER STUFF
//

wa.ui.Pages.add("jmx",wa.components.jmx.Jmx);
wa.ui.MainMenu.add({ label : "Server info", pageKey:"jmx", index:7, requiredServices:['jmx'], perspectives:['server']});

wa.bind("init", wa.components.jmx.Jmx.init);
wa.bind("ui.page.changed", wa.components.jmx.Jmx.pageChanged);<commit_msg>Moved neo4j jmx domain to the top of JMX browser.<commit_after>/*
 * Copyright (c) 2002-2010 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * 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/>.
 */

/**
 * JMX exploration page module for the monitor component.
 */
wa.components.jmx.Jmx = (function($) {

    var me = {},
        NEO4J_DOMAIN = "org.neo4j";

    me.basePage = $("

");
    me.ui = {};

    me.uiLoaded  = false;

    me.jmxData = null;
    me.currentBean = null;

    me.visible = false;

    //
    // PUBLIC
    //

    me.api = {

            getPage :  function() {
                return me.basePage;
            },

            /**
             * Get bean data, given a bean name. This takes a callback since it
             * is not for sure that data has been loaded yet.
             *
             * @param name
             *            is the bean name to find
             * @param cb
             *            is a callback that will be called with an array of
             *            matching beans (usually just one, but could be several
             *            if you use wildcard markers in the name)
             */
            findBean : function(name, cb) {

                var beanInfo = me.api.parseBeanName(name);

                // The requested bean is not available locally, check if there
                // is a server connection

                var server = wa.Servers.getCurrentServer();

                if( ! server ) { // Nope
                    cb([]);
                    return;
                }

                // Server available
                server.manage.jmx.getBean(beanInfo.domain, beanInfo.name, (function(cb) {
                    return function(data) {
                        cb(data);
                    };
                })(cb));

            },

            /**
             * Extract data from a bean name
             */
            parseBeanName : function(beanName) {

                var parts = beanName.split(":");

                return {domain:parts[0], name:parts[1]};

            },

            pageChanged : function(ev) {

                if(ev.data === "jmx") {
                    me.visible = true;

                    if( me.uiLoaded === false ) {
                        me.basePage.setTemplateURL("templates/components/monitor/jmx.tp");
                    }

                    var server = wa.Servers.getCurrentServer();

                    // If jmx data has not been loaded for the current server
                    if( server ) {

                        me.loadJMXDomains(me.server);

                    }

                } else {
                    me.visible = false;
                }
            },

            init : function() {
                $( window ).bind( "hashchange", me.hashchange );
                me.hashchange();
            }

    };

    //
    // PRIVATE
    //

    me.loadJMXDomains = function() {

        var server = wa.Servers.getCurrentServer();

        if( server ) {
            server.manage.jmx.query( ["*:*"], function(data) {

                me.jmxData = [];

                for( var i=0, l=data.length; i<l; i++ ) {
                    var bean = data[i];
                    me.getDomain(bean.domain).beans.push(bean);
                }

                me.render();
            });
        }
    };

    me.getDomain = function(domain) {
        for( var index in me.jmxData ) {
            if(me.jmxData[index].name === domain) {
                return me.jmxData[index];
            }
        }

        var domainObject = { name: domain, beans:[] };
        me.jmxData.push(domainObject);

        return domainObject;
    };

    /**
     * Triggered when the URL hash state changes.
     */
    me.hashchange = function(ev) {
        var beanName = $.bbq.getState( "jmxbean" );

        if( typeof(beanName) !== "undefined" && (me.currentBean === null || beanName !== me.currentBean.name)) {
            me.api.findBean(beanName, function(bean) {
                if(bean) {
                    me.currentBean = bean;
                    me.render();
                }

            });

        }
    };

    me.render = function() {
        if(me.visible) {

        	// Re-order jmx data to show neo4j domain first
        	if(me.jmxData !== null) {

        		jmxData = me.jmxData.sort(function(a,b) {
        			if( a.name === NEO4J_DOMAIN ) {
        				return -1;
        			} else if( b.name === NEO4J_DOMAIN ) {
        				return 1;
        			}
        			return 0;
        		});

        	} else {
        		jmxData = [];
        	}

            me.basePage.processTemplate({
                jmx : jmxData,
                server : me.server,
                bean : me.currentBean
            });
        }

    };

    //
    // CONSTRUCT
    //

    $('.mor_monitor_jmxbean_button').live('click', function(ev) {

        setTimeout((function(ev){
            return function() {
                $.bbq.pushState({
                    jmxbean : $('.bean-name',ev.target.parentNode).val()
                });
            };
        })(ev),0);

        ev.preventDefault();
    });

    wa.bind("servers.current.changed", function() {
        me.jmxData = null;

        // If the monitor page is currently visible, load jmx stuff
        if( me.visible === true ) {
            me.loadJMXDomains();
        }
    });

    return me.api;

})(jQuery);

//
// REGISTER STUFF
//

wa.ui.Pages.add("jmx",wa.components.jmx.Jmx);
wa.ui.MainMenu.add({ label : "Server info", pageKey:"jmx", index:7, requiredServices:['jmx'], perspectives:['server']});

wa.bind("init", wa.components.jmx.Jmx.init);
wa.bind("ui.page.changed", wa.components.jmx.Jmx.pageChanged);<|endoftext|>


<commit_before>var path          = require('path');
var AvisynthError = require('./errors').AvisynthError;
var colors        = require('./colors');
var addPlugin     = require('./plugin-system').addPlugin;
var utils         = require('./utils');

// This file contains helpers to make it easier to add core filters as plugins.
// It was in the plugin-definitions file, but it started to become too complex so
// I encapsulated it here instead. It may be eventually exposed to external usage.

// This function creates and adds a new plugin to the plugin system with ease.
// "name" can be either a name, or a combination of name and parameters in parens.
// Such an usage is employed extensively in plugin-definitions.js, serving as example.
// "options" can be an object with options, a parameter list (array or string), or type
// list if the name includes a parameter list. "types", if specified, is a type list
// for the "t" parameter modifier, types can also be in an array or string. Both these
// strings are separated by commas, and trim whitespaces for each item.
// Each parameter can be either a name, or a name with modifiers prefixed, separated by
// a colon, for example "ri:length" means "required int called length". In this usage,
// the name is optional (some avisynth plugins don't even accept some named parameters).
// The full list of modifiers is:
// q: quoted (string).
// p: filesystem path (resolved to absolute), implies q.
// r: required field. Lack of it is an error.
// f: forced file path, implies p and r.
// n: not a path (actually, not a string). Throws if a string is given.
// t: a type, this is checked against options.types. Implies q.
// b: field is cast to boolean.
// d: field is cast to a decimal number (integer or float).
// i: field is cast to integer, rounded as necessary. Implies d.
// v: a variable name (unquoted string), checked for syntactic validity.
// c: a color variable, can be an int, name or string (0x123ABC, 0, 'red', 'F0F', 'FF00FF').
// a: auto-type, strings get quoted, numbers and bools not.
//    (variables are only supported in "a" with an unmatched "t" type, and paths with "p").
//
// It might seem like a complicated function but once you see examples you'll notice
// it allows defining complex plugin signatures in a single line.
exports.newPlugin = function(name, options, types) {
    var actualName = name.indexOf('(') !== -1 ? name.match(/[^(]*/)[0] : name;
    addPlugin(actualName, createPlugin(name, options, types));
};

// Checks that a value is one of a set of allowed values, else throwing an AvisynthError.
function checkType(value, allowed) {
    if (value && allowed.indexOf(value) === -1) {
        throw new AvisynthError('bad type (' + value + ')! allowed values: ' + allowed);
    }
}

function createPlugin(name, options, types) {

    // Lazyness taken to new heights. First parameter can succintly describe some filters.
    // If combined with defining the second parameter, that will represent the types.
    if (name.indexOf('(') !== -1) {
        var matches = name.match(/([^() ]*)\s*(\((.*)\))?/);
        name = matches[1];
        if (options) { types = options; }
        options = matches[3];
    }

    // Conveniently cast options/types from string to array, for the condition below.
    if (typeof options === 'string') { options = options.split(/\s*,\s*/); }
    if (typeof types   === 'string') { types   =   types.split(/\s*,\s*/); }

    // If options is an array instead of object, it is treated as options.params.
    // Optionally, options.types can be inlined too, by passing a second array.
    if (options && utils.isDefined(options.length)) {
        options = {params: options, types: types};
    }

    // If options is still not defined, default it.
    if (!utils.isDefined(options)) { options = {}; }

    // These aren't being used, in favor of the cleaner options currently available.
    // I may just drop these options altogether, but I'm still not sure if they
    // can have any use, if we expose this plugin definition system eventually.
    // // We also support shorthands for the options properties.
    // if (options.p) { options.params = options.p; }
    // if (options.t) { options.types  = options.t; }
    //
    // // Similar to a cast above, but for options' properties.
    // if (typeof options.params === 'string') { options.params = options.params.split(/\s*,\s*/); }
    // if (typeof options.types  === 'string') { options.types  =  options.types.split(/\s*,\s*/); }

    // Utility that processes a parameter, used in core filters. The m variable is the modifier.
    var processParameter = parameterProcessor(options);

    return pluginImplementation(name, options, processParameter);
}

// Returns a plugin implementation to be added as a plugin by createPlugin
function pluginImplementation(name, options, processParameter) {
    return function() {
        // Construct parameter definitons.
        var definitions = [];
        if (options.params) {
            options.params.forEach(function(param) {
                // Each item in options.params should follow modifier:identifier format.
                var matches = param.match(/^((.*):)?(.*)$/);
                definitions.push({modifier: matches[2], identifier: matches[3]});
            });
        }

        // Construct parameter list.
        var params = [];
        // There are two iterators; "a" is for actual argument, "d" is for definiton.
        // These may progress independently if a definiton accepts multiple arguments.
        for (var a = 0, d = 0; a < arguments.length; a++, d++) {
            var definition = definitions[d];
            if (!definition) { throw new AvisynthError('too many arguments for ' + name); }
            var value = arguments[a];
            if (!utils.isDefined(value)) { continue; }
            var m = definition.modifier;
            if (/m/.test(m)) {
                // Multiple parameter modes.
                var multi = [value];
                var type = typeof value;
                while (typeof (value = arguments[++a]) === type) { multi.push(value); } a--;
                multi = multi.map(processParameter.bind(null, m));
                multi.forEach(function(p) { params.push(p); });
            } else {
                value = processParameter(m, value);
                if (definition.identifier) {
                    params.push(definition.identifier + '=' + value);
                } else {
                    // Now we reach a special case, because if the parameter is nameless,
                    // we have to avoid it being ambiguous with a previous one.
                    // This could happen if any previous parameter was omitted (named or not).
                    if (a > params.length) {
                        throw new AvisynthError('a skipped parameter makes ' + value + ' ambiguous!');
                    } else {
                        params.push(value);
                    }
                }
            }
        }

        // Ensure all required arguments are provided.
        for (var i = 0; i < definitions.length; i++) {
            var def = definitions[i];
            if (def.modifier && !utils.isDefined(arguments[i])) {
                if (/f/.test(def.modifier)) { throw new AvisynthError('filename is a required argument!'); }
                if (/r/.test(def.modifier)) { throw new AvisynthError('a required argument is missing!'); }
            }
        }

        return name + '(' + params.join(', ') + ')';
    };
}

// Creates a parameter processor.
function parameterProcessor(options) {
    // "m" is the modifier (assumed to be a string).
    return function(m, value) {
        var param = {m: m, value: value, options: options};
        processType(param);
        processPath(param);
        processString(param);
        processNonPath(param);
        processBoolean(param);
        processDecimal(param);
        processInteger(param);
        processColor(param);
        processVariable(param);
        processAutotype(param);
        return param.value;
    };
}

// The following functions perform the grunt work of the parameter processor.

function processType(param) {
    if (/t/.test(param.m) && !/a/.test(param.m)) {
        checkType(param.value, param.options.types);
    }
}

function processPath(param) {
    if (/[fp]/.test(param.m) && !/a/.test(param.m)) {
        param.value = path.resolve(param.value);
    }
}

function processString(param) {
    if (/[fpqt]/.test(param.m) && !/a/.test(param.m)) {
        param.value = '"' + param.value + '"';
    }
}

function processNonPath(param) {
    if (/n/.test(param.m) && typeof param.value === 'string') {
        throw new AvisynthError('only one path supported!');
    }
}

function processBoolean(param) {
    if (/b/.test(param.m) && param.value !== Boolean(param.value)) {
        throw new AvisynthError('expected boolean, got "' + param.value + '"');
    }
}

function processDecimal(param) {
    if (/d/.test(param.m) && param.value !== +param.value) {
        throw new AvisynthError('expected number, got "' + param.value + '"');
    }
}

function processInteger(param) {
    if (/i/.test(param.m) && param.value !== Math.round(param.value)) {
        throw new AvisynthError('expected integer, got "' + param.value + '"');
    }
}

function processColor(param) {
    if (/c/.test(param.m)) {
        param.value = colors.parse(param.value);
    }
}

function processVariable(param) {
    if (/v/.test(param.m)) {
        if (typeof param.value !== 'string') {
            throw new AvisynthError('variable must be a string!');
        }
        if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
            throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
        }
    }
}

function processAutotype(param) {
    if (/a/.test(param.m)) {
        // Auto-parameters should guess what the value is supposed to mean.
        // If it's a number or boolean, just output it as is.
        if (typeof param.value === 'number') { param.value = +param.value; }
        if (typeof param.value === 'boolean') { param.value = Boolean(param.value); }
        // But if it's a string, it depends on whether it can also be a type.
        if (typeof param.value === 'string') {
            // Types should not throw errors, but rather become variables if possible.
            if (/t/.test(param.m)) {
                try {
                    checkType(param.value, param.options.types);
                    param.value = '"' + param.value + '"';
                } catch (e) {
                    if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
                        throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
                    }
                }
            } else if (/p/.test(param.m)) {
                // Will be converted to a path.
                param.value = '"' + path.resolve(param.value) + '"';
            } else {
                if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
                    throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
                }
            }
        }
    }
}
<commit_msg>Further attempt to reduce complexity.<commit_after>var path          = require('path');
var AvisynthError = require('./errors').AvisynthError;
var colors        = require('./colors');
var addPlugin     = require('./plugin-system').addPlugin;
var utils         = require('./utils');

// This file contains helpers to make it easier to add core filters as plugins.
// It was in the plugin-definitions file, but it started to become too complex so
// I encapsulated it here instead. It may be eventually exposed to external usage.

// This function creates and adds a new plugin to the plugin system with ease.
// "name" can be either a name, or a combination of name and parameters in parens.
// Such an usage is employed extensively in plugin-definitions.js, serving as example.
// "options" can be an object with options, a parameter list (array or string), or type
// list if the name includes a parameter list. "types", if specified, is a type list
// for the "t" parameter modifier, types can also be in an array or string. Both these
// strings are separated by commas, and trim whitespaces for each item.
// Each parameter can be either a name, or a name with modifiers prefixed, separated by
// a colon, for example "ri:length" means "required int called length". In this usage,
// the name is optional (some avisynth plugins don't even accept some named parameters).
// The full list of modifiers is:
// q: quoted (string).
// p: filesystem path (resolved to absolute), implies q.
// r: required field. Lack of it is an error.
// f: forced file path, implies p and r.
// n: not a path (actually, not a string). Throws if a string is given.
// t: a type, this is checked against options.types. Implies q.
// b: field is cast to boolean.
// d: field is cast to a decimal number (integer or float).
// i: field is cast to integer, rounded as necessary. Implies d.
// v: a variable name (unquoted string), checked for syntactic validity.
// c: a color variable, can be an int, name or string (0x123ABC, 0, 'red', 'F0F', 'FF00FF').
// a: auto-type, strings get quoted, numbers and bools not.
//    (variables are only supported in "a" with an unmatched "t" type, and paths with "p").
//
// It might seem like a complicated function but once you see examples you'll notice
// it allows defining complex plugin signatures in a single line.
exports.newPlugin = function(name, options, types) {
    var actualName = name.indexOf('(') !== -1 ? name.match(/[^(]*/)[0] : name;
    addPlugin(actualName, createPlugin(name, options, types));
};

// Checks that a value is one of a set of allowed values, else throwing an AvisynthError.
function checkType(value, allowed) {
    if (value && allowed.indexOf(value) === -1) {
        throw new AvisynthError('bad type (' + value + ')! allowed values: ' + allowed);
    }
}

function createPlugin(name, options, types) {

    // Lazyness taken to new heights. First parameter can succintly describe some filters.
    // If combined with defining the second parameter, that will represent the types.
    if (name.indexOf('(') !== -1) {
        var matches = name.match(/([^() ]*)\s*(\((.*)\))?/);
        name = matches[1];
        if (options) { types = options; }
        options = matches[3];
    }

    // Conveniently cast options/types from string to array, for the condition below.
    if (typeof options === 'string') { options = options.split(/\s*,\s*/); }
    if (typeof types   === 'string') { types   =   types.split(/\s*,\s*/); }

    // If options is an array instead of object, it is treated as options.params.
    // Optionally, options.types can be inlined too, by passing a second array.
    if (options && utils.isDefined(options.length)) {
        options = {params: options, types: types};
    }

    // If options is still not defined, default it.
    if (!utils.isDefined(options)) { options = {}; }

    // These aren't being used, in favor of the cleaner options currently available.
    // I may just drop these options altogether, but I'm still not sure if they
    // can have any use, if we expose this plugin definition system eventually.
    // // We also support shorthands for the options properties.
    // if (options.p) { options.params = options.p; }
    // if (options.t) { options.types  = options.t; }
    //
    // // Similar to a cast above, but for options' properties.
    // if (typeof options.params === 'string') { options.params = options.params.split(/\s*,\s*/); }
    // if (typeof options.types  === 'string') { options.types  =  options.types.split(/\s*,\s*/); }

    // Utility that processes a parameter, used in core filters. The m variable is the modifier.
    var processParameter = parameterProcessor(options);

    return pluginImplementation(name, options, processParameter);
}

// Returns a plugin implementation to be added as a plugin by createPlugin
function pluginImplementation(name, options, processParameter) {
    return function() {
        // Construct parameter definitons.
        var definitions = [];
        if (options.params) {
            options.params.forEach(function(param) {
                // Each item in options.params should follow modifier:identifier format.
                var matches = param.match(/^((.*):)?(.*)$/);
                definitions.push({modifier: matches[2], identifier: matches[3]});
            });
        }

        // Construct parameter list.
        var params = [];
        // There are two iterators; "a" is for actual argument, "d" is for definiton.
        // These may progress independently if a definiton accepts multiple arguments.
        for (var a = 0, d = 0; a < arguments.length; a++, d++) {
            var definition = definitions[d];
            if (!definition) { throw new AvisynthError('too many arguments for ' + name); }
            var value = arguments[a];
            if (!utils.isDefined(value)) { continue; }
            var m = definition.modifier;
            if (/m/.test(m)) {
                // Multiple parameter modes.
                var multi = [value];
                var type = typeof value;
                while (typeof (value = arguments[++a]) === type) { multi.push(value); } a--;
                multi = multi.map(processParameter.bind(null, m));
                multi.forEach(function(p) { params.push(p); });
            } else {
                value = processParameter(m, value);
                if (definition.identifier) {
                    params.push(definition.identifier + '=' + value);
                } else {
                    // Now we reach a special case, because if the parameter is nameless,
                    // we have to avoid it being ambiguous with a previous one.
                    // This could happen if any previous parameter was omitted (named or not).
                    if (a > params.length) {
                        throw new AvisynthError('a skipped parameter makes ' + value + ' ambiguous!');
                    } else {
                        params.push(value);
                    }
                }
            }
        }

        // Ensure all required arguments are provided.
        for (var i = 0; i < definitions.length; i++) {
            var def = definitions[i];
            if (def.modifier && !utils.isDefined(arguments[i])) {
                if (/f/.test(def.modifier)) { throw new AvisynthError('filename is a required argument!'); }
                if (/r/.test(def.modifier)) { throw new AvisynthError('a required argument is missing!'); }
            }
        }

        return name + '(' + params.join(', ') + ')';
    };
}

// Creates a parameter processor.
function parameterProcessor(options) {
    // "m" is the modifier (assumed to be a string).
    return function(m, value) {
        var param = {m: m, value: value, options: options};
        if (!/a/.test(m)) {
            processType(param);
            processPath(param);
            processString(param);
        }
        processNonPath(param);
        processBoolean(param);
        processDecimal(param);
        processInteger(param);
        processColor(param);
        processVariable(param);
        processAutotype(param);
        return param.value;
    };
}

// The following functions perform the grunt work of the parameter processor.

function processType(param) {
    if (/t/.test(param.m)) {
        checkType(param.value, param.options.types);
    }
}

function processPath(param) {
    if (/[fp]/.test(param.m)) {
        param.value = path.resolve(param.value);
    }
}

function processString(param) {
    if (/[fpqt]/.test(param.m)) {
        param.value = '"' + param.value + '"';
    }
}

function processNonPath(param) {
    if (/n/.test(param.m) && typeof param.value === 'string') {
        throw new AvisynthError('only one path supported!');
    }
}

function processBoolean(param) {
    if (/b/.test(param.m) && param.value !== Boolean(param.value)) {
        throw new AvisynthError('expected boolean, got "' + param.value + '"');
    }
}

function processDecimal(param) {
    if (/d/.test(param.m) && param.value !== +param.value) {
        throw new AvisynthError('expected number, got "' + param.value + '"');
    }
}

function processInteger(param) {
    if (/i/.test(param.m) && param.value !== Math.round(param.value)) {
        throw new AvisynthError('expected integer, got "' + param.value + '"');
    }
}

function processColor(param) {
    if (/c/.test(param.m)) {
        param.value = colors.parse(param.value);
    }
}

function processVariable(param) {
    if (/v/.test(param.m)) {
        if (typeof param.value !== 'string') {
            throw new AvisynthError('variable must be a string!');
        }
        if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
            throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
        }
    }
}

function processAutotype(param) {
    if (/a/.test(param.m)) {
        // Auto-parameters should guess what the value is supposed to mean.
        // If it's a number or boolean, just output it as is.
        // But if it's a string, it depends on whether it can also be a type.
        if (typeof param.value === 'string') {
            // Types should not throw errors, but rather become variables if possible.
            if (/t/.test(param.m)) {
                try {
                    checkType(param.value, param.options.types);
                    param.value = '"' + param.value + '"';
                } catch (e) {
                    if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
                        throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
                    }
                }
            } else if (/p/.test(param.m)) {
                // Will be converted to a path.
                param.value = '"' + path.resolve(param.value) + '"';
            } else {
                if (!/^[a-z_][0-9a-z_]*$/i.test(param.value)) {
                    throw new AvisynthError('bad syntax for variable name "' + param.value + '"!');
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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.
 */
({
	/**
	 * Verify initial values are loaded correctly.
	 */
	testInitialOptionSelected : {
		test : function(cmp) {
			var inputSelectCmp = cmp.find("dynamicSelect");
			var optionCmps = inputSelectCmp.find("options");
			this.verifySelectedOption(optionCmps, "Option2", this.optionComponentSelected);
		}
	},

	testInitialOptionSelectedDOM : {
		test : function(cmp) {
			var inputSelectCmp = cmp.find("dynamicSelect");
            var options = inputSelectCmp.getElement().options;
            this.verifySelectedOption(options, "Option2", this.optionDOMSelected);
		}
	},

	/**
	 * Verify dynamic value change
	 */
	testChangeSelectionDynamic : {
		test : function(cmp) {
			var inputSelectCmp = cmp.find("dynamicSelect");

			inputSelectCmp.set("v.value", "Option4");

            var options = inputSelectCmp.getElement().options;
            this.verifySelectedOption(options, "Option4", this.optionDOMSelected);
		}
	},

	testChangeSelectionDynamicDOM : {
		test : function(cmp) {
			var inputSelectCmp = cmp.find("dynamicSelect");

			inputSelectCmp.set("v.value", "Option4");

			var options = inputSelectCmp.getElement().options;
			this.verifySelectedOption(options, "Option4", this.optionDOMSelected);
		}
	},

	verifySelectedOption : function(options, selectedLabel, predicate) {
		var selectedOptionLabel = "None Selected";
		var selectedOption = this.getSelectedOption(options, predicate.match);
		if (!$A.util.isUndefinedOrNull(selectedOption)) {
			selectedOptionLabel = $A.util.isComponent(selectedOption)?selectedOption.get("v.label"):selectedOption.label;
		}
		$A.test.assertEquals(selectedLabel, selectedOptionLabel,
			"Incorrect option selected; Comparing by \"" + predicate.description + "\".");
	},

	getSelectedOption : function(options, match) {
		for (var i=0; i<options.length; i++) {
			if (match(options[i]) === true) {
				return options[i];
			}
		}
		return null;
	},

	optionComponentSelected : { match: function(option) {
									return option.get("v.value");
								},
								description: "Match by Component" },

	optionDOMSelected : { match: function(option) {
							return option.selected;
							},
							description: "Match by DOM Element" }
})<commit_msg>Removed test that shouldn't exist anymore<commit_after><|endoftext|>


<commit_before>var expression = require('../expression'),
    kv = require('../store/kv'),
    tu = require('../utils/tint-utils'),
    Q = require('q'),
    log4js = require('log4js');

var logger = log4js.getLogger('dispatcher.resource');
var nodeInfo = require('../node');
var settings = require('../settings');

var events = require('../store/events'),
    eventNames = require('../event_names');

module.exports = {
    install: {
        byTint: installResourceForTint
    },
    remove: {
        all: removeResources,
        byTint: removeTintResources
    }
};

// ====================================================================================================================
// == Install
// ====================================================================================================================
function installResourceForTint(tint) {
    logger.debug('Installing resources for ' + tint.type + ' ' + tint.slug);

    getNodesForTint(tint)
        .then(function(nodes) {
            var promises = [];

            tint.resources.forEach(function(resource) {
                promises.push(installConfigurationResource(tint, nodes, resource))
            });

            if (tint.services) {
                tint.services.forEach(function (service) {
                    service.daemons.forEach(function (daemon) {
                        promises.push(installContainerResources(tint, nodes, service, daemon));
                    })
                });
            }

            return Q.all(promises);
        });
}

function getNodesForTint(tint) {
    // -- run through the groups in the tint to see which nodes participate in this tint. Once we have all the nodes,
    // -- we can distribute the resource to them.

    var result = [];

    var promises = [];
    if (tint.services) {
        tint.services.forEach(function(service) {
            service.daemons.forEach(function(daemon) {
                promises.push(expression.nodes(daemon.instances).then(function(nodes) {
                    nodes.forEach(function(node) {
                        if (result.indexOf(node) == -1) result.push(node);
                    });
                }));
            })
        })
    }

    return Q.all(promises)
        .then(function() { return result; });
}

function installConfigurationResource(tint, nodes, resource) {
    var promises = [];

    var tintFsDir = settings.get('data_dir') + '/tints/' + tu.id(tint) + '/resources';
    var tintConfigDir = tintFsDir + '/' + resource.id;

    nodes.forEach(function(node) {
        promises.push(
            registerResourceOnNode(tint, node, resource.id, 'template', tintConfigDir, createScope(tint))
        );
    });

    return Q.all(promises);
}

function installContainerResources(tint, nodes, service, daemon) {
    var promises = [];

    nodes.forEach(function(node) {
        promises.push(installResourcesForContainerOnNode(tint, node, service, daemon));
    });

    return Q.all(promises);
}

function installResourcesForContainerOnNode(tint, node, service, daemon) {
    logger.debug('create the resources needed by the service daemon');

    var volumes = daemon.configuration.Mounts;

    logger.debug('Iterate the volumes as they are defined in the daemon configuration');
    var promises = [];

    if (volumes) {
        volumes.forEach(function (volume) {
            promises.push(
                installResourcesForContainerVolumeOnNode(tint, node, service, daemon, volume)
            )
        });
    }

    return Q.all(promises);
}

function installResourcesForContainerVolumeOnNode(tint, node, service, daemon, volume) {
    var tintFsDir = settings.get('data_dir') + '/tints/' + tu.id(tint) + '/resources';

    var filename;
    if (volume.Source.indexOf('resource:') == 0) {
        // -- mount a configuration volume. This means we will take the data from consul and generate it into the
        // -- configuration folder on the FS
        filename = volume.Source.substr('resource:'.length);
        if (filename.indexOf('/') == 0) filename = filename.substring(1);

        volume.Source = tintFsDir + '/' + filename;
        return Q();

    } else return Q();
}

// ====================================================================================================================
// == Remove
// ====================================================================================================================

function removeResources() {
    return kv.list({key: 'nodes', separator: '/'}).then(function(nodes) {
        var promises = [];

        nodes.forEach(function(node) {

            promises.push(
                kv.multiflag('nodes/' + node + '/resources', 999)
                    .then(function() { events.fire(eventNames.RESOURCE_CLEANUP_PENDING, { node: node });})
            );
        });

        return Q.all(promises);
    });
}

function removeTintResources(tint) {
    var promises = [];

    getNodesForTint(tint).then(function(nodes) {
        nodes.forEach(function(node) {
            promises.push(removeTintResourcesOnNode(tint, node));
        });
    });

    return Q.all(promises)
}

function removeTintResourcesOnNode(tint, node) {
    logger.info('Flagging tint resources for removal');
    return kv.multiflag('nodes/' + node.id + '/resources/' + tu.id(tint), 999).then(function() {
        events.fire(eventNames.RESOURCE_UNINSTALL_PENDING, {tint: tu.id(tint), node: node.id});
    });
}

function registerResourceOnNode(tint, node, resourceName, resourceType, resourceFsPath, resourceScope) {
    return kv.set('nodes/' + node.id + '/resources/' + tu.id(tint) + '/' + resourceName, {
        id: resourceName,
        type: resourceType,
        tint: tu.id(tint),
        node: node.id,
        consulPath: 'resources/' + tu.id(tint) + '/' + resourceName,
        fsPath: resourceFsPath,
        scope: resourceScope
    }, null, 0).then(function() {
        events.fire(eventNames.RESOURCE_INSTALL_PENDING, {tint: tu.id(tint), resource: resourceName, node: node.id});
    });
}

function createScope(tint) {
    var tintId = tu.id(tint);

    var variables = {
        settings: settings.get('data_dir'),
        device: nodeInfo,
        data_dir: settings.get('data_dir') + '/tints/' + tintId + '/data',
        resources_dir: settings.get('data_dir') + '/tints/' + tintId + '/resources',
        config_dir: settings.get('data_dir') + '/tints/' + tintId + '/resources/config'
    };

    return variables;
}<commit_msg>added a missing return statement<commit_after>var expression = require('../expression'),
    kv = require('../store/kv'),
    tu = require('../utils/tint-utils'),
    Q = require('q'),
    log4js = require('log4js');

var logger = log4js.getLogger('dispatcher.resource');
var nodeInfo = require('../node');
var settings = require('../settings');

var events = require('../store/events'),
    eventNames = require('../event_names');

module.exports = {
    install: {
        byTint: installResourceForTint
    },
    remove: {
        all: removeResources,
        byTint: removeTintResources
    }
};

// ====================================================================================================================
// == Install
// ====================================================================================================================
function installResourceForTint(tint) {
    logger.debug('Installing resources for ' + tint.type + ' ' + tint.slug);

    return getNodesForTint(tint)
        .then(function(nodes) {
            var promises = [];

            tint.resources.forEach(function(resource) {
                promises.push(installConfigurationResource(tint, nodes, resource))
            });

            if (tint.services) {
                tint.services.forEach(function (service) {
                    service.daemons.forEach(function (daemon) {
                        promises.push(installContainerResources(tint, nodes, service, daemon));
                    })
                });
            }

            return Q.all(promises);
        });
}

function getNodesForTint(tint) {
    // -- run through the groups in the tint to see which nodes participate in this tint. Once we have all the nodes,
    // -- we can distribute the resource to them.

    var result = [];

    var promises = [];
    if (tint.services) {
        tint.services.forEach(function(service) {
            service.daemons.forEach(function(daemon) {
                promises.push(expression.nodes(daemon.instances).then(function(nodes) {
                    nodes.forEach(function(node) {
                        if (result.indexOf(node) == -1) result.push(node);
                    });
                }));
            })
        })
    }

    return Q.all(promises)
        .then(function() { return result; });
}

function installConfigurationResource(tint, nodes, resource) {
    var promises = [];

    var tintFsDir = settings.get('data_dir') + '/tints/' + tu.id(tint) + '/resources';
    var tintConfigDir = tintFsDir + '/' + resource.id;

    nodes.forEach(function(node) {
        promises.push(
            registerResourceOnNode(tint, node, resource.id, 'template', tintConfigDir, createScope(tint))
        );
    });

    return Q.all(promises);
}

function installContainerResources(tint, nodes, service, daemon) {
    var promises = [];

    nodes.forEach(function(node) {
        promises.push(installResourcesForContainerOnNode(tint, node, service, daemon));
    });

    return Q.all(promises);
}

function installResourcesForContainerOnNode(tint, node, service, daemon) {
    logger.debug('create the resources needed by the service daemon');

    var volumes = daemon.configuration.Mounts;

    logger.debug('Iterate the volumes as they are defined in the daemon configuration');
    var promises = [];

    if (volumes) {
        volumes.forEach(function (volume) {
            promises.push(
                installResourcesForContainerVolumeOnNode(tint, node, service, daemon, volume)
            )
        });
    }

    return Q.all(promises);
}

function installResourcesForContainerVolumeOnNode(tint, node, service, daemon, volume) {
    var tintFsDir = settings.get('data_dir') + '/tints/' + tu.id(tint) + '/resources';

    var filename;
    if (volume.Source.indexOf('resource:') == 0) {
        // -- mount a configuration volume. This means we will take the data from consul and generate it into the
        // -- configuration folder on the FS
        filename = volume.Source.substr('resource:'.length);
        if (filename.indexOf('/') == 0) filename = filename.substring(1);

        volume.Source = tintFsDir + '/' + filename;
        return Q();

    } else return Q();
}

// ====================================================================================================================
// == Remove
// ====================================================================================================================

function removeResources() {
    return kv.list({key: 'nodes', separator: '/'}).then(function(nodes) {
        var promises = [];

        nodes.forEach(function(node) {

            promises.push(
                kv.multiflag('nodes/' + node + '/resources', 999)
                    .then(function() { events.fire(eventNames.RESOURCE_CLEANUP_PENDING, { node: node });})
            );
        });

        return Q.all(promises);
    });
}

function removeTintResources(tint) {
    var promises = [];

    getNodesForTint(tint).then(function(nodes) {
        nodes.forEach(function(node) {
            promises.push(removeTintResourcesOnNode(tint, node));
        });
    });

    return Q.all(promises)
}

function removeTintResourcesOnNode(tint, node) {
    logger.info('Flagging tint resources for removal');
    return kv.multiflag('nodes/' + node.id + '/resources/' + tu.id(tint), 999).then(function() {
        events.fire(eventNames.RESOURCE_UNINSTALL_PENDING, {tint: tu.id(tint), node: node.id});
    });
}

function registerResourceOnNode(tint, node, resourceName, resourceType, resourceFsPath, resourceScope) {
    return kv.set('nodes/' + node.id + '/resources/' + tu.id(tint) + '/' + resourceName, {
        id: resourceName,
        type: resourceType,
        tint: tu.id(tint),
        node: node.id,
        consulPath: 'resources/' + tu.id(tint) + '/' + resourceName,
        fsPath: resourceFsPath,
        scope: resourceScope
    }, null, 0).then(function() {
        events.fire(eventNames.RESOURCE_INSTALL_PENDING, {tint: tu.id(tint), resource: resourceName, node: node.id});
    });
}

function createScope(tint) {
    var tintId = tu.id(tint);

    var variables = {
        settings: settings.get('data_dir'),
        device: nodeInfo,
        data_dir: settings.get('data_dir') + '/tints/' + tintId + '/data',
        resources_dir: settings.get('data_dir') + '/tints/' + tintId + '/resources',
        config_dir: settings.get('data_dir') + '/tints/' + tintId + '/resources/config'
    };

    return variables;
}<|endoftext|>


<commit_before>// @ts-check

const path = require("path")
const webpack = require("webpack")
const LoadablePlugin = require("@loadable/webpack-plugin")
const { getEntrypoints } = require("../utils/getEntrypoints")
const {
  BUILD_SERVER,
  NODE_ENV,
  basePath,
  isCI,
} = require("../../src/lib/environment")

exports.baseConfig = {
  mode: NODE_ENV,
  devtool: "source-map",
  stats: "normal", // or, `errors-only`
  entry: getEntrypoints(),
  output: {
    filename: "[name].js",
    path: path.resolve(basePath, "public/assets"),
    publicPath: "/assets/",
  },
  module: {
    rules: [
      {
        test: /\.coffee$/,
        include: path.resolve(basePath, "src"),
        use: ["cache-loader", "coffee-loader"],
      },
      {
        test: /\.(jade|pug)$/,
        include: path.resolve(basePath, "src"),
        use: [
          {
            loader: "pug-loader",
            options: {
              doctype: "html",
              root: __dirname,
            },
          },
        ],
      },
      {
        test: /(\.(js|ts)x?$)/,
        include: path.resolve(basePath, "src"),
        use: [
          {
            loader: "babel-loader",
            options: {
              cacheDirectory: !isCI && path.join(basePath, ".cache", "babel"),
            },
          },
        ],
      },
      // ESM support. See: https://github.com/apollographql/react-apollo/issues/1737#issuecomment-371178602
      {
        type: "javascript/auto",
        test: /\.mjs$/,
        use: [],
      },
    ],
  },
  plugins: [
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: JSON.stringify(NODE_ENV),
      },
    }),
    // Remove moment.js localization files
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    // Remove server-only modules from client bundles
    ...(BUILD_SERVER
      ? []
      : [
          // Remove server side of relay network layer.
          new webpack.IgnorePlugin(
            /^react-relay-network-modern-ssr\/node8\/server/
          ),
          // No matter what, we don't want the graphql-js package in client
          // bundles. This /may/ lead to a broken build when e.g. a reaction
          // module that's used on the client side imports something from
          // graphql-js, but that's better than silently including this.
          new webpack.IgnorePlugin(/^graphql(\/.*)?$/),
        ]),
    new webpack.NamedModulesPlugin(),
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
      "window.jQuery": "jquery",
      jade: "jade/runtime.js",
      waypoints: "jquery-waypoints/waypoints.js",
    }),
    new LoadablePlugin(),
  ],
  resolve: {
    alias: {
      "jquery.ui.widget": "blueimp-file-upload/js/vendor/jquery.ui.widget.js",

      // The following packages need to be resolved to the host app (force) to get
      // around issues involving `yarn link` and multiple instances. A  similar
      // configuration has been setup for SSR in `src/index`, via `require-control`.
      "styled-components": require.resolve("styled-components"),
      react: require.resolve("react"),
    },
    extensions: [
      ".mjs",
      ".js",
      ".jsx",
      ".ts",
      ".tsx",
      ".json",
      ".jade",
      ".coffee",
    ],
    // Symlink issues should be fixed via `yarn --pnp`
    modules: [path.resolve(basePath, "src"), "node_modules"],
    symlinks: false,
  },
  optimization: {
    // Extract webpack runtime code into it's own file
    runtimeChunk: {
      name: "runtime-manifest",
    },
    splitChunks: {
      cacheGroups: {
        // Separate vendor libraries from `node_modules` into a `commons.js`
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "common",
          minChunks: 10,
          chunks: "initial",
        },
      },
    },
  },
  externals: {
    // Don't bundle modules and consider them external
    request: "request",
  },
}
<commit_msg>Revert "Remove CDN_URL from webpack config"<commit_after>// @ts-check

const path = require("path")
const webpack = require("webpack")
const LoadablePlugin = require("@loadable/webpack-plugin")
const { getEntrypoints } = require("../utils/getEntrypoints")
const {
  BUILD_SERVER,
  NODE_ENV,
  basePath,
  isCI,
} = require("../../src/lib/environment")

exports.baseConfig = {
  mode: NODE_ENV,
  devtool: "source-map",
  stats: "normal", // or, `errors-only`
  entry: getEntrypoints(),
  output: {
    filename: "[name].js",
    path: path.resolve(basePath, "public/assets"),
    publicPath: "/assets/",
  },
  module: {
    rules: [
      {
        test: /\.coffee$/,
        include: path.resolve(basePath, "src"),
        use: ["cache-loader", "coffee-loader"],
      },
      {
        test: /\.(jade|pug)$/,
        include: path.resolve(basePath, "src"),
        use: [
          {
            loader: "pug-loader",
            options: {
              doctype: "html",
              root: __dirname,
            },
          },
        ],
      },
      {
        test: /(\.(js|ts)x?$)/,
        include: path.resolve(basePath, "src"),
        use: [
          {
            loader: "babel-loader",
            options: {
              cacheDirectory: !isCI && path.join(basePath, ".cache", "babel"),
            },
          },
        ],
      },
      // ESM support. See: https://github.com/apollographql/react-apollo/issues/1737#issuecomment-371178602
      {
        type: "javascript/auto",
        test: /\.mjs$/,
        use: [],
      },
    ],
  },
  plugins: [
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: JSON.stringify(NODE_ENV),
        CDN_URL: JSON.stringify(process.env.CDN_URL),
      },
    }),
    // Remove moment.js localization files
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    // Remove server-only modules from client bundles
    ...(BUILD_SERVER
      ? []
      : [
        // Remove server side of relay network layer.
        new webpack.IgnorePlugin(
          /^react-relay-network-modern-ssr\/node8\/server/
        ),
        // No matter what, we don't want the graphql-js package in client
        // bundles. This /may/ lead to a broken build when e.g. a reaction
        // module that's used on the client side imports something from
        // graphql-js, but that's better than silently including this.
        new webpack.IgnorePlugin(/^graphql(\/.*)?$/),
      ]),
    new webpack.NamedModulesPlugin(),
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
      "window.jQuery": "jquery",
      jade: "jade/runtime.js",
      waypoints: "jquery-waypoints/waypoints.js",
    }),
    new LoadablePlugin(),
  ],
  resolve: {
    alias: {
      "jquery.ui.widget": "blueimp-file-upload/js/vendor/jquery.ui.widget.js",

      // The following packages need to be resolved to the host app (force) to get
      // around issues involving `yarn link` and multiple instances. A  similar
      // configuration has been setup for SSR in `src/index`, via `require-control`.
      "styled-components": require.resolve("styled-components"),
      react: require.resolve("react"),
    },
    extensions: [
      ".mjs",
      ".js",
      ".jsx",
      ".ts",
      ".tsx",
      ".json",
      ".jade",
      ".coffee",
    ],
    // Symlink issues should be fixed via `yarn --pnp`
    modules: [path.resolve(basePath, "src"), "node_modules"],
    symlinks: false,
  },
  optimization: {
    // Extract webpack runtime code into it's own file
    runtimeChunk: {
      name: "runtime-manifest",
    },
    splitChunks: {
      cacheGroups: {
        // Separate vendor libraries from `node_modules` into a `commons.js`
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "common",
          minChunks: 10,
          chunks: "initial",
        },
      },
    },
  },
  externals: {
    // Don't bundle modules and consider them external
    request: "request",
  },
}
<|endoftext|>


<commit_before>/*!
 * This file is part of me-cms.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright   Copyright (c) Mirko Pagliai
 * @link        https://github.com/mirko-pagliai/me-cms
 * @license     https://opensource.org/licenses/mit-license.php MIT License
 */
/**
 * Gets the maximum height available.
 * The maximum available height is equal to the window height minus the topbar height.
 */
function getAvailableHeight()
{
    return $(window).height() - $("#userbar").outerHeight(true);
}

/**
 * Toggles the filter form
 */
function toggleFilterForm()
{
    var form = $(".filter-form legend");

    $(".fas", form).toggleClass("fa-eye fa-eye-slash");
    $(form).nextAll().toggle();
}

//On windows load and resize, it sets the maximum height available for the content
$(window).on("load resize", function () {
    $("#content").css("min-height", getAvailableHeight());
});

$(function () {
    //Gets query string as objects, removing empty values and pagination values
    var queryString = $.map(document.location.search.replace(/(^\?)/, "").split("&"), function (value) {
        value = value.split("=");

        if (value[0] === "direction" || value[0] === "page" || value[0] === "render" || value[0] === "sort") {
            return null;
        }

        if (value[1] === "" || value[1] === null || value[1] === undefined) {
            return null;
        }

        var obj = {};
        obj[value[0]] = value[1];

        return obj;
    });

    //If there are no query string values, toggles the filter form
    if (!Object.keys(queryString).length && $(".filter-form legend").length) {
        toggleFilterForm();
    }

    //On click on legend of a filter form, toggles the filter form
    $(".filter-form legend").click(function () {
        toggleFilterForm();
    });

    //If the `menuToBeOpen` cookie is present, opens the relative menu
    if (Cookies.get("menuToBeOpen")) {
        $("#accordionSidebar a[data-bs-target='#" + Cookies.get('menuToBeOpen') + "']")[0].click();
    }

    // When opening a menu, save the relative value in the `menuToBeOpen` cookie
    $("#accordionSidebar").on("shown.bs.collapse", function () {
        Cookies.set("menuToBeOpen", $(this).find('.collapse.show').attr('id'));
    });
});
<commit_msg>uses doublequotes<commit_after>/*!
 * This file is part of me-cms.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright   Copyright (c) Mirko Pagliai
 * @link        https://github.com/mirko-pagliai/me-cms
 * @license     https://opensource.org/licenses/mit-license.php MIT License
 */
/**
 * Gets the maximum height available.
 * The maximum available height is equal to the window height minus the topbar height.
 */
function getAvailableHeight()
{
    return $(window).height() - $("#userbar").outerHeight(true);
}

/**
 * Toggles the filter form
 */
function toggleFilterForm()
{
    var form = $(".filter-form legend");

    $(".fas", form).toggleClass("fa-eye fa-eye-slash");
    $(form).nextAll().toggle();
}

//On windows load and resize, it sets the maximum height available for the content
$(window).on("load resize", function () {
    $("#content").css("min-height", getAvailableHeight());
});

$(function () {
    //Gets query string as objects, removing empty values and pagination values
    var queryString = $.map(document.location.search.replace(/(^\?)/, "").split("&"), function (value) {
        value = value.split("=");

        if (value[0] === "direction" || value[0] === "page" || value[0] === "render" || value[0] === "sort") {
            return null;
        }

        if (value[1] === "" || value[1] === null || value[1] === undefined) {
            return null;
        }

        var obj = {};
        obj[value[0]] = value[1];

        return obj;
    });

    //If there are no query string values, toggles the filter form
    if (!Object.keys(queryString).length && $(".filter-form legend").length) {
        toggleFilterForm();
    }

    //On click on legend of a filter form, toggles the filter form
    $(".filter-form legend").click(function () {
        toggleFilterForm();
    });

    //If the `menuToBeOpen` cookie is present, opens the relative menu
    if (Cookies.get("menuToBeOpen")) {
        $("#accordionSidebar a[data-bs-target='#" + Cookies.get("menuToBeOpen") + "']")[0].click();
    }

    // When opening a menu, save the relative value in the `menuToBeOpen` cookie
    $("#accordionSidebar").on("shown.bs.collapse", function () {
        Cookies.set("menuToBeOpen", $(this).find(".collapse.show").attr("id"));
    });
});
<|endoftext|>


<commit_before>"use strict";

var path = require('path');
var _ = require('lodash');
var $ = require('jquery');
var Backbone = require('backbone');
var adminDispatcher = require('../../../dispatchers/admin-dispatcher');
var quoteConfigActions = require('../config.json').actions;
var Model = require('../models/quote');

Backbone.$ = $;

var Quotes = Backbone.Collection.extend({
  model: Model,

  comparator: function(a, b) {
    return b.id - a.id; // by cheapest path?
  },

  initialize: function() {
    _.bindAll(this);

    adminDispatcher.register(this.dispatcherCallback);
  },

  dispatcherCallback: function(payload) {
    var handleAction = {};

    handleAction[quoteConfigActions.setTemplateUrl] = this.setTemplateUrl;
    handleAction[quoteConfigActions.updateUrlWithParams] = this.updateUrlWithParams;
    handleAction[quoteConfigActions.fetchQuotes] = this.fetchQuotes;

    if (!_.isUndefined(this[payload.actionType])) {
      this[payload.actionType](payload.data);
    }
  },

  // sets the url based on webfinger templated links
  setTemplateUrl: function(newQuotingUrl) {
    if (_.isUndefined(newQuotingUrl)) {
      return false;
    }

    this.url = newQuotingUrl;
  },

  // update template url with real params
  updateUrlWithParams: function(quoteQueryParams) {
    if (_.isUndefined(quoteQueryParams) || _.isEmpty(quoteQueryParams)) {
      return false;
    }

    var external = this.get('external');
    var updatedUrl = this.url;

    updatedUrl = updatedUrl.replace(/{sender}/, quoteQueryParams.source_address);
    updatedUrl = updatedUrl.replace(/{receiver}/, quoteQueryParams.destination_address);
    updatedUrl = updatedUrl.replace(/{amount}/,
      quoteQueryParams.destination_amount + '+' + quoteQueryParams.destination_currency);

    this.url = updatedUrl;
  },

  fetchQuotes: function(quoteQueryParams) {

    // DELETE STUB DATA WHEN ENDPOINT IS COMPLETE
    var stubData = [{
      direction: 'to-ripple',
      state    : 'invoice',
      ripple: {
        destination_address  : 'r123456789',
        destination_amount   : 0.0001,
        destination_currency : 'USD',
        source_currency      : 'XRP',
        source_amount        : 0.00000000000003
      },
      external: {
        source_address       : 'acct:rod@bankoftherod.com',
        destination_address  : 'acct:rod2@bankoftherod.com',
        destination_currency : 'USD',
        destination_amount   : 0.0001
      }
    }];

    this.set(stubData);

    this.trigger('ready', this);

    this.updateUrlWithParams(quoteQueryParams);

    // TODO - use a real fetch when endpoint is complete
    // this.fetch({});
  },

  parse: function(data) {
    return data.bridge_payments;
  }
});

module.exports = Quotes;
<commit_msg>[TASK] Use gateway bridge quote url to fetch quotes<commit_after>"use strict";

var path = require('path');
var _ = require('lodash');
var $ = require('jquery');
var Backbone = require('backbone');
var adminDispatcher = require('../../../dispatchers/admin-dispatcher');
var quoteConfigActions = require('../config.json').actions;
var Model = require('../models/quote');

Backbone.$ = $;

var Quotes = Backbone.Collection.extend({
  model: Model,

  comparator: function(a, b) {
    return b.id - a.id; // by cheapest path?
  },

  initialize: function() {
    _.bindAll(this);

    adminDispatcher.register(this.dispatcherCallback);
  },

  dispatcherCallback: function(payload) {
    var handleAction = {};

    handleAction[quoteConfigActions.setTemplateUrl] = this.setTemplateUrl;
    handleAction[quoteConfigActions.updateUrlWithParams] = this.updateUrlWithParams;
    handleAction[quoteConfigActions.fetchQuotes] = this.fetchQuotes;

    if (!_.isUndefined(this[payload.actionType])) {
      this[payload.actionType](payload.data);
    }
  },

  // sets the url based on webfinger templated links
  setTemplateUrl: function(newQuotingUrl) {
    if (_.isUndefined(newQuotingUrl)) {
      return false;
    }

    this.url = newQuotingUrl;
  },

  // update template url with real params
  updateUrlWithParams: function(quoteQueryParams) {
    if (_.isUndefined(quoteQueryParams) || _.isEmpty(quoteQueryParams)) {
      return false;
    }

    var external = this.get('external');
    var updatedUrl = this.url;

    updatedUrl = updatedUrl.replace(/{sender}/, quoteQueryParams.source_address);
    updatedUrl = updatedUrl.replace(/{receiver}/, quoteQueryParams.destination_address);
    updatedUrl = updatedUrl.replace(/{amount}/,
      quoteQueryParams.destination_amount + '+' + quoteQueryParams.destination_currency);

    this.url = updatedUrl;
  },

  fetchQuotes: function(quoteQueryParams) {

    // DELETE STUB DATA WHEN ENDPOINT IS COMPLETE
    // var stubData = [{
    //   direction: 'to-ripple',
    //   state    : 'invoice',
    //   ripple: {
    //     destination_address  : 'r123456789',
    //     destination_amount   : 0.0001,
    //     destination_currency : 'USD',
    //     source_currency      : 'XRP',
    //     source_amount        : 0.00000000000003
    //   },
    //   external: {
    //     source_address       : 'acct:rod@bankoftherod.com',
    //     destination_address  : 'acct:rod2@bankoftherod.com',
    //     destination_currency : 'USD',
    //     destination_amount   : 0.0001
    //   }
    // }];

    // this.set(stubData);

    // this.trigger('ready', this);

    this.updateUrlWithParams(quoteQueryParams);

    // TODO - use a real fetch when endpoint is complete
    this.fetch({});
  },

  parse: function(data) {
    return data.bridge_payments;
  }
});

module.exports = Quotes;
<|endoftext|>


<commit_before>/**
    ServiceProfessional Service activity
**/
'use strict';

var ko = require('knockout'),
    Activity = require('../components/Activity'),
    ServiceProfessionalServiceViewModel = require('../viewmodels/ServiceProfessionalService'),
    RouteMatcher = require('../utils/Router').RouteMatcher,
    Route = require('../utils/Router').Route;

var A = Activity.extend(function ServiceProfessionalServiceActivity() {

    Activity.apply(this, arguments);

    this.accessLevel = this.app.UserType.serviceProfessional;
    this.viewModel = new ViewModel(this.app);
    // Defaults settings for navBar.
    this.navBar = Activity.createSubsectionNavBar('Job Title', {
        backLink: '/scheduling', helpLink: this.viewModel.helpLink
    });
    // Save defaults to restore on updateNavBarState when needed:
    this.defaultLeftAction = this.navBar.leftAction().model.toPlainObject(true);
    // Make navBar available at viewModel, needed for dekstop navigation
    this.viewModel.navBar = this.navBar;

    // On changing jobTitleID:
    this.registerHandler({
        target: this.viewModel.jobTitle,
        handler: function(/*jobTitle*/) {
            // Update navbar (may indicate the jobTitle name)
            this.updateNavBarState();
        }.bind(this)
    });

    // On changing jobTitleID:
    this.registerHandler({
        target: this.viewModel.jobTitleID,
        handler: function(jobTitleID) {
            if (jobTitleID) {
                /// Rewrite URL
                // IMPORTANT: When in isSelectionMode, pushState cannot be use
                // because it conflicts with the selection logic (on new-booking progress)
                // TODO: discarded URL rewrite until the bug with replaceState in HashbangHistory is fixed
                if (this.viewModel.isSelectionMode()) return;
                // If the URL didn't included the jobTitleID, or is different,
                // we put it to avoid reload/resume problems

                var found = /serviceProfessionalService\/(\d+)/i.exec(window.location);
                var urlID = found && found[1] |0;
                if (urlID !== jobTitleID) {
                    var url = '/serviceProfessionalService/' + jobTitleID;
                    if (this.viewModel.isAdditionMode()) url += '/new';
                    this.app.shell.replaceState(null, null, url);
                }
            }
        }.bind(this)
    });

    this.registerHandler({
        target: this.viewModel.clientID,
        handler: function(clientID) {
            var viewModel = this.viewModel,
                app = this.app;

            viewModel.client(null);

            if(clientID) {
                app.model.clients.getItem(clientID)
                .then(function(client) {
                    viewModel.client(client);
                })
                .catch(function(error) {
                    app.modals.showError({ title: 'Unable to load client.', error: error });
                });
            }
        }.bind(this)
    });

    // Go back with the selected pricing when triggered in the form/view
    this.viewModel.returnSelected = function(pricing, jobTitleID) {
        // Pass the selected client in the info
        this.requestData.selectedServices = pricing;
        this.requestData.selectedJobTitleID = jobTitleID;
        // And go back
        this.app.shell.goBack(this.requestData);
    }.bind(this);

    this.returnRequest = function returnRequest() {
        this.app.shell.goBack(this.requestData);
    }.bind(this);
});

exports.init = A.init;

A.prototype.applyOwnNavbarRules = function() {
    //jshint maxcomplexity:10

    var itIs = this.viewModel.isSelectionMode();

    if (this.requestData.title) {
        // Replace title by title if required
        this.navBar.title(this.requestData.title);
    }
    else {
        // Title must be empty
        this.navBar.title('');
    }

    if (this.requestData.cancelLink) {
        this.convertToCancelAction(this.navBar.leftAction(), this.requestData.cancelLink, this.requestData);
    }
    else {
        // Reset to defaults, or given title:
        this.navBar.leftAction().model.updateWith(this.defaultLeftAction, true);
        if (this.requestData.navTitle)
            this.navBar.leftAction().text(this.requestData.navTitle);

        var jid = this.viewModel.jobTitleID(),
            jname = this.viewModel.jobTitle() && this.viewModel.jobTitle().singularName() || 'Scheduler',
            url = this.mustReturnTo || (jid && '/jobtitles/' + jid || '/scheduling');

        this.navBar.leftAction().link(url);
        this.navBar.leftAction().text(jname);
    }

    if (itIs && !this.requestData.cancelLink) {
        // Uses a custom handler so it returns keeping the given state:
        this.navBar.leftAction().handler(this.returnRequest);
    }
    else if (!this.requestData.cancelLink) {
        this.navBar.leftAction().handler(null);
    }
};

A.prototype.updateNavBarState = function updateNavBarState() {
    var itIs = this.viewModel.isSelectionMode();

    this.viewModel.headerText(itIs ? 'Select services' : 'Services');

    // Perform updates that apply this request:
    this.app.model.onboarding.updateNavBar(this.navBar) ||
    //this.app.applyNavbarMustReturn(this.requestData) ||
    this.applyOwnNavbarRules();
};

A.prototype.show = function show(options) {
    //jshint maxcomplexity:8
    Activity.prototype.show.call(this, options);

    // Remember route to go back, from a request of 'mustReturn' or last requested
    this.mustReturnTo = this.requestData.route.query.mustReturn || this.mustReturnTo;

    // Reset: avoiding errors because persisted data for different ID on loading
    // or outdated info forcing update
    this.viewModel.reset();
    this.viewModel.requestData(this.requestData);
    this.viewModel.preSelectedServices(this.requestData.selectedServices || []);

    this.viewModel.isSelectionMode(this.requestData.selectPricing === true);

    var matcher = new RouteMatcher([
        new Route(':jobTitleID/new', { isNew: true }),
        new Route(':jobTitleID/client/:clientID/new', { isNew: true }),
        new Route(':jobTitleID/client/:clientID'),
        new Route('new', { isNew: true }),
        new Route(':jobTitleID'),
        new Route('')
    ]);

    var paramsDefaults = { jobTitleID: 0, isNew: false, clientID: null },
        params = matcher.match(options.route.segments.join('/'), paramsDefaults) || {};

    var jobTitleID = +params.jobTitleID;
    if (jobTitleID === 0 && options.selectedJobTitleID > 0)
        jobTitleID = options.selectedJobTitleID |0;

    this.viewModel.clientID(params.clientID);

    var isAdditionMode = params.isNew;

    if (isAdditionMode) {
        // Sets referrer as cancelLink
        var ref = this.app.shell.referrerRoute;
        ref = ref && ref.url || '/';
        this.requestData.cancelLink = ref;
        // Set for editor links in the view
        this.viewModel.cancelLink(ref);
    }
    else {
        // Set this page as cancelLink for editor links in the view
        this.viewModel.cancelLink('/serviceProfessionalService/' + jobTitleID);
    }

    this.viewModel.isAdditionMode(isAdditionMode);

    this.updateNavBarState();

    this.viewModel.jobTitleID(jobTitleID);

    if (jobTitleID === 0) {
        this.viewModel.clearData();
        this.viewModel.jobTitles.sync();
    }
    else {
        this.viewModel.loadServicesData();
    }
};

var UserJobProfile = require('../viewmodels/UserJobProfile');

function ViewModel(app) {
    // ViewModel has all of the properties of a ServiceProfessionalServiceViewModel
    ServiceProfessionalServiceViewModel.call(this, app);

    // Always load empty pricing types, regardless of view model mode
    this.loadEmptyPricingTypes(true);

    this.clientID = ko.observable(null);
    this.client = ko.observable(null);

    this.helpLink = '/help/relatedArticles/201967166-listing-and-pricing-your-services';
    this.isInOnboarding = app.model.onboarding.inProgress;
    this.headerText = ko.observable('Services');

    this.isLocked = this.isLoading;

    this.jobTitles = new UserJobProfile(app);
    this.jobTitles.baseUrl('/serviceProfessionalService');
    this.jobTitles.selectJobTitle = function(jobTitle) {
        this.jobTitleID(jobTitle.jobTitleID());
        this.loadServicesData();
        return false;
    }.bind(this);

    this.loadServicesData = function() {
        var clientID = this.clientID(),
            jobTitleID = this.jobTitleID(),
            model = app.model.serviceProfessionalServices,
            services = clientID ? model.getClientSpecificServicesForJobTitle(clientID, jobTitleID) :
                                  model.getList(jobTitleID);

        return this.loadData(null, jobTitleID, services);
    }.bind(this);

    this.clientName = ko.pureComputed(function() {
        return (this.client() && this.client().firstName()) || '';
    }, this);

    this.jobTitleName = ko.pureComputed(function() {
        return (this.jobTitle() && this.jobTitle().singularName()) || '';
    }, this);

    this.submitText = ko.pureComputed(function() {
        return (
            this.isLoading() ?
                'loading...' :
                'Save and continue'
        );
    }, this);

    this.onboardingNextReady = ko.computed(function() {
        var isin = app.model.onboarding.inProgress(),
            hasPricing = this.list().length > 0;

        return isin && hasPricing;
    }, this);

    /**
        Ends the selection process, ready to collect selection
        and passing it to the requester activity.
        Works too to pass to the next onboarding step
    **/
    this.endSelection = function(data, event) {

        if (app.model.onboarding.inProgress()) {
            // Ensure we keep the same jobTitleID in next steps as here:
            app.model.onboarding.selectedJobTitleID(this.jobTitleID());
            app.model.onboarding.goNext();
        }
        else {
            // Run method injected by the activity to return a
            // selected address:
            this.returnSelected(
                this.selectedServices().map(function(pricing) {
                    return pricing.model.toPlainObject(true);
                }),
                this.jobTitleID()
            );
        }

        event.preventDefault();
        event.stopImmediatePropagation();
    }.bind(this);

    this.selectedServiceRequest = function(pricing) {
        return pricing.model.toPlainObject(true);
    };
}

ViewModel._inherits(ServiceProfessionalServiceViewModel);
<commit_msg>removed unused headerText property of view model in service profesional services #282<commit_after>/**
    ServiceProfessional Service activity
**/
'use strict';

var ko = require('knockout'),
    Activity = require('../components/Activity'),
    ServiceProfessionalServiceViewModel = require('../viewmodels/ServiceProfessionalService'),
    RouteMatcher = require('../utils/Router').RouteMatcher,
    Route = require('../utils/Router').Route;

var A = Activity.extend(function ServiceProfessionalServiceActivity() {

    Activity.apply(this, arguments);

    this.accessLevel = this.app.UserType.serviceProfessional;
    this.viewModel = new ViewModel(this.app);
    // Defaults settings for navBar.
    this.navBar = Activity.createSubsectionNavBar('Job Title', {
        backLink: '/scheduling', helpLink: this.viewModel.helpLink
    });
    // Save defaults to restore on updateNavBarState when needed:
    this.defaultLeftAction = this.navBar.leftAction().model.toPlainObject(true);
    // Make navBar available at viewModel, needed for dekstop navigation
    this.viewModel.navBar = this.navBar;

    // On changing jobTitleID:
    this.registerHandler({
        target: this.viewModel.jobTitle,
        handler: function(/*jobTitle*/) {
            // Update navbar (may indicate the jobTitle name)
            this.updateNavBarState();
        }.bind(this)
    });

    // On changing jobTitleID:
    this.registerHandler({
        target: this.viewModel.jobTitleID,
        handler: function(jobTitleID) {
            if (jobTitleID) {
                /// Rewrite URL
                // IMPORTANT: When in isSelectionMode, pushState cannot be use
                // because it conflicts with the selection logic (on new-booking progress)
                // TODO: discarded URL rewrite until the bug with replaceState in HashbangHistory is fixed
                if (this.viewModel.isSelectionMode()) return;
                // If the URL didn't included the jobTitleID, or is different,
                // we put it to avoid reload/resume problems

                var found = /serviceProfessionalService\/(\d+)/i.exec(window.location);
                var urlID = found && found[1] |0;
                if (urlID !== jobTitleID) {
                    var url = '/serviceProfessionalService/' + jobTitleID;
                    if (this.viewModel.isAdditionMode()) url += '/new';
                    this.app.shell.replaceState(null, null, url);
                }
            }
        }.bind(this)
    });

    this.registerHandler({
        target: this.viewModel.clientID,
        handler: function(clientID) {
            var viewModel = this.viewModel,
                app = this.app;

            viewModel.client(null);

            if(clientID) {
                app.model.clients.getItem(clientID)
                .then(function(client) {
                    viewModel.client(client);
                })
                .catch(function(error) {
                    app.modals.showError({ title: 'Unable to load client.', error: error });
                });
            }
        }.bind(this)
    });

    // Go back with the selected pricing when triggered in the form/view
    this.viewModel.returnSelected = function(pricing, jobTitleID) {
        // Pass the selected client in the info
        this.requestData.selectedServices = pricing;
        this.requestData.selectedJobTitleID = jobTitleID;
        // And go back
        this.app.shell.goBack(this.requestData);
    }.bind(this);

    this.returnRequest = function returnRequest() {
        this.app.shell.goBack(this.requestData);
    }.bind(this);
});

exports.init = A.init;

A.prototype.applyOwnNavbarRules = function() {
    //jshint maxcomplexity:10

    var itIs = this.viewModel.isSelectionMode();

    if (this.requestData.title) {
        // Replace title by title if required
        this.navBar.title(this.requestData.title);
    }
    else {
        // Title must be empty
        this.navBar.title('');
    }

    if (this.requestData.cancelLink) {
        this.convertToCancelAction(this.navBar.leftAction(), this.requestData.cancelLink, this.requestData);
    }
    else {
        // Reset to defaults, or given title:
        this.navBar.leftAction().model.updateWith(this.defaultLeftAction, true);
        if (this.requestData.navTitle)
            this.navBar.leftAction().text(this.requestData.navTitle);

        var jid = this.viewModel.jobTitleID(),
            jname = this.viewModel.jobTitle() && this.viewModel.jobTitle().singularName() || 'Scheduler',
            url = this.mustReturnTo || (jid && '/jobtitles/' + jid || '/scheduling');

        this.navBar.leftAction().link(url);
        this.navBar.leftAction().text(jname);
    }

    if (itIs && !this.requestData.cancelLink) {
        // Uses a custom handler so it returns keeping the given state:
        this.navBar.leftAction().handler(this.returnRequest);
    }
    else if (!this.requestData.cancelLink) {
        this.navBar.leftAction().handler(null);
    }
};

A.prototype.updateNavBarState = function updateNavBarState() {
    // Perform updates that apply this request:
    this.app.model.onboarding.updateNavBar(this.navBar) ||
    //this.app.applyNavbarMustReturn(this.requestData) ||
    this.applyOwnNavbarRules();
};

A.prototype.show = function show(options) {
    //jshint maxcomplexity:8
    Activity.prototype.show.call(this, options);

    // Remember route to go back, from a request of 'mustReturn' or last requested
    this.mustReturnTo = this.requestData.route.query.mustReturn || this.mustReturnTo;

    // Reset: avoiding errors because persisted data for different ID on loading
    // or outdated info forcing update
    this.viewModel.reset();
    this.viewModel.requestData(this.requestData);
    this.viewModel.preSelectedServices(this.requestData.selectedServices || []);

    this.viewModel.isSelectionMode(this.requestData.selectPricing === true);

    var matcher = new RouteMatcher([
        new Route(':jobTitleID/new', { isNew: true }),
        new Route(':jobTitleID/client/:clientID/new', { isNew: true }),
        new Route(':jobTitleID/client/:clientID'),
        new Route('new', { isNew: true }),
        new Route(':jobTitleID'),
        new Route('')
    ]);

    var paramsDefaults = { jobTitleID: 0, isNew: false, clientID: null },
        params = matcher.match(options.route.segments.join('/'), paramsDefaults) || {};

    var jobTitleID = +params.jobTitleID;
    if (jobTitleID === 0 && options.selectedJobTitleID > 0)
        jobTitleID = options.selectedJobTitleID |0;

    this.viewModel.clientID(params.clientID);

    var isAdditionMode = params.isNew;

    if (isAdditionMode) {
        // Sets referrer as cancelLink
        var ref = this.app.shell.referrerRoute;
        ref = ref && ref.url || '/';
        this.requestData.cancelLink = ref;
        // Set for editor links in the view
        this.viewModel.cancelLink(ref);
    }
    else {
        // Set this page as cancelLink for editor links in the view
        this.viewModel.cancelLink('/serviceProfessionalService/' + jobTitleID);
    }

    this.viewModel.isAdditionMode(isAdditionMode);

    this.updateNavBarState();

    this.viewModel.jobTitleID(jobTitleID);

    if (jobTitleID === 0) {
        this.viewModel.clearData();
        this.viewModel.jobTitles.sync();
    }
    else {
        this.viewModel.loadServicesData();
    }
};

var UserJobProfile = require('../viewmodels/UserJobProfile');

function ViewModel(app) {
    // ViewModel has all of the properties of a ServiceProfessionalServiceViewModel
    ServiceProfessionalServiceViewModel.call(this, app);

    // Always load empty pricing types, regardless of view model mode
    this.loadEmptyPricingTypes(true);

    this.clientID = ko.observable(null);
    this.client = ko.observable(null);

    this.helpLink = '/help/relatedArticles/201967166-listing-and-pricing-your-services';
    this.isInOnboarding = app.model.onboarding.inProgress;

    this.isLocked = this.isLoading;

    this.jobTitles = new UserJobProfile(app);
    this.jobTitles.baseUrl('/serviceProfessionalService');
    this.jobTitles.selectJobTitle = function(jobTitle) {
        this.jobTitleID(jobTitle.jobTitleID());
        this.loadServicesData();
        return false;
    }.bind(this);

    this.loadServicesData = function() {
        var clientID = this.clientID(),
            jobTitleID = this.jobTitleID(),
            model = app.model.serviceProfessionalServices,
            services = clientID ? model.getClientSpecificServicesForJobTitle(clientID, jobTitleID) :
                                  model.getList(jobTitleID);

        return this.loadData(null, jobTitleID, services);
    }.bind(this);

    this.clientName = ko.pureComputed(function() {
        return (this.client() && this.client().firstName()) || '';
    }, this);

    this.jobTitleName = ko.pureComputed(function() {
        return (this.jobTitle() && this.jobTitle().singularName()) || '';
    }, this);

    this.submitText = ko.pureComputed(function() {
        return (
            this.isLoading() ?
                'loading...' :
                'Save and continue'
        );
    }, this);

    this.onboardingNextReady = ko.computed(function() {
        var isin = app.model.onboarding.inProgress(),
            hasPricing = this.list().length > 0;

        return isin && hasPricing;
    }, this);

    /**
        Ends the selection process, ready to collect selection
        and passing it to the requester activity.
        Works too to pass to the next onboarding step
    **/
    this.endSelection = function(data, event) {

        if (app.model.onboarding.inProgress()) {
            // Ensure we keep the same jobTitleID in next steps as here:
            app.model.onboarding.selectedJobTitleID(this.jobTitleID());
            app.model.onboarding.goNext();
        }
        else {
            // Run method injected by the activity to return a
            // selected address:
            this.returnSelected(
                this.selectedServices().map(function(pricing) {
                    return pricing.model.toPlainObject(true);
                }),
                this.jobTitleID()
            );
        }

        event.preventDefault();
        event.stopImmediatePropagation();
    }.bind(this);

    this.selectedServiceRequest = function(pricing) {
        return pricing.model.toPlainObject(true);
    };
}

ViewModel._inherits(ServiceProfessionalServiceViewModel);
<|endoftext|>