<commit_before>(function () {
    requirejs.config({
        baseUrl: 'Scripts',
        paths: {
            knockout: 'lib/knockout-3.4.0.debug',
            jquery: 'lib/jquery-2.2.3.min',
            text: 'lib/text',
            bootstrap: 'lib/bootstrap.min',
            modernizer: 'lib/modernizr-2.8.3'
        },

        // Explicitly specify that bootstrap is dependant on jquery to avoid dependency errors
        shim: {
            "bootstrap": { "deps": ['jquery'] }
        }
    });
})();

var ns = ns || {};

ns.postbox = {
    subscribers: [],

    subscribe: function (callback, topic, source) {
        var found = false;
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i].source === source && this.subscribers[i].topic === topic) {
                found = true;
                this.subscribers[i].callback = callback;
            }
        }
        if (!found) {
            this.subscribers.push({ topic: topic, callback: callback, source: source });
        }
    },
    notify: function (value, topic) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i].topic === topic) {
                this.subscribers[i].callback(value);
            }
        }
    }
};

require(['knockout', 'app/viewmodel', 'app/config'],
    function (ko, viewmodel, config) {

    // Top bar menu
    ko.components.register(config.menuComponent, {
        viewModel: { require: 'app/components/topbarmenu/topbarmenuViewModel' },
        template: { require: 'text!app/components/topbarmenu/topbarmenu.html' }
    });

    // Annotations
    ko.components.register(config.annotationsComponent, {
        viewModel: { require: 'app/components/annotations/annotationsViewModel' },
        template: { require: 'text!app/components/annotations/annotations.html' }
    });

    // Favorites
    ko.components.register(config.favoritesComponent, {
        viewModel: { require: 'app/components/favorites/favoritesViewModel' },
        template: { require: 'text!app/components/favorites/favorites.html' }
    });

    // Full page post
    ko.components.register(config.fullPagePostComponent, {
        viewModel: { require: 'app/components/fullpagepost/fullpagepostViewModel' },
        template: { require: 'text!app/components/fullpagepost/fullpagepost.html' }
    });

    // List element
    ko.components.register(config.listElementComponent, {
        viewModel: { require: 'app/components/listelement/listelementViewModel' },
        template: { require: 'text!app/components/listelement/listelement.html' }
    });

    // List of elements with the search bar (essentially, main element)
    ko.components.register(config.listOfElementsWithSearchComponent, {
        viewModel: { require: 'app/components/listofelementswithsearch/listofelementswithsearchViewModel' },
        template: { require: 'text!app/components/listofelementswithsearch/listofelementswithsearch.html' }
    });

    // Questions
    ko.components.register(config.questionsComponent, {
        viewModel: { require: 'app/components/questions/questionsViewModel' },
        template: { require: 'text!app/components/questions/questions.html' }
    });

    // Question
    ko.components.register(config.questionComponent, {
        viewModel: { require: 'app/components/question/questionViewModel' },
        template: { require: 'text!app/components/question/question.html' }
    });

    // User
    ko.components.register(config.userComponent, {
        viewModel: { require: 'app/components/user/userViewModel' },
        template: { require: 'text!app/components/user/user.html' }
    });

    // Tags
    ko.components.register(config.tagsComponent, {
        viewModel: { require: 'app/components/tags/tagsViewModel' },
        template: { require: 'text!app/components/tags/tags.html' }
    });

    // Comments
    ko.components.register(config.commentsComponent, {
        viewModel: { require: 'app/components/comments/commentsViewModel' },
        template: { require: 'text!app/components/comments/comments.html' }
    });

    // Answers
    ko.components.register(config.answersComponent, {
        viewModel: { require: 'app/components/answers/answersViewModel' },
        template: { require: 'text!app/components/answers/answers.html' }
    });

    // Search bar
    ko.components.register(config.searchBarComponent, {
        viewModel: { require: 'app/components/searchbar/searchbarViewModel' },
        template: { require: 'text!app/components/searchbar/searchbar.html' }
    });

    // Start page
    ko.components.register(config.startPageComponent, {
        viewModel: { require: 'app/components/startpage/startpageViewModel' },
        template: { require: 'text!app/components/startpage/startpage.html' }
    });

    ko.applyBindings(viewmodel);
});<commit_msg>Fixed dependency issues that caused mobile dropdown to not work<commit_after>(function () {
    requirejs.config({
        baseUrl: 'Scripts',
        paths: {
            knockout: 'lib/knockout-3.4.0.debug',
            jquery: 'lib/jquery-2.2.3.min',
            text: 'lib/text',
            bootstrap: 'lib/bootstrap.min',
            modernizer: 'lib/modernizr-2.8.3'
        },

        // Explicitly specify that bootstrap is dependant on jquery to avoid dependency errors
        shim: {
            "bootstrap": { "deps": ['jquery'] }
        }
    });
})();

var ns = ns || {};

ns.postbox = {
    subscribers: [],

    subscribe: function (callback, topic, source) {
        var found = false;
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i].source === source && this.subscribers[i].topic === topic) {
                found = true;
                this.subscribers[i].callback = callback;
            }
        }
        if (!found) {
            this.subscribers.push({ topic: topic, callback: callback, source: source });
        }
    },
    notify: function (value, topic) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i].topic === topic) {
                this.subscribers[i].callback(value);
            }
        }
    }
};

require(['knockout', 'app/viewmodel', 'app/config', 'jquery', 'bootstrap'],
    function (ko, viewmodel, config, $, bs) {

    // Top bar menu
    ko.components.register(config.menuComponent, {
        viewModel: { require: 'app/components/topbarmenu/topbarmenuViewModel' },
        template: { require: 'text!app/components/topbarmenu/topbarmenu.html' }
    });

    // Annotations
    ko.components.register(config.annotationsComponent, {
        viewModel: { require: 'app/components/annotations/annotationsViewModel' },
        template: { require: 'text!app/components/annotations/annotations.html' }
    });

    // Favorites
    ko.components.register(config.favoritesComponent, {
        viewModel: { require: 'app/components/favorites/favoritesViewModel' },
        template: { require: 'text!app/components/favorites/favorites.html' }
    });

    // Full page post
    ko.components.register(config.fullPagePostComponent, {
        viewModel: { require: 'app/components/fullpagepost/fullpagepostViewModel' },
        template: { require: 'text!app/components/fullpagepost/fullpagepost.html' }
    });

    // List element
    ko.components.register(config.listElementComponent, {
        viewModel: { require: 'app/components/listelement/listelementViewModel' },
        template: { require: 'text!app/components/listelement/listelement.html' }
    });

    // List of elements with the search bar (essentially, main element)
    ko.components.register(config.listOfElementsWithSearchComponent, {
        viewModel: { require: 'app/components/listofelementswithsearch/listofelementswithsearchViewModel' },
        template: { require: 'text!app/components/listofelementswithsearch/listofelementswithsearch.html' }
    });

    // Questions
    ko.components.register(config.questionsComponent, {
        viewModel: { require: 'app/components/questions/questionsViewModel' },
        template: { require: 'text!app/components/questions/questions.html' }
    });

    // Question
    ko.components.register(config.questionComponent, {
        viewModel: { require: 'app/components/question/questionViewModel' },
        template: { require: 'text!app/components/question/question.html' }
    });

    // User
    ko.components.register(config.userComponent, {
        viewModel: { require: 'app/components/user/userViewModel' },
        template: { require: 'text!app/components/user/user.html' }
    });

    // Tags
    ko.components.register(config.tagsComponent, {
        viewModel: { require: 'app/components/tags/tagsViewModel' },
        template: { require: 'text!app/components/tags/tags.html' }
    });

    // Comments
    ko.components.register(config.commentsComponent, {
        viewModel: { require: 'app/components/comments/commentsViewModel' },
        template: { require: 'text!app/components/comments/comments.html' }
    });

    // Answers
    ko.components.register(config.answersComponent, {
        viewModel: { require: 'app/components/answers/answersViewModel' },
        template: { require: 'text!app/components/answers/answers.html' }
    });

    // Search bar
    ko.components.register(config.searchBarComponent, {
        viewModel: { require: 'app/components/searchbar/searchbarViewModel' },
        template: { require: 'text!app/components/searchbar/searchbar.html' }
    });

    // Start page
    ko.components.register(config.startPageComponent, {
        viewModel: { require: 'app/components/startpage/startpageViewModel' },
        template: { require: 'text!app/components/startpage/startpage.html' }
    });

    ko.applyBindings(viewmodel);
});<|endoftext|>


<commit_before>// Promisify synchronous function

"use strict";

var callable         = require("es5-ext/object/valid-callable")
  , deferred         = require("../../deferred")
  , isPromise        = require("../../is-promise")
  , processArguments = require("../_process-arguments");

var apply = Function.prototype.apply, applyFn;

applyFn = function (fn, args, resolve, reject) {
	var value;
	try {
		value = apply.call(fn, this, args);
	} catch (e) {
		reject(e);
		return;
	}
	resolve(value);
};

module.exports = function (length) {
	var fn, result;
	fn = callable(this);
	if (fn.returnsPromise) return fn;
	if (length != null) length >>>= 0;
	result = function () {
		var args, def;
		args = processArguments(arguments, length);

		if (isPromise(args)) {
			if (args.failed) return args;
			def = deferred();
			args.done(
				function (args) {
					applyFn.call(this, fn, args, def.resolve, def.reject);
				}.bind(this),
				def.reject
			);
		} else {
			def = deferred();
			applyFn.call(this, fn, args, def.resolve, def.reject);
		}

		return def.promise;
	};
	result.returnsPromise = true;
	return result;
};
<commit_msg>refactor: lint<commit_after>// Promisify synchronous function

"use strict";

var isValue          = require("es5-ext/object/is-value")
  , callable         = require("es5-ext/object/valid-callable")
  , toNaturalNumber  = require("es5-ext/number/to-pos-integer")
  , deferred         = require("../../deferred")
  , isPromise        = require("../../is-promise")
  , processArguments = require("../_process-arguments");

var apply = Function.prototype.apply, applyFn;

applyFn = function (fn, args, resolve, reject) {
	var value;
	try {
		value = apply.call(fn, this, args);
	} catch (e) {
		reject(e);
		return;
	}
	resolve(value);
};

module.exports = function (length) {
	var fn, result;
	fn = callable(this);
	if (fn.returnsPromise) return fn;
	if (isValue(length)) length = toNaturalNumber(length);
	result = function () {
		var args, def;
		args = processArguments(arguments, length);

		if (isPromise(args)) {
			if (args.failed) return args;
			def = deferred();
			args.done(
				function (resolvedArgs) {
					applyFn.call(this, fn, resolvedArgs, def.resolve, def.reject);
				}.bind(this),
				def.reject
			);
		} else {
			def = deferred();
			applyFn.call(this, fn, args, def.resolve, def.reject);
		}

		return def.promise;
	};
	result.returnsPromise = true;
	return result;
};
<|endoftext|>


<commit_before>const db = require('../db');
const XLSX = require('xlsx');
const _ = require('underscore');
const moment = require('moment');
module.exports = {
    findByBcode: async function(req, res, next) {
        try {
            let result = await db.XMLY5000.query(`

            	DROP temporary TABLE IF EXISTS sorddt_tmp;
                       CREATE TEMPORARY TABLE sorddt_tmp (INDEX index_skno (OD_SKNO))
                        SELECT st.OD_ID,st.OD_CTNO,st.OD_SKNO,st.OD_PRICE,OD_NO,OD_QTY
                                        FROM SORDDT st JOIN (
                                            SELECT MAX(OD_ID) OD_ID FROM SORDDT WHERE OD_CTNO = ? AND OD_NO > '2014-06-01' AND OD_QTY > 0
                                            GROUP BY OD_CTNO,OD_SKNO ORDER BY OD_ID
                                        ) stt
                                        ON (st.OD_ID = stt.OD_ID)
                                        ORDER BY st.OD_CTNO,st.OD_SKNO;

                SELECT DISTINCT OD_NO,OD_CTNO,FirstSale,Price,OD_PRICE,SUM(Amount) Amount,SK_NO,SK_BCODE,SK_NAME,CAST(SK_SPEC AS varchar(max)) SK_SPEC,SK_UNIT,SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
						FROM (
						   SELECT
							CASE WHEN (sd.OD_PRICE is null) THEN 0 ELSE 1 END FirstSale
		 					,CASE WHEN(sd.OD_PRICE is null) THEN SK_LPRICE2 ELSE sd.OD_PRICE END Price
		 					,CASE WHEN(sd.OD_QTY is null) THEN 0 ELSE sd.OD_QTY END Amount
		 					,CASE WHEN (b.SK_KINDNAME IS null) THEN '' ELSE b.SK_KINDNAME END SK_KINDNAME
							,a.*
							,OD_NO
							,OD_PRICE
							,OD_CTNO
							FROM
								(SELECT SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,SK_SUPPNO,
								SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY
								 from SSTOCK WHERE SK_BCODE = ? ) a
								left join SSTOCKKIND b ON (a.SK_IKIND = b.SK_KINDID)
								left join sorddt_tmp sd
								ON (a.SK_NO = sd.OD_SKNO )
							) t
							group by OD_NO,OD_CTNO,FirstSale,Price,OD_PRICE,
							SK_NO,SK_BCODE,SK_NAME,CAST(SK_SPEC AS varchar(max)),SK_UNIT,
							SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,
							SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
							ORDER BY OD_CTNO`, [req.get('CustomerNO'), req.params.code]);
            if (result[result.length - 1].length > 0)
                res.send(result[result.length - 1]);
            else
                res.send([]);
        } catch (e) {
            res.status(500).send(e);
        }
    },
    findByNo: async function(req, res, next) {
        try {
            let result = await db.XMLY5000.query(`
            	DROP temporary TABLE IF EXISTS sorddt_tmp;
                       CREATE TEMPORARY TABLE sorddt_tmp (INDEX index_skno (OD_SKNO))
                        SELECT st.OD_ID,st.OD_CTNO,st.OD_SKNO,st.OD_PRICE,OD_NO,OD_QTY
                                        FROM SORDDT st JOIN (
                                            SELECT MAX(OD_ID) OD_ID FROM SORDDT WHERE OD_CTNO = ? AND OD_NO > '2014-06-01' AND OD_QTY > 0
                                            GROUP BY OD_CTNO,OD_SKNO ORDER BY OD_ID
                                        ) stt
                                        ON (st.OD_ID = stt.OD_ID)
                                        ORDER BY st.OD_CTNO,st.OD_SKNO;

            	SELECT FirstSale,Price,Amount,SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
						FROM (
						   SELECT
							CASE WHEN (sd.OD_PRICE is null) then 0 else 1 end FirstSale
							,CASE WHEN(sd.OD_PRICE is null) then SK_LPRICE2 else sd.OD_PRICE end as Price
							,CASE WHEN(sd.OD_QTY is null) then 0 else sd.OD_QTY end as Amount
							,CASE WHEN (b.SK_KINDNAME IS null) then '' else b.SK_KINDNAME end SK_KINDNAME
							,a.*
							FROM
								(SELECT * FROM SSTOCK WHERE SK_NO = ? ) a
								LEFT JOIN SSTOCKKIND b ON(a.SK_IKIND = b.SK_KINDID)
								LEFT JOIN sorddt_tmp sd
								ON (a.SK_NO = sd.OD_SKNO)
							) t `, [req.get('CustomerNO'), req.params.no]);
            if (result[result.length - 1].length > 0)
                res.send(result[result.length - 1]);
            else
                res.send([]);
        } catch (e) {
            res.status(500).send(e);
        }
    },

}
<commit_msg>Update product.js<commit_after>const db = require('../db');
const XLSX = require('xlsx');
const _ = require('underscore');
const moment = require('moment');
module.exports = {
    findByBcode: async function(req, res, next) {
        try {
            let result = await db.XMLY5000.query(`

            	DROP temporary TABLE IF EXISTS sorddt_tmp;
                       CREATE TEMPORARY TABLE sorddt_tmp (INDEX index_skno (OD_SKNO))
                        SELECT st.OD_ID,st.OD_CTNO,st.OD_SKNO,st.OD_PRICE,OD_NO,OD_QTY
                                        FROM SORDDT st JOIN (
                                            SELECT MAX(OD_ID) OD_ID FROM SORDDT WHERE OD_CTNO = ? AND OD_NO > '2014-06-01' AND OD_QTY > 0
                                            GROUP BY OD_CTNO,OD_SKNO ORDER BY OD_ID
                                        ) stt
                                        ON (st.OD_ID = stt.OD_ID)
                                        ORDER BY st.OD_CTNO,st.OD_SKNO;

                SELECT DISTINCT OD_NO,OD_CTNO,FirstSale,Price,OD_PRICE,SUM(Amount) Amount,SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
						FROM (
						   SELECT
							CASE WHEN (sd.OD_PRICE is null) THEN 0 ELSE 1 END FirstSale
		 					,CASE WHEN(sd.OD_PRICE is null) THEN SK_LPRICE2 ELSE sd.OD_PRICE END Price
		 					,CASE WHEN(sd.OD_QTY is null) THEN 0 ELSE sd.OD_QTY END Amount
		 					,CASE WHEN (b.SK_KINDNAME IS null) THEN '' ELSE b.SK_KINDNAME END SK_KINDNAME
							,a.*
							,OD_NO
							,OD_PRICE
							,OD_CTNO
							FROM
								(SELECT SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,SK_SUPPNO,
								SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY
								 from SSTOCK WHERE SK_BCODE = ? ) a
								left join SSTOCKKIND b ON (a.SK_IKIND = b.SK_KINDID)
								left join sorddt_tmp sd
								ON (a.SK_NO = sd.OD_SKNO )
							) t
							group by OD_NO,OD_CTNO,FirstSale,Price,OD_PRICE,
							SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,
							SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,
							SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
							ORDER BY OD_CTNO`, [req.get('CustomerNO'), req.params.code]);
            if (result[result.length - 1].length > 0)
                res.send(result[result.length - 1]);
            else
                res.send([]);
        } catch (e) {
            res.status(500).send(e);
        }
    },
    findByNo: async function(req, res, next) {
        try {
            let result = await db.XMLY5000.query(`
            	DROP temporary TABLE IF EXISTS sorddt_tmp;
                       CREATE TEMPORARY TABLE sorddt_tmp (INDEX index_skno (OD_SKNO))
                        SELECT st.OD_ID,st.OD_CTNO,st.OD_SKNO,st.OD_PRICE,OD_NO,OD_QTY
                                        FROM SORDDT st JOIN (
                                            SELECT MAX(OD_ID) OD_ID FROM SORDDT WHERE OD_CTNO = ? AND OD_NO > '2014-06-01' AND OD_QTY > 0
                                            GROUP BY OD_CTNO,OD_SKNO ORDER BY OD_ID
                                        ) stt
                                        ON (st.OD_ID = stt.OD_ID)
                                        ORDER BY st.OD_CTNO,st.OD_SKNO;

            	SELECT FirstSale,Price,Amount,SK_NO,SK_BCODE,SK_NAME,SK_SPEC,SK_UNIT,SK_SUPPNO,SK_SUPPNAME,SK_LOCATE,SK_LPRICE1,SK_LPRICE2,SK_IKIND,SK_NOWQTY,SK_KINDNAME
						FROM (
						   SELECT
							CASE WHEN (sd.OD_PRICE is null) then 0 else 1 end FirstSale
							,CASE WHEN(sd.OD_PRICE is null) then SK_LPRICE2 else sd.OD_PRICE end as Price
							,CASE WHEN(sd.OD_QTY is null) then 0 else sd.OD_QTY end as Amount
							,CASE WHEN (b.SK_KINDNAME IS null) then '' else b.SK_KINDNAME end SK_KINDNAME
							,a.*
							FROM
								(SELECT * FROM SSTOCK WHERE SK_NO = ? ) a
								LEFT JOIN SSTOCKKIND b ON(a.SK_IKIND = b.SK_KINDID)
								LEFT JOIN sorddt_tmp sd
								ON (a.SK_NO = sd.OD_SKNO)
							) t `, [req.get('CustomerNO'), req.params.no]);
            if (result[result.length - 1].length > 0)
                res.send(result[result.length - 1]);
            else
                res.send([]);
        } catch (e) {
            res.status(500).send(e);
        }
    },

}
<|endoftext|>


<commit_before>const AkairoHandler = require('./AkairoHandler');
const Inhibitor = require('./Inhibitor');

/** @extends AkairoHandler */
class InhibitorHandler extends AkairoHandler {
    /**
     * Loads inhibitors and checks messages.
     * @param {AkairoClient} client - The Akairo client.
     * @param {Object} options - Options from client.
     */
    constructor(client, options = {}) {
        super(client, options.inhibitorDirectory, Inhibitor);

        /**
         * Directory to inhibitors.
         * @readonly
         * @name InhibitorHandler#directory
         * @type {string}
         */

        /**
         * Inhibitors loaded, mapped by ID to Inhibitor.
         * @name InhibitorHandler#modules
         * @type {Collection<string, Inhibitor>}
         */
    }

    /**
     * Tests inhibitors against the message.
     *
Rejects with the reason if blocked.
     * @param {string} type - Type of inhibitor, 'all', 'pre', or 'post'.
     * @param {Message} message - Message to test.
     * @param {Command} [command] - Command to use.
     * @returns {Promise<string>}
     */
    test(type, message, command) {
        const promises = this.modules.filter(i => i.type === type && i.enabled).map(inhibitor => {
            const inhibited = inhibitor.exec(message, command);

            if (inhibited instanceof Promise) {
                return inhibited.catch(err => {
                    if (err instanceof Error) throw err;
                    return Promise.reject(inhibitor.reason);
                });
            }

            if (!inhibited) return Promise.resolve();
            return Promise.reject(inhibitor.reason);
        });

        return Promise.all(promises);
    }

    /**
     * Loads an inhibitor.
     * @method
     * @param {string|Inhibitor} thing - Module or path to module.
     * @name InhibitorHandler#load
     * @returns {Inhibitor}
     */

    /**
     * Adds an inhibitor.
     * @method
     * @param {string} filename - Filename to lookup in the directory.
     *
A .js extension is assumed.
     * @name InhibitorHandler#add
     * @returns {Inhibitor}
     */

    /**
     * Removes an inhibitor.
     * @method
     * @param {string} id - ID of the inhibitor.
     * @name InhibitorHandler#remove
     * @returns {Inhibitor}
     */

    /**
     * Reloads an inhibitor.
     * @method
     * @param {string} id - ID of the inhibitor.
     * @name InhibitorHandler#reload
     * @returns {Inhibitor}
     */

    /**
     * Reloads all inhibitors.
     * @method
     * @name InhibitorHandler#reloadAll
     * @returns {InhibitorHandler}
     */
}

module.exports = InhibitorHandler;

/**
 * Emitted when an inhibitor is added.
 * @event InhibitorHandler#add
 * @param {Inhibitor} inhibitor - Inhibitor added.
 */

/**
 * Emitted when an inhibitor is removed.
 * @event InhibitorHandler#remove
 * @param {Inhibitor} inhibitor - Inhibitor removed.
 */

/**
 * Emitted when an inhibitor is reloaded.
 * @event InhibitorHandler#reload
 * @param {Inhibitor} inhibitor - Inhibitor reloaded.
 */

/**
 * Emitted when an inhibitor is enabled.
 * @event InhibitorHandler#enable
 * @param {Inhibitor} inhibitor - Inhibitor enabled.
 */

/**
 * Emitted when an inhibitor is disabled.
 * @event InhibitorHandler#disable
 * @param {Inhibitor} inhibitor - Inhibitor disabled.
 */
<commit_msg>Change inhibitor testing to Promise.race instead of Promise.all<commit_after>const AkairoHandler = require('./AkairoHandler');
const Inhibitor = require('./Inhibitor');

/** @extends AkairoHandler */
class InhibitorHandler extends AkairoHandler {
    /**
     * Loads inhibitors and checks messages.
     * @param {AkairoClient} client - The Akairo client.
     * @param {Object} options - Options from client.
     */
    constructor(client, options = {}) {
        super(client, options.inhibitorDirectory, Inhibitor);

        /**
         * Directory to inhibitors.
         * @readonly
         * @name InhibitorHandler#directory
         * @type {string}
         */

        /**
         * Inhibitors loaded, mapped by ID to Inhibitor.
         * @name InhibitorHandler#modules
         * @type {Collection<string, Inhibitor>}
         */
    }

    /**
     * Tests inhibitors against the message.
     *
Rejects with the reason if blocked.
     * @param {string} type - Type of inhibitor, 'all', 'pre', or 'post'.
     * @param {Message} message - Message to test.
     * @param {Command} [command] - Command to use.
     * @returns {Promise<string>}
     */
    test(type, message, command) {
        const promises = this.modules.filter(i => i.type === type && i.enabled).map(inhibitor => {
            const inhibited = inhibitor.exec(message, command);

            if (inhibited instanceof Promise) {
                return inhibited.catch(err => {
                    if (err instanceof Error) throw err;
                    return Promise.reject(inhibitor.reason);
                });
            }

            if (!inhibited) return Promise.resolve();
            return Promise.reject(inhibitor.reason);
        });

        return Promise.race(promises);
    }

    /**
     * Loads an inhibitor.
     * @method
     * @param {string|Inhibitor} thing - Module or path to module.
     * @name InhibitorHandler#load
     * @returns {Inhibitor}
     */

    /**
     * Adds an inhibitor.
     * @method
     * @param {string} filename - Filename to lookup in the directory.
     *
A .js extension is assumed.
     * @name InhibitorHandler#add
     * @returns {Inhibitor}
     */

    /**
     * Removes an inhibitor.
     * @method
     * @param {string} id - ID of the inhibitor.
     * @name InhibitorHandler#remove
     * @returns {Inhibitor}
     */

    /**
     * Reloads an inhibitor.
     * @method
     * @param {string} id - ID of the inhibitor.
     * @name InhibitorHandler#reload
     * @returns {Inhibitor}
     */

    /**
     * Reloads all inhibitors.
     * @method
     * @name InhibitorHandler#reloadAll
     * @returns {InhibitorHandler}
     */
}

module.exports = InhibitorHandler;

/**
 * Emitted when an inhibitor is added.
 * @event InhibitorHandler#add
 * @param {Inhibitor} inhibitor - Inhibitor added.
 */

/**
 * Emitted when an inhibitor is removed.
 * @event InhibitorHandler#remove
 * @param {Inhibitor} inhibitor - Inhibitor removed.
 */

/**
 * Emitted when an inhibitor is reloaded.
 * @event InhibitorHandler#reload
 * @param {Inhibitor} inhibitor - Inhibitor reloaded.
 */

/**
 * Emitted when an inhibitor is enabled.
 * @event InhibitorHandler#enable
 * @param {Inhibitor} inhibitor - Inhibitor enabled.
 */

/**
 * Emitted when an inhibitor is disabled.
 * @event InhibitorHandler#disable
 * @param {Inhibitor} inhibitor - Inhibitor disabled.
 */
<|endoftext|>


<commit_before>import joi from 'joi';

const schema = joi.object().keys({
  name: joi.string().min(1).required(),
  path: joi.string().min(1).required(),
  port: joi.number(),
  type: joi.string(),
  servers: joi.object().required().pattern(
    /[/s/S]*/,
    joi.object().keys({
      env: joi.object().pattern(
        /[/s/S]*/,
        [joi.string(), joi.number(), joi.bool()]
      ),
      settings: joi.string()
    })
  ),
  deployCheckWaitTime: joi.number(),
  deployCheckPort: joi.number(),
  enableUploadProgressBar: joi.bool(),
  dockerImage: joi.string(),
  docker: joi.object().keys({
    image: joi.string().trim(),
    imagePort: joi.number(),
    imageFrontendServer: joi.string(),
    args: joi.array().items(joi.string()),
    bind: joi.string().trim(),
    prepareBundle: joi.bool(),
    networks: joi
      .array()
      .items(joi.string()),
    buildInstructions: joi.array().items(joi.string()),
    stopAppDuringPrepareBundle: joi.bool()
  }),
  buildOptions: joi.object().keys({
    serverOnly: joi.bool(),
    debug: joi.bool(),
    cleanAfterBuild: joi.bool(),
    buildLocation: joi.string(),
    mobileSettings: joi.object(),
    server: joi.string().uri(),
    allowIncompatibleUpdates: joi.boolean(),
    executable: joi.string()
  }),
  env: joi
    .object()
    .keys({
      ROOT_URL: joi
        .string()
        .regex(
          new RegExp('^(http|https)://', 'i'),
          'valid url with "http://" or "https://"'
        )
        .required(),
      MONGO_URL: joi.string()
    })
    .pattern(/[\s\S]*/, [joi.string(), joi.number(), joi.bool()]),
  log: joi.object().keys({
    driver: joi.string(),
    opts: joi.object()
  }),
  volumes: joi.object(),
  nginx: joi.object().keys({
    clientUploadLimit: joi.string().trim()
  }),
  ssl: joi
    .object()
    .keys({
      autogenerate: joi
        .object()
        .keys({
          email: joi.string().email().required(),
          domains: joi.string().required()
        }),
      crt: joi.string().trim(),
      key: joi.string().trim(),
      port: joi.number(),
      upload: joi.boolean()
    })
    .and('crt', 'key')
    .without('autogenerate', ['crt', 'key'])
    .or('crt', 'autogenerate')
});

export default function(
  config,
  {
    addDepreciation,
    combineErrorDetails,
    VALIDATE_OPTIONS,
    serversExist,
    addLocation
  }
) {
  let details = [];
  details = combineErrorDetails(
    details,
    joi.validate(config.app, schema, VALIDATE_OPTIONS)
  );
  if (config.app.name && config.app.name.indexOf(' ') > -1) {
    details.push({
      message: 'has a space',
      path: 'name'
    });
  }
  if (
    typeof config.app.ssl === 'object' &&
    'autogenerate' in config.app.ssl &&
    'PORT' in config.app.env
  ) {
    details.push({
      message: 'PORT can not be set when using ssl.autogenerate',
      path: 'env'
    });
  }
  details = combineErrorDetails(
    details,
    serversExist(config.servers, config.app.servers)
  );

  // Depreciations
  if (config.app.ssl) {
    details = addDepreciation(
      details,
      'ssl',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  if (config.app.nginx) {
    details = addDepreciation(
      details,
      'nginx',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  if (config.app.docker && config.app.docker.imageFrontendServer) {
    details = addDepreciation(
      details,
      'docker.imageFrontendServer',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  return addLocation(details, config.meteor ? 'meteor' : 'app');
}
<commit_msg>Validate for at least one server in `app.servers`<commit_after>import joi from 'joi';

const schema = joi.object().keys({
  name: joi.string().min(1).required(),
  path: joi.string().min(1).required(),
  port: joi.number(),
  type: joi.string(),
  servers: joi.object().min(1).required().pattern(
    /[/s/S]*/,
    joi.object().keys({
      env: joi.object().pattern(
        /[/s/S]*/,
        [joi.string(), joi.number(), joi.bool()]
      ),
      settings: joi.string()
    })
  ),
  deployCheckWaitTime: joi.number(),
  deployCheckPort: joi.number(),
  enableUploadProgressBar: joi.bool(),
  dockerImage: joi.string(),
  docker: joi.object().keys({
    image: joi.string().trim(),
    imagePort: joi.number(),
    imageFrontendServer: joi.string(),
    args: joi.array().items(joi.string()),
    bind: joi.string().trim(),
    prepareBundle: joi.bool(),
    networks: joi
      .array()
      .items(joi.string()),
    buildInstructions: joi.array().items(joi.string()),
    stopAppDuringPrepareBundle: joi.bool()
  }),
  buildOptions: joi.object().keys({
    serverOnly: joi.bool(),
    debug: joi.bool(),
    cleanAfterBuild: joi.bool(),
    buildLocation: joi.string(),
    mobileSettings: joi.object(),
    server: joi.string().uri(),
    allowIncompatibleUpdates: joi.boolean(),
    executable: joi.string()
  }),
  env: joi
    .object()
    .keys({
      ROOT_URL: joi
        .string()
        .regex(
          new RegExp('^(http|https)://', 'i'),
          'valid url with "http://" or "https://"'
        )
        .required(),
      MONGO_URL: joi.string()
    })
    .pattern(/[\s\S]*/, [joi.string(), joi.number(), joi.bool()]),
  log: joi.object().keys({
    driver: joi.string(),
    opts: joi.object()
  }),
  volumes: joi.object(),
  nginx: joi.object().keys({
    clientUploadLimit: joi.string().trim()
  }),
  ssl: joi
    .object()
    .keys({
      autogenerate: joi
        .object()
        .keys({
          email: joi.string().email().required(),
          domains: joi.string().required()
        }),
      crt: joi.string().trim(),
      key: joi.string().trim(),
      port: joi.number(),
      upload: joi.boolean()
    })
    .and('crt', 'key')
    .without('autogenerate', ['crt', 'key'])
    .or('crt', 'autogenerate')
});

export default function(
  config,
  {
    addDepreciation,
    combineErrorDetails,
    VALIDATE_OPTIONS,
    serversExist,
    addLocation
  }
) {
  let details = [];

  details = combineErrorDetails(
    details,
    joi.validate(config.app, schema, VALIDATE_OPTIONS)
  );
  if (config.app.name && config.app.name.indexOf(' ') > -1) {
    details.push({
      message: 'has a space',
      path: 'name'
    });
  }
  if (
    typeof config.app.ssl === 'object' &&
    'autogenerate' in config.app.ssl &&
    'PORT' in config.app.env
  ) {
    details.push({
      message: 'PORT can not be set when using ssl.autogenerate',
      path: 'env'
    });
  }
  details = combineErrorDetails(
    details,
    serversExist(config.servers, config.app.servers)
  );

  // Depreciations
  if (config.app.ssl) {
    details = addDepreciation(
      details,
      'ssl',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  if (config.app.nginx) {
    details = addDepreciation(
      details,
      'nginx',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  if (config.app.docker && config.app.docker.imageFrontendServer) {
    details = addDepreciation(
      details,
      'docker.imageFrontendServer',
      'Use the reverse proxy instead',
      'https://git.io/vN5tn'
    );
  }

  return addLocation(details, config.meteor ? 'meteor' : 'app');
}
<|endoftext|>


<commit_before>/**
 * jquery.privateShare
 *
 * A jquery plugin that renders sharebuttons for facebook, google+ and twitter
 * in accordance with the very restrictive german privacy laws - e.G. without loading any 3rd party API
 *
 * @author Sebastian Antosch
 * @email s.antosch@i-san.de
 * @copyright 2016 I-SAN.de Webdesign & Hosting GbR
 *
 */

(function ($) {
    "use strict";

    // Abort if jQuery is not loaded
    if (!window.jQuery || !$) {
        throw new Error('jquery.privateShare needs jQuery Library to be loaded - which is not.');
    }

    var
        /**
         * Holds some constants
         */
        constants = {
            FB: {
                POPUP: {
                    WIDTH: 560,
                    HEIGHT: 630
                },
                ICON: {
                    BLANK: 'f',
                    FONTAWESOME: '',
                },
                COLOR: '#3B5998'
            },
            GP: {
                POPUP: {
                    WIDTH: 505,
                    HEIGHT: 665
                },
                ICON: {
                    BLANK: 'G+',
                    FONTAWESOME: '',
                },
                COLOR: '#DB4437'
            },
            TW: {
                POPUP: {
                    WIDTH: 695,
                    HEIGHT: 254
                },
                ICON: {
                    BLANK: 't',
                    FONTAWESOME: '',
                },
                COLOR: '#0084B4'
            },
            POPUPSETTINGS: {
                directories: 0,
                fullscreen: 0,
                location: 0,
                menubar: 0,
                status: 0,
                titlebar: 0,
                toolbar: 0
            }
        },

        /**
         * Holds the functions to generate the sharelinks
         * @type {{}}
         */
        getShareLink = {

            /**
             * Generates a facebook share link
             * @param {string} url
             * @returns {string}
             */
            FB: function (url) {
                return 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(url);
            },

            /**
             * Generates a Google+ Share Link
             * @param {string} url
             * @returns {string}
             */
            GP: function (url) {
                return 'https://plus.google.com/share?url=' + encodeURIComponent(url);
            },

            /**
             * Generates a Twitter tweet link with an optional message
             * @param {string} url
             * @param {string} message
             * @returns {string}
             */
            TW: function (url, message) {
                // Generate tweet text
                var tweet = url;
                // attach message only if link < length limit
                if (tweet.length < 136 && message && message.length > 0) {
                    tweet += (' - ' + message);

                    // crop the tweet if necessary (only do this automatically if it is too long due to the description)
                    // If only the link is already too long, the user has to deal with it, e.g. shorten it.
                    if (tweet.length > 140) {
                        tweet = tweet.substring(0, 137) + '...';
                    }
                }
                return 'https://twitter.com/home?status=' + encodeURIComponent(tweet);
            }
        },

        /**
         * Opens a popup for a share-service with specific dimensions.
         * Opens a new tab on mobile view instead
         * @param {string} service - FB (Facebook), GP (Google+), TW (Twitter)
         * @param {string} link - the link to be called containing all share parameters
         * @return {Window} - the opened window instance
         */
        openPopup = function (service, link) {
            var width = constants[service].POPUP.WIDTH,
                height = constants[service].POPUP.HEIGHT,
                $win = $(window),
                wwidth = $win.width(),
                wheight = $win.height,
                settings = constants.POPUPSETTINGS,
                settingsstring = '',
                win;

            // Open in a new tab if the popup would exceed the parent windows dimensions
            if (width > wwidth || height > wheight) {
                win = window.open(link, '_blank');
                win.focus();
                return win;
            }

            // Otherwise open as a correctly sized and positioned popup
            settings.width = width;
            settings.height = height;
            settings.top = (wheight - height) / 2;
            settings.left = (wwidth - width) / 2;

            $.each(function (key, value) {
                settingsstring += (key + "=" + value + ",");
            });
            win = window.open(link, "privateSharePopup", settingsstring);
            win.focus();
            return win;
        },

        /**
         * Attaches the sharelink functionality to a supplied link element
         * @param {jQuery} $link - a jQuery link object
         * @param {string} service - FB (Facebook), GP (Google+), TW (Twitter)
         * @param {string} url - the link that shall be shared
         * @param {string} message - an additional message, only for tweet links
         * @return {jQuery}
         */
        attachSharelink = function ($link, service, url, message) {

            // if no url is supplied, use the current one
            if (!url) {
                url = window.location.href;
            }

            // get the share link
            var href = getShareLink[service](url, message),
                alreadyClicked = false;

            // set the sharelink
            $link.attr('href', href);
            $link.on('click touchstart', function (e) {

                // Only fire once every 500ms preventing ghostclicks,
                // this allows us to listen for touchstart as well
                if (!alreadyClicked) {
                    alreadyClicked = true;
                    setTimeout(function () { alreadyClicked = false; }, 500);

                    // Open the sharelink
                    openPopup(service, href);
                }

                e.preventDefault();
            });
        },

        /**
         * Creates a jQuery sharebutton
         * @param service
         * @param options
         * @returns {jQuery}
         */
        createButton = function (service, options) {

            // TODO use options
            var icontype = 'BLANK',
                size = 64,
                borderRadius = size / 2,
                $button;

            // Create the dom
            $button =  $('a')
                .css({
                    'background-color':     constants[service].COLOR,
                    'cursor':               'pointer',
                    'display':              'inline-block',
                    'border-radius':        borderRadius
                }).append(
                    $(constants[service].ICON[icontype])
                        .css({
                            'color':        '#ffffff',
                            'text-align':   'center',
                            'display':      'block',
                            'width':        size,
                            'line-height':  size,
                            'font-size':    Math.floor(size * 0.6)
                        })
                );

            // Attach functionality
            attachSharelink($button, service, options.url, options.message);

            // return
            return $button;
        },

        // Save all public functions in here
        publicFunctions = {

            /**
             * Attaches a facebook-sharelink
             * @param {jQuery] $this
             * @param {Object} options
             */
            shareFb: function ($this, options) {
                attachSharelink($this, 'FB', options.url, null);
            },

            /**
             * Attaches a googleplus-sharelink
             * @param {jQuery] $this
             * @param {Object} options
             */
            shareGp: function ($this, options) {
                attachSharelink($this, 'GP', options.url, null);
            },

            /**
             * Attaches a twitter-tweetlink
             * @param {jQuery] $this
             * @param options
             */
            shareTw: function ($this, options) {
                attachSharelink($this, 'TW', options.url, options.message);
            },

            /**
             * Appends a facebook-sharebutton
             * @param $this
             * @param options
             */
            appendShareFb: function ($this, options) {
                $this.append(createButton('FB', options));
            },

            /**
             * Appends a googleplus-sharebutton
             * @param $this
             * @param options
             */
            appendShareGp: function ($this, options) {
                $this.append(createButton('GP', options));
            },

            /**
             * Appends a twitter-tweetbutton
             * @param $this
             * @param options
             */
            appendShareTw: function ($this, options) {
                $this.append(createButton('TW', options));
            },

            /**
             * Appends all available buttons
             * @param $this
             * @param options
             */
            appendAll: function ($this, options) {
                $this
                    .privateShare('appendShareFb', options)
                    .privateShare('appendShareGp', options)
                    .privateShare('appendShareTw', options);
            },

            /**
             * Scans the dom for elements where jq.privateShare shall be attached
             * @param $parent - only scan children of this parent, optional
             */
            init: function ($parent) {
                $parent.find('*[data-jqpshare]').not('*[data-jqpshare-initialized]')
                    .each(function () {
                        var $this = $(this);

                        $this.privateShare($this.data('jqpshare'), $this.data());
                    });
            }
        };

    /**
     * Initializes the privateShare functionality
     * @param {string} action - what to do
     * @param {Object} options - some options, optional
     * @returns {jQuery}
     */
    $.fn.privateShare = function (action, options) {
        if (typeof publicFunctions[action] === "function") {
            return this.each(function () {
                publicFunctions[action]($(this), options);
            });
        }
        throw new Error('jquery.privateShare has no function "' + action + '"!');
    };

    /**
     * Scan the dom on document.ready
     */
    $(document).ready(function () {
        publicFunctions.init($('body'));
    });

}(jQuery));<commit_msg>fixes, working alpha! Still a lot to do<commit_after>/**
 * jquery.privateShare
 *
 * A jquery plugin that renders sharebuttons for facebook, google+ and twitter
 * in accordance with the very restrictive german privacy laws - e.G. without loading any 3rd party API
 *
 * @author Sebastian Antosch
 * @email s.antosch@i-san.de
 * @copyright 2016 I-SAN.de Webdesign & Hosting GbR
 *
 */

(function ($) {
    "use strict";

    // Abort if jQuery is not loaded
    if (!window.jQuery || !$) {
        throw new Error('jquery.privateShare needs jQuery Library to be loaded - which is not.');
    }

    var
        /**
         * Holds some constants
         */
        constants = {
            FB: {
                POPUP: {
                    WIDTH: 560,
                    HEIGHT: 630
                },
                ICON: {
                    BLANK: 'f',
                    FONTAWESOME: '',
                },
                TITLE: 'Share on Facebook',
                COLOR: '#3B5998'
            },
            GP: {
                POPUP: {
                    WIDTH: 505,
                    HEIGHT: 665
                },
                ICON: {
                    BLANK: 'G+',
                    FONTAWESOME: '',
                },
                TITLE: 'Share on Google+',
                COLOR: '#DB4437'
            },
            TW: {
                POPUP: {
                    WIDTH: 695,
                    HEIGHT: 254
                },
                ICON: {
                    BLANK: 't',
                    FONTAWESOME: '',
                },
                TITLE: 'Tweet this',
                COLOR: '#0084B4'
            },
            POPUPSETTINGS: {
                directories: 0,
                fullscreen: 0,
                location: 0,
                menubar: 0,
                status: 0,
                titlebar: 0,
                toolbar: 0
            }
        },

        /**
         * Holds the functions to generate the sharelinks
         * @type {{}}
         */
        getShareLink = {

            /**
             * Generates a facebook share link
             * @param {string} url
             * @returns {string}
             */
            FB: function (url) {
                return 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(url);
            },

            /**
             * Generates a Google+ Share Link
             * @param {string} url
             * @returns {string}
             */
            GP: function (url) {
                return 'https://plus.google.com/share?url=' + encodeURIComponent(url);
            },

            /**
             * Generates a Twitter tweet link with an optional message
             * @param {string} url
             * @param {string} message
             * @returns {string}
             */
            TW: function (url, message) {
                // Generate tweet text
                var tweet = url;
                // attach message only if link < length limit
                if (tweet.length < 136 && message && message.length > 0) {
                    tweet += (' - ' + message);

                    // crop the tweet if necessary (only do this automatically if it is too long due to the description)
                    // If only the link is already too long, the user has to deal with it, e.g. shorten it.
                    if (tweet.length > 140) {
                        tweet = tweet.substring(0, 137) + '...';
                    }
                }
                return 'https://twitter.com/home?status=' + encodeURIComponent(tweet);
            }
        },

        /**
         * Opens a popup for a share-service with specific dimensions.
         * Opens a new tab on mobile view instead
         * @param {string} service - FB (Facebook), GP (Google+), TW (Twitter)
         * @param {string} link - the link to be called containing all share parameters
         * @return {Window} - the opened window instance
         */
        openPopup = function (service, link) {
            var width = constants[service].POPUP.WIDTH,
                height = constants[service].POPUP.HEIGHT,
                $win = $(window),
                wwidth = $win.width(),
                wheight = $win.height(),
                settings = constants.POPUPSETTINGS,
                settingsstring = '',
                win;

            // Open in a new tab if the popup would exceed the parent windows dimensions
            if (width > wwidth || height > wheight) {
                win = window.open(link, '_blank');
                win.focus();
                return win;
            }

            // Otherwise open as a correctly sized and positioned popup
            settings.width = width;
            settings.height = height;
            settings.top = (wheight - height) / 2;
            settings.left = (wwidth - width) / 2;

            $.each(settings, function (key, value) {
                settingsstring += (key + "=" + value + ",");
            });
            win = window.open(link, "privateSharePopup", settingsstring);
            win.focus();
            return win;
        },

        /**
         * Attaches the sharelink functionality to a supplied link element
         * @param {jQuery} $link - a jQuery link object
         * @param {string} service - FB (Facebook), GP (Google+), TW (Twitter)
         * @param {string} url - the link that shall be shared
         * @param {string} message - an additional message, only for tweet links
         * @return {jQuery}
         */
        attachSharelink = function ($link, service, url, message) {

            // if no url is supplied, use the current one
            if (!url) {
                url = window.location.href;
            }

            // get the share link
            var href = getShareLink[service](url, message),
                alreadyClicked = false;

            // set the sharelink
            $link.attr('href', href);
            $link.on('click touchstart', function (e) {

                // Only fire once every 500ms preventing ghostclicks,
                // this allows us to listen for touchstart as well
                if (!alreadyClicked) {
                    alreadyClicked = true;
                    setTimeout(function () { alreadyClicked = false; }, 500);

                    // Open the sharelink
                    openPopup(service, href);
                }

                e.preventDefault();
            });
        },

        /**
         * Creates a jQuery sharebutton
         * @param service
         * @param options
         * @returns {jQuery}
         */
        createButton = function (service, options) {

            // TODO use options
            var icontype = 'BLANK',
                size = 32,
                borderRadius = size / 2,
                $button;

            // Create the dom
            $button =  $('')
                .css({
                    'background-color':     constants[service].COLOR,
                    'cursor':               'pointer',
                    'display':              'inline-block',
                    'border-radius':        borderRadius,
                    'text-decoration':      'none',
                    'outline':              'none'
                })
                .prop('title', constants[service].TITLE)
                .append(
                    $(constants[service].ICON[icontype])
                        .css({
                            'color':        '#ffffff',
                            'text-align':   'center',
                            'display':      'block',
                            'width':        size,
                            'height':       size,
                            'line-height':  Math.floor(size * 0.96) + 'px', // optical center
                            'font-size':    Math.floor(size * 0.6) + 'px'
                        })
                );

            // Attach functionality
            attachSharelink($button, service, options.url, options.message);

            // return
            return $button;
        },

        // Save all public functions in here
        publicFunctions = {

            /**
             * Attaches a facebook-sharelink
             * @param {jQuery] $this
             * @param {Object} options
             */
            shareFb: function ($this, options) {
                attachSharelink($this, 'FB', options.url, null);
            },

            /**
             * Attaches a googleplus-sharelink
             * @param {jQuery] $this
             * @param {Object} options
             */
            shareGp: function ($this, options) {
                attachSharelink($this, 'GP', options.url, null);
            },

            /**
             * Attaches a twitter-tweetlink
             * @param {jQuery] $this
             * @param options
             */
            shareTw: function ($this, options) {
                attachSharelink($this, 'TW', options.url, options.message);
            },

            /**
             * Appends a facebook-sharebutton
             * @param $this
             * @param options
             */
            appendShareFb: function ($this, options) {
                $this.append(createButton('FB', options));
            },

            /**
             * Appends a googleplus-sharebutton
             * @param $this
             * @param options
             */
            appendShareGp: function ($this, options) {
                $this.append(createButton('GP', options));
            },

            /**
             * Appends a twitter-tweetbutton
             * @param $this
             * @param options
             */
            appendShareTw: function ($this, options) {
                $this.append(createButton('TW', options));
            },

            /**
             * Appends all available buttons
             * @param $this
             * @param options
             */
            appendAll: function ($this, options) {
                $this
                    .privateShare('appendShareFb', options)
                    .privateShare('appendShareGp', options)
                    .privateShare('appendShareTw', options);
            },

            /**
             * Scans the dom for elements where jq.privateShare shall be attached
             * @param $parent - only scan children of this parent, optional
             */
            init: function ($parent) {
                $parent.find('*[data-jqpshare]').not('*[data-jqpshare-initialized]')
                    .each(function () {
                        var $this = $(this);

                        $this.privateShare($this.data('jqpshare'), $this.data());
                    });
            }
        };

    /**
     * Initializes the privateShare functionality
     * @param {string} action - what to do
     * @param {Object} options - some options, optional
     * @returns {jQuery}
     */
    $.fn.privateShare = function (action, options) {
        if (typeof publicFunctions[action] === "function") {
            return this.each(function () {
                publicFunctions[action]($(this), options);
            });
        }
        throw new Error('jquery.privateShare has no function "' + action + '"!');
    };

    /**
     * Scan the dom on document.ready
     */
    $(document).ready(function () {
        publicFunctions.init($('body'));
    });

}(jQuery));<|endoftext|>


<commit_before>import Component from '@glimmer/component';
import { map } from 'rsvp';
import { isEmpty } from '@ember/utils';
import { htmlSafe } from '@ember/string';
import { timeout } from 'ember-concurrency';
import { inject as service } from '@ember/service';
import {tracked} from '@glimmer/tracking';
import { action } from '@ember/object';
import {restartableTask} from "ember-concurrency-decorators";

export default class VisualizeCourseSessionTypes extends Component {
  @service router;
  @service intl;
  @tracked data;
  @tracked tooltipContent = null;
  @tracked tooltipTitle = null;
  chartType = 'horz-bar';

  @restartableTask
  *load(element, [course]) {
    const sessions = yield course.get('sessions');
    const dataMap = yield map(sessions.toArray(), async session => {
      const sessionType = await session.get('sessionType');
      const hours = await session.get('totalSumDuration');
      const minutes = Math.round(hours * 60);
      return {
        sessionTitle: session.get('title'),
        sessionTypeTitle: sessionType.get('title'),
        sessionTypeId: sessionType.get('id'),
        minutes,
      };
    });

    const mappedSessionTypes = dataMap.reduce((set, obj) => {
      let existing = set.findBy('label', obj.sessionTypeTitle);
      if (!existing) {
        existing = {
          data: 0,
          label: obj.sessionTypeTitle,
          meta: {
            sessionType: obj.sessionTypeTitle,
            sessionTypeId: obj.sessionTypeId,
            sessions: []
          }
        };
        set.pushObject(existing);
      }
      existing.data += obj.minutes;
      existing.meta.sessions.pushObject(obj.sessionTitle);

      return set;
    }, []);

    const totalMinutes = mappedSessionTypes.mapBy('data').reduce((total, minutes) => total + minutes, 0);
    this.data = mappedSessionTypes.map(obj => {
      const percent = (obj.data / totalMinutes * 100).toFixed(1);
      obj.label = `${obj.meta.sessionType} ${percent}%`;
      obj.meta.totalMinutes = totalMinutes;
      obj.meta.percent = percent;
      return obj;
    }).sort((first, second) => {
      return first.data - second.data;
    });
  }

  @action
  barClick(obj) {
    if (this.args.isIcon || isEmpty(obj) || obj.empty || isEmpty(obj.meta)) {
      return;
    }
    this.router.transitionTo('course-visualize-session-type', this.args.course.get('id'), obj.meta.sessionTypeId);
  }

  @restartableTask
  *barHover(obj) {
    yield timeout(100);
    if (this.args.isIcon || isEmpty(obj) || obj.empty) {
      this.tooltipTitle = null;
      this.tooltipContent = null;
      return;
    }
    const { label, data, meta } = obj;

    const title = htmlSafe(`${label} ${data} ${this.intl.t('general.minutes')}`);
    const sessions = meta.sessions.uniq().sort().join();

    this.tooltipTitle = title;
    this.tooltipContent = sessions;
  }
}
<commit_msg>allow chart type to be specified on input.<commit_after>import Component from '@glimmer/component';
import { map } from 'rsvp';
import { isEmpty } from '@ember/utils';
import { htmlSafe } from '@ember/string';
import { timeout } from 'ember-concurrency';
import { inject as service } from '@ember/service';
import {tracked} from '@glimmer/tracking';
import { action } from '@ember/object';
import {restartableTask} from "ember-concurrency-decorators";

export default class VisualizeCourseSessionTypes extends Component {
  @service router;
  @service intl;
  @tracked data;
  @tracked tooltipContent = null;
  @tracked tooltipTitle = null;

  get chartType() {
    return this.args.chartType || 'horz-bar';
  }

  @restartableTask
  *load(element, [course]) {
    const sessions = yield course.get('sessions');
    const dataMap = yield map(sessions.toArray(), async session => {
      const sessionType = await session.get('sessionType');
      const hours = await session.get('totalSumDuration');
      const minutes = Math.round(hours * 60);
      return {
        sessionTitle: session.get('title'),
        sessionTypeTitle: sessionType.get('title'),
        sessionTypeId: sessionType.get('id'),
        minutes,
      };
    });

    const mappedSessionTypes = dataMap.reduce((set, obj) => {
      let existing = set.findBy('label', obj.sessionTypeTitle);
      if (!existing) {
        existing = {
          data: 0,
          label: obj.sessionTypeTitle,
          meta: {
            sessionType: obj.sessionTypeTitle,
            sessionTypeId: obj.sessionTypeId,
            sessions: []
          }
        };
        set.pushObject(existing);
      }
      existing.data += obj.minutes;
      existing.meta.sessions.pushObject(obj.sessionTitle);

      return set;
    }, []);

    const totalMinutes = mappedSessionTypes.mapBy('data').reduce((total, minutes) => total + minutes, 0);
    this.data = mappedSessionTypes.map(obj => {
      const percent = (obj.data / totalMinutes * 100).toFixed(1);
      obj.label = `${obj.meta.sessionType} ${percent}%`;
      obj.meta.totalMinutes = totalMinutes;
      obj.meta.percent = percent;
      return obj;
    }).sort((first, second) => {
      return first.data - second.data;
    });
  }

  @restartableTask
  *barHover(obj) {
    yield timeout(100);
    if (this.args.isIcon || isEmpty(obj) || obj.empty) {
      this.tooltipTitle = null;
      this.tooltipContent = null;
      return;
    }
    const { label, data, meta } = obj;

    const title = htmlSafe(`${label} ${data} ${this.intl.t('general.minutes')}`);
    const sessions = meta.sessions.uniq().sort().join();

    this.tooltipTitle = title;
    this.tooltipContent = sessions;
  }

  @action
  barClick(obj) {
    if (this.args.isIcon || isEmpty(obj) || obj.empty || isEmpty(obj.meta)) {
      return;
    }
    this.router.transitionTo('course-visualize-session-type', this.args.course.get('id'), obj.meta.sessionTypeId);
  }
}
<|endoftext|>


<commit_before>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(FILL_ME_IN);
    expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Change values in AboutInheritance.js<commit_after>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(2);
    expect(this.swedishChef.hobby).toEqual("cooking");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual("chillin");
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual("eat a tire");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(3);
    expect(this.gonzo.hobby).toEqual("daredevil performer");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual("eat a tire");
  });
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

treeherder.directive('ngRightClick', [
    '$parse',
    function($parse) {
    return function(scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function(event) {
            scope.$apply(function() {
                event.preventDefault();
                fn(scope, {$event:event});
            });
        });
    };
}]);

// allow an input on a form to request focus when the value it sets in its
// ``focus-me`` directive is true.  You can set ``focus-me="focusInput"`` and
// when ``$scope.focusInput`` changes to true, it will request focus on
// the element with this directive.
treeherder.directive('focusMe', [
  '$timeout',
  function($timeout) {
  return {
    link: function(scope, element, attrs) {
      scope.$watch(attrs.focusMe, function(value) {
        if(value === true) {
          $timeout(function() {
            element[0].focus();
            scope[attrs.focusMe] = false;
          }, 0);
        }
      });
    }
  };
}]);
// Select all text input in an html element on click.
treeherder.directive('selectOnClick', [
    function () {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                element.on('click', function () {
                    this.select();
                });
        }
    };
}]);
// Prevent default behavior on left mouse click. Useful
// for html anchor's that need to do in application actions
// on left click but default href type functionality on
// middle or right mouse click.
treeherder.directive('preventDefaultOnLeftClick', [
    function(){
        return {
            restrict: 'A',
            link: function(scope, element, attrs){
                element.on('click', function(event){
                    if(event.which === 1){
                        event.preventDefault();
                    }
                });
            }
        }
    }
]);
treeherder.directive('thFilterByBuildername', [
    'thJobFilters',
    function(thJobFilters){
        return {
            restrict: 'A',
            link: function(scope, element, attrs){
                element.on('click', function(event){

                    // Only execute the in page search if the
                    // user does a left click
                    if(event.which === 1){

                        thJobFilters.setSearchQuery(scope.buildbotJobname || "");

                        // Need to tell angular to run the digest cycle
                        // to process the new values in
                        // thJobFilters.searchQuery
                        if(!scope.$$phase){
                            scope.$apply();
                        }
                    }
                });
            }
        }
    }
]);

treeherder.directive('thNotificationBox', [
    'thNotify',
    function(thNotify){
    return {
        restrict: "E",
        templateUrl: "partials/main/thNotificationsBox.html",
        link: function(scope, element, attr) {
            scope.notifier = thNotify
            scope.alert_class_prefix = "alert-"
        }
    }
}]);

treeherder.directive('thFaviconLink', [
    'ThRepositoryModel', 'thFavicons',
    function(ThRepositoryModel, thFavicons){
    return {
        restrict: "E",
        link: function(scope, element, attr) {
            scope.currentTreeStatus = ThRepositoryModel.getCurrentTreeStatus
            scope.$watch('currentTreeStatus()', function(newVal) {
                if (newVal) {
                    scope.favicon = thFavicons[ThRepositoryModel.getCurrentTreeStatus()];
                }
            });
        },
        template: '<link id="favicon" type="image/png" rel="shortcut icon" href="{{favicon}}" />'
    }
}]);

treeherder.directive('numbersOnly', function(){
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {
       modelCtrl.$parsers.push(function (inputValue) {
           // this next is necessary for when using ng-required on your input.
           // In such cases, when a letter is typed first, this parser will be called
           // again, and the 2nd time, the value will be undefined
           if (inputValue == undefined) return ''
           var transformedInput = inputValue.replace(/[^0-9]/g, '');
           if (transformedInput!=inputValue) {
              modelCtrl.$setViewValue(transformedInput);
              modelCtrl.$render();
           }

           return transformedInput;
       });
     }
   };
});

treeherder.directive("thMultiSelect", [
    '$log',
    function($log){
    return {
        restrict: "E",
        templateUrl: "partials/main/thMultiSelect.html",
        scope: {
            leftList: "=",
            rightList: "="
        },
        link: function(scope, element, attrs){

            scope.leftSelected = [];
            scope.rightSelected = [];
            // move the elements selected from one list to the other
            var move_options = function(what, from, to){
                var found;
                for(var i=0;i<what.length; i++){
                    found = from.indexOf(what[i]);
                    if(found !== -1){
                        to.push(from.splice(found, 1)[0]);
                    }
                }
            }
            scope.move_left = function(){
                move_options(scope.rightSelected, scope.rightList, scope.leftList);
            };
            scope.move_right = function(){
                move_options(scope.leftSelected, scope.leftList, scope.rightList);
            };
        }
    }
}]);

treeherder.directive("thTruncatedList", [
    '$log',
    function($log){
    // transforms a list of elements in a shortened list
    // with a "more" link
    return {
        restrict: "E",
        scope: {
            // number of visible elements
            visible: "@",
            elem_list: "=elements"
        },
        link: function(scope, element, attrs){
            scope.visible = parseInt(scope.visible)
            if(typeof scope.visible !== 'number'
                || scope.visible < 0
                || isNaN(scope.visible)){
                throw new TypeError("The visible parameter must be a positive number")
            }
            // cloning the original list to avoid
            scope.$watch("elem_list", function(newValue, oldValue){
                if(newValue){
                    var elem_list_clone = angular.copy(newValue);
                    scope.visible = Math.min(scope.visible, elem_list_clone.length);
                    var visible_content = elem_list_clone.splice(0, scope.visible);
                    $(element[0]).empty();
                    $(element[0]).append(visible_content.join(", "));
                    if(elem_list_clone.length > 0){
                        $(element[0]).append(
                            $("")
                            .attr("title", elem_list_clone.join(", "))
                            .text(" and "+ elem_list_clone.length+ " others")
                            .tooltip()
                        );
                    }
                }
            });
        }
    }
}]);
<commit_msg>Bug 1085030 - Refocus bug entry on subsequent additions<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

treeherder.directive('ngRightClick', [
    '$parse',
    function($parse) {
    return function(scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function(event) {
            scope.$apply(function() {
                event.preventDefault();
                fn(scope, {$event:event});
            });
        });
    };
}]);

// allow an input on a form to request focus when the value it sets in its
// ``focus-me`` directive is true.  You can set ``focus-me="focusInput"`` and
// when ``$scope.focusInput`` changes to true, it will request focus on
// the element with this directive.
treeherder.directive('focusMe', [
  '$timeout',
  function($timeout) {
  return {
    link: function(scope, element, attrs) {
      scope.$watch(attrs.focusMe, function(value) {
        if (value) {
          $timeout(function() {
            element[0].focus();
          }, 0);
        } else {
          element[0].blur();
        }
      });
    }
  };
}]);
// Select all text input in an html element on click.
treeherder.directive('selectOnClick', [
    function () {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                element.on('click', function () {
                    this.select();
                });
        }
    };
}]);
// Prevent default behavior on left mouse click. Useful
// for html anchor's that need to do in application actions
// on left click but default href type functionality on
// middle or right mouse click.
treeherder.directive('preventDefaultOnLeftClick', [
    function(){
        return {
            restrict: 'A',
            link: function(scope, element, attrs){
                element.on('click', function(event){
                    if(event.which === 1){
                        event.preventDefault();
                    }
                });
            }
        }
    }
]);
treeherder.directive('thFilterByBuildername', [
    'thJobFilters',
    function(thJobFilters){
        return {
            restrict: 'A',
            link: function(scope, element, attrs){
                element.on('click', function(event){

                    // Only execute the in page search if the
                    // user does a left click
                    if(event.which === 1){

                        thJobFilters.setSearchQuery(scope.buildbotJobname || "");

                        // Need to tell angular to run the digest cycle
                        // to process the new values in
                        // thJobFilters.searchQuery
                        if(!scope.$$phase){
                            scope.$apply();
                        }
                    }
                });
            }
        }
    }
]);

treeherder.directive('thNotificationBox', [
    'thNotify',
    function(thNotify){
    return {
        restrict: "E",
        templateUrl: "partials/main/thNotificationsBox.html",
        link: function(scope, element, attr) {
            scope.notifier = thNotify
            scope.alert_class_prefix = "alert-"
        }
    }
}]);

treeherder.directive('thFaviconLink', [
    'ThRepositoryModel', 'thFavicons',
    function(ThRepositoryModel, thFavicons){
    return {
        restrict: "E",
        link: function(scope, element, attr) {
            scope.currentTreeStatus = ThRepositoryModel.getCurrentTreeStatus
            scope.$watch('currentTreeStatus()', function(newVal) {
                if (newVal) {
                    scope.favicon = thFavicons[ThRepositoryModel.getCurrentTreeStatus()];
                }
            });
        },
        template: '<link id="favicon" type="image/png" rel="shortcut icon" href="{{favicon}}" />'
    }
}]);

treeherder.directive('numbersOnly', function(){
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {
       modelCtrl.$parsers.push(function (inputValue) {
           // this next is necessary for when using ng-required on your input.
           // In such cases, when a letter is typed first, this parser will be called
           // again, and the 2nd time, the value will be undefined
           if (inputValue == undefined) return ''
           var transformedInput = inputValue.replace(/[^0-9]/g, '');
           if (transformedInput!=inputValue) {
              modelCtrl.$setViewValue(transformedInput);
              modelCtrl.$render();
           }

           return transformedInput;
       });
     }
   };
});

treeherder.directive("thMultiSelect", [
    '$log',
    function($log){
    return {
        restrict: "E",
        templateUrl: "partials/main/thMultiSelect.html",
        scope: {
            leftList: "=",
            rightList: "="
        },
        link: function(scope, element, attrs){

            scope.leftSelected = [];
            scope.rightSelected = [];
            // move the elements selected from one list to the other
            var move_options = function(what, from, to){
                var found;
                for(var i=0;i<what.length; i++){
                    found = from.indexOf(what[i]);
                    if(found !== -1){
                        to.push(from.splice(found, 1)[0]);
                    }
                }
            }
            scope.move_left = function(){
                move_options(scope.rightSelected, scope.rightList, scope.leftList);
            };
            scope.move_right = function(){
                move_options(scope.leftSelected, scope.leftList, scope.rightList);
            };
        }
    }
}]);

treeherder.directive("thTruncatedList", [
    '$log',
    function($log){
    // transforms a list of elements in a shortened list
    // with a "more" link
    return {
        restrict: "E",
        scope: {
            // number of visible elements
            visible: "@",
            elem_list: "=elements"
        },
        link: function(scope, element, attrs){
            scope.visible = parseInt(scope.visible)
            if(typeof scope.visible !== 'number'
                || scope.visible < 0
                || isNaN(scope.visible)){
                throw new TypeError("The visible parameter must be a positive number")
            }
            // cloning the original list to avoid
            scope.$watch("elem_list", function(newValue, oldValue){
                if(newValue){
                    var elem_list_clone = angular.copy(newValue);
                    scope.visible = Math.min(scope.visible, elem_list_clone.length);
                    var visible_content = elem_list_clone.splice(0, scope.visible);
                    $(element[0]).empty();
                    $(element[0]).append(visible_content.join(", "));
                    if(elem_list_clone.length > 0){
                        $(element[0]).append(
                            $("")
                            .attr("title", elem_list_clone.join(", "))
                            .text(" and "+ elem_list_clone.length+ " others")
                            .tooltip()
                        );
                    }
                }
            });
        }
    }
}]);
<|endoftext|>