<commit_before>$(document).ready(function(){
	var h_header = $('#header').outerHeight();
	$('#content').css('margin-top',h_header);
	$('.popup-compare').css('margin-top',-h_header);

		$('.toogle-menu').click(function(event){
		$(this).parents('.main-menu').find('ul').slideToggle();
		event.preventDefault();
		event.stopPropagation();
	});

	$("a").click(function(event) {
    $(this).parents('.main-menu').find('ul').slideUp();
  });

});
$(document).load(function(){

});
$(document).resize(function(){
	var h_header = $('#header').outerHeight();
	$('#content').css('margin-top',h_header);
});

 $(document).click(function(){
     $('.main-menu').find('ul').slideUp();
});
  <commit_msg>Fixed menu navigation disappearing bug<commit_after>$(document).ready(function(){
	var h_header = $('#header').outerHeight();
	$('#content').css('margin-top',h_header);
	$('.popup-compare').css('margin-top',-h_header);

		$('.toogle-menu').click(function(event){
		$(this).parents('.main-menu').find('ul').slideToggle();
		event.preventDefault();
		event.stopPropagation();
	});

});
$(document).load(function(){

});
$(document).resize(function(){
	var h_header = $('#header').outerHeight();
	$('#content').css('margin-top',h_header);
});

 $(document).click(function(){
 	console.log($(document).width());
 	if($(document).width() < 760){
     $('.main-menu').find('ul').slideUp();
 }
});
  <|endoftext|>


<commit_before>window.testHelpers = {

    DEFAULT_VALUES: {
        /* Оптимальное время проверки существования элемента, иначе зависает */
        maxTimeout: 6000,
        timeoutStep: 500
    },

    waitCondition: function (condition, success, error, maxTimeout, step) {
        maxTimeout = maxTimeout || window.testHelpers.DEFAULT_VALUES.maxTimeout;
        step = step || window.testHelpers.DEFAULT_VALUES.timeoutStep;
        var i = 1;

        var waiting = function () {
            if (condition()) {
                if ($.isFunction(success)) {
                    success();
                }

                return;
            }

            if (i * step >= maxTimeout) {
                if ($.isFunction(error)) {
                    error();
                }

                return;
            }

            ++i;
            setTimeout(waiting, step);
        };

        setTimeout(waiting, step);
    },

    waitView: function (viewName, success, error, maxTimeout, step) {
        var waitView = function () {
            if (!window.configWindow.contextApp) {
                return false;
            }

            var view = window.configWindow.contextApp.context.controls[viewName];

            if(window.configWindow.contextApp.name == viewName) {
                view = window.configWindow.contextApp;
            }

            return view && view.isLoaded();
        };

        window.testHelpers.waitCondition(waitView, success, error, maxTimeout, step);
    },

    waitModalView: function (viewName, success, error, maxTimeout, step) {
        var waitView = function () {
            var view = window.currentView.context.controls[viewName] || window.configWindow.contextApp.context.controls[viewName];
            return view && view.isLoaded();
        };

        window.testHelpers.waitCondition(waitView, success, error, maxTimeout, step);
    },

    getDate: function (iterator) {
        iterator = iterator || 0;
        iterator *= 1000 * 60 * 60 * 24;

        var currentDate = this.getStandartDate(new Date(Date.now() + iterator));

        // TODO: Следить за датами
        return this.getFormattedDate(currentDate);
    },

    getFormattedDate: function (date) {
        return window.configWindow.moment(date).format("YYYY-MM-DDTHH:mm:ss.SSS");
    },

    getStandartDate: function (date) {
        if(typeof date === 'number') {
            return this.getStandartDate(new Date(date * 1000));
        }
        // YYYY-MM-DD
        return String.prototype.concat(date.getFullYear(), '-', date.getMonth() + 1, '-', date.getDate()); //+1 - January-0
    },

    convertDate: function (date) {
        // TODO: Только если формат даты "DD.MM.YYYY"
        var reg = date.match(/^\d{2}.\d{2}.\d{4}$/);

        if (reg && reg.length != 0) {
            var items = date.split('.');
            return this.getFormattedDate(items[2] + '-' + items[1] + '-' + items[0]);
        }

        return this.getFormattedDate(date);
    },

    convertToUnixTime: function (date, element) {
        if(!element) {
            return date;
        }

        if(element.getMode) {
            switch (element.getMode()) {
                case 'Date':
                case 'Time':
                case 'DateTime':
                    return date;
                default:
                    return new Date(date).getTime() / 1000;
            }
        }

        return date;
    },

    getControlByName: function (controlName) {
        var indexInfo = /\[(\d+)\]/.exec(controlName);

        if (indexInfo == null) {
            return window.currentViewContext.controls[controlName] || window.configWindow.contextApp.context.controls[controlName];
        } else {
            var itemIndex = parseInt(indexInfo[1]);
            var itemName = controlName.match(/\w+/)[0];

            if (window.currentListBox == undefined || isNaN(itemIndex) || window.currentListBox.getItems().getByIndex(itemIndex) == undefined) {
                return undefined;
            }

            var result = window.currentListBox.findAllChildrenByName(itemName)[itemIndex];

            if (window.currentListBox.setSelectedItem) {
                window.currentListBox.setSelectedItem(window.currentListBox.getItems().getByIndex(itemIndex));
            }

            return result;
        }
    },

    parseTableRow: function ($cells) {
        var result = '';

        $cells.each(function (i, item) {
            result += '|' + window.configWindow.$(item).find('.pl-label:visible').text().trim().replace(/"/g, '\'');
        });

        return result + '|';
    }
};

function TestInfoHelper($info) {
    var passed = $info.find('#passed-count');
    var failed = $info.find('#failed-count');
    var ignored = $info.find('#ignored-count');

    this.incrementPassed = function () {
        var oldValue = parseInt(passed.text());
        passed.text(++oldValue);
    };

    this.incrementFailed = function () {
        var oldValue = parseInt(failed.text());
        failed.text(++oldValue);
    };

    this.incrementIgnored = function () {
        var oldValue = parseInt(ignored.text());
        ignored.text(++oldValue);
    };
}<commit_msg>Incorrect data on phantomjs (integration tests)<commit_after>window.testHelpers = {

    DEFAULT_VALUES: {
        /* Оптимальное время проверки существования элемента, иначе зависает */
        maxTimeout: 6000,
        timeoutStep: 500
    },

    waitCondition: function (condition, success, error, maxTimeout, step) {
        maxTimeout = maxTimeout || window.testHelpers.DEFAULT_VALUES.maxTimeout;
        step = step || window.testHelpers.DEFAULT_VALUES.timeoutStep;
        var i = 1;

        var waiting = function () {
            if (condition()) {
                if ($.isFunction(success)) {
                    success();
                }

                return;
            }

            if (i * step >= maxTimeout) {
                if ($.isFunction(error)) {
                    error();
                }

                return;
            }

            ++i;
            setTimeout(waiting, step);
        };

        setTimeout(waiting, step);
    },

    waitView: function (viewName, success, error, maxTimeout, step) {
        var waitView = function () {
            if (!window.configWindow.contextApp) {
                return false;
            }

            var view = window.configWindow.contextApp.context.controls[viewName];

            if(window.configWindow.contextApp.name == viewName) {
                view = window.configWindow.contextApp;
            }

            return view && view.isLoaded();
        };

        window.testHelpers.waitCondition(waitView, success, error, maxTimeout, step);
    },

    waitModalView: function (viewName, success, error, maxTimeout, step) {
        var waitView = function () {
            var view = window.currentView.context.controls[viewName] || window.configWindow.contextApp.context.controls[viewName];
            return view && view.isLoaded();
        };

        window.testHelpers.waitCondition(waitView, success, error, maxTimeout, step);
    },

    getDate: function (iterator) {
        iterator = iterator || 0;
        iterator *= 1000 * 60 * 60 * 24;

        var currentDate = this.getStandartDate(new Date(Date.now() + iterator));

        // TODO: Следить за датами
        return this.getFormattedDate(currentDate);
    },

    getFormattedDate: function (date) {
        return window.configWindow.moment(date.replace(/\./g, '/')).format("YYYY-MM-DDTHH:mm:ss.SSS");
    },

    getStandartDate: function (date) {
        if(typeof date === 'number') {
            return this.getStandartDate(new Date(date * 1000));
        }
        // YYYY-MM-DD
        return String.prototype.concat(date.getFullYear(), '-', date.getMonth() + 1, '-', date.getDate()); //+1 - January-0
    },

    convertDate: function (date) {
        // TODO: Только если формат даты "DD.MM.YYYY"
        var reg = date.match(/^\d{2}.\d{2}.\d{4}$/);

        if (reg && reg.length != 0) {
            var items = date.split('.');
            return this.getFormattedDate(items[2] + '-' + items[1] + '-' + items[0]);
        }

        return this.getFormattedDate(date);
    },

    convertToUnixTime: function (date, element) {
        if(!element) {
            return date;
        }

        if(element.getMode) {
            switch (element.getMode()) {
                case 'Date':
                case 'Time':
                case 'DateTime':
                    return date;
                default:
                    return new Date(date).getTime() / 1000;
            }
        }

        return date;
    },

    getControlByName: function (controlName) {
        var indexInfo = /\[(\d+)\]/.exec(controlName);

        if (indexInfo == null) {
            return window.currentViewContext.controls[controlName] || window.configWindow.contextApp.context.controls[controlName];
        } else {
            var itemIndex = parseInt(indexInfo[1]);
            var itemName = controlName.match(/\w+/)[0];

            if (window.currentListBox == undefined || isNaN(itemIndex) || window.currentListBox.getItems().getByIndex(itemIndex) == undefined) {
                return undefined;
            }

            var result = window.currentListBox.findAllChildrenByName(itemName)[itemIndex];

            if (window.currentListBox.setSelectedItem) {
                window.currentListBox.setSelectedItem(window.currentListBox.getItems().getByIndex(itemIndex));
            }

            return result;
        }
    },

    parseTableRow: function ($cells) {
        var result = '';

        $cells.each(function (i, item) {
            result += '|' + window.configWindow.$(item).find('.pl-label:visible').text().trim().replace(/"/g, '\'');
        });

        return result + '|';
    }
};

function TestInfoHelper($info) {
    var passed = $info.find('#passed-count');
    var failed = $info.find('#failed-count');
    var ignored = $info.find('#ignored-count');

    this.incrementPassed = function () {
        var oldValue = parseInt(passed.text());
        passed.text(++oldValue);
    };

    this.incrementFailed = function () {
        var oldValue = parseInt(failed.text());
        failed.text(++oldValue);
    };

    this.incrementIgnored = function () {
        var oldValue = parseInt(ignored.text());
        ignored.text(++oldValue);
    };
}<|endoftext|>


<commit_before>'use strict';

//@params hostname
//@returns promise
// - @promise resolved -> "community"
// - @promise rejected -> Error("community not found);
//----------------------------------------------------
//

var Promise = require('bluebird');
var snmp = Promise.promisifyAll(require('snmp-native'));
var config = require('config');
var communities = config.get('snmp.communities');
var oids = config.get('snmp.oids');

function findSnmpCommunity(hostname){
    if(typeof hostname !== 'string') throw new Error('Bad input argument, should be of type string');

    findSnmpCommunity.sessions = [];
    var workQueue = []; //we will push promisses here for the generator later to pick up

    //we itterate through all the communities, create a new snmp object and push session objects to sessions and workQueue. WorkQueue is just an arra of promises (as getAsync returns a promise). We can later call Promise.any on it (see Promise.courutine below)
    communities.forEach(function(community){
        var session = new snmp.Session({host: hostname, community: community});
        workQueue.push(session.getAsync({oid: oids.hostname}));
        findSnmpCommunity.sessions.push(session);
    });

    var resultPromise = Promise.coroutine(function *gen(){
        var result;
        try{
            result = yield Promise.any(workQueue);
        }
        catch(err){
            result = Promise.reject(err);
        }
        //we clean up sessions
        findSnmpCommunity.sessions.forEach(function(sessionToClose){
            sessionToClose.close();
        });
        return Promise.resolve(result); //we resolve the promise, if it is successfull, the community will be as the filfilled value, if rejected, err wil refect
    })();

    return resultPromise;
}

module.exports = findSnmpCommunity;<commit_msg>remade findSnmpCommunity to return an object {hostname: hostname, community: community}, tests have to be adjusted accordingly<commit_after>'use strict';

//@params hostname
//@returns promise
// - @promise resolved -> {hostname: hostname, community: community }
// - @promise rejected -> Error(Promise.aggregateError);
//----------------------------------------------------
//

var Promise = require('bluebird');
var snmp = Promise.promisifyAll(require('snmp-native'));
var config = require('config');
var communities = config.get('snmp.communities');
var oids = config.get('snmp.oids');

function findSnmpCommunity(hostname){
    if(typeof hostname !== 'string') throw new Error('Bad input argument, should be of type string');

    findSnmpCommunity.sessions = [];
    var workQueue = []; //we will push promisses here for the generator later to pick up

    //we itterate through all the communities, create a new snmp object and push session objects to sessions and workQueue. WorkQueue is just an arra of promises (as getAsync returns a promise). We can later call Promise.any on it (see Promise.courutine below)
    communities.forEach(function(community){
        var session = new snmp.Session({host: hostname, community: community});
        //workQueue.push(session.getAsync({oid: oids.hostname}))
        var promise = session.getAsync({oid: oids.hostname})
        .then(function(res){
            return Promise.resolve({hostname: res[0].value, community: session.options.community});
        });
        workQueue.push(promise);
        findSnmpCommunity.sessions.push(session);
    });

    var resultPromise = Promise.coroutine(function *gen(){
        var result;
        try{
            result = yield Promise.any(workQueue);
        }
        catch(err){
            result = Promise.reject(err);
        }

        //we clean up sessions
        findSnmpCommunity.sessions.forEach(function(sessionToClose){
            sessionToClose.close();
        });

        return Promise.resolve(result); //we resolve the promise, if it is successfull, the community will be as the filfilled value, if rejected, err wil refect
    })();

    return resultPromise;
}

module.exports = findSnmpCommunity;<|endoftext|>


<commit_before>import * as t from 'babel-types';
import * as validators from '../validators';
import arrayOfStrings from '../util/arrayOfStrings';
import {notCallExpression, callExpression} from '../util/callExpressions';
import {ifAllTruthy, ifAnyTruthy, anyTruthy, allTruthy} from '../util/conditionals';
import {createConst, createLet} from '../util/createVariable';
import dataValidator from '../util/dataValidator';
import globals from '../util/globals';
import importMethod from '../util/importMethod';
import template from '../util/moduleTemplate';
import {returnTrue, returnFalse} from '../util/returnBooleans';
import templateExpression from '../util/templateExpression';
import generateProgram from './program';
import requireModules from './requireModules';
import validator from './validator';

/*
 * Common Babel nodes.
 */

const lengthIdentifier = t.identifier('length');
const nodeIdentifier   = t.identifier('node');
const validIdentifier  = t.identifier('valid');

const valueParserASTNodes = t.memberExpression(
    t.identifier('valueParserAST'),
    t.identifier('nodes')
);

const firstValueParserNode = createConst(
    nodeIdentifier,
    t.memberExpression(
        valueParserASTNodes,
        t.numericLiteral(0),
        true
    )
);

/**
 * Test that the nodes from postcss-value-parser is a certain length. This
 * uses strict equality by default, but you can supply an alternate operator.
 *
 * @private
 * @param  {number} length          The length to check
 * @param  {type} operator = '==='  The operator to use.
 * @return {Babel}                  The binary expression.
 * @example
 * const equality = valueParserNodesLength(5);
 * //=> valueParserAST.nodes.length === 5;
 *
 * const lessThan = valueParserNodesLength(5, '<');
 * //=> valueParserAST.nodes.length < 5;
 */

function valueParserNodesLength (length, operator = '===') {
    return t.binaryExpression(
        operator,
        t.memberExpression(
            valueParserASTNodes,
            lengthIdentifier
        ),
        t.numericLiteral(length)
    );
}

function handleKeywords (keywords, settings, id) {
    if (!settings.keywords.length) {
        return;
    }
    const list = settings.keywords.length === 1 ?
        t.stringLiteral(settings.keywords[0]) :
        t.identifier(`${id}Keywords`);
    settings.conditions.push(callExpression('isKeyword', nodeIdentifier, list));
    if (settings.keywords.length === 1) {
        return;
    }
    keywords.push(createConst(
        t.identifier(`${id}Keywords`),
        arrayOfStrings(settings.keywords.filter(Boolean))
    ));
}

const dependencies = {
    valueParser: 'postcss-value-parser',
    isKeyword: './validators/isKeyword',
    isVariable: './validators/isVariable',
};

function addDependency (dep) {
    if (!dependencies[dep]) {
        dependencies[dep] = `./validators/${dep}`;
    }
}

function generatePositionValidator ({candidates, identifier}) {
    const func = 'isPosition';
    addDependency(func);
    return createConst(t.identifier(identifier), callExpression(
        func,
        t.booleanLiteral(candidates[0].separator === ',')
    ));
}

function genericValidatorStub (name, {identifier}) {
    addDependency(name);
    return createConst(t.identifier(identifier), t.identifier(name));
}

function keyword (config, candidate) {
    config.keywords.push(candidate.value);
    return config;
}

function dataString (config, candidate) {
    const camel = candidate.type === 'string' ? 'isString' : dataValidator(candidate.value);
    if (!validators[camel]) {
        return config;
    }
    addDependency(camel);
    if (camel === 'isPosition') {
        config.preConditions.push(
            template(`if (${camel}(true)(valueParserAST)) { return true; }`)()
        );
        return config;
    }
    if (camel === 'isTransformList' || camel === 'isShadowT' || camel === 'isFilterFunctionList') {
        config.preConditions.push(
            template(`if (${camel}(valueParserAST)) { return true; }`)()
        );
        return config;
    }
    if (candidate.min === 1) {
        const method = candidate.separator === ',' ? 'isComma' : 'isSpace';
        addDependency(method);
        addDependency('isEven');
        config.repeatingConditions.push(
            ifAnyTruthy([
                allTruthy(
                    templateExpression(`even`),
                    allTruthy(
                        notCallExpression(camel, nodeIdentifier),
                        notCallExpression('isVariable', nodeIdentifier)
                    ),
                ),
                allTruthy(
                    templateExpression(`!even`),
                    notCallExpression(method, nodeIdentifier)
                ),
            ], [
                t.expressionStatement(
                    t.assignmentExpression(
                        '=',
                        validIdentifier,
                        t.booleanLiteral(false)
                    ),
                ),
            ])
        );
        const tmpl = `return valid && !isEven(valueParserAST.nodes.length)`;
        if (candidate.max !== false) {
            config.repeatingReturn = template(`${tmpl} && len;`)({
                len: valueParserNodesLength((candidate.max * 2) - 1, '<='),
            });
        } else {
            config.repeatingReturn = template(`${tmpl};`)();
        }

        return config;
    }
    config.conditions.push(callExpression(camel, nodeIdentifier));
    return config;
}

const candidateTypes = {
    keyword,
    data: dataString,
    string: dataString,
};

function createValidator (opts) {
    if (opts.candidates.length === 1) {
        const {value} = opts.candidates[0];
        switch (value) {
        case 'bg-size':
        case 'repeat-style':
            return genericValidatorStub(dataValidator(value), opts);
        case 'position':
            return generatePositionValidator(opts);
        }
    }
    const settings = opts.candidates.reduce((config, candidate) => {
        const {type} = candidate;
        if (candidateTypes[type]) {
            return candidateTypes[type](config, candidate);
        }
        return config;
    }, {
        keywords: [],
        conditions: [],
        preConditions: [],
        repeatingConditions: [],
        repeatingReturn: false,
    });

    let keywords = [];

    if (settings.repeatingConditions.length) {
        handleKeywords(keywords, settings, opts.identifier);
        addDependency('isEven');

        let body = [];

        if (settings.conditions.length) {
            body.push(
                firstValueParserNode,
                ifAllTruthy([
                    valueParserNodesLength(1),
                    ...settings.conditions,
                ], [
                    returnTrue,
                ])
            );
        }

        body.push(createLet(
            validIdentifier,
            t.booleanLiteral(true)
        ));

        if (settings.preConditions.length) {
            body.push(settings.preConditions);
        }

        body.push(
            template('valueParserAST.walk((node, index) => { const even = isEven(index); CONDITIONS; return false; });')({
                CONDITIONS: settings.repeatingConditions,
            })
        );

        if (settings.repeatingReturn) {
            body.push(settings.repeatingReturn);
        }

        return validator(opts.identifier, keywords, body);
    }

    if (settings.keywords.length) {
        /*
         * This handles the simplest case; where the grammar defines a list
         * of keywords and nothing else. In this case we can use the
         * isKeywordFactory function which saves us from having to generate
         * a validator ourselves. It produces output such as:
         *
         * const property = isKeywordFactory(['foo', 'bar', 'baz']);
         */
        if (!settings.conditions.length && !settings.preConditions.length) {
            const identifier = 'isKeywordFactory';
            addDependency(identifier);
            return [
                createConst(t.identifier(opts.identifier), callExpression(
                    identifier,
                    arrayOfStrings(settings.keywords.filter(Boolean))
                )),
            ];
        }
        handleKeywords(keywords, settings, opts.identifier);
    }

    const block = settings.conditions.length ? [
        firstValueParserNode,
        t.returnStatement(anyTruthy(...settings.conditions)),
    ] : [
        returnTrue,
    ];

    return validator(opts.identifier, keywords, [
        ...(settings.preConditions.length ? settings.preConditions : t.emptyStatement()),
        t.ifStatement(
            valueParserNodesLength(1),
            t.blockStatement(block)
        ),
        returnFalse,
    ]);
}

function generateValidatorMap (config) {
    const validatorMap = config.reduce((map, {identifier, properties}) => {
        properties.forEach(property => {
            if (map[property]) {
                return;
            }
            map[property] = identifier;
        });
        return map;
    }, {});
    return createConst(
        t.identifier('validators'),
        t.objectExpression(Object.keys(validatorMap).sort().map(key => {
            return t.objectProperty(
                t.stringLiteral(key),
                t.identifier(validatorMap[key])
            );
        }))
    );
}

export default config => {
    const funcs = config.reduce((list, descriptor) => {
        const fn = createValidator(descriptor);
        if (fn) {
            return [...list, fn];
        }
        return list;
    }, []);
    return generateProgram([
        requireModules(...Object.keys(dependencies).map(key => {
            return {
                identifier: key,
                module: dependencies[key],
            };
        })),
        t.importDeclaration([
            importMethod(t.identifier('invalidMessage')),
            importMethod(t.identifier('unknownMessage')),
        ], t.stringLiteral('./util/validationMessages')),
        ...funcs,
        generateValidatorMap(config),
        createConst(
            t.identifier('cssGlobals'),
            arrayOfStrings(globals)
        ),
        template(`
            /**
             * The main entry point of this module takes a CSS property/value
             * pair, and validates it. It will return either \`true\` if valid,
             * or a message object if either invalid or unknown.
             *
             * @param {string} property The CSS property to validate.
             * @param {string|valueParser} value Either a string or an AST yielded
             * by postcss-value-parser.
             * @return {boolean|object}
             * @example
Valid CSS (string)

             * import cssValues from 'css-values';
             *
             * cssValues('color', 'transparent');
             * //=> true
             * @example
Valid CSS (valueParser)

             * import valueParser from 'postcss-value-parser';
             * import cssValues from 'css-values';
             *
             * cssValues('color', valueParser('transparent'));
             * //=> true
             * @example
Invalid CSS (string, recognised properties)

             * import cssValues from 'css-values';
             *
             * cssValues('color', 'traansparent');
             * //=> {type: 'invalid', message: '"traansparent" is not a valid value for "color".'}
             * @example
Invalid CSS (string, unknown properties)

             * import cssValues from 'css-values';
             *
             * cssValues('colr', 'transparent');
             * //=> {type: 'unknown', message: '"colr" is not a recognised property.'}
             */
            export default function cssValues (property, value) {
                if (typeof value === 'string') {
                    value = valueParser(value);
                }
                const first = value.nodes[0];
                if (value.nodes.length === 1 && (isKeyword(first, cssGlobals) || isVariable(first))) {
                    return true;
                }
                if (validators[property]) {
                    if (!!validators[property](value) === false) {
                        return invalidMessage('"' + value + '" is not a valid value for "' + property + '".');
                    }
                    return true;
                }
                // Pass through unknown properties
                return unknownMessage('"' + property + '" is not a recognised property.');
            }
        `, {preserveComments: true})(),
    ]);
};
<commit_msg>Restructure keyword validator generation.<commit_after>import * as t from 'babel-types';
import * as validators from '../validators';
import arrayOfStrings from '../util/arrayOfStrings';
import {notCallExpression, callExpression} from '../util/callExpressions';
import {ifAllTruthy, ifAnyTruthy, anyTruthy, allTruthy} from '../util/conditionals';
import {createConst, createLet} from '../util/createVariable';
import dataValidator from '../util/dataValidator';
import globals from '../util/globals';
import importMethod from '../util/importMethod';
import template from '../util/moduleTemplate';
import {returnTrue, returnFalse} from '../util/returnBooleans';
import templateExpression from '../util/templateExpression';
import generateProgram from './program';
import requireModules from './requireModules';
import validator from './validator';

/*
 * Common Babel nodes.
 */

const lengthIdentifier = t.identifier('length');
const nodeIdentifier   = t.identifier('node');
const validIdentifier  = t.identifier('valid');

const valueParserASTNodes = t.memberExpression(
    t.identifier('valueParserAST'),
    t.identifier('nodes')
);

const firstValueParserNode = createConst(
    nodeIdentifier,
    t.memberExpression(
        valueParserASTNodes,
        t.numericLiteral(0),
        true
    )
);

/**
 * Test that the nodes from postcss-value-parser is a certain length. This
 * uses strict equality by default, but you can supply an alternate operator.
 *
 * @private
 * @param  {number} length          The length to check
 * @param  {type} operator = '==='  The operator to use.
 * @return {Babel}                  The binary expression.
 * @example
 * const equality = valueParserNodesLength(5);
 * //=> valueParserAST.nodes.length === 5;
 *
 * const lessThan = valueParserNodesLength(5, '<');
 * //=> valueParserAST.nodes.length < 5;
 */

function valueParserNodesLength (length, operator = '===') {
    return t.binaryExpression(
        operator,
        t.memberExpression(
            valueParserASTNodes,
            lengthIdentifier
        ),
        t.numericLiteral(length)
    );
}

const dependencies = {
    valueParser: 'postcss-value-parser',
    isKeyword: './validators/isKeyword',
    isVariable: './validators/isVariable',
};

function addDependency (dep) {
    if (!dependencies[dep]) {
        dependencies[dep] = `./validators/${dep}`;
    }
}

function generatePositionValidator ({candidates, identifier}) {
    const func = 'isPosition';
    addDependency(func);
    return createConst(t.identifier(identifier), callExpression(
        func,
        t.booleanLiteral(candidates[0].separator === ',')
    ));
}

function genericValidatorStub (name, {identifier}) {
    addDependency(name);
    return createConst(t.identifier(identifier), t.identifier(name));
}

function keyword (config, candidate) {
    config.keywords.push(candidate.value);
    return config;
}

function dataString (config, candidate) {
    const camel = candidate.type === 'string' ? 'isString' : dataValidator(candidate.value);
    if (!validators[camel]) {
        return config;
    }
    addDependency(camel);
    if (camel === 'isPosition') {
        config.preConditions.push(
            template(`if (${camel}(true)(valueParserAST)) { return true; }`)()
        );
        return config;
    }
    if (camel === 'isTransformList' || camel === 'isShadowT' || camel === 'isFilterFunctionList') {
        config.preConditions.push(
            template(`if (${camel}(valueParserAST)) { return true; }`)()
        );
        return config;
    }
    if (candidate.min === 1) {
        const method = candidate.separator === ',' ? 'isComma' : 'isSpace';
        addDependency(method);
        addDependency('isEven');
        config.repeatingConditions.push(
            ifAnyTruthy([
                allTruthy(
                    templateExpression(`even`),
                    allTruthy(
                        notCallExpression(camel, nodeIdentifier),
                        notCallExpression('isVariable', nodeIdentifier)
                    ),
                ),
                allTruthy(
                    templateExpression(`!even`),
                    notCallExpression(method, nodeIdentifier)
                ),
            ], [
                t.expressionStatement(
                    t.assignmentExpression(
                        '=',
                        validIdentifier,
                        t.booleanLiteral(false)
                    ),
                ),
            ])
        );
        const tmpl = `return valid && !isEven(valueParserAST.nodes.length)`;
        if (candidate.max !== false) {
            config.repeatingReturn = template(`${tmpl} && len;`)({
                len: valueParserNodesLength((candidate.max * 2) - 1, '<='),
            });
        } else {
            config.repeatingReturn = template(`${tmpl};`)();
        }

        return config;
    }
    config.conditions.push(callExpression(camel, nodeIdentifier));
    return config;
}

const candidateTypes = {
    keyword,
    data: dataString,
    string: dataString,
};

function createValidator (opts) {
    if (opts.candidates.length === 1) {
        const {value} = opts.candidates[0];
        switch (value) {
        case 'bg-size':
        case 'repeat-style':
            return genericValidatorStub(dataValidator(value), opts);
        case 'position':
            return generatePositionValidator(opts);
        }
    }
    const settings = opts.candidates.reduce((config, candidate) => {
        const {type} = candidate;
        if (candidateTypes[type]) {
            return candidateTypes[type](config, candidate);
        }
        return config;
    }, {
        keywords: [],
        conditions: [],
        preConditions: [],
        repeatingConditions: [],
        repeatingReturn: false,
    });

    let keywords = [];

    if (settings.keywords.length) {
        /*
         * This handles the simplest case; where the grammar defines a list
         * of keywords and nothing else. In this case we can use the
         * isKeywordFactory function which saves us from having to generate
         * a validator ourselves. It produces output such as:
         *
         * const property = isKeywordFactory(['foo', 'bar', 'baz']);
         */
        if (
            !settings.conditions.length &&
            !settings.preConditions.length &&
            !settings.repeatingConditions.length
        ) {
            const identifier = 'isKeywordFactory';
            addDependency(identifier);
            return [
                createConst(t.identifier(opts.identifier), callExpression(
                    identifier,
                    arrayOfStrings(settings.keywords.filter(Boolean))
                )),
            ];
        }
        /*
         * Otherwise, we need to generate a list of keywords that are used
         * as another validation condition. If we only have one keyword,
         * this can be inlined inside the `isKeyword` function - i.e.
         * `isKeyword(node, 'foo')`. Otherwise, we create a new list
         * and use it as a reference - `isKeyword(node, propertyKeywords)`,
         * where `propertyKeywords` is `const propertyKeywords = ['foo', 'bar']`
         */
        const keywordsList = t.identifier(`${opts.identifier}Keywords`);
        const list = settings.keywords.length === 1 ? t.stringLiteral(settings.keywords[0]) : keywordsList;
        settings.conditions.push(callExpression('isKeyword', nodeIdentifier, list));
        if (settings.keywords.length > 1) {
            keywords.push(createConst(
                keywordsList,
                arrayOfStrings(settings.keywords.filter(Boolean))
            ));
        }
    }

    if (settings.repeatingConditions.length) {
        addDependency('isEven');

        let body = [];

        if (settings.conditions.length) {
            body.push(
                firstValueParserNode,
                ifAllTruthy([
                    valueParserNodesLength(1),
                    ...settings.conditions,
                ], [
                    returnTrue,
                ])
            );
        }

        body.push(createLet(
            validIdentifier,
            t.booleanLiteral(true)
        ));

        if (settings.preConditions.length) {
            body.push(settings.preConditions);
        }

        body.push(
            template('valueParserAST.walk((node, index) => { const even = isEven(index); CONDITIONS; return false; });')({
                CONDITIONS: settings.repeatingConditions,
            })
        );

        if (settings.repeatingReturn) {
            body.push(settings.repeatingReturn);
        }

        return validator(opts.identifier, keywords, body);
    }

    const block = settings.conditions.length ? [
        firstValueParserNode,
        t.returnStatement(anyTruthy(...settings.conditions)),
    ] : [
        returnTrue,
    ];

    return validator(opts.identifier, keywords, [
        ...(settings.preConditions.length ? settings.preConditions : t.emptyStatement()),
        t.ifStatement(
            valueParserNodesLength(1),
            t.blockStatement(block)
        ),
        returnFalse,
    ]);
}

function generateValidatorMap (config) {
    const validatorMap = config.reduce((map, {identifier, properties}) => {
        properties.forEach(property => {
            if (map[property]) {
                return;
            }
            map[property] = identifier;
        });
        return map;
    }, {});
    return createConst(
        t.identifier('validators'),
        t.objectExpression(Object.keys(validatorMap).sort().map(key => {
            return t.objectProperty(
                t.stringLiteral(key),
                t.identifier(validatorMap[key])
            );
        }))
    );
}

export default config => {
    const funcs = config.reduce((list, descriptor) => {
        const fn = createValidator(descriptor);
        if (fn) {
            return [...list, fn];
        }
        return list;
    }, []);
    return generateProgram([
        requireModules(...Object.keys(dependencies).map(key => {
            return {
                identifier: key,
                module: dependencies[key],
            };
        })),
        t.importDeclaration([
            importMethod(t.identifier('invalidMessage')),
            importMethod(t.identifier('unknownMessage')),
        ], t.stringLiteral('./util/validationMessages')),
        ...funcs,
        generateValidatorMap(config),
        createConst(
            t.identifier('cssGlobals'),
            arrayOfStrings(globals)
        ),
        template(`
            /**
             * The main entry point of this module takes a CSS property/value
             * pair, and validates it. It will return either \`true\` if valid,
             * or a message object if either invalid or unknown.
             *
             * @param {string} property The CSS property to validate.
             * @param {string|valueParser} value Either a string or an AST yielded
             * by postcss-value-parser.
             * @return {boolean|object}
             * @example
Valid CSS (string)

             * import cssValues from 'css-values';
             *
             * cssValues('color', 'transparent');
             * //=> true
             * @example
Valid CSS (valueParser)

             * import valueParser from 'postcss-value-parser';
             * import cssValues from 'css-values';
             *
             * cssValues('color', valueParser('transparent'));
             * //=> true
             * @example
Invalid CSS (string, recognised properties)

             * import cssValues from 'css-values';
             *
             * cssValues('color', 'traansparent');
             * //=> {type: 'invalid', message: '"traansparent" is not a valid value for "color".'}
             * @example
Invalid CSS (string, unknown properties)

             * import cssValues from 'css-values';
             *
             * cssValues('colr', 'transparent');
             * //=> {type: 'unknown', message: '"colr" is not a recognised property.'}
             */
            export default function cssValues (property, value) {
                if (typeof value === 'string') {
                    value = valueParser(value);
                }
                const first = value.nodes[0];
                if (value.nodes.length === 1 && (isKeyword(first, cssGlobals) || isVariable(first))) {
                    return true;
                }
                if (validators[property]) {
                    if (!!validators[property](value) === false) {
                        return invalidMessage('"' + value + '" is not a valid value for "' + property + '".');
                    }
                    return true;
                }
                // Pass through unknown properties
                return unknownMessage('"' + property + '" is not a recognised property.');
            }
        `, {preserveComments: true})(),
    ]);
};
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
        Project = mongoose.model('Project'),
        Organisation = mongoose.model('Organisation'),
        BankAccount = mongoose.model('BankAccount'),
        States = mongoose.model('States'),
        config = require('meanio').loadConfig(),
        _ = require('lodash');

module.exports = function (Search) {

    /**
     * Maximum number of search results to be displayed when using paginated
     * search function.
     */
    var pageSize = 10;

    /**
     * Formulates received search query depending on the type of
     * data searched
     * @param {JSON}
     * @returns {JSON}
     */

    function prepareQueries(searchBy) {

        /**
         * Formats queries to correct form
         */
        return _.map(JSON.parse(searchBy), function (query) {
            var search = {};

            if (query.value === 'payments') {
                query.field = 'payments';
                query.value = {$exists: true, $gt: {$size: 0}};
            }
            if (query.value === 'approved.granted_sum_eur') {
                query.field = 'approved.granted_sum_eur';
                query.value = {$exists: true};
            }
            if (query.dateField) {
                if (query.startYear && !query.endYear) {
                    query.field = query.dateField;
                    query.value = {
                        $gte: new Date(query.startYear, query.startMonth - 1, query.startDay + 1)
                                .toISOString()
                    };
                }
                if (query.endYear && !query.startYear) {
                    query.field = query.dateField;
                    query.value = {
                        $lte: new Date(query.endYear, query.endMonth - 1, query.endDay + 1)
                                .toISOString()
                    };
                }
                if (query.startYear && query.endYear) {
                    query.field = query.dateField;
                    query.value = {
                        $gte: new Date(query.startYear, query.startMonth - 1, query.startDay + 1)
                                .toISOString(),
                        $lte: new Date(query.endYear, query.endMonth - 1, query.endDay + 1)
                                .toISOString()
                    };
                }
            }
            if (query.value === 'Käynnissä olevat hankkeet') {
                query.value = {$in: ["allekirjoitettu", "väliraportti", "loppuraportti"]};
            }
            if (typeof query.value === 'string') {
                query.value = new RegExp(query.value, 'i');
            }
            search[query.field] = query.value;
            return search;
        });
    };

    return {
        /**
         * Searches for projects. The results are ordered and paginated
         * according to the given HTTP GET parameters in the request object.
         *
         * @param {type} req    The request object.
         * @returns {JSON}
         */
        searchProjects: function (req, res) {
            var ordering = req.query.ordering;
            var ascending = req.query.ascending;
            var page = req.query.page;
            if (typeof ordering === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "ordering"!'
                });
            }
            if (typeof ascending === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "ascending"!'
                });
            }
            if (typeof page === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "page"!'
                });
            }

            var queries = prepareQueries(req.query.searchBy);
            var orderingJSON = {};
            orderingJSON[ordering] = ascending === 'true' ? 1 : -1;

            /**
             * Format search query when search involves organisations
             * collection, and perform search using Organisation model.
             */

            var params = _.map(JSON.parse(req.query.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            })

            Organisation.find({$and: params}, function (err, orgs) {

                /**
                 * Map found organisations to an array of ids
                 */
                orgs = orgs.map(function (org) {
                    return org._id;
                });

                /**
                 * Combine organisation and project parameters in one query
                 * and query the projects collection
                 */
                queries = _.filter(queries, function (query) {
                    return typeof query.org_fields === 'undefined';
                })

                queries.push({organisation: {$in: orgs}});

                Project.find({$and: queries}, {_id: 1, project_ref: 1, title: 1,
                    organisation: 1, description: 1})
                        .populate('organisation', {_id: 1, name: 1})
                        .sort(orderingJSON)
                        .skip((page - 1) * pageSize)
                        .limit(pageSize)
                        .exec(function (err, results) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                res.json(results);
                            }
                        });
            });
        },
        /**
         * Returns all projects matching the given search query in the HTTP POST
         * parameter <tt>searchBy</tt>. The fields in the documents will be
         * restricted to only the ones specified in parameters
         * <tt>projFields</tt> and <tt>orgFields</tt>, which are strings of
         * field names separated with spaces.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchAllProjects: function (req, res) {
            var queries = prepareQueries(req.query.searchBy);

            var params = _.map(JSON.parse(req.query.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            });

            var projFields = req.query.projFields;
            var orgFields = req.query.orgFields;

            Organisation.find({$and: params}, function (err, orgs) {
                orgs = orgs.map(function (org) {
                    return org._id;
                });

                queries = _.filter(queries, function (query) {
                    return typeof query.orgField === 'undefined';
                });

                queries.push({organisation: {$in: orgs}});

                Project.find({$and: queries})
                        .populate('organisation', orgFields)
                        .select(projFields)
                        .exec(function (err, results) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                results.forEach(function (result) {
                                    result._id                  = undefined;
                                    result.in_review            = undefined;
                                    if (Object.keys(result.approved).length === 0) {
                                        result.approved = undefined;
                                    }
                                    result.rejected             = undefined;
                                    result.required_appendices  = undefined;
                                    result.signed               = undefined;
                                    result.intermediary_reports = undefined;
                                    result.payments             = undefined;
                                    result.end_report           = undefined;
                                    result.ended                = undefined;
                                    result.updated              = undefined;
                                    result.appendices           = undefined;
                                    if (Object.keys(result.funding).length === 0) {
                                        result.funding = undefined;
                                    }
                                    if (Object.keys(result.organisation).length === 0) {
                                        result.organisation = undefined;
                                    } else {
                                        result.organisation._id = undefined;
                                    }
                                });
                                res.json(results);
                            }
                        });
            });
        },
        /**
         * Returns all payments matching the given search query in the HTTP POST
         * parameter <tt>searchPay</tt>.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchPayments: function (req, res) {

            var choice = _.values(JSON.parse(req.query.choice));

            var queries = prepareQueries(req.query.searchBy);

            /**
             * Query paid sums
             */
            if (choice.indexOf('payments') > -1) {
                queries.push({"payments": {$exists: true, $gt: {$size: 0}}});

                Project.find({$and: queries}, function (err, projects) {
                    if (err) {
                        return res.status(500).json({
                            error: 'Virhe maksujen hakutoiminnossa'
                        });
                    } else {
                        projects = _.flattenDeep(_.map(projects, function (project) {
                            var payments = _.map(project.payments, function (payment) {
                                return {"sum": payment.sum_eur,
                                    "date": payment.payment_date,
                                    "ref": project.project_ref,
                                    "title": project.title,
                                    "coordinator": project.coordinator,
                                    "region": project.region};
                            });
                            return payments;
                        }));
                        res.json(projects);
                    }
                });

            }

            if (choice.indexOf('approved.granted_sum_eur') > -1) {
                queries.push({"approved.granted_sum_eur": {$exists: true}});

                Project.find({$and: queries}, function (err, projects) {
                    if (err) {
                        return res.status(500).json({
                            error: 'Virhe maksujen hakutoiminnossa'
                        });
                    } else {
                        projects = _.map(projects, function (project) {

                            return {"sum": project.approved.granted_sum_eur,
                                "date": project.approved.approved_date,
                                "ref": project.project_ref,
                                "title": project.title,
                                "id": project._id}
                        });
                        res.json(projects);
                    }
                });

            }

        },
        /**
         * Returns all organisations matching the given search query in the HTTP
         * POST parameter <tt>searchOrg</tt>.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchOrgs: function (req, res) {
            var queries = prepareQueries(req.query.searchBy);

            Organisation.find({$and: queries}, function (err, orgs) {
                if (err) {
                    return res.status(500).json({
                        error: 'Virhe järjestöjen hakutoiminnossa'
                    });
                } else {
                    orgs = _.map(orgs, function (org) {
                        return {
                            "name": org.name,
                            "representative": org.representative.name +
                                    ", " + org.representative.email + ", " +
                                    org.representative.phone,
                            "exec_manager": org.exec_manager,
                            "communications_rep": org.communications_rep,
                            "address": org.address.street + ", " +
                                    org.address.postal_code + " " +
                                    org.address.city + ", " +
                                    org.address.country,
                            "tel": org.tel,
                            "email": org.email,
                            "website": org.website,
                            "legal_status": org.legal_status,
                            "description": org.description,
                            "int_links": org.int_links,
                            "nat_local_links": org.nat_local_links,
                            "other_funding_budget": org.other_funding_budget,
                            "accounting_audit": org.accounting_audit
                        }
                    })
                    res.json(orgs);
                }

            });

        },
        /**
         * Returns the number of all projects matching the search query given by
         * the HTTP POST parameter <tt>searchBy</tt>.
         *
         * @param {type} req
         * @param {type} res
         * @returns {undefined}
         */
        countSearchResults: function (req, res) {
            var queries = prepareQueries(req.body.searchBy);

            var params = _.map(JSON.parse(req.body.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            })

            Organisation.find({$and: params}, function (err, orgs) {

                orgs = orgs.map(function (org) {
                    return org._id;
                });

                queries = _.filter(queries, function (query) {
                    return typeof query.orgField === 'undefined';
                })

                queries.push({organisation: {$in: orgs}});

                Project.count({$and: queries})
                        .populate('organisation', {name: 1})
                        .exec(function (err, result) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                res.json({projectCount: result});
                            }
                        });
            });
        }
    };
};
<commit_msg>Fixed the URLs in the organisation search results.<commit_after>'use strict';

var mongoose = require('mongoose'),
        Project = mongoose.model('Project'),
        Organisation = mongoose.model('Organisation'),
        BankAccount = mongoose.model('BankAccount'),
        States = mongoose.model('States'),
        config = require('meanio').loadConfig(),
        _ = require('lodash');

module.exports = function (Search) {

    /**
     * Maximum number of search results to be displayed when using paginated
     * search function.
     */
    var pageSize = 10;

    /**
     * Formulates received search query depending on the type of
     * data searched
     * @param {JSON}
     * @returns {JSON}
     */

    function prepareQueries(searchBy) {

        /**
         * Formats queries to correct form
         */
        return _.map(JSON.parse(searchBy), function (query) {
            var search = {};

            if (query.value === 'payments') {
                query.field = 'payments';
                query.value = {$exists: true, $gt: {$size: 0}};
            }
            if (query.value === 'approved.granted_sum_eur') {
                query.field = 'approved.granted_sum_eur';
                query.value = {$exists: true};
            }
            if (query.dateField) {
                if (query.startYear && !query.endYear) {
                    query.field = query.dateField;
                    query.value = {
                        $gte: new Date(query.startYear, query.startMonth - 1, query.startDay + 1)
                                .toISOString()
                    };
                }
                if (query.endYear && !query.startYear) {
                    query.field = query.dateField;
                    query.value = {
                        $lte: new Date(query.endYear, query.endMonth - 1, query.endDay + 1)
                                .toISOString()
                    };
                }
                if (query.startYear && query.endYear) {
                    query.field = query.dateField;
                    query.value = {
                        $gte: new Date(query.startYear, query.startMonth - 1, query.startDay + 1)
                                .toISOString(),
                        $lte: new Date(query.endYear, query.endMonth - 1, query.endDay + 1)
                                .toISOString()
                    };
                }
            }
            if (query.value === 'Käynnissä olevat hankkeet') {
                query.value = {$in: ["allekirjoitettu", "väliraportti", "loppuraportti"]};
            }
            if (typeof query.value === 'string') {
                query.value = new RegExp(query.value, 'i');
            }
            search[query.field] = query.value;
            return search;
        });
    };

    return {
        /**
         * Searches for projects. The results are ordered and paginated
         * according to the given HTTP GET parameters in the request object.
         *
         * @param {type} req    The request object.
         * @returns {JSON}
         */
        searchProjects: function (req, res) {
            var ordering = req.query.ordering;
            var ascending = req.query.ascending;
            var page = req.query.page;
            if (typeof ordering === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "ordering"!'
                });
            }
            if (typeof ascending === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "ascending"!'
                });
            }
            if (typeof page === 'undefined') {
                return res.status(500).json({
                    error: 'Kyselystä puuttuu kenttä "page"!'
                });
            }

            var queries = prepareQueries(req.query.searchBy);
            var orderingJSON = {};
            orderingJSON[ordering] = ascending === 'true' ? 1 : -1;

            /**
             * Format search query when search involves organisations
             * collection, and perform search using Organisation model.
             */

            var params = _.map(JSON.parse(req.query.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            })

            Organisation.find({$and: params}, function (err, orgs) {

                /**
                 * Map found organisations to an array of ids
                 */
                orgs = orgs.map(function (org) {
                    return org._id;
                });

                /**
                 * Combine organisation and project parameters in one query
                 * and query the projects collection
                 */
                queries = _.filter(queries, function (query) {
                    return typeof query.org_fields === 'undefined';
                })

                queries.push({organisation: {$in: orgs}});

                Project.find({$and: queries}, {_id: 1, project_ref: 1, title: 1,
                    organisation: 1, description: 1})
                        .populate('organisation', {_id: 1, name: 1})
                        .sort(orderingJSON)
                        .skip((page - 1) * pageSize)
                        .limit(pageSize)
                        .exec(function (err, results) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                res.json(results);
                            }
                        });
            });
        },
        /**
         * Returns all projects matching the given search query in the HTTP POST
         * parameter <tt>searchBy</tt>. The fields in the documents will be
         * restricted to only the ones specified in parameters
         * <tt>projFields</tt> and <tt>orgFields</tt>, which are strings of
         * field names separated with spaces.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchAllProjects: function (req, res) {
            var queries = prepareQueries(req.query.searchBy);

            var params = _.map(JSON.parse(req.query.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            });

            var projFields = req.query.projFields;
            var orgFields = req.query.orgFields;

            Organisation.find({$and: params}, function (err, orgs) {
                orgs = orgs.map(function (org) {
                    return org._id;
                });

                queries = _.filter(queries, function (query) {
                    return typeof query.orgField === 'undefined';
                });

                queries.push({organisation: {$in: orgs}});

                Project.find({$and: queries})
                        .populate('organisation', orgFields)
                        .select(projFields)
                        .exec(function (err, results) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                results.forEach(function (result) {
                                    result._id                  = undefined;
                                    result.in_review            = undefined;
                                    if (Object.keys(result.approved).length === 0) {
                                        result.approved = undefined;
                                    }
                                    result.rejected             = undefined;
                                    result.required_appendices  = undefined;
                                    result.signed               = undefined;
                                    result.intermediary_reports = undefined;
                                    result.payments             = undefined;
                                    result.end_report           = undefined;
                                    result.ended                = undefined;
                                    result.updated              = undefined;
                                    result.appendices           = undefined;
                                    if (Object.keys(result.funding).length === 0) {
                                        result.funding = undefined;
                                    }
                                    if (Object.keys(result.organisation).length === 0) {
                                        result.organisation = undefined;
                                    } else {
                                        result.organisation._id = undefined;
                                    }
                                });
                                res.json(results);
                            }
                        });
            });
        },
        /**
         * Returns all payments matching the given search query in the HTTP POST
         * parameter <tt>searchPay</tt>.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchPayments: function (req, res) {

            var choice = _.values(JSON.parse(req.query.choice));

            var queries = prepareQueries(req.query.searchBy);

            /**
             * Query paid sums
             */
            if (choice.indexOf('payments') > -1) {
                queries.push({"payments": {$exists: true, $gt: {$size: 0}}});

                Project.find({$and: queries}, function (err, projects) {
                    if (err) {
                        return res.status(500).json({
                            error: 'Virhe maksujen hakutoiminnossa'
                        });
                    } else {
                        projects = _.flattenDeep(_.map(projects, function (project) {
                            var payments = _.map(project.payments, function (payment) {
                                return {"sum": payment.sum_eur,
                                    "date": payment.payment_date,
                                    "ref": project.project_ref,
                                    "title": project.title,
                                    "coordinator": project.coordinator,
                                    "region": project.region};
                            });
                            return payments;
                        }));
                        res.json(projects);
                    }
                });

            }

            if (choice.indexOf('approved.granted_sum_eur') > -1) {
                queries.push({"approved.granted_sum_eur": {$exists: true}});

                Project.find({$and: queries}, function (err, projects) {
                    if (err) {
                        return res.status(500).json({
                            error: 'Virhe maksujen hakutoiminnossa'
                        });
                    } else {
                        projects = _.map(projects, function (project) {

                            return {"sum": project.approved.granted_sum_eur,
                                "date": project.approved.approved_date,
                                "ref": project.project_ref,
                                "title": project.title,
                                "id": project._id}
                        });
                        res.json(projects);
                    }
                });

            }

        },
        /**
         * Returns all organisations matching the given search query in the HTTP
         * POST parameter <tt>searchOrg</tt>.
         *
         * @param {type} req Request object.
         * @param {type} res Response object.
         */
        searchOrgs: function (req, res) {
            var queries = prepareQueries(req.query.searchBy);

            Organisation.find({$and: queries}, function (err, orgs) {
                if (err) {
                    return res.status(500).json({
                        error: 'Virhe järjestöjen hakutoiminnossa'
                    });
                } else {
                    orgs = _.map(orgs, function (org) {
                        return {
                            "_id": org._id,
                            "name": org.name,
                            "representative": org.representative.name +
                                    ", " + org.representative.email + ", " +
                                    org.representative.phone,
                            "exec_manager": org.exec_manager,
                            "communications_rep": org.communications_rep,
                            "address": org.address.street + ", " +
                                    org.address.postal_code + " " +
                                    org.address.city + ", " +
                                    org.address.country,
                            "tel": org.tel,
                            "email": org.email,
                            "website": org.website,
                            "legal_status": org.legal_status,
                            "description": org.description,
                            "int_links": org.int_links,
                            "nat_local_links": org.nat_local_links,
                            "other_funding_budget": org.other_funding_budget,
                            "accounting_audit": org.accounting_audit
                        }
                    })
                    res.json(orgs);
                }

            });

        },
        /**
         * Returns the number of all projects matching the search query given by
         * the HTTP POST parameter <tt>searchBy</tt>.
         *
         * @param {type} req
         * @param {type} res
         * @returns {undefined}
         */
        countSearchResults: function (req, res) {
            var queries = prepareQueries(req.body.searchBy);

            var params = _.map(JSON.parse(req.body.searchBy), function (query) {
                var search = {};
                if (typeof query.orgField !== 'undefined') {
                    search[query.orgField] = new RegExp(query.orgValue, 'i');
                }
                return search;
            })

            Organisation.find({$and: params}, function (err, orgs) {

                orgs = orgs.map(function (org) {
                    return org._id;
                });

                queries = _.filter(queries, function (query) {
                    return typeof query.orgField === 'undefined';
                })

                queries.push({organisation: {$in: orgs}});

                Project.count({$and: queries})
                        .populate('organisation', {name: 1})
                        .exec(function (err, result) {
                            if (err) {
                                return res.status(500).json({
                                    error: 'Virhe hankkeiden hakutoiminnossa'
                                });
                            } else {
                                res.json({projectCount: result});
                            }
                        });
            });
        }
    };
};
<|endoftext|>


<commit_before>import path from 'path';
import SeleniumHelper from '../helpers/selenium-helper';

const {
    clickText,
    clickButton,
    clickXpath,
    findByText,
    findByXpath,
    getDriver,
    getLogs,
    loadUri,
    rightClickText,
    scope
} = new SeleniumHelper();

const uri = path.resolve(__dirname, '../../build/index.html');

let driver;

describe('Working with the blocks', () => {
    beforeAll(() => {
        driver = getDriver();
    });

    afterAll(async () => {
        await driver.quit();
    });

    test('Blocks report when clicked in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Operators', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('join', scope.blocksTab); // Click "join <hello> <world>" block
        await findByText('apple banana', scope.reportedValue); // Tooltip with result
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Switching sprites updates the block menus', async () => {
        await loadUri(uri);
        await clickText('Sound', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        // "Meow" sound block should be visible
        await findByText('Meow', scope.blocksTab);
        await clickText('Backdrops'); // Switch to the backdrop
        // Now "pop" sound block should be visible and motion blocks hidden
        await findByText('pop', scope.blocksTab);
        await clickText('Motion', scope.blocksTab);
        await findByText('Stage selected: no motion blocks');

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating variables', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation

        // Expect a default variable "my variable" to be visible
        await clickText('my\u00A0variable', scope.blocksTab);
        await findByText('0', scope.reportedValue);

        await clickText('Make a Variable');
        let el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('score');
        await clickButton('OK');
        await clickText('Make a Variable');
        el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('second variable');
        await clickButton('OK');

        // Make sure reporting works on a new variable
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('score', scope.blocksTab);
        await findByText('0', scope.reportedValue); // Tooltip with result

        // And there should be a monitor visible
        await rightClickText('score', scope.monitors);
        await clickText('slider');
        await findByXpath("//input[@step='1']");

        // Changing the slider to a decimal should make it have a step size of 0.01
        await rightClickText('score', scope.monitors);
        await clickText('change slider range');
        el = await findByXpath("//input[@name='Maximum value']");
        await el.sendKeys('.1');
        await clickButton('OK');
        await findByXpath("//input[@step='0.01'][@max='100.1']");

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating a list', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation

        await clickText('Make a List');
        let el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');

        // Click the "add <thing> to list" block 3 times
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing', scope.reportedValue); // Tooltip with result

        // Interact with the monitor, adding an item
        await findByText('list1', scope.monitors); // Just to be sure it is there
        await clickText('+', scope.monitors);
        el = await findByXpath(`//body//${scope.monitors}//input`);
        await el.sendKeys('thing2');
        await el.click(); // Regression for "clicking active input erases value" bug.
        await clickText('list1', scope.monitors); // Blur the input to submit

        // Check that the list value has been propagated.
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing thing2', scope.reportedValue); // Tooltip with result

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Custom procedures', async () => {
        await loadUri(uri);
        await clickText('My Blocks');
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('Make a Block');
        // Click on the "add an input" buttons
        await clickText('number or text', scope.modal);
        await clickText('boolean', scope.modal);
        await clickText('Add a label', scope.modal);
        await clickText('OK', scope.modal);

        // Make sure a "define" block has been added to the workspace
        await findByText('define', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Adding an extension', async () => {
        await loadUri(uri);
        await clickXpath('//button[@title="Add Extension"]');

        await clickText('Pen');
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        // Make sure toolbox has been scrolled to the pen extension
        await findByText('stamp', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Record option from sound block menu opens sound recorder', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Sound', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('Meow', scope.blocksTab); // Click "play sound <Meow> until done" block
        await clickText('record'); // Click "record..." option in the block's sound menu
        // Access has been force denied, so close the alert that comes up
        await driver.sleep(1000); // getUserMedia requests are very slow to fail for some reason
        await driver.switchTo().alert()
            .accept();
        await findByText('Record Sound'); // Sound recorder is open
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Renaming costume changes the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('newname');

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('newname', scope.blocksTab);
    });

    test('Renaming costume with a special character should not break toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('<NewCostume>');

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('<NewCostume>', scope.blocksTab);

        await clickText('Sound', scope.blocksTab);
    });

    test('Adding costumes DOES update the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // By default, costume2 is in the costume tab
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('costume2', scope.blocksTab);

        // Also check that adding a new costume does update the list
        await clickText('Costumes');
        const el = await findByXpath('//button[@aria-label="Choose a Costume"]');
        await driver.actions().mouseMove(el)
            .perform();
        await driver.sleep(500); // Wait for thermometer menu to come up
        await clickXpath('//button[@aria-label="Paint"]');
        await clickText('costume3', scope.costumesTab);
        // Check that the menu has been updated
        await clickText('Code');
        await clickText('costume3', scope.blocksTab);
    });

    // Skipped because it was flakey on travis, but seems to run locally ok
    test.skip('Adding a sound DOES update the default sound name in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Sounds');
        await clickXpath('//button[@aria-label="Choose a Sound"]');
        await clickText('A Bass', scope.modal); // Should close the modal
        await clickText('Code');
        await clickText('Sound', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('A\u00A0Bass', scope.blocksTab); // Need   for block text
    });

    // Regression test for switching between editor/player causing toolbox to stop updating
    test('"See inside" after being on project page re-initializing variables', async () => {
        const playerUri = path.resolve(__dirname, '../../build/player.html');
        await loadUri(playerUri);
        await clickText('See inside');
        await clickText('Variables');
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('my\u00A0variable');

        await clickText('See Project Page');
        await clickText('See inside');

        await clickText('Variables');
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('my\u00A0variable');
    });

    // Regression test for switching editor tabs causing toolbox to stop updating
    test('Creating variables after adding extensions updates the toolbox', async () => {
        await loadUri(uri);
        await clickText('Costumes');
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll
        await clickText('Make a List');
        const el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');
        await clickText('list1', scope.blocksTab);
    });
});
<commit_msg>Skip blocks “renaming costume wtih special character” test<commit_after>import path from 'path';
import SeleniumHelper from '../helpers/selenium-helper';

const {
    clickText,
    clickButton,
    clickXpath,
    findByText,
    findByXpath,
    getDriver,
    getLogs,
    loadUri,
    rightClickText,
    scope
} = new SeleniumHelper();

const uri = path.resolve(__dirname, '../../build/index.html');

let driver;

describe('Working with the blocks', () => {
    beforeAll(() => {
        driver = getDriver();
    });

    afterAll(async () => {
        await driver.quit();
    });

    test('Blocks report when clicked in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Operators', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('join', scope.blocksTab); // Click "join <hello> <world>" block
        await findByText('apple banana', scope.reportedValue); // Tooltip with result
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Switching sprites updates the block menus', async () => {
        await loadUri(uri);
        await clickText('Sound', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        // "Meow" sound block should be visible
        await findByText('Meow', scope.blocksTab);
        await clickText('Backdrops'); // Switch to the backdrop
        // Now "pop" sound block should be visible and motion blocks hidden
        await findByText('pop', scope.blocksTab);
        await clickText('Motion', scope.blocksTab);
        await findByText('Stage selected: no motion blocks');

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating variables', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation

        // Expect a default variable "my variable" to be visible
        await clickText('my\u00A0variable', scope.blocksTab);
        await findByText('0', scope.reportedValue);

        await clickText('Make a Variable');
        let el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('score');
        await clickButton('OK');
        await clickText('Make a Variable');
        el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('second variable');
        await clickButton('OK');

        // Make sure reporting works on a new variable
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('score', scope.blocksTab);
        await findByText('0', scope.reportedValue); // Tooltip with result

        // And there should be a monitor visible
        await rightClickText('score', scope.monitors);
        await clickText('slider');
        await findByXpath("//input[@step='1']");

        // Changing the slider to a decimal should make it have a step size of 0.01
        await rightClickText('score', scope.monitors);
        await clickText('change slider range');
        el = await findByXpath("//input[@name='Maximum value']");
        await el.sendKeys('.1');
        await clickButton('OK');
        await findByXpath("//input[@step='0.01'][@max='100.1']");

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating a list', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation

        await clickText('Make a List');
        let el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');

        // Click the "add <thing> to list" block 3 times
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing', scope.reportedValue); // Tooltip with result

        // Interact with the monitor, adding an item
        await findByText('list1', scope.monitors); // Just to be sure it is there
        await clickText('+', scope.monitors);
        el = await findByXpath(`//body//${scope.monitors}//input`);
        await el.sendKeys('thing2');
        await el.click(); // Regression for "clicking active input erases value" bug.
        await clickText('list1', scope.monitors); // Blur the input to submit

        // Check that the list value has been propagated.
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing thing2', scope.reportedValue); // Tooltip with result

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Custom procedures', async () => {
        await loadUri(uri);
        await clickText('My Blocks');
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('Make a Block');
        // Click on the "add an input" buttons
        await clickText('number or text', scope.modal);
        await clickText('boolean', scope.modal);
        await clickText('Add a label', scope.modal);
        await clickText('OK', scope.modal);

        // Make sure a "define" block has been added to the workspace
        await findByText('define', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Adding an extension', async () => {
        await loadUri(uri);
        await clickXpath('//button[@title="Add Extension"]');

        await clickText('Pen');
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        // Make sure toolbox has been scrolled to the pen extension
        await findByText('stamp', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Record option from sound block menu opens sound recorder', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickText('Sound', scope.blocksTab);
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        await clickText('Meow', scope.blocksTab); // Click "play sound <Meow> until done" block
        await clickText('record'); // Click "record..." option in the block's sound menu
        // Access has been force denied, so close the alert that comes up
        await driver.sleep(1000); // getUserMedia requests are very slow to fail for some reason
        await driver.switchTo().alert()
            .accept();
        await findByText('Record Sound'); // Sound recorder is open
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Renaming costume changes the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('newname');

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('newname', scope.blocksTab);
    });

    test.skip('Renaming costume with a special character should not break toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('<NewCostume>');

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('<NewCostume>', scope.blocksTab);

        await clickText('Sound', scope.blocksTab);
    });

    test('Adding costumes DOES update the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // By default, costume2 is in the costume tab
        await clickText('Looks', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('costume2', scope.blocksTab);

        // Also check that adding a new costume does update the list
        await clickText('Costumes');
        const el = await findByXpath('//button[@aria-label="Choose a Costume"]');
        await driver.actions().mouseMove(el)
            .perform();
        await driver.sleep(500); // Wait for thermometer menu to come up
        await clickXpath('//button[@aria-label="Paint"]');
        await clickText('costume3', scope.costumesTab);
        // Check that the menu has been updated
        await clickText('Code');
        await clickText('costume3', scope.blocksTab);
    });

    // Skipped because it was flakey on travis, but seems to run locally ok
    test.skip('Adding a sound DOES update the default sound name in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Sounds');
        await clickXpath('//button[@aria-label="Choose a Sound"]');
        await clickText('A Bass', scope.modal); // Should close the modal
        await clickText('Code');
        await clickText('Sound', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('A\u00A0Bass', scope.blocksTab); // Need   for block text
    });

    // Regression test for switching between editor/player causing toolbox to stop updating
    test('"See inside" after being on project page re-initializing variables', async () => {
        const playerUri = path.resolve(__dirname, '../../build/player.html');
        await loadUri(playerUri);
        await clickText('See inside');
        await clickText('Variables');
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('my\u00A0variable');

        await clickText('See Project Page');
        await clickText('See inside');

        await clickText('Variables');
        await driver.sleep(500); // Wait for scroll to finish
        await clickText('my\u00A0variable');
    });

    // Regression test for switching editor tabs causing toolbox to stop updating
    test('Creating variables after adding extensions updates the toolbox', async () => {
        await loadUri(uri);
        await clickText('Costumes');
        await clickText('Code');
        await clickText('Variables', scope.blocksTab);
        await driver.sleep(500); // Wait for scroll
        await clickText('Make a List');
        const el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');
        await clickText('list1', scope.blocksTab);
    });
});
<|endoftext|>


<commit_before>(function() {
	var svgshape = document.getElementById( 'notification-shapeone' ),
		s = Snap( svgshape.querySelector( 'svg' ) ),
		path = s.select( 'path' ),
		pathConfig = {
			from : path.attr( 'd' ),
			to : svgshape.getAttribute( 'data-path-to' )
	},
	bttn = document.getElementById( 'notification-triggerone' );
	// make sure..
	bttn.disabled = false;

	bttn.addEventListener( 'click', function() {
	// simulate loading (for demo purposes only)
		classie.add( bttn, 'active' );
		setTimeout( function() {

			path.animate( { 'path' : pathConfig.to }, 300, mina.easeinout );

			classie.remove( bttn, 'active' );

			// create the notification
			var notification = new NotificationFx({
				wrapper : svgshape,
				message : 'SONGS
OF THE WEEK
Oct 3rd, 2015',
				layout : 'other',
				effect : 'cornerexpand',
				type : 'notice', // notice, warning or error
				onClose : function() {
					bttn.disabled = false;
					setTimeout(function() {
						path.animate( { 'path' : pathConfig.from }, 300, mina.easeinout );
					}, 200 );
				}
			});

			// show the notification
			notification.show();

		}, 0 );

		// disable the button (for demo purposes only)
		this.disabled = true;
	} );
})();<commit_msg>update<commit_after>(function() {
	var svgshape = document.getElementById( 'notification-shapeone' ),
		s = Snap( svgshape.querySelector( 'svg' ) ),
		path = s.select( 'path' ),
		pathConfig = {
			from : path.attr( 'd' ),
			to : svgshape.getAttribute( 'data-path-to' )
	},
	bttn = document.getElementById( 'notification-triggerone' );
	// make sure..
	bttn.disabled = false;

	bttn.addEventListener( 'click', function() {
	// simulate loading (for demo purposes only)
		classie.add( bttn, 'active' );
		setTimeout( function() {

			path.animate( { 'path' : pathConfig.to }, 300, mina.easeinout );

			classie.remove( bttn, 'active' );

			// create the notification
			var notification = new NotificationFx({
				wrapper : svgshape,
				message : 'SONGS
OF THE WEEK
Oct 4th, 2015',
				layout : 'other',
				effect : 'cornerexpand',
				type : 'notice', // notice, warning or error
				onClose : function() {
					bttn.disabled = false;
					setTimeout(function() {
						path.animate( { 'path' : pathConfig.from }, 300, mina.easeinout );
					}, 200 );
				}
			});

			// show the notification
			notification.show();

		}, 0 );

		// disable the button (for demo purposes only)
		this.disabled = true;
	} );
})();<|endoftext|>