<commit_before>'use strict';

module.exports = function(gulp, config) {
    var ngConfig     = require('gulp-ng-config');

    var configAngular = config.angular;

    /**
     * AngularJS configuration generator for a module of constants
     *
     * @see https://www.npmjs.com/package/gulp-ng-config
     */
    gulp.task('angular:conf:production', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('production');
    });
    gulp.task('angular:conf:staging', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('staging');
    });
    gulp.task('angular:conf:local', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('local');
    });

    function configGenerator(environment) {
        return gulp.src(configAngular.configSrc)
            .pipe(ngConfig(configAngular.conf.moduleName, {
                wrap: true,
                createModule: configAngular.conf.createModule,
                environment: environment
            }))
            .pipe(gulp.dest(configAngular.dest))
        ;
    }
};
<commit_msg>fix param name in angular:conf<commit_after>'use strict';

module.exports = function(gulp, config) {
    var ngConfig     = require('gulp-ng-config');

    var configAngular = config.angular;

    /**
     * AngularJS configuration generator for a module of constants
     *
     * @see https://www.npmjs.com/package/gulp-ng-config
     */
    gulp.task('angular:conf:production', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('production');
    });
    gulp.task('angular:conf:staging', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('staging');
    });
    gulp.task('angular:conf:local', 'AngularJS configuration generator for a module of constants.', function () {
        return configGenerator('local');
    });

    function configGenerator(environment) {
        return gulp.src(configAngular.conf.src)
            .pipe(ngConfig(configAngular.conf.moduleName, {
                wrap: true,
                createModule: configAngular.conf.createModule,
                environment: environment
            }))
            .pipe(gulp.dest(configAngular.dest))
        ;
    }
};
<|endoftext|>


<commit_before>import Route from '@ember/routing/route';
import { tracked } from '@glimmer/tracking';
import { later, cancel } from '@ember/runloop';
import { Promise } from 'rsvp';
import { set as emSet } from '@ember/object';
import { inject as service } from '@ember/service';
import { isArray } from '@ember/array';
import { debug } from '@ember/debug';
import fetch from 'fetch';

class Content {
    @tracked user = null;
    @tracked page = null;
    @tracked ping = null;
    @tracked refresh = null;
    @tracked structure = null;
    @tracked rtoken = null;
    @tracked status = null;
    @tracked popup = null;
    @tracked tabs = [];
    @tracked navEntries = [];
    @tracked error = null;
    @tracked loadingBanner = null;
}

/**
 * @module
 */
export default class OpenXpkiRoute extends Route {
    @service('oxi-config') oxiConfig;
    @service('oxi-locale') oxiLocale;
    @service('intl') intl;

    // Reserved Ember property "queryParams"
    // https://api.emberjs.com/ember/3.17/classes/Route/properties/queryParams?anchor=queryParams
    queryParams = {
        // refreshModel==true causes an "in-place" transition, so the model
        // hooks for this route (and any child routes) will re-fire
        startat:  { refreshModel: true },
        limit:    { refreshModel: true },
        force:    { refreshModel: true },
    };
    needReboot = ["login", "logout", "login!logout", "welcome"];

    content = new Content();
    /*
    Custom handlers for exceptions returned by the server (HTTP status codes):
        [
            {
                status_code: [ 403, ... ], // array or string
                redirect: "https://...",
            },
            { ... }
        ]
    */
    serverExceptions = [];

    // Reserved Ember function
    beforeModel(transition) {
        let queryParams = transition.to.queryParams;
        let modelId = transition.to.params.model_id;
        debug("openxpki/route - beforeModel: model_id = " + modelId);

        // "force" is only evaluated above using "refreshModel: true"
        if (queryParams.force) {
            delete queryParams.force;
        }

        let structureIfNeeded; // chain of ajax calls via Promises

        /*
         * load base page structure first first time or for special pages ("needReboot")
         */
        if (!this.content.navEntries.length || this.needReboot.indexOf(modelId) >= 0) {
            // don't send request yet, only create a lambda via arrow function expression
            structureIfNeeded = () => {
                return this.sendAjax({
                    page: "bootstrap!structure",
                    baseurl: window.location.pathname,
                });
            };
        }
        else {
            structureIfNeeded = () => { Promise.resolve() };
        }

        /*
         * load requested page part
         */
        let request = {
            page: modelId
        };
        if (queryParams.limit) { request.limit = queryParams.limit }
        if (queryParams.startat) { request.startat = queryParams.startat }

        // load as top content if 'modelId' is part of navigation or in 'needReboot' list
        let flatList = this.content.navEntries.reduce((p, n) => p.concat(n, n.entries || []), []);
        if (flatList.findBy("key", modelId) || this.needReboot.indexOf(modelId) >= 0) {
            request.target = "top";
        }
        return this.oxiConfig.ready // localconfig.js might change rootURL, so first thing is to query it
            .then( () => structureIfNeeded() )
            .then( () => this.sendAjax(request) );
    }

    // Reserved Ember function
    model(params/*, transition*/) {
        this.content.page = params.model_id; this._updateNavEntryActiveState(this.content);
        return this.content;
    }

    /**
     * Send AJAX request quietly, i.e. without showing the "loading" banner or
     * dimming the page.
     *
     * @param {hash} request - Request data
     * @return {Promise} Promise receiving the JSON document on success or `{}` on error
     */
    sendAjaxQuiet(request) {
        return this.sendAjax(request, true);
    }

    /**
     * Send AJAX request.
     *
     * @param {hash} request - Request data
     * @param {bool} isQuiet - set to `true` to hide optical hints (loading banner)
     * @return {Promise} Promise receiving the JSON document on success or `{}` on error
     */
    sendAjax(request, isQuiet = false) {
        if (! isQuiet) this._setLoadingBanner(this.intl.t('site.banner.loading'));
        debug("openxpki/route - sendAjax: " + ['page','action'].map(p=>request[p]?`${p} = ${request[p]}`:null).filter(e=>e!==null).join(", "));

        if (this.content.refresh) {
            cancel(this.content.refresh);
            this.content.refresh = null;
        }

        let realTarget = this._resolveTarget(request.target); // has to be done before "this.content.popup = null"

        let data = {
            ...request,
            '_': new Date().getTime(),
        }
        let url = this.oxiConfig.backendUrl;
        let fetchParams = {
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
                'X-OPENXPKI-Client': '1',
            },
        };

        // POST
        if (request.action) {
            fetchParams.method = "POST";
            fetchParams.headers['Content-Type'] = 'application/json';
            fetchParams.body = JSON.stringify({
                ...data,
                _rtoken: this.content.rtoken,
            });
        }
        // GET
        else {
            fetchParams.method = "GET";
            fetchParams.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
            url += '?' + this._toUrlParams(data);
        }

        return fetch(url, fetchParams)
        .then(response => {
            // If OK: unpack JSON data
            if (response.ok) {
                return response.json();
            }
            // Handle non-2xx HTTP status codes
            else {
                this._handleServerException(response.status);
                return null;
            }
        })
        // Network error, thrown by fetch() itself
        .catch(error => {
            this._setLoadingBanner(null);
            this.content.error = this.intl.t('error_popup.message.network', { reason: error.message });
            return null;
        })
        .then(doc => {
            // Errors occured and handlers above returned null
            if (!doc) return {};

            // Successful request
            this.content.status = doc.status;
            this.content.popup = null;

            // Ping
            if (doc.ping) {
                debug("openxpki/route - sendAjax response: \"ping\" " + doc.ping);
                if (this.content.ping) { cancel(this.content.ping) }
                this._ping(doc.ping);
            }

            // Auto refresh
            if (doc.refresh) {
                debug("openxpki/route - sendAjax response: \"refresh\" " + doc.refresh.href + ", " + doc.refresh.timeout);
                this._autoRefreshOnce(doc.refresh.href, doc.refresh.timeout);
            }

            // Redirect
            if (doc.goto) {
                debug("openxpki/route - sendAjax response: \"goto\" " + doc.goto);
                this._redirect(doc.goto, doc.type, doc.loading_banner);
                return doc;
            }

            // "Bootstrapping" - menu, user info, locale
            if (doc.structure) {
                debug("openxpki/route - sendAjax response: \"structure\"");
                this.content.navEntries = doc.structure; this._updateNavEntryActiveState();
                this.content.user = doc.user;
                this.content.rtoken = doc.rtoken;

                // set locale
                if (doc.language) this.oxiLocale.locale = doc.language;

                // set custom exception handling
                if (doc.on_exception) this.serverExceptions = doc.on_exception;
            }
            // Page contents
            else {
                if (doc.page && doc.main) {
                    debug("openxpki/route - sendAjax response: \"page\" and \"main\"");
                    this._setPageContent(realTarget, doc.page, doc.main, doc.right);
                }
            }

            this._setLoadingBanner(null);
            return doc; // calling code might handle other data
        })
        // Client side error
        .catch(error => {
            this._setLoadingBanner(null);
            this.content.error = this.intl.t('error_popup.message.client', { reason: error });
            return null;
        })
    }

    /*
     * Convert plain (not nested!) key => value hash into URL parameter string.
     * Source: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/web.url-search-params.js
     *
     * TODO: Replace with...
     *
     *     _toUrlParams(data) {
     *         let params = new URLSearchParams();
     *         Object.keys(data).forEach(k => params.set(k, data[k] ?? ''));
     *         return params.toString();
     *     }
     *
     * ...once https://github.com/babel/ember-cli-babel/issues/395 is fixed and we can
     * set up @babel/preset-env to use core-js version 3.x
     */
    _toUrlParams(entries) {
        let result = [];
        let URLPARAM_FIND = /[!'()~]|%20/g;
        let URLPARAM_REPLACE = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+' };
        let serialize = v => encodeURIComponent(v ?? '').replace(URLPARAM_FIND, match => URLPARAM_REPLACE[match]);

        Object.keys(entries).forEach(k => result.push(serialize(k) + '=' + serialize(entries[k])));
        return result.join('&');
    }

    _resolveTarget(requestTarget) {
        let target = requestTarget || 'self';
        // Pseudo-target "self" is transformed so new content will be shown in the
        // currently active place: a modal popup, an active tab or on top (i.e. single hidden tab)
        if (target === 'self') {
            if (this.content.popup) { target = 'popup' }
            else if (this.content.tabs.length > 1) { target = 'active' }
            else { target = 'top' }
        }
        if (target === 'modal') target = 'popup'; // FIXME remove support for legacy target 'modal'
        return target;
    }

    // Sets the loading state, i.e. dims the page and shows a banner with the
    // given message.
    // If 'message' is set to null, the banner will be hidden.
    _setLoadingBanner(message) {
        // note that we cannot use the Ember "loading" event as this would only
        // trigger on route changes, but not if we do sendAjax()
        if (message) {
            // remove focus from button to prevent user from doing another
            // submit by hitting enter
            document.activeElement.blur();
        }
        this.content.loadingBanner = message;
    }

    _ping(href, timeout) {
        this.content.ping = later(this, () => {
            fetch(href, {
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    'X-OPENXPKI-Client': '1',
                },
            })
            .catch(error => {
                /* eslint-disable-next-line no-console */
                console.error(`Error loading ${href} (network error: ${error.name})`);
            });
            return this._ping(href, timeout);
        }, timeout);
    }

    _autoRefreshOnce(href, timeout) {
        this.content.refresh = later(this, function() {
            this.sendAjax({ page: href });
        }, timeout);
    }

    _redirect(url, type = 'internal', banner = this.intl.t('site.banner.redirecting')) {
        if (type == 'external' || /^(http|\/)/.test(url)) {
            this._setLoadingBanner(banner); // never hide banner as browser will open a new page
            window.location.href = url;
        }
        else {
            this.transitionTo("openxpki", url);
        }
    }

    // Apply custom exception handler for given status code if one was set up
    // (bootstrap parameter 'on_exception').
    _handleServerException(status_code) {
        debug(`Exception - handling server HTTP status code: ${status_code}`);
        // Check custom exception handlers
        for (let handler of this.serverExceptions) {
            let codes = isArray(handler.status_code) ? handler.status_code : [ handler.status_code ];
            if (codes.find(c => c == status_code)) {
                // Show message
                if (handler.message) {
                    this._setLoadingBanner(null);
                    this.content.error = handler.message;
                }
                // Redirect
                else if (handler.redirect) {
                    // we intentionally do NOT remove the loading banner here
                    debug(`Exception - redirecting to ${handler.redirect}`);
                    this._redirect(handler.redirect);
                }
                return;
            }
        }
        // Unhandled exception
        this._setLoadingBanner(null);
        this.content.error = this.intl.t('error_popup.message.server', { code: status_code });
    }

    _setPageContent(target, page, main, right) {
        let newTab = {
            active: true,
            page: page,
            main: main,
            right: right
        };

        // Mark the first form on screen: only the first one is allowed to focus
        // its first input field.
        let isFirst = true;
        for (const section of [...(newTab.main||[]), ...(newTab.right||[])]) {
            if (section.type === "form") {
                section.content.isFirstForm = isFirst;
                if (isFirst) isFirst = false;
            }
        }

        // Popup
        if (target === "popup") {
            this.content.popup = newTab;
        }
        // New tab
        else if (target === "tab") {
            let tabs = this.content.tabs;
            tabs.setEach("active", false);
            tabs.pushObject(newTab);
        }
        // Current tab
        else if (target === "active") {
            let tabs = this.content.tabs;
            let index = tabs.indexOf(tabs.findBy("active"));
            tabs.replace(index, 1, [newTab]); // top
        }
        // Set as only tab
        else {
            this.content.tabs = [newTab];
        }
    }

    _updateNavEntryActiveState() {
        let page = this.content.page;
        for (const entry of this.content.navEntries) {
            emSet(entry, "active", (entry.key === page));
            if (entry.entries) {
                entry.entries.setEach("active", false);
                let subEntry = entry.entries.findBy("key", page);
                if (subEntry) {
                    emSet(subEntry, "active", true);
                    emSet(entry, "active", true);
                }
            }
        }
        this.content.navEntries = this.content.navEntries; // trigger updates
    }
}<commit_msg>UI: log errors to console, too<commit_after>import Route from '@ember/routing/route';
import { tracked } from '@glimmer/tracking';
import { later, cancel } from '@ember/runloop';
import { Promise } from 'rsvp';
import { set as emSet } from '@ember/object';
import { inject as service } from '@ember/service';
import { isArray } from '@ember/array';
import { debug } from '@ember/debug';
import fetch from 'fetch';

class Content {
    @tracked user = null;
    @tracked page = null;
    @tracked ping = null;
    @tracked refresh = null;
    @tracked structure = null;
    @tracked rtoken = null;
    @tracked status = null;
    @tracked popup = null;
    @tracked tabs = [];
    @tracked navEntries = [];
    @tracked error = null;
    @tracked loadingBanner = null;
}

/**
 * @module
 */
export default class OpenXpkiRoute extends Route {
    @service('oxi-config') oxiConfig;
    @service('oxi-locale') oxiLocale;
    @service('intl') intl;

    // Reserved Ember property "queryParams"
    // https://api.emberjs.com/ember/3.17/classes/Route/properties/queryParams?anchor=queryParams
    queryParams = {
        // refreshModel==true causes an "in-place" transition, so the model
        // hooks for this route (and any child routes) will re-fire
        startat:  { refreshModel: true },
        limit:    { refreshModel: true },
        force:    { refreshModel: true },
    };
    needReboot = ["login", "logout", "login!logout", "welcome"];

    content = new Content();
    /*
    Custom handlers for exceptions returned by the server (HTTP status codes):
        [
            {
                status_code: [ 403, ... ], // array or string
                redirect: "https://...",
            },
            { ... }
        ]
    */
    serverExceptions = [];

    // Reserved Ember function
    beforeModel(transition) {
        let queryParams = transition.to.queryParams;
        let modelId = transition.to.params.model_id;
        debug("openxpki/route - beforeModel: model_id = " + modelId);

        // "force" is only evaluated above using "refreshModel: true"
        if (queryParams.force) {
            delete queryParams.force;
        }

        let structureIfNeeded; // chain of ajax calls via Promises

        /*
         * load base page structure first first time or for special pages ("needReboot")
         */
        if (!this.content.navEntries.length || this.needReboot.indexOf(modelId) >= 0) {
            // don't send request yet, only create a lambda via arrow function expression
            structureIfNeeded = () => {
                return this.sendAjax({
                    page: "bootstrap!structure",
                    baseurl: window.location.pathname,
                });
            };
        }
        else {
            structureIfNeeded = () => { Promise.resolve() };
        }

        /*
         * load requested page part
         */
        let request = {
            page: modelId
        };
        if (queryParams.limit) { request.limit = queryParams.limit }
        if (queryParams.startat) { request.startat = queryParams.startat }

        // load as top content if 'modelId' is part of navigation or in 'needReboot' list
        let flatList = this.content.navEntries.reduce((p, n) => p.concat(n, n.entries || []), []);
        if (flatList.findBy("key", modelId) || this.needReboot.indexOf(modelId) >= 0) {
            request.target = "top";
        }
        return this.oxiConfig.ready // localconfig.js might change rootURL, so first thing is to query it
            .then( () => structureIfNeeded() )
            .then( () => this.sendAjax(request) );
    }

    // Reserved Ember function
    model(params/*, transition*/) {
        this.content.page = params.model_id; this._updateNavEntryActiveState(this.content);
        return this.content;
    }

    /**
     * Send AJAX request quietly, i.e. without showing the "loading" banner or
     * dimming the page.
     *
     * @param {hash} request - Request data
     * @return {Promise} Promise receiving the JSON document on success or `{}` on error
     */
    sendAjaxQuiet(request) {
        return this.sendAjax(request, true);
    }

    /**
     * Send AJAX request.
     *
     * @param {hash} request - Request data
     * @param {bool} isQuiet - set to `true` to hide optical hints (loading banner)
     * @return {Promise} Promise receiving the JSON document on success or `{}` on error
     */
    sendAjax(request, isQuiet = false) {
        if (! isQuiet) this._setLoadingBanner(this.intl.t('site.banner.loading'));
        debug("openxpki/route - sendAjax: " + ['page','action'].map(p=>request[p]?`${p} = ${request[p]}`:null).filter(e=>e!==null).join(", "));

        if (this.content.refresh) {
            cancel(this.content.refresh);
            this.content.refresh = null;
        }

        let realTarget = this._resolveTarget(request.target); // has to be done before "this.content.popup = null"

        let data = {
            ...request,
            '_': new Date().getTime(),
        }
        let url = this.oxiConfig.backendUrl;
        let fetchParams = {
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
                'X-OPENXPKI-Client': '1',
            },
        };

        // POST
        if (request.action) {
            fetchParams.method = "POST";
            fetchParams.headers['Content-Type'] = 'application/json';
            fetchParams.body = JSON.stringify({
                ...data,
                _rtoken: this.content.rtoken,
            });
        }
        // GET
        else {
            fetchParams.method = "GET";
            fetchParams.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
            url += '?' + this._toUrlParams(data);
        }

        return fetch(url, fetchParams)
        .then(response => {
            // If OK: unpack JSON data
            if (response.ok) {
                return response.json();
            }
            // Handle non-2xx HTTP status codes
            else {
                this._handleServerException(response.status);
                return null;
            }
        })
        // Network error, thrown by fetch() itself
        .catch(error => {
            this._setLoadingBanner(null);
            console.error('The server connection seems to be lost', error);
            this.content.error = this.intl.t('error_popup.message.network', { reason: error.message });
            return null;
        })
        .then(doc => {
            // Errors occured and handlers above returned null
            if (!doc) return {};

            // Successful request
            this.content.status = doc.status;
            this.content.popup = null;

            // Ping
            if (doc.ping) {
                debug("openxpki/route - sendAjax response: \"ping\" " + doc.ping);
                if (this.content.ping) { cancel(this.content.ping) }
                this._ping(doc.ping);
            }

            // Auto refresh
            if (doc.refresh) {
                debug("openxpki/route - sendAjax response: \"refresh\" " + doc.refresh.href + ", " + doc.refresh.timeout);
                this._autoRefreshOnce(doc.refresh.href, doc.refresh.timeout);
            }

            // Redirect
            if (doc.goto) {
                debug("openxpki/route - sendAjax response: \"goto\" " + doc.goto);
                this._redirect(doc.goto, doc.type, doc.loading_banner);
                return doc;
            }

            // "Bootstrapping" - menu, user info, locale
            if (doc.structure) {
                debug("openxpki/route - sendAjax response: \"structure\"");
                this.content.navEntries = doc.structure; this._updateNavEntryActiveState();
                this.content.user = doc.user;
                this.content.rtoken = doc.rtoken;

                // set locale
                if (doc.language) this.oxiLocale.locale = doc.language;

                // set custom exception handling
                if (doc.on_exception) this.serverExceptions = doc.on_exception;
            }
            // Page contents
            else {
                if (doc.page && doc.main) {
                    debug("openxpki/route - sendAjax response: \"page\" and \"main\"");
                    this._setPageContent(realTarget, doc.page, doc.main, doc.right);
                }
            }

            this._setLoadingBanner(null);
            return doc; // calling code might handle other data
        })
        // Client side error
        .catch(error => {
            this._setLoadingBanner(null);
            console.error('There was an error while processing the data', error);
            this.content.error = this.intl.t('error_popup.message.client', { reason: error });
            return null;
        })
    }

    /*
     * Convert plain (not nested!) key => value hash into URL parameter string.
     * Source: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/web.url-search-params.js
     *
     * TODO: Replace with...
     *
     *     _toUrlParams(data) {
     *         let params = new URLSearchParams();
     *         Object.keys(data).forEach(k => params.set(k, data[k] ?? ''));
     *         return params.toString();
     *     }
     *
     * ...once https://github.com/babel/ember-cli-babel/issues/395 is fixed and we can
     * set up @babel/preset-env to use core-js version 3.x
     */
    _toUrlParams(entries) {
        let result = [];
        let URLPARAM_FIND = /[!'()~]|%20/g;
        let URLPARAM_REPLACE = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+' };
        let serialize = v => encodeURIComponent(v ?? '').replace(URLPARAM_FIND, match => URLPARAM_REPLACE[match]);

        Object.keys(entries).forEach(k => result.push(serialize(k) + '=' + serialize(entries[k])));
        return result.join('&');
    }

    _resolveTarget(requestTarget) {
        let target = requestTarget || 'self';
        // Pseudo-target "self" is transformed so new content will be shown in the
        // currently active place: a modal popup, an active tab or on top (i.e. single hidden tab)
        if (target === 'self') {
            if (this.content.popup) { target = 'popup' }
            else if (this.content.tabs.length > 1) { target = 'active' }
            else { target = 'top' }
        }
        if (target === 'modal') target = 'popup'; // FIXME remove support for legacy target 'modal'
        return target;
    }

    // Sets the loading state, i.e. dims the page and shows a banner with the
    // given message.
    // If 'message' is set to null, the banner will be hidden.
    _setLoadingBanner(message) {
        // note that we cannot use the Ember "loading" event as this would only
        // trigger on route changes, but not if we do sendAjax()
        if (message) {
            // remove focus from button to prevent user from doing another
            // submit by hitting enter
            document.activeElement.blur();
        }
        this.content.loadingBanner = message;
    }

    _ping(href, timeout) {
        this.content.ping = later(this, () => {
            fetch(href, {
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    'X-OPENXPKI-Client': '1',
                },
            })
            .catch(error => {
                /* eslint-disable-next-line no-console */
                console.error(`Error loading ${href} (network error: ${error.name})`);
            });
            return this._ping(href, timeout);
        }, timeout);
    }

    _autoRefreshOnce(href, timeout) {
        this.content.refresh = later(this, function() {
            this.sendAjax({ page: href });
        }, timeout);
    }

    _redirect(url, type = 'internal', banner = this.intl.t('site.banner.redirecting')) {
        if (type == 'external' || /^(http|\/)/.test(url)) {
            this._setLoadingBanner(banner); // never hide banner as browser will open a new page
            window.location.href = url;
        }
        else {
            this.transitionTo("openxpki", url);
        }
    }

    // Apply custom exception handler for given status code if one was set up
    // (bootstrap parameter 'on_exception').
    _handleServerException(status_code) {
        debug(`Exception - handling server HTTP status code: ${status_code}`);
        // Check custom exception handlers
        for (let handler of this.serverExceptions) {
            let codes = isArray(handler.status_code) ? handler.status_code : [ handler.status_code ];
            if (codes.find(c => c == status_code)) {
                // Show message
                if (handler.message) {
                    this._setLoadingBanner(null);
                    console.error(handler.message);
                    this.content.error = handler.message;
                }
                // Redirect
                else if (handler.redirect) {
                    // we intentionally do NOT remove the loading banner here
                    debug(`Exception - redirecting to ${handler.redirect}`);
                    this._redirect(handler.redirect);
                }
                return;
            }
        }
        // Unhandled exception
        this._setLoadingBanner(null);
        console.error(`Server did not return expected data: ${status_code}`);
        this.content.error = this.intl.t('error_popup.message.server', { code: status_code });
    }

    _setPageContent(target, page, main, right) {
        let newTab = {
            active: true,
            page: page,
            main: main,
            right: right
        };

        // Mark the first form on screen: only the first one is allowed to focus
        // its first input field.
        let isFirst = true;
        for (const section of [...(newTab.main||[]), ...(newTab.right||[])]) {
            if (section.type === "form") {
                section.content.isFirstForm = isFirst;
                if (isFirst) isFirst = false;
            }
        }

        // Popup
        if (target === "popup") {
            this.content.popup = newTab;
        }
        // New tab
        else if (target === "tab") {
            let tabs = this.content.tabs;
            tabs.setEach("active", false);
            tabs.pushObject(newTab);
        }
        // Current tab
        else if (target === "active") {
            let tabs = this.content.tabs;
            let index = tabs.indexOf(tabs.findBy("active"));
            tabs.replace(index, 1, [newTab]); // top
        }
        // Set as only tab
        else {
            this.content.tabs = [newTab];
        }
    }

    _updateNavEntryActiveState() {
        let page = this.content.page;
        for (const entry of this.content.navEntries) {
            emSet(entry, "active", (entry.key === page));
            if (entry.entries) {
                entry.entries.setEach("active", false);
                let subEntry = entry.entries.findBy("key", page);
                if (subEntry) {
                    emSet(subEntry, "active", true);
                    emSet(entry, "active", true);
                }
            }
        }
        this.content.navEntries = this.content.navEntries; // trigger updates
    }
}<|endoftext|>


<commit_before>/*
 * grunt-static-inline
 * https://github.com/dayvson/grunt-static-inline
 *
 * Copyright (c) 2013 Maxwell Da Silva
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {
  'use strict';
  var datauri = require('datauri');
  var path = require('path');
  var description = 'A grunt plugin to replace url from static files such as img,js,css an put inline in a template';
  var resolveFilePath = function(templatePath, src, basepath){
    var srcPath;
    if(!grunt.file.isPathAbsolute(src)){
      srcPath = path.resolve(path.dirname(templatePath), src);
    }else if(grunt.file.isPathAbsolute(src) && basepath){
      srcPath = path.resolve(basepath + src);
    }
    return srcPath;
  };

  var baseTAGReplace = function(templatePath, tag, src, basepath){
    var result = '';
    var srcPath = resolveFilePath(templatePath, src, basepath);
    if(srcPath){
      result = '<TAG>CONTENT</TAG>'
                 .replace(/CONTENT/g, grunt.file.read(srcPath).trim())
                 .replace(/TAG/g, tag);
    }
    return result;
  };

  var findReplaceScript = function(templatePath, content, basepath){
    return content.replace(/<script[^<]src=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?><\/script>/g, function(match, src){
      return baseTAGReplace(templatePath, "script", src, basepath);
    });
  };

  var findReplaceLink = function(templatePath, content, basepath){
    return content.replace(/<link[^<]*href=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?\s*>/g, function(match, src){
      return baseTAGReplace(templatePath, "style", src, basepath);
    });
  };

  var findReplaceImg = function(templatePath, content, basepath){
    return content.replace(/<img[^<]*src=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?\s*>/g, function(match, src){
        var srcPath = resolveFilePath(templatePath, src, basepath);
        if(srcPath){
          return match.replace(/inline=['"]true['"]/g, '').replace(src, datauri(srcPath));
        }
        return '';
    });
  };

  var findReplaceVariables = function(options, content){
    Object.keys(options.vars).forEach(function(key){
      var re = new RegExp(options.prefix + key + options.suffix, "g");
      content = content.replace(re, options.vars[key]);
    });
    return content;
  };

  var findAndReplace = function(options, templatePath, content){
    var result = findReplaceLink(templatePath, content, options.basepath);
    result = findReplaceScript(templatePath, result, options.basepath);
    result = findReplaceImg(templatePath, result, options.basepath);
    return findReplaceVariables(options, result);
  };

  var staticInlineTask = function(){
    var options = this.options({
        prefix: '@{',
        suffix: '}@',
        vars: {}
      });

    this.files.forEach(function(f){
      var srcFile = f.orig.src[0];
      var destFile = f.dest;
      var content = grunt.file.read(srcFile);
      content = findAndReplace(options, srcFile, content);
      grunt.file.write(destFile, content);
    });
  };
  grunt.registerMultiTask('staticinline', description, staticInlineTask);

};
<commit_msg>Build tags using string concatenation<commit_after>/*
 * grunt-static-inline
 * https://github.com/dayvson/grunt-static-inline
 *
 * Copyright (c) 2013 Maxwell Da Silva
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {
  'use strict';
  var datauri = require('datauri');
  var path = require('path');
  var description = 'A grunt plugin to replace url from static files such as img,js,css an put inline in a template';
  var resolveFilePath = function(templatePath, src, basepath){
    var srcPath;
    if(!grunt.file.isPathAbsolute(src)){
      srcPath = path.resolve(path.dirname(templatePath), src);
    }else if(grunt.file.isPathAbsolute(src) && basepath){
      srcPath = path.resolve(basepath + src);
    }
    return srcPath;
  };

  var baseTAGReplace = function(templatePath, tag, src, basepath){
    var result = '';
    var srcPath = resolveFilePath(templatePath, src, basepath);
    if(srcPath){
      result = '<' + tag + '>' +  grunt.file.read(srcPath).trim() + '</' + tag + '>';
    }
    return result;
  };

  var findReplaceScript = function(templatePath, content, basepath){
    return content.replace(/<script[^<]src=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?><\/script>/g, function(match, src){
      return baseTAGReplace(templatePath, "script", src, basepath);
    });
  };

  var findReplaceLink = function(templatePath, content, basepath){
    return content.replace(/<link[^<]*href=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?\s*>/g, function(match, src){
      return baseTAGReplace(templatePath, "style", src, basepath);
    });
  };

  var findReplaceImg = function(templatePath, content, basepath){
    return content.replace(/<img[^<]*src=['"]([^'"]+)['"][^<]*inline=['"]true['"][^<]*\/?\s*>/g, function(match, src){
        var srcPath = resolveFilePath(templatePath, src, basepath);
        if(srcPath){
          return match.replace(/inline=['"]true['"]/g, '').replace(src, datauri(srcPath));
        }
        return '';
    });
  };

  var findReplaceVariables = function(options, content){
    Object.keys(options.vars).forEach(function(key){
      var re = new RegExp(options.prefix + key + options.suffix, "g");
      content = content.replace(re, options.vars[key]);
    });
    return content;
  };

  var findAndReplace = function(options, templatePath, content){
    var result = findReplaceLink(templatePath, content, options.basepath);
    result = findReplaceScript(templatePath, result, options.basepath);
    result = findReplaceImg(templatePath, result, options.basepath);
    return findReplaceVariables(options, result);
  };

  var staticInlineTask = function(){
    var options = this.options({
        prefix: '@{',
        suffix: '}@',
        vars: {}
      });

    this.files.forEach(function(f){
      var srcFile = f.orig.src[0];
      var destFile = f.dest;
      var content = grunt.file.read(srcFile);
      content = findAndReplace(options, srcFile, content);
      grunt.file.write(destFile, content);
    });
  };
  grunt.registerMultiTask('staticinline', description, staticInlineTask);

};
<|endoftext|>


<commit_before>var gulp = require('gulp');
var browserify = require('browserify');
var buffer = require('vinyl-buffer');
var source = require('vinyl-source-stream');
var uglify = require('gulp-uglify');
var handleErrors = require('./error');
var sourcemaps = require('gulp-sourcemaps');

module.exports = function () {
    var dest = gulp.dest('./build/');
    var browserified = browserify('./lib/index.js', {
        insertGlobals: false,
        transform: ['partialify', 'bulkify']
    });

    return browserified
        .bundle()
        .on('error', handleErrors.onError)
        .pipe(source('index.js'))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .pipe(sourcemaps.write())
        .pipe(dest);
};
<commit_msg>srcmaps<commit_after>var gulp = require('gulp');
var browserify = require('browserify');
var buffer = require('vinyl-buffer');
var source = require('vinyl-source-stream');
var uglify = require('gulp-uglify');
var handleErrors = require('./error');
var sourcemaps = require('gulp-sourcemaps');

module.exports = function () {
    var dest = gulp.dest('./build/');
    var browserified = browserify('./lib/index.js', {
        debug: true,
        insertGlobals: false,
        transform: ['partialify', 'bulkify']
    });

    return browserified
        .bundle()
        .on('error', handleErrors.onError)
        .pipe(source('index.js'))
        .pipe(buffer())
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .pipe(sourcemaps.write('./'))
        .pipe(dest);
};
<|endoftext|>


<commit_before>/**
  Frontend-starter

  @author Bartosz Sak

  https://github.com/implico/frontend-starter

  The MIT License (MIT)

  *******************
  Configuration file

*/

module.exports = function(dirs) {

  var fs        = require('fs'),
      minimist  = require('minimist');

  var config = {

    main: {
      isDev: null,
      isRestart: null
    },

    styles: {

      sourceMaps: true,
      sourceMapsRoot: '/src/styles/',

      autoprefixer: {
        browsers: ['> 1%', 'last 3 versions', 'IE >= 9']
      },

      sass: {
      },

      cssnano: {
        safe: true
      },

      inject: {
        src: true,    //function must return: a stream (if cancels) or a glob array passed to the src
        sourceMapsInit: true,
        sassGlob: true,
        sass: true,
        autoprefixer: true,
        optimizeMediaQueries: true, //group-css-media-queries
        optimize: true,             //cssnano
        sourceMapsWrite: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        sass: {
          outputStyle: 'expanded'
        },

        inject: {
          optimizeMediaQueries: false,
          optimize: false
        }
      },

      prod: {
        sourceMaps: false,
      },

      //compatibility fallback, to be removed
      common: {}
    },

    fonts: {

      inject: {
        src: true,    //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,  //gulp-changed plugin
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
      },

      prod: {
      }
    },

    sprites: {

      items: [
        //you can add more items (dirs), simply add an element
        {
          imgSource: dirs.src.images + 'sprites/',
          imgDest: dirs.dist.images,
          //options passed to the plugin
          options: {
            imgName: 'sprites.png',
            imgPath: '../img/sprites.png',
            cssName: '_sprites.scss',
            cssSpritesheetName: 'spritesheet',
            cssVarMap: function (sprite) {
              sprite.name = /*'sprite_' + */sprite.name;
            }
          }
        }
      ],

      inject: {
        init: true,
        imgSrc: true,
        imgOptimize: true,
        imgDest: true,
        cssSrc: true,
        cssDest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          imgOptimize: false
        }
      },

      prod: {
      }
    },

    js: {
      sourceMaps: true,
      sourceMapsRoot: '/src/',
      concatVendorApp: true,    //if true, app.js and vendor.js are merged into app.js
      babel: {
        app: true,
        vendor: false,
        options: {
          presets: ['es2015-without-strict'],
          plugins: []
        }
      },

      comps: {
        main: {
          filename: 'app',      //set to false to not produce any output file (for sub-comps); if not set, defaults to comp id
                                //.js extension added automatically unless the name contains a dot
          //filenameVendor: 'vendor',//if concatVendorApp is false, specifies the vendor filename

          bower: ['**/*.js'],   //set only name of the package
          vendor: ['**/*.js'],  //path relative to the appropriate directory
          app: ['**/*.js'],     //path relative to the appropriate directory

          //set prioritized paths
          priority: {
            vendor: [],
            app: []
          },

          //set other comp ids to include
          dependencies: [],

          //set comps to exclude all loaded scripts in other comps, e.g.
          //excludeIn: ['comp1', 'comp2'] //excluded in selected comps
          //excludeIn: true   //excluded in all other comps
          //excludeIn: false  //no exclusion
          excludeIn: false,

          watch: true  //not needed, watch blocked only if false
        }
      },

      mainBowerFiles: {
        paths: {
          bowerDirectory: dirs.bower,
          bowerrc: dirs.app + '.bowerrc',
          bowerJson: dirs.app + 'bower.json'
        },
        overrides: {}
      },

      inject: {
        //receive stream and an object { comp, ...} (see the source injectorData for more)
        src: true,
        lint: false,
        lintFailAfterError: true,
        sourceMapsInit: true,
        babel: true,
        concat: true,
        sourceMapsWrite: true,
        minify: true,
        concatVendorApp: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          minify: false
        }
      },

      prod: {
        sourceMaps: false,

        jsHint: {
          enable: false
        }
      },

      //compatibility fallback, to be removed
      common: {
        comps: {
          main: {
            priority: {
              vendor: [],
              app: []
            }
          },
          html5shiv: {}
        }
      }
    },

    images: {
      imagemin: {
        optimizationLevel: 3,
        progressive: true,
        interlaced: true
      },

      inject: {
        src: true,      //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,    //gulp-changed plugin
        optimize: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          optimize: false
        }
      },

      prod: {

      },

      //compatibility fallback
      optimize: {}
    },

    views: {

      htmlmin: {
        collapseWhitespace: true
      },

      inject: {
        src: true,      //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,    //gulp-changed plugin
        optimize: true, //htmlmin
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          optimize: false
        }
      },

      prod: {
      },

      //compatibility fallback, to be removed
      common: {}
    },

    customDirs: {
      inject: {
        init: true,    //receives the object of custom directory definitions
        finish: true,  //receives an object: { streams, dirInfos } (array of streams, custom directory definitions)
        reload: true
      },
      dev: {
      },
      prod: {
      }
    },

    lint: {

      options: {
        extends: 'eslint:recommended',
        parserOptions: {
          ecmaVersion: 6
        },
        envs: ['browser', 'jquery'],
        globals: {
          //allowed global variables
        },
        rules: {
          //your ESLint rules here
        }
      },
      formatParams: [], //parameters passed to format function
      formatOnce: true, //use eslint.format() vs eslint.formatEach()

      inject: {
        src: true,
        lint: true,
        format: true,
        finish: true
      },

      dev: {
        options: {
          rules: {
            'no-console': 0
          }
        }
      },

      prod: {

      }
    },

    browserSync: {
      options: {
        //tip: set host to "[project-name].localhost" and set "open" to "external"
        host: 'localhost',
        open: 'local',
        port: 80,
        reloadOnRestart: true,
        server: {
          baseDir: dirs.dist.main
        }
      },

      inject: {
        init: true  //overrides default browserSync run, must return a promise or stream
      },

      dev: {
      },

      //only for prod:preview task
      prod: {
      },

      //compatibility fallback
      common: {
        options: {}
      }
    },

    clean: {

      inject: {
        //functions receive current glob array (incremental, containing previously added patterns) passed at the end to the del function
        init: true,
        cache: true,
        styles: true,
        sprites: true,
        fonts: true,
        js: true,
        images: true,
        views: true,
        custom: true,
        del: true,
        finish: true
      },

      dev: {
      },

      prod: {
        inject: {
          cache: false
        }
      }
    },

    watch: {
      inject: {
        init: true,
        styles: true,
        fonts: true,
        sprites: true,
        js: true,
        jsApp: true,
        jsVendor: true,
        images: true,
        views: true,
        finish: true
      },

      dev: {
      },

      prod: {
      }
    }
  }

  //parse cli args
  var optsConfig = {
    string: ['env'],
    boolean: ['prod', 'restart'],
    alias: {
      prod: 'p',
      restart: 'r'
    },
    default: {
      prod: process.env.NODE_ENV === 'production'
    }
  }
  var opts = minimist(process.argv.slice(2), optsConfig);
  config.main.isDev = !opts.prod && opts.env !== 'production';
  config.main.isRestart = opts.restart;

  //custom config file
  var noCustomFile = false;
  try {
    fs.accessSync(dirs.app + dirs.customConfig.configFile, fs.R_OK);
  }
  catch (ex) {
    noCustomFile = true;
    console.error('Frontend-starter warning: no custom config definitions file present (' + dirs.customConfig.configFile + ')');
  }

  //custom config file - require
  if (!noCustomFile) {
    require(dirs.app + dirs.customConfig.configFile)(config, dirs);
  }

  //compatibility falback
  if (config.clean.views === false) {
    //config.clean.inject.views = false;
    console.error('Frontend-starter error (deprecated): please rename "config.clean.views = false;" into "config.clean.inject.views = false;" in your ' + dirs.customConfig.configFile + ' file');
    process.exit(1);
  }

  return config;
}<commit_msg>cssnano safe opts fix<commit_after>/**
  Frontend-starter

  @author Bartosz Sak

  https://github.com/implico/frontend-starter

  The MIT License (MIT)

  *******************
  Configuration file

*/

module.exports = function(dirs) {

  var fs        = require('fs'),
      minimist  = require('minimist');

  var config = {

    main: {
      isDev: null,
      isRestart: null
    },

    styles: {

      sourceMaps: true,
      sourceMapsRoot: '/src/styles/',

      autoprefixer: {
        browsers: ['> 1%', 'last 3 versions', 'IE >= 9']
      },

      sass: {
      },

      cssnano: {
        safe: true,
        mergeIdents: false,
        discardUnused: false
      },

      inject: {
        src: true,    //function must return: a stream (if cancels) or a glob array passed to the src
        sourceMapsInit: true,
        sassGlob: true,
        sass: true,
        autoprefixer: true,
        optimizeMediaQueries: true, //group-css-media-queries
        optimize: true,             //cssnano
        sourceMapsWrite: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        sass: {
          outputStyle: 'expanded'
        },

        inject: {
          optimizeMediaQueries: false,
          optimize: false
        }
      },

      prod: {
        sourceMaps: false,
      },

      //compatibility fallback, to be removed
      common: {}
    },

    fonts: {

      inject: {
        src: true,    //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,  //gulp-changed plugin
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
      },

      prod: {
      }
    },

    sprites: {

      items: [
        //you can add more items (dirs), simply add an element
        {
          imgSource: dirs.src.images + 'sprites/',
          imgDest: dirs.dist.images,
          //options passed to the plugin
          options: {
            imgName: 'sprites.png',
            imgPath: '../img/sprites.png',
            cssName: '_sprites.scss',
            cssSpritesheetName: 'spritesheet',
            cssVarMap: function (sprite) {
              sprite.name = /*'sprite_' + */sprite.name;
            }
          }
        }
      ],

      inject: {
        init: true,
        imgSrc: true,
        imgOptimize: true,
        imgDest: true,
        cssSrc: true,
        cssDest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          imgOptimize: false
        }
      },

      prod: {
      }
    },

    js: {
      sourceMaps: true,
      sourceMapsRoot: '/src/',
      concatVendorApp: true,    //if true, app.js and vendor.js are merged into app.js
      babel: {
        app: true,
        vendor: false,
        options: {
          presets: ['es2015-without-strict'],
          plugins: []
        }
      },

      comps: {
        main: {
          filename: 'app',      //set to false to not produce any output file (for sub-comps); if not set, defaults to comp id
                                //.js extension added automatically unless the name contains a dot
          //filenameVendor: 'vendor',//if concatVendorApp is false, specifies the vendor filename

          bower: ['**/*.js'],   //set only name of the package
          vendor: ['**/*.js'],  //path relative to the appropriate directory
          app: ['**/*.js'],     //path relative to the appropriate directory

          //set prioritized paths
          priority: {
            vendor: [],
            app: []
          },

          //set other comp ids to include
          dependencies: [],

          //set comps to exclude all loaded scripts in other comps, e.g.
          //excludeIn: ['comp1', 'comp2'] //excluded in selected comps
          //excludeIn: true   //excluded in all other comps
          //excludeIn: false  //no exclusion
          excludeIn: false,

          watch: true  //not needed, watch blocked only if false
        }
      },

      mainBowerFiles: {
        paths: {
          bowerDirectory: dirs.bower,
          bowerrc: dirs.app + '.bowerrc',
          bowerJson: dirs.app + 'bower.json'
        },
        overrides: {}
      },

      inject: {
        //receive stream and an object { comp, ...} (see the source injectorData for more)
        src: true,
        lint: false,
        lintFailAfterError: true,
        sourceMapsInit: true,
        babel: true,
        concat: true,
        sourceMapsWrite: true,
        minify: true,
        concatVendorApp: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          minify: false
        }
      },

      prod: {
        sourceMaps: false,

        jsHint: {
          enable: false
        }
      },

      //compatibility fallback, to be removed
      common: {
        comps: {
          main: {
            priority: {
              vendor: [],
              app: []
            }
          },
          html5shiv: {}
        }
      }
    },

    images: {
      imagemin: {
        optimizationLevel: 3,
        progressive: true,
        interlaced: true
      },

      inject: {
        src: true,      //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,    //gulp-changed plugin
        optimize: true,
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          optimize: false
        }
      },

      prod: {

      },

      //compatibility fallback
      optimize: {}
    },

    views: {

      htmlmin: {
        collapseWhitespace: true
      },

      inject: {
        src: true,      //function must return: a stream (if cancels) or a glob array passed to the src
        limit: true,    //gulp-changed plugin
        optimize: true, //htmlmin
        dest: true,
        finish: true,
        reload: true
      },

      dev: {
        inject: {
          optimize: false
        }
      },

      prod: {
      },

      //compatibility fallback, to be removed
      common: {}
    },

    customDirs: {
      inject: {
        init: true,    //receives the object of custom directory definitions
        finish: true,  //receives an object: { streams, dirInfos } (array of streams, custom directory definitions)
        reload: true
      },
      dev: {
      },
      prod: {
      }
    },

    lint: {

      options: {
        extends: 'eslint:recommended',
        parserOptions: {
          ecmaVersion: 6
        },
        envs: ['browser', 'jquery'],
        globals: {
          //allowed global variables
        },
        rules: {
          //your ESLint rules here
        }
      },
      formatParams: [], //parameters passed to format function
      formatOnce: true, //use eslint.format() vs eslint.formatEach()

      inject: {
        src: true,
        lint: true,
        format: true,
        finish: true
      },

      dev: {
        options: {
          rules: {
            'no-console': 0
          }
        }
      },

      prod: {

      }
    },

    browserSync: {
      options: {
        //tip: set host to "[project-name].localhost" and set "open" to "external"
        host: 'localhost',
        open: 'local',
        port: 80,
        reloadOnRestart: true,
        server: {
          baseDir: dirs.dist.main
        }
      },

      inject: {
        init: true  //overrides default browserSync run, must return a promise or stream
      },

      dev: {
      },

      //only for prod:preview task
      prod: {
      },

      //compatibility fallback
      common: {
        options: {}
      }
    },

    clean: {

      inject: {
        //functions receive current glob array (incremental, containing previously added patterns) passed at the end to the del function
        init: true,
        cache: true,
        styles: true,
        sprites: true,
        fonts: true,
        js: true,
        images: true,
        views: true,
        custom: true,
        del: true,
        finish: true
      },

      dev: {
      },

      prod: {
        inject: {
          cache: false
        }
      }
    },

    watch: {
      inject: {
        init: true,
        styles: true,
        fonts: true,
        sprites: true,
        js: true,
        jsApp: true,
        jsVendor: true,
        images: true,
        views: true,
        finish: true
      },

      dev: {
      },

      prod: {
      }
    }
  }

  //parse cli args
  var optsConfig = {
    string: ['env'],
    boolean: ['prod', 'restart'],
    alias: {
      prod: 'p',
      restart: 'r'
    },
    default: {
      prod: process.env.NODE_ENV === 'production'
    }
  }
  var opts = minimist(process.argv.slice(2), optsConfig);
  config.main.isDev = !opts.prod && opts.env !== 'production';
  config.main.isRestart = opts.restart;

  //custom config file
  var noCustomFile = false;
  try {
    fs.accessSync(dirs.app + dirs.customConfig.configFile, fs.R_OK);
  }
  catch (ex) {
    noCustomFile = true;
    console.error('Frontend-starter warning: no custom config definitions file present (' + dirs.customConfig.configFile + ')');
  }

  //custom config file - require
  if (!noCustomFile) {
    require(dirs.app + dirs.customConfig.configFile)(config, dirs);
  }

  //compatibility falback
  if (config.clean.views === false) {
    //config.clean.inject.views = false;
    console.error('Frontend-starter error (deprecated): please rename "config.clean.views = false;" into "config.clean.inject.views = false;" in your ' + dirs.customConfig.configFile + ' file');
    process.exit(1);
  }

  return config;
}<|endoftext|>


<commit_before>/*
@title Search HN Who is hiring?
@input
{
  "content-type" : "application/json",
  "example" : "[\"seattle\"]"
}
@output
{
  "content-type" : "application/json"
}
*/

var _ = require('underscore');

module.exports = function(req, res, next) {
  var query = req.query['query'] || req.body;

  this.request('a7medkamel/tm-data/blob/master/hn/who_is_hiring.js', function(err, httpResponse, body){
    if (err) {
      next(err);
      return;
    }

    if (_.isEmpty(query)) {
      query = [];
    }

    if (_.isString(query)) {
      query = [query]
    }

    var jobs = JSON.parse(body);

    var found = _.filter(jobs, function(job){
      if (!job.text) {
        return;
      }

      var text = job.text.toLowerCase();
      return !!_.find(query, function(term){ return text.indexOf(term) != -1; });
    });

    res.send(found);
  })
};<commit_msg>make search term lowercase<commit_after>/*
@title Search HN Who is hiring?
@input
{
  "content-type" : "application/json",
  "example" : "[\"seattle\"]"
}
@output
{
  "content-type" : "application/json"
}
*/

var _ = require('underscore');

module.exports = function(req, res, next) {
  var query = req.query['query'] || req.body;

  this.request('a7medkamel/tm-data/blob/master/hn/who_is_hiring.js', function(err, httpResponse, body){
    if (err) {
      next(err);
      return;
    }

    if (_.isEmpty(query)) {
      query = [];
    }

    if (_.isString(query)) {
      query = [query]
    }

    var jobs = JSON.parse(body);

    var found = _.filter(jobs, function(job){
      if (!job.text) {
        return;
      }

      var text = job.text.toLowerCase();
      return !!_.find(query, function(term){ return text.indexOf(term.toLowerCase()) != -1; });
    });

    res.send(found);
  })
};<|endoftext|>


<commit_before>function hasAppleColorEmoji () {
  var widths = []
  tags = [document.createElement('span'), document.createElement('span')]
  tags.forEach(function(tag, i) {
    tag.innerText = "☺"
    tag.style.fontFamily = i === 1 ? "thisisnotafont" : "AppleColorEmoji"
    document.body.appendChild(tag)
    widths.push(tag.offsetWidth)
    tag.remove()
  })
  return widths[0] != widths[1]
}
<commit_msg>Single quotes<commit_after>function hasAppleColorEmoji () {
  var widths = []
  tags = [document.createElement('span'), document.createElement('span')]
  tags.forEach(function(tag, i) {
    tag.innerText = '☺'
    tag.style.fontFamily = i === 1 ? 'thisisnotafont' : 'AppleColorEmoji'
    document.body.appendChild(tag)
    widths.push(tag.offsetWidth)
    tag.remove()
  })
  return widths[0] != widths[1]
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
	/**
	 * Test to make sure timepicker is now accessible with inputDefaultError
	 */
	testAccessible : {
        test : [function(cmp){
        	cmp.get("hours").set("v.value", "100");
        	cmp.get("c.updateHours").runDeprecated({});
        },function(cmp){
        	cmp.get("minutes").set("v.value", "100");
        	cmp.get("c.updateMinutes").runDeprecated({});
        },function(cmp){
        	$A.test.assertAccessible();
        }]
    },

    testHoursAttribute : {
        attributes : {  "hours" : 5 },
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("5", "" + hours, "The item in the hours was not set correctly");
        }
    },

    testMinAttribute : {
        attributes : {  "minutes" : 50},
        test : function(cmp) {
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("50", "" + minutes,  "The item in the minutes was not set correctly");
        }
    },

    testInputSelNotPreset : {
        attributes : { "is24HourFormat" : true  },
        test : function(cmp) {
            $A.test.assertNull(cmp.get("ampm").getElement(),"The inputSelect for am/pm is rendered and it should not be");
        }
    },

    testInputSelPresent : {
        attributes : { "is24HourFormat" : false},
        test : function(cmp) {
            $A.test.assertNotNull(cmp.get("ampm").getElement(),"The inputSelect for am/pm is not rendered and it should be");
        }
    },

    /*
     * Two paths that I am testing for: path 1: The initial time the component
     * is being rendered (tests end with initVals) path 2: After the component
     * is being rendered (tests end with AlreadyRendered)
     */

    // These next two test constitute:
    // testValidTimeForPeriodAfterAlreadyRendered
    testErrorMessagesPeriodHours : {
        attributes : {"is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 100);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesPeriodMinutes : {
        attributes : { "is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", 100);
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals( "1","" + minutesErrorList,"Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    // These next two tests constitute:
    // testValidTimeFor24HourAfterAlreadyRendered
    testErrorMessages24hrHours : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 25);
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrMinutes : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", 60);
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    //Test Boundary Cases for 24 hour
    testValidTimeFor24HourWithInitValsBoundaryAtEnd : {
        attributes : {"is24HourFormat" : true, "hours" : 23,"minutes" : 60},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("23",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");
            debugger;
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("00", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeFor24HourWithInitValsBoundaryCloseToEnd : {
        attributes : {"is24HourFormat" : true, "hours" : 24,"minutes" : 59},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("59", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeFor24HourWithInitValsBoundaryAtBegin : {
        attributes : {"is24HourFormat" : true, "hours" : 00,"minutes" : 00},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("00", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    //Test Boundary Cases for period
    testValidTimeForPeriodWithInitValsBoundaryAtBegin : {
        attributes : {"is24HourFormat" : false,"hours" : 01,"minutes" : 00},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("1","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("00", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeForPeriodWithInitValsBoundaryAtEnd : {
        attributes : {"is24HourFormat" : false,"hours" : 12,"minutes" : 01},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("12","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeForPeriodWithInitValsBoundaryCloseToEnd : {
        attributes : {"is24HourFormat" : false,"hours" : 11,"minutes" : 59},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("11","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("59", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testInvalidTimeForPeriodOnAfterAlreadyRendered : {
        attributes : {"is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 14);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("14", "" + hours,"The item in the hours textbox was not correctly converted");
        }
    },

    //Testing for non number values
    testErrorMessages24hrHoursWithSpecialChar : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "$25");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrHoursWithDecimal : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "4.0");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrHoursWithEmptyString : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithSpecialCharWithNum : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "$25");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithDecimal : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "4.0");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithEmptyString : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    // Will work after fix: W-1856385
    testInvalidTimeFor24HourWithInitVals : {
        attributes : {"is24HourFormat" : true,"hours" : 48,"minutes" : 61},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0", "" + hours,"The item in the hours textbox was not correctly converted");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test .assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted");

        }
    },

    // Will work after fix: W-1856385
    testInvalidTimeForPeriodWithInitVals : {
        attributes : {"is24HourFormat" : false,"hours" : 14,"minutes" : 61},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("2", "" + hours,"The item in the hours textbox was not correctly converted");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted");
        }
    },

    testInvalidTimeFor24HourAfterAlreadyRendered : {
        attributes : {"is24HourFormat" : true,"hours" : 48,"minutes" : 61},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 14);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("14", "" + hours, "The item in the hours textbox was not correctly converted");
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("01", "" + minutes, "The item in the minutes textbox was not correctly converted");
        }
    }
})
<commit_msg>The accessibility tool does not work with older broswers (ie7,8). Disabling the test from running in those settings. @rev ctatlah@ @bug W-2367496@<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
	/**
	 * Test to make sure timepicker is now accessible with inputDefaultError
	 */
	testAccessible : {
    	browsers: ["-IE7","-IE8"],
        test : [function(cmp){
        	cmp.get("hours").set("v.value", "100");
        	cmp.get("c.updateHours").runDeprecated({});
        },function(cmp){
        	cmp.get("minutes").set("v.value", "100");
        	cmp.get("c.updateMinutes").runDeprecated({});
        },function(cmp){
        	$A.test.assertAccessible();
        }]
    },

    testHoursAttribute : {
        attributes : {  "hours" : 5 },
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("5", "" + hours, "The item in the hours was not set correctly");
        }
    },

    testMinAttribute : {
        attributes : {  "minutes" : 50},
        test : function(cmp) {
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("50", "" + minutes,  "The item in the minutes was not set correctly");
        }
    },

    testInputSelNotPreset : {
        attributes : { "is24HourFormat" : true  },
        test : function(cmp) {
            $A.test.assertNull(cmp.get("ampm").getElement(),"The inputSelect for am/pm is rendered and it should not be");
        }
    },

    testInputSelPresent : {
        attributes : { "is24HourFormat" : false},
        test : function(cmp) {
            $A.test.assertNotNull(cmp.get("ampm").getElement(),"The inputSelect for am/pm is not rendered and it should be");
        }
    },

    /*
     * Two paths that I am testing for: path 1: The initial time the component
     * is being rendered (tests end with initVals) path 2: After the component
     * is being rendered (tests end with AlreadyRendered)
     */

    // These next two test constitute:
    // testValidTimeForPeriodAfterAlreadyRendered
    testErrorMessagesPeriodHours : {
        attributes : {"is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 100);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesPeriodMinutes : {
        attributes : { "is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", 100);
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals( "1","" + minutesErrorList,"Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    // These next two tests constitute:
    // testValidTimeFor24HourAfterAlreadyRendered
    testErrorMessages24hrHours : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 25);
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrMinutes : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", 60);
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    //Test Boundary Cases for 24 hour
    testValidTimeFor24HourWithInitValsBoundaryAtEnd : {
        attributes : {"is24HourFormat" : true, "hours" : 23,"minutes" : 60},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("23",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");
            debugger;
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("00", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeFor24HourWithInitValsBoundaryCloseToEnd : {
        attributes : {"is24HourFormat" : true, "hours" : 24,"minutes" : 59},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("59", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeFor24HourWithInitValsBoundaryAtBegin : {
        attributes : {"is24HourFormat" : true, "hours" : 00,"minutes" : 00},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0",""+hours, "The item in the hours textbox was not correctly converted from 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("00", "" + minutes, "The item in the minutes textbox was not correctly converted ");
        }
    },

    //Test Boundary Cases for period
    testValidTimeForPeriodWithInitValsBoundaryAtBegin : {
        attributes : {"is24HourFormat" : false,"hours" : 01,"minutes" : 00},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("1","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("00", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeForPeriodWithInitValsBoundaryAtEnd : {
        attributes : {"is24HourFormat" : false,"hours" : 12,"minutes" : 01},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("12","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testValidTimeForPeriodWithInitValsBoundaryCloseToEnd : {
        attributes : {"is24HourFormat" : false,"hours" : 11,"minutes" : 59},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("11","" + hours, "The item in the hours textbox was not correctly converted from to 24hours correctly");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("59", "" + minutes,"The item in the minutes textbox was not correctly converted ");
        }
    },

    testInvalidTimeForPeriodOnAfterAlreadyRendered : {
        attributes : {"is24HourFormat" : false},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 14);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("14", "" + hours,"The item in the hours textbox was not correctly converted");
        }
    },

    //Testing for non number values
    testErrorMessages24hrHoursWithSpecialChar : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "$25");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrHoursWithDecimal : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "4.0");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessages24hrHoursWithEmptyString : {
        attributes : {"is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("hours").set("v.value", "");
            var evt = {
                "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hoursErrorList = cmp.get("hourError").getElement().children.length;
            $A.test.assertEquals("1","" + hoursErrorList,"Erroroneous input was put in the hours inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithSpecialCharWithNum : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "$25");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithDecimal : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "4.0");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    testErrorMessagesMinutesWithEmptyString : {
        attributes : { "is24HourFormat" : true},
        test : function(cmp) {
            cmp.get("minutes").set("v.value", "");
            var evt = {
            "getSource" : function() {
                return cmp.get("minutes")
            }
            };
            cmp.get("c.updateMinutes").runDeprecated(evt);

            var minutesErrorList = cmp.get("minuteError").getElement().children.length;
            $A.test.assertEquals("1","" + minutesErrorList, "Erroroneous input was put in the Minutes inputInteger component, an error message did not appear");
        }
    },

    // Will work after fix: W-1856385
    testInvalidTimeFor24HourWithInitVals : {
        attributes : {"is24HourFormat" : true,"hours" : 48,"minutes" : 61},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("0", "" + hours,"The item in the hours textbox was not correctly converted");

            var minutes = cmp.get("minutes").get("v.value");
            $A.test .assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted");

        }
    },

    // Will work after fix: W-1856385
    testInvalidTimeForPeriodWithInitVals : {
        attributes : {"is24HourFormat" : false,"hours" : 14,"minutes" : 61},
        test : function(cmp) {
            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("2", "" + hours,"The item in the hours textbox was not correctly converted");

            var minutes = cmp.get("minutes").get("v.value");

            $A.test.assertEquals("01", "" + minutes,"The item in the minutes textbox was not correctly converted");
        }
    },

    testInvalidTimeFor24HourAfterAlreadyRendered : {
        attributes : {"is24HourFormat" : true,"hours" : 48,"minutes" : 61},
        test : function(cmp) {
            cmp.get("hours").set("v.value", 14);
            var evt = {
            "getSource" : function() {
                return cmp.get("hours")
            }
            };
            cmp.get("c.updateHours").runDeprecated(evt);

            var hours = cmp.get("hours").get("v.value");
            $A.test.assertEquals("14", "" + hours, "The item in the hours textbox was not correctly converted");
            var minutes = cmp.get("minutes").get("v.value");
            $A.test.assertEquals("01", "" + minutes, "The item in the minutes textbox was not correctly converted");
        }
    }
})
<|endoftext|>