<commit_before>$(function() {
    var row = 3;
    var col = 3;
    var count = 2;
    var delay = 500;
    var mineDistr = [],
        mineDistr2;

    var bombLocations = generateBombLocation(row, col, count);
    console.log("Bombs located at: " + bombLocations + '(location begins from 0)');

    $.each(bombLocations, function(k, v) {
        var selector = '.box:nth-child(' + (v + 1) + ') div';
        $(selector).addClass("bomb");
    });

    mineDistr2 = getMineDistr(row, col, bombLocations);
    var index;
    for (var i = 0; i < row; i++) {
        for (var j = 0; j < col; j++) {
            index = i * col + j;
            mineDistr[index] = mineDistr2[i][j];
        }
    }
    $.each(mineDistr, function(k, v) {
        if (v !== -1) {
            $(".box:nth-child(" + (k + 1) + ") div").html(v);
        }
    });

    $("body").on("click", '.box button', function() {
        var boxPos = $(".box").index($(this).parent());
        console.log("You clicked the " + boxPos + "th button");
        if ($.inArray(boxPos, bombLocations) != -1) {
            $(".box button").hide();
            $(".box div").show();
            setTimeout(function() {
                alert("Do not be sad, you can do better next time.");
            }, delay);
        } else {
            var i = Math.floor(boxPos / col);
            var j = boxPos % col;
            var indexes = getCurrentSurroundIndex(row, col, i, j, bombLocations);
            $.each(indexes, function(k, v) {
                $(".box:nth-child(" + (v + 1) + ") button").hide();
                $(".box:nth-child(" + (v + 1) + ") div").show();
            });
            if ($(".box div:hidden").parent().size() === count) {
                setTimeout(function() {
                    alert("Congratulations, you win!");
                }, delay);

            }
        }

    });
});

function generateBombLocation(row, col, count) {
    var array = [];
    for (var i = 0; i < row * col; i++) {
        array[i] = i;
    }
    array.sort(randomSort);
    return array.slice(0, count);
};

function randomSort() {
    return 0.5 - Math.random();
}

function getMineDistr(row, col, bombLocations) {
    var m, n;
    //初始化为0
    var bombArray2 = [],
        surroundArray2 = [];
    for (var i = 0; i < row; i++) {
        bombArray2[i] = [];
        surroundArray2[i] = [];
        for (var j = 0; j < col; j++) {
            bombArray2[i][j] = 0;
            surroundArray2[i][j] = -1;
        }
    }
    //有雷的位置,bombArray2设为1
    for (var i = 0; i < bombLocations.length; i++) {
        m = Math.floor(bombLocations[i] / col);
        n = bombLocations[i] % col;
        bombArray2[m][n] = 1;
    }

    for (var i = 0; i < row; i++) {
        for (var j = 0; j < col; j++) {
            surroundArray2[i][j] = getCurrentSurroundBombNum(row, col, i, j, bombArray2);
        }
    }

    return surroundArray2;
}

function getCurrentSurroundBombNum(row, col, i, j, bombArray2) {
    var bombNum = 0;
    var arrayI = [i, i, i, i + 1, i + 1, i + 1, i - 1, i - 1, i - 1];
    var arrayJ = [j, j - 1, j + 1, j - 1, j, j + 1, j - 1, j, j + 1];
    for (var k = 0; k < arrayI.length; k++) {
        if (isValidPosition(row, col, arrayI[k], arrayJ[k])) {
            bombNum += bombArray2[arrayI[k]][arrayJ[k]];
        }
    }
    return bombNum;
}

function getCurrentSurroundIndex(row, col, i, j, bombLocations) {
    var indexArray = [],
        index;
    var arrayI = [i, i, i, i + 1, i + 1, i + 1, i - 1, i - 1, i - 1];
    var arrayJ = [j, j - 1, j + 1, j - 1, j, j + 1, j - 1, j, j + 1];
    for (var k = 0; k < arrayI.length; k++) {
        if (isValidPosition(row, col, arrayI[k], arrayJ[k])) {
            index = arrayI[k] * col + arrayJ[k];
            if ($.inArray(index, bombLocations) === -1) {
                indexArray.push(index);
            }
        }
    }
    return indexArray;
}

function isValidPosition(row, col, i, j) {
    if (i >= 0 && i < row && j >= 0 && j < row) {
        return true;
    }
    return false;
}
<commit_msg>add bomb class or bomb number to div, not both<commit_after>$(function() {
    var row = 3;
    var col = 3;
    var count = 2;
    var delay = 500;
    var mineDistr = [],
        mineDistr2;

    var bombLocations = generateBombLocation(row, col, count);
    console.log("Bombs located at: " + bombLocations + '(location begins from 0)');

    mineDistr2 = getMineDistr(row, col, bombLocations);
    var index;
    for (var i = 0; i < row; i++) {
        for (var j = 0; j < col; j++) {
            index = i * col + j;
            mineDistr[index] = mineDistr2[i][j];
        }
    }
    $.each(mineDistr, function(k, v) {
        var selector = '.box:nth-child(' + (k + 1) + ') div';
        if($.inArray(k, bombLocations) !== -1) {
            $(selector).addClass("bomb");
        }
        else {
            $(selector).html(v);
        }
    });

    $("body").on("click", '.box button', function() {
        var boxPos = $(".box").index($(this).parent());
        console.log("You clicked the " + boxPos + "th button");
        if ($.inArray(boxPos, bombLocations) != -1) {
            $(".box button").hide();
            $(".box div").show();
            setTimeout(function() {
                alert("Do not be sad, you can do better next time.");
            }, delay);
        } else {
            var i = Math.floor(boxPos / col);
            var j = boxPos % col;
            var indexes = getCurrentSurroundIndex(row, col, i, j, bombLocations);
            $.each(indexes, function(k, v) {
                $(".box:nth-child(" + (v + 1) + ") button").hide();
                $(".box:nth-child(" + (v + 1) + ") div").show();
            });
            if ($(".box div:hidden").parent().size() === count) {
                setTimeout(function() {
                    alert("Congratulations, you win!");
                }, delay);

            }
        }

    });
});

function generateBombLocation(row, col, count) {
    var array = [];
    for (var i = 0; i < row * col; i++) {
        array[i] = i;
    }
    array.sort(randomSort);
    return array.slice(0, count);
};

function randomSort() {
    return 0.5 - Math.random();
}

function getMineDistr(row, col, bombLocations) {
    var m, n;
    //初始化为0
    var bombArray2 = [],
        surroundArray2 = [];
    for (var i = 0; i < row; i++) {
        bombArray2[i] = [];
        surroundArray2[i] = [];
        for (var j = 0; j < col; j++) {
            bombArray2[i][j] = 0;
            surroundArray2[i][j] = -1;
        }
    }
    //有雷的位置,bombArray2设为1
    for (var i = 0; i < bombLocations.length; i++) {
        m = Math.floor(bombLocations[i] / col);
        n = bombLocations[i] % col;
        bombArray2[m][n] = 1;
    }

    for (var i = 0; i < row; i++) {
        for (var j = 0; j < col; j++) {
            surroundArray2[i][j] = getCurrentSurroundBombNum(row, col, i, j, bombArray2);
        }
    }

    return surroundArray2;
}

function getCurrentSurroundBombNum(row, col, i, j, bombArray2) {
    var bombNum = 0;
    var arrayI = [i, i, i, i + 1, i + 1, i + 1, i - 1, i - 1, i - 1];
    var arrayJ = [j, j - 1, j + 1, j - 1, j, j + 1, j - 1, j, j + 1];
    for (var k = 0; k < arrayI.length; k++) {
        if (isValidPosition(row, col, arrayI[k], arrayJ[k])) {
            bombNum += bombArray2[arrayI[k]][arrayJ[k]];
        }
    }
    return bombNum;
}

function getCurrentSurroundIndex(row, col, i, j, bombLocations) {
    var indexArray = [],
        index;
    var arrayI = [i, i, i, i + 1, i + 1, i + 1, i - 1, i - 1, i - 1];
    var arrayJ = [j, j - 1, j + 1, j - 1, j, j + 1, j - 1, j, j + 1];
    for (var k = 0; k < arrayI.length; k++) {
        if (isValidPosition(row, col, arrayI[k], arrayJ[k])) {
            index = arrayI[k] * col + arrayJ[k];
            if ($.inArray(index, bombLocations) === -1) {
                indexArray.push(index);
            }
        }
    }
    return indexArray;
}

function isValidPosition(row, col, i, j) {
    if (i >= 0 && i < row && j >= 0 && j < row) {
        return true;
    }
    return false;
}
<|endoftext|>


<commit_before>/**
* Abstract class for lookup widget
* Extend this class with your implementation that consumes your search API
*
* @class LookupAbstract
* @constructor HttpClient
*/
export class LookupAbstract {
    constructor(){
    }

    setDefaultSelection() {
        return { id: this.value, text: this.value };
    }

    search(item) {
        //Override this class with your API to execute the query
        return new Promise((resolve) => {
          resolve({ response: [ { id: 1234, text: 'Test' } ] });
        })
    }

    formatItem(item) {
        return `<div class="select2-user-result"><strong>${item.id}</strong> ${item.text}</div>`;
    }

    formatSelection(item) {
        return item.id;
    }
}<commit_msg>changed the default's for formatItem and formatSelection to be code and description<commit_after>/**
* Abstract class for lookup widget
* Extend this class with your implementation that consumes your search API
*
* @class LookupAbstract
* @constructor HttpClient
*/
export class LookupAbstract {
    constructor(){
    }

    setDefaultSelection() {
        return { id: this.value, text: this.value };
    }

    search(item) {
        //Override this class with your API to execute the query
        return new Promise((resolve) => {
          resolve({ response: [ { code: 1234, description: 'Test' } ] });
        })
    }

    formatItem(item) {
        return `<div class="select2-user-result"><strong>${item.code}</strong> ${item.description}</div>`;
    }

    formatSelection(item) {
        return item.code;
    }
}<|endoftext|>


<commit_before>/*
 * bdajax.js
 * =========
 *
 * AJAX convenience library.
 *
 * This library provides functions for common ajax communication, event
 * rebinding and error handling.
 *
 * XHTML namespace extension:
 * ----------------------------
 *
 * Beside functions you can use inside your own javascrips, this library
 * provides behaviour for performing ajax actions defined in the markup. This
 * action definitions use a XML namespace extension. Thus your markup must
 * define this namespace.
 * ::
 *
 *
 *         ...
 *
 *
 * You can either trigger a single ajax ``action`` or a bunch of ajax
 * ``actions`` with this mechanism.
 *
 * Perform a single action
 * -----------------------
 *
 * Tell ``bdajax`` to bind an ``action`` in the markup.
 * ::
 *
 *     fubar
 *
 * ``bdajax`` binds on page load the click event to ``bdajax.action`` callback
 * on all dom elements defining an ``ajax:action`` attribute. This attribute
 * contains the actionname which gets send to the server.
 *
 * ``ajax.target`` is used to define the context on which the request is made
 * and the query string you want to deliver. We need this because href might
 * map to something completely different for a non-javascript fallback or the
 * dom element is not a hyperlink at all.
 *
 * When the action is triggered, ``bdajax`` calls a view named ``ajaxaction``
 * and expects a JSON response (see ``bdajax._action`` for details).
 *
 * Perform multiple actions
 * ------------------------
 *
 * Tell ``bdajax`` to bind multiple ``actions`` in the markup.
 * ::
 *
 *     fubar
 *
 * ``bdajax`` binds on page load the click event to ``bdajax.actions`` callback
 * on all dom elements defining an ``ajax:actions`` attribute. This attribute
 * contains the actions ``name`` which gets send to the server.
 *
 * ``ajax.target`` is behaves the same way as when performing a single action.
 *
 * When actions is triggered, ``bdajax`` calls a view named ``ajaxactions``
 * and expects a JSON response containing a list of single actions which are
 * then executed in sequence order (see ``bdajax._actions`` for details).
 *
 * Credits:
 * --------
 *
 *     * Written by Robert Niederreiter <rnix@squarewave.at>
 *
 * Dependencies:
 * -------------
 *     * jQuery
 *     * jQuery tools
 */

jQuery(document).ready(function() {
	jQuery('#ajax-spinner')
        .hide()
        .ajaxStart(function() {
            jQuery(this).show();
        })
        .ajaxStop(function() {
            jQuery(this).hide();
        })
    ;
	jQuery('a[ajax\\:actions]').actions();
    jQuery('a[ajax\\:action]').action();
});

/*
 * jQuery plugin for bdajax.action
 */
jQuery.fn.action = function() {
    jQuery(this).bind('click', bdajax.action);
}

/*
 * jQuery plugin for bdajax.actions
 */
jQuery.fn.actions = function() {
    jQuery(this).bind('click', bdajax.actions);
}

bdajax = {

	/*
	 * Strip query from URL if existent and return.
	 *
	 * @param url: Request URL as string.
	 */
	parseurl: function(url) {
        var idx = url.indexOf('?');
        if (idx != -1) {
            url = url.substring(0, idx);
        }
		return url;
	},

	/*
     * Parse query string from URL if existent and return query parameters as
     * object.
     *
     * @param url: Request URL as string.
     */
	parsequery: function(url) {
		var params = {};
		var idx = url.indexOf('?');
        if (idx != -1) {
            var parameters = url.slice(idx + 1).split('&');
            for (var i = 0;  i < parameters.length; i++) {
                var param = parameters[i].split('=');
                params[param[0]] = param[1];
            }
        }
		return params;
	},

	/*
     * Error messages.
     */
    ajaxerrors: {
        timeout: 'The request has timed out. Pleasae try again.',
        error: 'An error occoured while processing the request. Aborting.',
        parsererror: 'The Response could not be parsed. Aborting.',
        unknown: 'An unknown error occoured while request. Aborting.'
    },

    /*
     * Get error message.
     *
     * @param status: ``jQuery.ajax`` error callback status
     */
    ajaxerror: function(status) {
        if (status == 'notmodified') { return; }
        if (status == null) { status = 'unknown' }
        return bdajax.ajaxerrors[status];
    },

	/*
	 * Perform an ajax request.
	 *
	 * By default it sends requests of type ``html`` and displays a
	 * ``bdajax.error`` Message if request fails.
	 *
	 * Example:
	 *
	 *     bdajax.request({
	 *         success: function(data) {
     *             // do something with data.
     *         },
	 *         url: 'foo',
	 *         params: {
     *             a: 'a',
     *             b: 'b'
     *         },
	 *         type: 'json',
	 *         error: function() {
     *             // whatever you want to do on error
     *             bdajax.error('Request failed');
     *         }
	 *     });
	 *
	 * The given ``url`` might contain a query string. The query is parsed and
	 * written to request parameters. If the same request parameter is defined
	 * in URL request query AND params object, latter one rules.
	 *
	 * @param config: object containing request configuration.
	 *     Configuration fields:
	 *         success: Callback if request is successful.
	 *         url: Request url as string.
	 *         params: Query parameters for request as Object (optional).
	 *         type: ``xml``, ``json``, ``script``, or ``html`` (optional).
	 *         error: Callback if request fails (optional).
	 */
	request: function(config) {
		var success = config.success
		var url = config.url
		var params = config.params
		var type = config.type
		var error = config.error
		if (url.indexOf('?') != -1) {
			var addparams = params;
			params = bdajax.parsequery(url);
			url = bdajax.parseurl(url);
			for (var key in addparams) {
                params[key] = addparams[key];
            }
		} else {
			if (!params) { params = {}; }
		}
		if (!type) { type = 'html'; }
	    if (!error) {
	        error = function(request, status) {
				var err = bdajax.ajaxerror(status);
				if (err) { bdajax.error(err); }
	        }
	    }
	    jQuery.ajax({
	        url: url,
	        dataType: type,
	        data: params,
	        success: success,
	        error: error
	    });
	},

	/*
	 * Perform JSON request to server and alter element(s).
	 *
	 * This function expects as response an array containing a name
	 * mapping to class and/or id attributes of the dom element to alter
	 * and the html payload which is used as data replacement.
	 *
	 * @param config: object containing action configuration.
     *     Configuration fields:
     *         name: Action name.
     *         element: Dom element the event is bound to.
     *         event: thrown event.
	 */
	_action: function(config) {
		var target = jQuery(config.element).attr('ajax:target');
        var url = bdajax.parseurl(target);
        var params = bdajax.parsequery(target);
        params.name = config.name;
		var error = function(req, status, exception) {
            bdajax.error(exception);
        };
		bdajax.request({
            url: bdajax.parseurl(config.url) + '/' + config.view,
            type: 'json',
            params: config.params,
            success: function(data) {
                var name = data[0];
                jQuery('#' + name).replaceWith(data[1]);
                jQuery('.' + name).replaceWith(data[1]);
				jQuery('#' + name + ' a[ajax\\:action]').action();
                jQuery('#' + name + ' a[ajax\\:actions]').actions();
                jQuery('.' + name + ' a[ajax\\:action]').action();
                jQuery('.' + name + ' a[ajax\\:actions]').actions();
            },
            error: error
        });
		config.event.preventDefault();
	},

	/*
	 * Perform JSON request to server and perform specified action(s).
	 *
	 * This function expects as response an array containing the action name(s)
     * mapping to given actions name.
	 *
	 * @param config: object containing action configuration.
     *     Configuration fields:
     *         name: Actions name.
     *         element: Dom element the event is bound to.
     *         event: thrown event.
	 */
	_actions: function(config) {
        var target = jQuery(config.element).attr('ajax:target');
        var url = bdajax.parseurl(target);
        var params = bdajax.parsequery(target);
        params.name = config.name;
        var error = function(req, status, exception) {
            bdajax.error(exception);
        };
        bdajax.request({
            url: url + '/ajaxactions',
            type: 'json',
            params: params,
            success: function(data) {
                if (!data) {
                    bdajax.error('Server response empty.');
                    return;
                }
                for (var i = 0; i < data.length; i++) {
                    params.name = data[i];
					bdajax.request({
			            url: bdajax.parseurl(url) + '/ajaxaction',
			            type: 'json',
			            params: params,
			            success: function(data) {
			                var name = data[0];
			                jQuery('#' + name).replaceWith(data[1]);
			                jQuery('.' + name).replaceWith(data[1]);
							jQuery('#' + name + ' a[ajax\\:action]').action();
                            jQuery('#' + name + ' a[ajax\\:actions]').actions();
							jQuery('.' + name + ' a[ajax\\:action]').action();
                            jQuery('.' + name + ' a[ajax\\:actions]').actions();
			            },
			            error: error
			        });
                }
            },
            error: error
        });
        config.event.preventDefault();
    },

	/*
	 * Callback handler for an action binding.
	 */
	action: function(event) {
		bdajax._action({
			name: jQuery(this).attr('ajax:action'),
			element: this,
            event: event
        });
	},

	/*
     * Callback handler for an actions binding.
     */
	actions: function(event) {
		bdajax._actions({
			name: jQuery(this).attr('ajax:actions'),
			element: this,
			event: event
		});
    },

	/*
     * Display System message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    message: function(message) {
        jQuery('#ajax-message').overlay({
            expose: {
                color: '#fff',
                loadSpeed: 200
            },
            onBeforeLoad: function() {
                var overlay = this.getOverlay();
                jQuery('.message', overlay).html(message);
            },
            closeOnClick: false,
            api: true
        }).load();
    },

    /*
     * Display Error message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    error: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('error')
        ;
        bdajax.message(message);
    },

    /*
     * Display Info message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    info: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('info')
        ;
        bdajax.message(message);
    },

    /*
     * Display Warning message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    warning: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('warning')
        ;
        bdajax.message(message);
    }
};<commit_msg><commit_after>/*
 * bdajax.js
 * =========
 *
 * AJAX convenience library.
 *
 * This library provides functions for common ajax communication, event
 * rebinding and error handling.
 *
 * XHTML namespace extension:
 * --------------------------
 *
 * Beside functions you can use inside your own javascrips, this library
 * provides behaviour for performing ajax actions defined in the markup. This
 * action definitions use a XML namespace extension. Thus your markup must
 * define this namespace.
 * ::
 *
 *
 *         ...
 *
 *
 * You can define the following attributes in your markup:
 *
 *   * ajax:action="actionname" - perform ajax action
 *
 *   * ajax:actiomode="replace" (XXX)
 *       - replace - replace HTML at selector (default)
 *       - inner - replace inner HTML at selector
 *
 *   * ajax:actions="actionsname" - perform ajax actions querying actions from
 *                                  the server
 *
 *   * ajax:actions="actionname1 actionname2" - perform ajax actions directly (XXX)
 *
 *   * ajax:selector="a.someclass" (XXX)
 *
 *   * ajax:event="eventname" - trigger event on selector (XXX)
 *
 *   * ajax:call="someLocalFunction" - call function with selector (XXX)
 *
 * Perform a single action
 * -----------------------
 *
 * Tell ``bdajax`` to bind an ``action`` in the markup.
 * ::
 *
 *     fubar
 *
 * ``bdajax`` binds on page load the click event to ``bdajax.action`` callback
 * on all dom elements defining an ``ajax:action`` attribute. This attribute
 * contains the actionname which gets send to the server.
 *
 * ``ajax.target`` is used to define the context on which the request is made
 * and the query string you want to deliver. We need this because href might
 * map to something completely different for a non-javascript fallback or the
 * dom element is not a hyperlink at all.
 *
 * When the action is triggered, ``bdajax`` calls a view named ``ajaxaction``
 * and expects a JSON response (see ``bdajax._action`` for details).
 *
 * Perform multiple actions
 * ------------------------
 *
 * Tell ``bdajax`` to bind multiple ``actions`` in the markup.
 * ::
 *
 *     fubar
 *
 * ``bdajax`` binds on page load the click event to ``bdajax.actions`` callback
 * on all dom elements defining an ``ajax:actions`` attribute. This attribute
 * contains the actions ``name`` which gets send to the server.
 *
 * ``ajax.target`` is behaves the same way as when performing a single action.
 *
 * When actions is triggered, ``bdajax`` calls a view named ``ajaxactions``
 * and expects a JSON response containing a list of single actions which are
 * then executed in sequence order (see ``bdajax._actions`` for details).
 *
 * Credits:
 * --------
 *
 *     * Written by Robert Niederreiter <rnix@squarewave.at>
 *
 * Dependencies:
 * -------------
 *     * jQuery
 *     * jQuery tools
 */

jQuery(document).ready(function() {
	jQuery('#ajax-spinner')
        .hide()
        .ajaxStart(function() {
            jQuery(this).show();
        })
        .ajaxStop(function() {
            jQuery(this).hide();
        })
    ;
	jQuery('a[ajax\\:actions]').actions();
    jQuery('a[ajax\\:action]').action();
});

/*
 * jQuery plugin for bdajax.action
 */
jQuery.fn.action = function() {
    jQuery(this).bind('click', bdajax.action);
}

/*
 * jQuery plugin for bdajax.actions
 */
jQuery.fn.actions = function() {
    jQuery(this).bind('click', bdajax.actions);
}

bdajax = {

	/*
	 * Strip query from URL if existent and return.
	 *
	 * @param url: Request URL as string.
	 */
	parseurl: function(url) {
        var idx = url.indexOf('?');
        if (idx != -1) {
            url = url.substring(0, idx);
        }
		return url;
	},

	/*
     * Parse query string from URL if existent and return query parameters as
     * object.
     *
     * @param url: Request URL as string.
     */
	parsequery: function(url) {
		var params = {};
		var idx = url.indexOf('?');
        if (idx != -1) {
            var parameters = url.slice(idx + 1).split('&');
            for (var i = 0;  i < parameters.length; i++) {
                var param = parameters[i].split('=');
                params[param[0]] = param[1];
            }
        }
		return params;
	},

	/*
     * Error messages.
     */
    ajaxerrors: {
        timeout: 'The request has timed out. Pleasae try again.',
        error: 'An error occoured while processing the request. Aborting.',
        parsererror: 'The Response could not be parsed. Aborting.',
        unknown: 'An unknown error occoured while request. Aborting.'
    },

    /*
     * Get error message.
     *
     * @param status: ``jQuery.ajax`` error callback status
     */
    ajaxerror: function(status) {
        if (status == 'notmodified') { return; }
        if (status == null) { status = 'unknown' }
        return bdajax.ajaxerrors[status];
    },

	/*
	 * Perform an ajax request.
	 *
	 * By default it sends requests of type ``html`` and displays a
	 * ``bdajax.error`` Message if request fails.
	 *
	 * Example:
	 *
	 *     bdajax.request({
	 *         success: function(data) {
     *             // do something with data.
     *         },
	 *         url: 'foo',
	 *         params: {
     *             a: 'a',
     *             b: 'b'
     *         },
	 *         type: 'json',
	 *         error: function() {
     *             // whatever you want to do on error
     *             bdajax.error('Request failed');
     *         }
	 *     });
	 *
	 * The given ``url`` might contain a query string. The query is parsed and
	 * written to request parameters. If the same request parameter is defined
	 * in URL request query AND params object, latter one rules.
	 *
	 * @param config: object containing request configuration.
	 *     Configuration fields:
	 *         success: Callback if request is successful.
	 *         url: Request url as string.
	 *         params: Query parameters for request as Object (optional).
	 *         type: ``xml``, ``json``, ``script``, or ``html`` (optional).
	 *         error: Callback if request fails (optional).
	 */
	request: function(config) {
		var success = config.success
		var url = config.url
		var params = config.params
		var type = config.type
		var error = config.error
		if (url.indexOf('?') != -1) {
			var addparams = params;
			params = bdajax.parsequery(url);
			url = bdajax.parseurl(url);
			for (var key in addparams) {
                params[key] = addparams[key];
            }
		} else {
			if (!params) { params = {}; }
		}
		if (!type) { type = 'html'; }
	    if (!error) {
	        error = function(request, status) {
				var err = bdajax.ajaxerror(status);
				if (err) { bdajax.error(err); }
	        }
	    }
	    jQuery.ajax({
	        url: url,
	        dataType: type,
	        data: params,
	        success: success,
	        error: error
	    });
	},

	/*
	 * Perform JSON request to server and alter element(s).
	 *
	 * This function expects as response an array containing a name
	 * mapping to class and/or id attributes of the dom element to alter
	 * and the html payload which is used as data replacement.
	 *
	 * @param config: object containing action configuration.
     *     Configuration fields:
     *         name: Action name.
     *         element: Dom element the event is bound to.
     *         event: thrown event.
	 */
	_action: function(config) {
		var target = jQuery(config.element).attr('ajax:target');
        var url = bdajax.parseurl(target);
        var params = bdajax.parsequery(target);
        params.name = config.name;
		var error = function(req, status, exception) {
            bdajax.error(exception);
        };
		bdajax.request({
            url: bdajax.parseurl(config.url) + '/' + config.view,
            type: 'json',
            params: config.params,
            success: function(data) {
                var name = data[0];
                jQuery('#' + name).replaceWith(data[1]);
                jQuery('.' + name).replaceWith(data[1]);
				jQuery('#' + name + ' a[ajax\\:action]').action();
                jQuery('#' + name + ' a[ajax\\:actions]').actions();
                jQuery('.' + name + ' a[ajax\\:action]').action();
                jQuery('.' + name + ' a[ajax\\:actions]').actions();
            },
            error: error
        });
		config.event.preventDefault();
	},

	/*
	 * Perform JSON request to server and perform specified action(s).
	 *
	 * This function expects as response an array containing the action name(s)
     * mapping to given actions name.
	 *
	 * @param config: object containing action configuration.
     *     Configuration fields:
     *         name: Actions name.
     *         element: Dom element the event is bound to.
     *         event: thrown event.
	 */
	_actions: function(config) {
        var target = jQuery(config.element).attr('ajax:target');
        var url = bdajax.parseurl(target);
        var params = bdajax.parsequery(target);
        params.name = config.name;
        var error = function(req, status, exception) {
            bdajax.error(exception);
        };
        bdajax.request({
            url: url + '/ajaxactions',
            type: 'json',
            params: params,
            success: function(data) {
                if (!data) {
                    bdajax.error('Server response empty.');
                    return;
                }
                for (var i = 0; i < data.length; i++) {
                    params.name = data[i];
					bdajax.request({
			            url: bdajax.parseurl(url) + '/ajaxaction',
			            type: 'json',
			            params: params,
			            success: function(data) {
			                var name = data[0];
			                jQuery('#' + name).replaceWith(data[1]);
			                jQuery('.' + name).replaceWith(data[1]);
							jQuery('#' + name + ' a[ajax\\:action]').action();
                            jQuery('#' + name + ' a[ajax\\:actions]').actions();
							jQuery('.' + name + ' a[ajax\\:action]').action();
                            jQuery('.' + name + ' a[ajax\\:actions]').actions();
			            },
			            error: error
			        });
                }
            },
            error: error
        });
        config.event.preventDefault();
    },

	/*
	 * Callback handler for an action binding.
	 */
	action: function(event) {
		bdajax._action({
			name: jQuery(this).attr('ajax:action'),
			element: this,
            event: event
        });
	},

	/*
     * Callback handler for an actions binding.
     */
	actions: function(event) {
		bdajax._actions({
			name: jQuery(this).attr('ajax:actions'),
			element: this,
			event: event
		});
    },

	/*
     * Display System message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    message: function(message) {
        jQuery('#ajax-message').overlay({
            expose: {
                color: '#fff',
                loadSpeed: 200
            },
            onBeforeLoad: function() {
                var overlay = this.getOverlay();
                jQuery('.message', overlay).html(message);
            },
            closeOnClick: false,
            api: true
        }).load();
    },

    /*
     * Display Error message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    error: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('error')
        ;
        bdajax.message(message);
    },

    /*
     * Display Info message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    info: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('info')
        ;
        bdajax.message(message);
    },

    /*
     * Display Warning message as overlay.
     *
     * @param message: Message to display as String or DOM element.
     */
    warning: function(message) {
        jQuery("#ajax-message .message")
            .removeClass('error warning info')
            .addClass('warning')
        ;
        bdajax.message(message);
    }
};<|endoftext|>


<commit_before>const _ = require('lodash');
// to add options/change gui: simply add/change this
let gui = {
    reset      : {emoji: '♻',  text: 'Reset the content of this message\n'},
    peace      : {emoji: '☮',  text: 'You slept peacefully.\n'},
    custom     : {emoji: '📧', text: 'Click this to add a custom message.\n'},
    roleblocked: {emoji: '🚫', text: 'You got rolebocked last night.\n'},
    jailed     : {emoji: '🏰', text: 'You were jailed last night.\n'},
    controlled : {emoji: '💫', text: 'You got controlled by a Witch last night.\n'},
    mafia_kill : {emoji: '💀', text: 'You got killed by the Mafia last night.\n'},
    sk_kill    : {emoji: '🔫', text: 'You got killed by the Serial Killer last night\n'},
    immune     : {emoji: '👎', text: 'Your target was immune to your attack.\n'},
    bodyguarded: {emoji: '💪', text: 'Your target was protected by the Bodyguard and you were killed by them last night.\n'},
    send       : {emoji: '✅',  text: '\n**You have 30 minutes to use this menu then it will time out!\n' +
                                     'If you are satisfied with the messages click the  ✅ below this message to send all (not empty) messages out.\n' +
                                     'To cancel click the 🇽**'},
    abort      : {emoji: '🇽', text: ''},

    // get the correct text to an emoji
    text_for_emote: function (emoji_name) {
        return Array.from(gui).find(elem => {if (elem.emoji === emoji_name) return elem;}).text;
    },

    // construct text for displaying a legend of which emoji does what
    legend_text: function () {
        return '\n\n\n**Legend:**\n' + Array.from(gui).map(elem => {return elem.emoji + ' : ' + elem.text;}).join('') + this.send.text;
    },

    // Iterator (ignores the 'send' emote keep that in mind)
    [Symbol.iterator]: function*() {
        for (let key in this) {
            if (this.hasOwnProperty(key) && typeof(this[key]) !== 'function' && key !== 'send' && key !== 'abort') yield this[key];
        }
    }
};
let msgs, alive_players;

exports.run = function (bot, msg) {
    if (!msg.member.hasPermission('ADMINISTRATOR') && !bot.mafia.isMod(msg.author.id)) {
        msg.channel.send(':negative_squared_cross_mark:  |  You are not a game moderator.');
        return;
    }

    // The node.js default is 10 open listeners... I need one for each person + one for sending/canceling will be reset to 10 again at the end
    process.setMaxListeners(0);

    msgs = [];
    alive_players = bot.mafia.getRole().members.array();

    // iterate over all alive players and send one message per player to the (hopefully secret) channel, with emoji-reactions as menu
    msg.channel.send('__**----------- Night Message Menu -----------**__');
    for (let member of alive_players) {
        msg.channel.send('**Send ' + member + ' following night message:**\nYou slept peacefully.')
           .then(tmp_msg => {
               // create reaction collector to log the emoji reactions TODO: Make an utilityfunction to create menus with collectors
               let collector = tmp_msg.createReactionCollector((_, user) => [msg.author.id].concat(bot.mafia.getModsID()).indexOf(user.id) > -1, {time: 1800000});

               // save sent message id to edit this message later - to save the whole object wont work b/c conten updates won't affect it (necessarily)
               // save the collector to be able to stop it later - this way it can be easily determend to which message each collector belongs
               msgs.push([tmp_msg.id, collector]);
               // create events - 'collect' triggers every time an emoji get added 'end' when collector stops
               collector.on('collect', reaction => _reactionEventMessageGroup(reaction, msg, bot));
               // clear reactions when the collector stops
               collector.on('end', () => { tmp_msg.clearReactions(); });

               // get gui as array and then everytime one got succesfully posted, post the next one and stop this collector once all are done
               let gui_elems = Array.from(gui);
               let collector2 = tmp_msg.createReactionCollector((_, user) => user.id === bot.user.id, {time: 100000});
               collector2.on('collect', reaction => {
                   if (reaction.message.reactions.array().length < gui_elems.length) {
                       tmp_msg.react(gui_elems[reaction.message.reactions.array().length].emoji).catch(console.log);
                   }
                   else {
                       collector2.stop();
                   }
               });
               tmp_msg.react(gui_elems[0].emoji).catch(console.log); // initial reaction to start the chain reaction
           });
    }

    // send legend & send + cancel button & create reaction collector + store it for later use as above
    msg.channel.send(gui.legend_text())
       .then(tmp_msg => {
           // this somehow fails to make them appear in always the same order...
           tmp_msg.react(gui.send.emoji).then(tmp_msg.react(gui.abort.emoji)).catch(console.log);
           let collector = tmp_msg.createReactionCollector((_, user) => [msg.author.id].concat(bot.mafia.getModsID()).indexOf(user.id) > -1, {time: 1800000});
           msgs.push(['send/cancel', collector]);
           collector.on('collect', reaction => _reactionEventMessageSendCancel(reaction, bot));
           collector.on('end', reaction => {
               tmp_msg.clearReactions();
               if (reaction.message !== undefined && reaction.message.reactions.array().length === 0) {
                   tmp_msg.edit('\n\n**Menu timed out... are 30 minutes really not enough? Or did you just forgot to send/cancel?**');
               }
               else {
                   tmp_msg.edit('\n\n**Finished!**');
               }
           });
       });
};

// fires every time a reaction (emoji) from the command-initiator is added to one of the messages
function _reactionEventMessageGroup(reaction, own_msg, bot) {
    let msg = reaction.message;
    if (reaction.emoji.name === gui.reset.emoji) {
        msg.edit(msg.content.split('\n')[0]);
        // This can be used to reset the gui after pressing the reset emoji but it is more irritating than usefull imo...
        // for (let tmp_user of reaction.users.array()) {
        //     if (tmp_user === msg.author) continue;
        //     for (let tmp_reaction of msg.reactions.array())
        //         tmp_reaction.remove(tmp_user);
        // }
        return;
    }
    // Handling of custom messages - only accepts messages from the one who initiated the command
    if (reaction.emoji.name === gui.custom.emoji) {
        msg.channel.send('\n\n**Enter your custom message for **' + msg.mentions.users.array()[0])
           .then(send_msg => {
               // waits for the next message (timeout 5 min) of the author and add the content of it to this persons 'to send messages' list
               msg.channel.awaitMessages(m => [own_msg.author.id].concat(bot.mafia.getModsID()).indexOf(m.author.id) > -1, {max: 1, time: 300000})
                  .then(collected => {
                      msg.edit(msg.content + '\n' + collected.array()[0]);
                      send_msg.delete();
                      collected.array()[0].delete(); // dont know what permissions the bot will have so... maybe just comment that out
                  });
           });
        return;
    }
    let content_addition = gui.text_for_emote(reaction.emoji.name);
    if (!content_addition) return;
    msg.edit(msg.content + '\n' + content_addition);
}

// fires every time an reaction (emoji) form command-initiator is added to the last message (legende + send/cancel)
function _reactionEventMessageSendCancel(reaction, bot) {
    let emo_name = reaction.emoji.name;
    if ([gui.abort.emoji, gui.send.emoji].indexOf(emo_name) < 0) return;

    if (emo_name === gui.abort.emoji) {
        msgs.map(pair => pair[1].stop()); // stops all collectors and thus removes all emojis from all messages
        return;
    }

    if (emo_name === gui.send.emoji) {
        let remaining_msgs = [];
        for (let pair of msgs) {
            // do not handle the send/cancel message here
            if (pair[0] === 'send/cancel') {
                remaining_msgs.push(pair);
                continue;
            }
            // need to get the message again... content could have canged...
            reaction.message.channel.fetchMessage(pair[0]).then(tmp_msg => {
                let tmp_msg_content_array = tmp_msg.content.split('\n').splice(1);
                let continue_function = true;
                if (tmp_msg_content_array.length === 0) {
                    remaining_msgs.push(pair);
                    continue_function = false;
                }
                if (continue_function) { // kinda ugly but you can't use continue inside a function ofc...
                    let recipient = tmp_msg.mentions.users.array()[0];
                    recipient.send(tmp_msg_content_array.join('\n'))
                             .then(send_msg => {
                                 tmp_msg.edit('**Message successful sent to: ' + recipient + ':**\n' + send_msg.content + '\n')
                                        .then(() => {
                                            pair[1].stop(); // stop the collector that was related to the message
                                        }).catch(console.error);
                             });
                }
            });
        }
        msgs = remaining_msgs;
    }
    // let the bot time to update the msgs array - we dont need to rush the clearing of the emojis and listeners
    bot.setTimeout( () => {
        if (msgs.length === 0 || (msgs.length === 1 && msgs[0][0] === 'send/cancel')) {
            msgs.map(pair => pair[1].stop());
            process.setMaxListeners(10); // reset the maximum of event listeners back to node.js defaults 10
        }
    }, 5000);
}

exports.info = {
    name       : 'nm',
    usage      : 'nm',
    description: 'Shows a menue to send alive (or just killed) players messages what happend last/this night'
};
<commit_msg>Requested changes done<commit_after>// to add options/change gui: simply add/change this
let gui = {
    reset      : {emoji: '♻',  text: 'Reset the content of this message\n'},
    peace      : {emoji: '☮',  text: 'You slept peacefully.\n'},
    custom     : {emoji: '📧', text: 'Click this to add a custom message.\n'},
    roleblocked: {emoji: '🚫', text: 'You got rolebocked last night.\n'},
    jailed     : {emoji: '🏰', text: 'You were jailed last night.\n'},
    controlled : {emoji: '💫', text: 'You got controlled by a Witch last night.\n'},
    mafia_kill : {emoji: '💀', text: 'You got killed by the Mafia last night.\n'},
    sk_kill    : {emoji: '🔫', text: 'You got killed by the Serial Killer last night\n'},
    immune     : {emoji: '👎', text: 'Your target was immune to your attack.\n'},
    bodyguarded: {emoji: '💪', text: 'Your target was protected by the Bodyguard and you were killed by them last night.\n'},
    send       : {emoji: '✅',  text: '\n**You have 30 minutes to use this menu then it will time out!\n' +
                                     'If you are satisfied with the messages click the  ✅ below this message to send all (not empty) messages out.\n' +
                                     'To cancel click the 🇽**'},
    abort      : {emoji: '🇽', text: ''},

    // get the correct text to an emoji
    text_for_emote: function (emoji_name) {
        return Array.from(gui).find(elem => {if (elem.emoji === emoji_name) return elem;}).text;
    },

    // construct text for displaying a legend of which emoji does what
    legend_text: function () {
        return '\n\n\n**Legend:**\n' + Array.from(gui).map(elem => {return elem.emoji + ' : ' + elem.text;}).join('') + this.send.text;
    },

    // Iterator (ignores the 'send' emote keep that in mind)
    [Symbol.iterator]: function*() {
        for (let key in this) {
            if (this.hasOwnProperty(key) && typeof(this[key]) !== 'function' && key !== 'send' && key !== 'abort') yield this[key];
        }
    }
};
let msgs, alive_players;

exports.run = function (bot, msg) {
    if (!msg.member.hasPermission('ADMINISTRATOR') && !bot.mafia.isMod(msg.author.id)) {
        msg.channel.send(':negative_squared_cross_mark:  |  You are not a game moderator.');
        return;
    }

    // The node.js default is 10 open listeners... I need one for each person + one for sending/canceling will be reset to 10 again at the end
    process.setMaxListeners(100);

    msgs = [];
    alive_players = bot.mafia.getPlayers();

    // iterate over all alive players and send one message per player to the (hopefully secret) channel, with emoji-reactions as menu
    msg.channel.send('__**----------- Night Message Menu -----------**__');
    for (let [, user] of alive_players) {
        msg.channel.send('**Send ' + user + ' (alias ' + user.displayName + ') following night message:**\nYou slept peacefully.')
           .then(tmp_msg => {
               // create reaction collector to log the emoji reactions TODO: Make an utilityfunction to create menus with collectors
               let collector = tmp_msg.createReactionCollector((_, user) => [msg.author.id].concat(bot.mafia.getModsID()).indexOf(user.id) > -1, {time: 1800000});

               // save sent message id to edit this message later - to save the whole object wont work b/c conten updates won't affect it (necessarily)
               // save the collector to be able to stop it later - this way it can be easily determend to which message each collector belongs
               msgs.push([tmp_msg.id, collector]);
               // create events - 'collect' triggers every time an emoji get added 'end' when collector stops
               collector.on('collect', reaction => _reactionEventMessageGroup(reaction, msg, bot));
               // clear reactions when the collector stops
               collector.on('end', () => { tmp_msg.clearReactions(); });

               // get gui as array and then everytime one got succesfully posted, post the next one and stop this collector once all are done
               let gui_elems = Array.from(gui);
               let collector2 = tmp_msg.createReactionCollector((_, user) => user.id === bot.user.id, {time: 100000});
               collector2.on('collect', reaction => {
                   if (reaction.message.reactions.array().length < gui_elems.length) {
                       tmp_msg.react(gui_elems[reaction.message.reactions.array().length].emoji).catch(console.log);
                   }
                   else {
                       collector2.stop();
                   }
               });
               tmp_msg.react(gui_elems[0].emoji).catch(console.log); // initial reaction to start the chain reaction
           });
    }

    // send legend & send + cancel button & create reaction collector + store it for later use as above
    msg.channel.send(gui.legend_text())
       .then(tmp_msg => {
           // this somehow fails to make them appear in always the same order... not to important tho
           tmp_msg.react(gui.send.emoji).then(tmp_msg.react(gui.abort.emoji)).catch(console.log);
           let collector = tmp_msg.createReactionCollector((a, user) => [msg.author.id].concat(bot.mafia.getModsID()).indexOf(user.id) > -1, {time: 1800000});
           msgs.push(['send/cancel', collector]);
           collector.on('collect', reaction => _reactionEventMessageSendCancel(reaction, bot));
           collector.on('end', reaction => {
               tmp_msg.clearReactions();
               if (reaction.message !== undefined && reaction.message.reactions.array().length === 0) {
                   tmp_msg.edit('\n\n**Menu timed out... are 30 minutes really not enough? Or did you just forgot to send/cancel?**');
               }
               else {
                   tmp_msg.edit('\n\n**Finished!**');
               }
           });
       });
};

// fires every time a reaction (emoji) from the command-initiator is added to one of the messages
function _reactionEventMessageGroup(reaction, own_msg, bot) {
    let msg = reaction.message;
    if (reaction.emoji.name === gui.reset.emoji) {
        msg.edit(msg.content.split('\n')[0]);
        // This can be used to reset the gui after pressing the reset emoji but it is more irritating than usefull imo...
        // for (let tmp_user of reaction.users.array()) {
        //     if (tmp_user === msg.author) continue;
        //     for (let tmp_reaction of msg.reactions.array())
        //         tmp_reaction.remove(tmp_user);
        // }
        return;
    }
    // Handling of custom messages - only accepts messages from the one who initiated the command
    if (reaction.emoji.name === gui.custom.emoji) {
        msg.channel.send('\n\n**Enter your custom message.**') // + msg.mentions.users.array()[0])
           .then(() => {
               // waits for the next message (timeout 5 min) of the author and add the content of it to this persons 'to send messages' list
               msg.channel.awaitMessages(m => [own_msg.author.id].concat(bot.mafia.getModsID()).indexOf(m.author.id) > -1, {max: 1, time: 300000})
                  .then(collected => {
                      msg.edit(msg.content + '\n' + collected.array()[0]);
                  });
           });
        return;
    }
    let content_addition = gui.text_for_emote(reaction.emoji.name);
    if (!content_addition) return;
    msg.edit(msg.content + '\n' + content_addition);
}

// fires every time an reaction (emoji) form command-initiator is added to the last message (legende + send/cancel)
function _reactionEventMessageSendCancel(reaction, bot) {
    let emo_name = reaction.emoji.name;
    if ([gui.abort.emoji, gui.send.emoji].indexOf(emo_name) < 0) return;

    if (emo_name === gui.abort.emoji) {
        msgs.map(pair => pair[1].stop()); // stops all collectors and thus removes all emojis from all messages
        return;
    }

    if (emo_name === gui.send.emoji) {
        let remaining_msgs = [];
        for (let pair of msgs) {
            // do not handle the send/cancel message here
            if (pair[0] === 'send/cancel') {
                remaining_msgs.push(pair);
                continue;
            }
            // need to get the message again... content could have canged...
            reaction.message.channel.fetchMessage(pair[0]).then(tmp_msg => {
                let recipient_id          = tmp_msg.content.split('<@')[1];
                recipient_id = recipient_id.replace('!','');
                recipient_id = recipient_id.split('>')[0];
                let recipient             = alive_players.get(recipient_id);
                let tmp_msg_content_array = tmp_msg.content.split('\n').splice(1);
                let continue_function     = true;
                if (tmp_msg_content_array.length === 0) {
                    remaining_msgs.push(pair);
                    continue_function = false;
                }
                if (continue_function) { // kinda ugly but you can't use continue inside a function ofc...
                    recipient.send(tmp_msg_content_array.join('\n'))
                             .then(send_msg => {
                                 tmp_msg.edit('**Message successful sent to: ' + recipient + ' (alias ' + recipient.displayName + '):**\n' + send_msg.content + '\n')
                                        .then(() => {
                                            pair[1].stop(); // stop the collector that was related to the message
                                        }).catch(console.error);
                             });
                }
            }).catch(console.error);
        }
        msgs = remaining_msgs;
    }
    // let the bot time to update the msgs array - we dont need to rush the clearing of the emojis and listeners
    bot.setTimeout( () => {
        if (msgs.length === 0 || (msgs.length === 1 && msgs[0][0] === 'send/cancel')) {
            msgs.map(pair => pair[1].stop());
            process.setMaxListeners(10); // reset the maximum of event listeners back to node.js defaults 10
        }
    }, 5000);
}

exports.info = {
    name       : 'nm',
    usage      : 'nm',
    description: 'Shows a menue to send alive (or just killed) players messages what happend last/this night'
};
<|endoftext|>


<commit_before>var {
  Dimensions,
  DeviceEventEmitter,
  NativeModules
} = require('react-native');

var DeviceUtil = NativeModules.DisplayDeviceUtil;

class Display {

  updateProps(newWidth, newHeight) {
    this.width = newWidth;
    this.height = newHeight;
  }

  constructor() {
    this.width = Dimensions.get("window").width;
    this.height = Dimensions.get("window").height;
    //Enable console messages by changing to true: Display.verbose = true
    this.verbose = false;
  }

  percentage(type, value) {
    if (type == 'width') {
      return value * (this.height / 100);
    } else if (type == 'height') {
      return value * (this.width / 100);
    } else {
      return 'Invalid Type (width / height)';
    }
  }

  isPortrait() {
    if (this.width < this.height) {
      return true;
    } else {
      return false;
    }
  }

  isLandscape() {
    if (this.width > this.height) {
      return true;
    } else {
      return false;
    }
  }

  isTablet() {
    return DeviceUtil.isTablet;
  }

  isPhone() {
    return DeviceUtil.isPhone;
  }

  onOrientationDidChange(handler) {
    var main = this;
    return DeviceEventEmitter.addListener(
      'orientationDidChange',
      function(newDimensions) {
        main.updateProps(newDimensions.width, newDimensions.height);
        var orientation = main.updateOrientation(newDimensions.orientation);
        handler(newDimensions.width, newDimensions.height, orientation);
      }
    );
  }
  updateOrientation(orientation) {
    var o = {};
    switch (orientation) {
      case 1:
        o = {
          current: 'portrait',
          idle: false,
          facing: ''
        };
        break;
      case 2:
        o = {
          current: 'portrait',
          idle: false,
          facing: ''
        };
        break;
      case 3:
        o = {
          current: 'landscape',
          idle: false,
          facing: ''
        };
        break;
      case 4:
        o = {
          current: 'landscape',
          idle: false,
          facing: ''
        };
        break;
      case 5:
        o = {
          current: this.orientation.current,
          idle: true,
          facing: 'up'
        };
        break;
      case 6:
        o = {
          current: this.orientation.current,
          idle: true,
          facing: 'down'
        };
        break;
    }
    if (this.verbose) {
      console.log('Orientation Updated');
      console.log(o);
    }
    this.orientation = o;
    return o;
  }
}

module.exports = new Display();
<commit_msg>OCD fix for import order so it's alphabetical.<commit_after>var {
  DeviceEventEmitter,
  Dimensions,
  NativeModules
} = require('react-native');

var DeviceUtil = NativeModules.DisplayDeviceUtil;

class Display {

  updateProps(newWidth, newHeight) {
    this.width = newWidth;
    this.height = newHeight;
  }

  constructor() {
    this.width = Dimensions.get("window").width;
    this.height = Dimensions.get("window").height;
    //Enable console messages by changing to true: Display.verbose = true
    this.verbose = false;
  }

  percentage(type, value) {
    if (type == 'width') {
      return value * (this.height / 100);
    } else if (type == 'height') {
      return value * (this.width / 100);
    } else {
      return 'Invalid Type (width / height)';
    }
  }

  isPortrait() {
    if (this.width < this.height) {
      return true;
    } else {
      return false;
    }
  }

  isLandscape() {
    if (this.width > this.height) {
      return true;
    } else {
      return false;
    }
  }

  isTablet() {
    return DeviceUtil.isTablet;
  }

  isPhone() {
    return DeviceUtil.isPhone;
  }

  onOrientationDidChange(handler) {
    var main = this;
    return DeviceEventEmitter.addListener(
      'orientationDidChange',
      function(newDimensions) {
        main.updateProps(newDimensions.width, newDimensions.height);
        var orientation = main.updateOrientation(newDimensions.orientation);
        handler(newDimensions.width, newDimensions.height, orientation);
      }
    );
  }
  updateOrientation(orientation) {
    var o = {};
    switch (orientation) {
      case 1:
        o = {
          current: 'portrait',
          idle: false,
          facing: ''
        };
        break;
      case 2:
        o = {
          current: 'portrait',
          idle: false,
          facing: ''
        };
        break;
      case 3:
        o = {
          current: 'landscape',
          idle: false,
          facing: ''
        };
        break;
      case 4:
        o = {
          current: 'landscape',
          idle: false,
          facing: ''
        };
        break;
      case 5:
        o = {
          current: this.orientation.current,
          idle: true,
          facing: 'up'
        };
        break;
      case 6:
        o = {
          current: this.orientation.current,
          idle: true,
          facing: 'down'
        };
        break;
    }
    if (this.verbose) {
      console.log('Orientation Updated');
      console.log(o);
    }
    this.orientation = o;
    return o;
  }
}

module.exports = new Display();
<|endoftext|>


<commit_before><commit_msg>fix(QField): prevent focus when clicking on bottom slot #14383 (#14386)<commit_after><|endoftext|>


<commit_before>/**
 * Express configuration
 */
'use strict';

var compress = require('compression');
var favicon = require('serve-favicon');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var path = require('path');
var methodOverride = require('method-override');
var errorHandler = require('errorhandler');<% if (useAuth) { %>
var flash = require('express-flash');
var expressValidator = require('express-validator');
var passport = require('passport');
var auth = require('../auth');
var session = require('express-session');<% } %>

// Configuration files<% if (useAuth) { %>
var secrets = require('./secrets');<% } %>
var settings = require('./env/default');
var security = require('./security');

var expressConfig = function(app, express<% if (dbOption !== 'none') { %>, db<% } %>) {

  var hour = 3600000;
  var day = hour * 24;
  var week = day * 7;

  // Get current server environment
  var env = app.get('env');

  // Remove x-powered-by header (doesn't let clients know we are using Express)
  app.disable('x-powered-by');

  // Setup port for server to run on
  app.set('port', settings.server.port);

   // Setup view engine for server side templating<% if (singlePageApplication) { %>
  app.engine('.html', require('ejs').__express);
  app.set('view engine', 'html');<% } %><% if (!singlePageApplication) { %>
  app.engine('<%= htmlOption === 'jade' ? 'jade' : '' %><%= htmlOption === 'swig' ? 'swig' : '' %>', require('<%= htmlOption %>').renderFile);
  app.set('view engine', '<%= htmlOption === 'jade' ? 'jade' : '' %><%= htmlOption === 'swig' ? 'swig' : '' %>');<% } %>

  // Setup path where all server templates will reside
  app.set('views', path.join(settings.root, 'server/templates'));

  // Enable GZip compression for all static assets
  app.use(compress());

  if (env === 'development') {
    // Include livereload script on all pages
    app.use(require('connect-livereload')());
    // Load bower_components
    app.use(express.static(path.join(settings.root, '.tmp'), {maxAge: 0}));
    app.use('/bower_components', express.static(path.join(settings.root, 'client/bower_components'), {maxAge: 0}));
  }
  // Load favicon
  app.use(favicon(path.join(settings.root, settings.staticAssets, '/favicon.ico')));
  // Load static assets
  app.use(express.static(path.join(settings.root, settings.staticAssets), {maxAge: week}));

  // Returns middleware that parses both json and urlencoded.
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: true }));

  // Returns middleware that parses cookies
  app.use(cookieParser());<% if (useAuth) { %>

  // Initialize form validation
  app.use(expressValidator());

  /**
   * Enable HTTP Method Overrides (POST, GET, DELETE, PUT, etc)
   * Override HTML forms with method="POST" using ?_method=PUT at the end of action URLs
   * ex <form method="POST" action="/someurl?_method=PUT">
   */
  app.use(methodOverride('_method'));

  // override with the X-HTTP-Method-Override header in the request
  app.use(methodOverride('X-HTTP-Method-Override'));

  /**
   * Create cookie that keeps track of user sessions
   */
  app.use(session({
    secret: secrets.sessionSecret,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: true, // Only server can manipulate cookies
      maxAge: day
    }
  }));

  // Initialize Authentication
  auth.init(db.user);
  app.use(passport.initialize());
  app.use(passport.session());

  // Initialize server validation flash messages
  app.use(flash());

  // Initialize Security
  app.use(security);

  app.use(function(req, res, next) {
    // Make Node environment available in templates
    res.locals.env = env;
    // Make user object available in templates.
    res.locals.user = req.user;
    next();
  });

  /**
   * Remember original destination before login.
   * Go back to that original destination once successfully logged in
   * (Unless specified in the ignoredPaths array)
   */
  app.use(function(req, res, next) {
    var path = req.path.split('/')[1];
    var regExp = new RegExp(settings.server.loginIgnorePaths.join('|'), 'i');
    if (regExp.test(path)) {
      return next();
    }
    req.session.returnTo = req.path;
    next();
  });<% } %>

  // Setup log level for server console output
  app.use(logger(settings.server.logLevel));

  if (env === 'development') {

    // Disable caching for easier testing
    app.use(function noCache(req, res, next) {
      res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
      res.header('Pragma', 'no-cache');
      res.header('Expires', 0);
      next();
    });
  }

  // Load routes
  require(path.join(settings.root,'./server/routes'))(app);

  // 404 Error Handler
  app.use(function(req, res) {
    res.status(404);
    res.format({
      html: function() {
        res.render('error', {
          status: 404,
          message: 'Page not found',
          error: {}
        });
      },
      json: function() {
        res.json({
          status: 404,
          message: 'Page not found',
          error: {}
        });
      },
      text: function() {
        res.send(404 + ': ' + 'Page not found');
      }
    });
  });

  if (env === 'development') {
    // Development Error Handler.
    // Log out stack trace
    return app.use(errorHandler());
  }

  // Production Error Handler.
  app.use(function(err, req, res, next) {

    var error = err.error || err;
    var message = err.message;
    var status = err.status || 500;

    res.status(status);
    res.format({
      html: function() {
        res.render('error', {
          status: status,
          message: message,
          error: {}
        });
      },
      json: function() {
        res.json({
          status: status,
          message: message,
          error: {}
        });
      },
      text: function() {
        res.send(status + ': ' + message);
      }
    });
  });

};

module.exports = expressConfig;
<commit_msg>Added no-cache for static assets on development env<commit_after>/**
 * Express configuration
 */
'use strict';

var compress = require('compression');
var favicon = require('serve-favicon');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var path = require('path');
var methodOverride = require('method-override');
var errorHandler = require('errorhandler');<% if (useAuth) { %>
var flash = require('express-flash');
var expressValidator = require('express-validator');
var passport = require('passport');
var auth = require('../auth');
var session = require('express-session');<% } %>

// Configuration files<% if (useAuth) { %>
var secrets = require('./secrets');<% } %>
var settings = require('./env/default');
var security = require('./security');

var expressConfig = function(app, express<% if (dbOption !== 'none') { %>, db<% } %>) {

  var hour = 3600000;
  var day = hour * 24;
  var week = day * 7;

  // Get current server environment
  var env = app.get('env');

  // Remove x-powered-by header (doesn't let clients know we are using Express)
  app.disable('x-powered-by');

  // Setup port for server to run on
  app.set('port', settings.server.port);

   // Setup view engine for server side templating<% if (singlePageApplication) { %>
  app.engine('.html', require('ejs').__express);
  app.set('view engine', 'html');<% } %><% if (!singlePageApplication) { %>
  app.engine('<%= htmlOption === 'jade' ? 'jade' : '' %><%= htmlOption === 'swig' ? 'swig' : '' %>', require('<%= htmlOption %>').renderFile);
  app.set('view engine', '<%= htmlOption === 'jade' ? 'jade' : '' %><%= htmlOption === 'swig' ? 'swig' : '' %>');<% } %>

  // Setup path where all server templates will reside
  app.set('views', path.join(settings.root, 'server/templates'));

  // Enable GZip compression for all static assets
  app.use(compress());

  if (env === 'development') {
    // Include livereload script on all pages
    app.use(require('connect-livereload')());
    // Load bower_components
    app.use(express.static(path.join(settings.root, '.tmp'), {maxAge: 0}));
    app.use('/bower_components', express.static(path.join(settings.root, 'client/bower_components'), {maxAge: 0}));

    // Load static assets with no-cache
    app.use(express.static(path.join(settings.root, settings.staticAssets), {maxAge: 0}));
  }
  // Load favicon
  app.use(favicon(path.join(settings.root, settings.staticAssets, '/favicon.ico')));
  if (env !== 'development') {
    // Load static assets cached
    app.use(express.static(path.join(settings.root, settings.staticAssets), {maxAge: week}));
  }

  // Returns middleware that parses both json and urlencoded.
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: true }));

  // Returns middleware that parses cookies
  app.use(cookieParser());<% if (useAuth) { %>

  // Initialize form validation
  app.use(expressValidator());

  /**
   * Enable HTTP Method Overrides (POST, GET, DELETE, PUT, etc)
   * Override HTML forms with method="POST" using ?_method=PUT at the end of action URLs
   * ex <form method="POST" action="/someurl?_method=PUT">
   */
  app.use(methodOverride('_method'));

  // override with the X-HTTP-Method-Override header in the request
  app.use(methodOverride('X-HTTP-Method-Override'));

  /**
   * Create cookie that keeps track of user sessions
   */
  app.use(session({
    secret: secrets.sessionSecret,
    saveUninitialized: true,
    resave: true,
    cookie: {
      httpOnly: true, // Only server can manipulate cookies
      maxAge: day
    }
  }));

  // Initialize Authentication
  auth.init(db.user);
  app.use(passport.initialize());
  app.use(passport.session());

  // Initialize server validation flash messages
  app.use(flash());

  // Initialize Security
  app.use(security);

  app.use(function(req, res, next) {
    // Make Node environment available in templates
    res.locals.env = env;
    // Make user object available in templates.
    res.locals.user = req.user;
    next();
  });

  /**
   * Remember original destination before login.
   * Go back to that original destination once successfully logged in
   * (Unless specified in the ignoredPaths array)
   */
  app.use(function(req, res, next) {
    var path = req.path.split('/')[1];
    var regExp = new RegExp(settings.server.loginIgnorePaths.join('|'), 'i');
    if (regExp.test(path)) {
      return next();
    }
    req.session.returnTo = req.path;
    next();
  });<% } %>

  // Setup log level for server console output
  app.use(logger(settings.server.logLevel));

  if (env === 'development') {

    // Disable caching for easier testing
    app.use(function noCache(req, res, next) {
      res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
      res.header('Pragma', 'no-cache');
      res.header('Expires', 0);
      next();
    });
  }

  // Load routes
  require(path.join(settings.root,'./server/routes'))(app);

  // 404 Error Handler
  app.use(function(req, res) {
    res.status(404);
    res.format({
      html: function() {
        res.render('error', {
          status: 404,
          message: 'Page not found',
          error: {}
        });
      },
      json: function() {
        res.json({
          status: 404,
          message: 'Page not found',
          error: {}
        });
      },
      text: function() {
        res.send(404 + ': ' + 'Page not found');
      }
    });
  });

  if (env === 'development') {
    // Development Error Handler.
    // Log out stack trace
    return app.use(errorHandler());
  }

  // Production Error Handler.
  app.use(function(err, req, res, next) {

    var error = err.error || err;
    var message = err.message;
    var status = err.status || 500;

    res.status(status);
    res.format({
      html: function() {
        res.render('error', {
          status: status,
          message: message,
          error: {}
        });
      },
      json: function() {
        res.json({
          status: status,
          message: message,
          error: {}
        });
      },
      text: function() {
        res.send(status + ': ' + message);
      }
    });
  });

};

module.exports = expressConfig;
<|endoftext|>


<commit_before>// javascript functions

//globals
var sections;
var userPosition;
var map;

//init globals
function initPage(){
  sections = [
    document.getElementById("home_section"),
    document.getElementById("about_section"),
    document.getElementById("locations_section"),
    document.getElementById("media_section"),
    document.getElementById("contact_section"),
    ];

    if(navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        setLocation, fail, {timeout:10000});
    } else {
      fail();
    }
}

function initMap(){
  var center = null;
  if(userPosition){
    center = userPosition;
  } else {
    center = new google.maps.LatLng(38.898013, -77.036539);
  }

  var mapOptions = {
    center: center,
    zoom: 8 };
  map = new google.maps.Map(
    document.getElementById("locationMap"),
    mapOptions);

  addMarker(38.885492, -77.097200, "Arlington, VA");
  addMarker(38.916833, -77.226124, "McLean, VA");
  addMarker(38.998082, -76.910128, "Greenbelt, MD");
}

function addMarker(lat, lng, title) {
  var mark = new google.maps.Marker({
    position: new google.maps.LatLng(lat, lng),
    map: map,
    title: title
  });
}

function showSection(name){
  sections.forEach(function (item, index, array) {
    item.style.display = "none";
    if(item.id.indexOf(name) == 0){
      item.style.display = "";
    }
  });
}

function setLocation(position) {
  console.log("Latitude: " + position.coords.latitude);
  console.log("Longitude: " + position.coords.longitude);
  userPosition = position;

}

function fail() {
  console.log("Geolocation information not available or not authorized.");
  userPosition = null;
}
<commit_msg>Add console debug<commit_after>// javascript functions

//globals
var sections;
var userPosition;
var map;

//init globals
function initPage(){
  sections = [
    document.getElementById("home_section"),
    document.getElementById("about_section"),
    document.getElementById("locations_section"),
    document.getElementById("media_section"),
    document.getElementById("contact_section"),
    ];

    if(navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        setLocation, fail, {timeout:10000});
    } else {
      fail();
    }
}

function initMap(){
  var center = null;
  if(userPosition){
    center = userPosition;
  } else {
    center = new google.maps.LatLng(38.898013, -77.036539);
  }

  console.log("Center position: " + center);

  var mapOptions = {
    center: center,
    zoom: 8 };
  map = new google.maps.Map(
    document.getElementById("locationMap"),
    mapOptions);

  addMarker(38.885492, -77.097200, "Arlington, VA");
  addMarker(38.916833, -77.226124, "McLean, VA");
  addMarker(38.998082, -76.910128, "Greenbelt, MD");
}

function addMarker(lat, lng, title) {
  var mark = new google.maps.Marker({
    position: new google.maps.LatLng(lat, lng),
    map: map,
    title: title
  });
}

function showSection(name){
  sections.forEach(function (item, index, array) {
    item.style.display = "none";
    if(item.id.indexOf(name) == 0){
      item.style.display = "";
    }
  });
}

function setLocation(position) {
  console.log("Latitude: " + position.coords.latitude);
  console.log("Longitude: " + position.coords.longitude);
  userPosition = position;

}

function fail() {
  console.log("Geolocation information not available or not authorized.");
  userPosition = null;
}
<|endoftext|>


<commit_before>'use strict';
/* This service handles everything related to configuration files
 *
 * It is a provider of resources and services.
 *
 * this.configuration needs to be set from the outside through
 * defineConfiguration(), typically by a route that enters the application.
 *
 *
 *
 * As of now a valid conf file contains four sections
 *   main
 *   navbar
 *   plugins
 *   resources
 *
 * Uses $http to retrieve additional configuration files that are embedded
 * through the fileUrl property.
 * This could eventually be replaced by using the arethusa resource service
 * once it's clear where such conf files will be stored. Or maybe not,
 * we'll see.
 *
 */
angular.module('arethusa.core').service('configurator', [
  '$injector',
  '$http',
  '$rootScope',
  'Resource',
  '$timeout',
  function ($injector, $http, $rootScope, Resource, $timeout) {
    var self = this;
    var includeParam = 'fileUrl';
    var filesToInclude = function (obj) {
      return arethusaUtil.findNestedProperties(obj, includeParam)[includeParam];
    };
    var loadStates = {};
    var checkLoadStatus = function () {
      var loaded = true;
      angular.forEach(loadStates, function (loadState, obj) {
        loaded = loaded && loadState;
      });
      if (loaded) {
        broadcastLoading();
      }
    };
    var broadcastLoading = function () {
      $rootScope.$broadcast('confLoaded');
    };
    // While using a timeout might seem hacky, it's just the write tool for the
    // job.
    // A run of the program looks like this:
    //
    // 1 Route with resolve function waits for the configurator to start
    // 2 Route resolves to start the MainCtrl
    // 3 MainCtrl waits for confLoaded event
    // 4 MainCtrl initializes the State object
    // 5 MainCtrl waits for stateLoaded event
    // 6 MainCtrl initializes itself and all plugins
    //
    // When the configurator needs to load external files (in 1)
    // the route resolves before the configurator is finished.
    // Only when it is finished the confLoaded event is fired.
    // External files are loaded asynchonously, the event is fired
    // from within a callback.
    // It is therefore guaranteed to happen after 2. Step 2 starts
    // the MainCtrl - it is the place where the listener to the
    // confLoaded event is defined.
    //
    // If there are no external files to load, the configurator gets
    // a chance to fire his confLoaded event while still being in step 1.
    // The event is fired, but as 2 hasn't run yet, there are no listeners.
    // The application fails to load properly.
    //
    // We therefore wrap the confLoaded event in a $timeout function.
    // Timeout with a delay of 0 defers the execution of the wrapped
    // function to the end of the current execution queue.
    // This is just what we need. Step 2 is already in the execution queue
    // at this time - if we can guarantee that the confLoaded event
    // happens after 2, everything is fine.
    var delayedBroadcast = function () {
      $timeout(function () {
        broadcastLoading();
      });
    };
    var includeExternalFiles = function (arrayOfObjects) {
      angular.forEach(arrayOfObjects, function (obj, i) {
        loadStates[obj] = false;
        $http.get(obj[includeParam]).then(function (res) {
          // We have to delete fileUrl upfront.
          // When the object gets extended by a response, this response
          // might contain another fileUrl property. If that is the case
          // we then go on to look for such properties and call the function
          // recursively.
          delete obj[includeParam];
          angular.extend(obj, res.data);
          // As we want to load all potential external conf file BEFORE the application
          // starts, we have to apply this little trick:
          // Before the asynchronous call to retrieve an external file is made, we
          // declare a false load status, which we resolve inside the callback.
          //
          // Once we say we are loaded, we immediately check if our now extended object
          // has another external file to load. If it does, the load status of our
          // object will be immediately false again.
          // On the end of each callback we check all load states. If everything has
          // loaded successfully, we broadcast an event an let others now that the
          // configurator is finished.
          loadStates[obj] = true;
          includeExternalFiles(filesToInclude(obj));
          checkLoadStatus();
        });
      });
    };
    // Receives an external file and resolves it to a valid configuration file.
    // The second param is optional.
    this.defineConfiguration = function (confFile, location) {
      var conf = this.loadConfFile(confFile, location);
      this.configuration = conf.data;
      this.confFileLocation = conf.location;
    };
    this.loadConfFile = function (confFile, location) {
      var conf = {
          location: location,
          data: confFile
        };
      var fti = filesToInclude(conf.data);
      if (fti.length === 0) {
        delayedBroadcast();
      } else {
        // this function will eventually broadcast the load event
        includeExternalFiles(fti);
      }
      return conf;
    };
    // Returns an empty configuration files with all sections
    // as empty object properties.
    // Useful for the configuration editor.
    this.getConfTemplate = function () {
      return {
        main: {},
        navbar: {},
        plugins: {},
        resources: {}
      };
    };
    // Merges two configuration objects.
    // There is a clear contract that has to be fulfilled to make this work:
    //
    // The datatypes of individual properties need to be static.
    // E.g.
    //
    // {
    //   plugins: {
    //     morph: {
    //       retrievers: ['x']
    //     }
    //   }
    // }
    //
    // If plugins.morph.retrievers is an Array, it can only be an Array and nothing
    // else. The same goes for Objects, Strings, and Numbers.
    //
    // Objects call the function recursively.
    // Arrays are flat-pushed.
    // Strings and Numbers are overwritten.
    // a is extended with properties in b, that are not present in a.
    this.mergeConfigurations = function (a, b) {
      var that = this;
      angular.forEach(b, function (value, key) {
        var origVal = a[key];
        if (origVal) {
          // Every Array is an Object, but not every Object is an Array!
          // This defines the order of the if-else conditional.
          if (angular.isArray(origVal)) {
            arethusaUtil.pushAll(origVal, value);
          } else if (angular.isObject(origVal)) {
            that.mergeConfigurations(origVal, value);
          } else {
            a[key] = value;
          }
        } else {
          a[key] = value;
        }
      });
      return a;
    };
    this.getService = function (serviceName) {
      return $injector.get(serviceName);
    };
    this.getServices = function (serviceNames) {
      if (serviceNames) {
        var that = this;
        // inject to an object, we want the names as well
        return arethusaUtil.inject({}, serviceNames, function (obj, name) {
          obj[name] = that.getService(name);
        });
      } else {
        return {};
      }
    };
    // right now very hacky, not sure about the design of the conf file atm
    // we therefore just tell the service where the conf for specific things
    // is to be found in the JSON tree.
    // I guess the key is to abstract the conf file a little more.
    this.configurationFor = function (plugin) {
      var conf = this.configuration;
      return conf[plugin] || conf.plugins[plugin] || conf.resources[plugin];
    };
    function standardProperties() {
      return [
        'name',
        'main',
        'template',
        'external',
        'listener',
        'contextMenu',
        'contextMenuTemplate',
        'noView'
      ];
    }
    // Delegates a set of standard properties to the given object to allow
    // a more direct access.
    this.delegateConf = function (obj, otherKeys) {
      var props = arethusaUtil.pushAll(standardProperties(), otherKeys);
      angular.forEach(props, function (property, i) {
        obj[property] = obj.conf[property];
      });
    };
    this.getConfAndDelegate = function (name, obj, keys) {
      obj.conf = self.configurationFor(name);
      self.delegateConf(obj, keys);
      return obj;
    };
    this.getRetrievers = function (retrievers) {
      return arethusaUtil.inject({}, retrievers, function (memo, name, conf) {
        var Retriever = self.getService(name);
        memo[name] = new Retriever(conf);
      });
    };
    this.getRetriever = function(retrievers) {
      var retrs = self.getRetrievers(retrievers);
      return retrs[Object.keys(retrs)[0]];
    };
    this.provideResource = function (name) {
      var conf = this.configuration.resources[name];
      return new Resource(conf);
    };
  }
]);
<commit_msg>Reformatting configurator<commit_after>'use strict';
/* This service handles everything related to configuration files
 *
 * It is a provider of resources and services.
 *
 * this.configuration needs to be set from the outside through
 * defineConfiguration(), typically by a route that enters the application.
 *
 *
 *
 * As of now a valid conf file contains four sections
 *   main
 *   navbar
 *   plugins
 *   resources
 *
 * Uses $http to retrieve additional configuration files that are embedded
 * through the fileUrl property.
 * This could eventually be replaced by using the arethusa resource service
 * once it's clear where such conf files will be stored. Or maybe not,
 * we'll see.
 *
 */
angular.module('arethusa.core').service('configurator', [
  '$injector',
  '$http',
  '$rootScope',
  'Resource',
  '$timeout',
  function ($injector, $http, $rootScope, Resource, $timeout) {
    var self = this;
    var includeParam = 'fileUrl';

    function filesToInclude(obj) {
      return arethusaUtil.findNestedProperties(obj, includeParam)[includeParam];
    }

    var loadStates = {};

    function checkLoadStatus() {
      var loaded = true;
      angular.forEach(loadStates, function (loadState, obj) {
        loaded = loaded && loadState;
      });
      if (loaded) {
        broadcastLoading();
      }
    }

    function broadcastLoading() {
      $rootScope.$broadcast('confLoaded');
    }

    // While using a timeout might seem hacky, it's just the write tool for the
    // job.
    // A run of the program looks like this:
    //
    // 1 Route with resolve function waits for the configurator to start
    // 2 Route resolves to start the MainCtrl
    // 3 MainCtrl waits for confLoaded event
    // 4 MainCtrl initializes the State object
    // 5 MainCtrl waits for stateLoaded event
    // 6 MainCtrl initializes itself and all plugins
    //
    // When the configurator needs to load external files (in 1)
    // the route resolves before the configurator is finished.
    // Only when it is finished the confLoaded event is fired.
    // External files are loaded asynchonously, the event is fired
    // from within a callback.
    // It is therefore guaranteed to happen after 2. Step 2 starts
    // the MainCtrl - it is the place where the listener to the
    // confLoaded event is defined.
    //
    // If there are no external files to load, the configurator gets
    // a chance to fire his confLoaded event while still being in step 1.
    // The event is fired, but as 2 hasn't run yet, there are no listeners.
    // The application fails to load properly.
    //
    // We therefore wrap the confLoaded event in a $timeout function.
    // Timeout with a delay of 0 defers the execution of the wrapped
    // function to the end of the current execution queue.
    // This is just what we need. Step 2 is already in the execution queue
    // at this time - if we can guarantee that the confLoaded event
    // happens after 2, everything is fine.
    function delayedBroadcast() {
      $timeout(function () {
        broadcastLoading();
      });
    }

    function includeExternalFiles(arrayOfObjects) {
      angular.forEach(arrayOfObjects, function (obj, i) {
        loadStates[obj] = false;
        $http.get(obj[includeParam]).then(function (res) {
          // We have to delete fileUrl upfront.
          // When the object gets extended by a response, this response
          // might contain another fileUrl property. If that is the case
          // we then go on to look for such properties and call the function
          // recursively.
          delete obj[includeParam];
          angular.extend(obj, res.data);
          // As we want to load all potential external conf file BEFORE the application
          // starts, we have to apply this little trick:
          // Before the asynchronous call to retrieve an external file is made, we
          // declare a false load status, which we resolve inside the callback.
          //
          // Once we say we are loaded, we immediately check if our now extended object
          // has another external file to load. If it does, the load status of our
          // object will be immediately false again.
          // On the end of each callback we check all load states. If everything has
          // loaded successfully, we broadcast an event an let others now that the
          // configurator is finished.
          loadStates[obj] = true;
          includeExternalFiles(filesToInclude(obj));
          checkLoadStatus();
        });
      });
    }

    // Receives an external file and resolves it to a valid configuration file.
    // The second param is optional.
    this.defineConfiguration = function (confFile, location) {
      var conf = this.loadConfFile(confFile, location);
      this.configuration = conf.data;
      this.confFileLocation = conf.location;
    };

    this.loadConfFile = function (confFile, location) {
      var conf = {
          location: location,
          data: confFile
        };
      var fti = filesToInclude(conf.data);
      if (fti.length === 0) {
        delayedBroadcast();
      } else {
        // this function will eventually broadcast the load event
        includeExternalFiles(fti);
      }
      return conf;
    };

    // Returns an empty configuration files with all sections
    // as empty object properties.
    // Useful for the configuration editor.
    this.getConfTemplate = function () {
      return {
        main: {},
        navbar: {},
        plugins: {},
        resources: {}
      };
    };

    // Merges two configuration objects.
    // There is a clear contract that has to be fulfilled to make this work:
    //
    // The datatypes of individual properties need to be static.
    // E.g.
    //
    // {
    //   plugins: {
    //     morph: {
    //       retrievers: ['x']
    //     }
    //   }
    // }
    //
    // If plugins.morph.retrievers is an Array, it can only be an Array and nothing
    // else. The same goes for Objects, Strings, and Numbers.
    //
    // Objects call the function recursively.
    // Arrays are flat-pushed.
    // Strings and Numbers are overwritten.
    // a is extended with properties in b, that are not present in a.
    this.mergeConfigurations = function (a, b) {
      var that = this;
      angular.forEach(b, function (value, key) {
        var origVal = a[key];
        if (origVal) {
          // Every Array is an Object, but not every Object is an Array!
          // This defines the order of the if-else conditional.
          if (angular.isArray(origVal)) {
            arethusaUtil.pushAll(origVal, value);
          } else if (angular.isObject(origVal)) {
            that.mergeConfigurations(origVal, value);
          } else {
            a[key] = value;
          }
        } else {
          a[key] = value;
        }
      });
      return a;
    };

    this.getService = function (serviceName) {
      return $injector.get(serviceName);
    };

    this.getServices = function (serviceNames) {
      if (serviceNames) {
        var that = this;
        // inject to an object, we want the names as well
        return arethusaUtil.inject({}, serviceNames, function (obj, name) {
          obj[name] = that.getService(name);
        });
      } else {
        return {};
      }
    };

    // right now very hacky, not sure about the design of the conf file atm
    // we therefore just tell the service where the conf for specific things
    // is to be found in the JSON tree.
    // I guess the key is to abstract the conf file a little more.
    this.configurationFor = function (plugin) {
      var conf = this.configuration;
      return conf[plugin] || conf.plugins[plugin] || conf.resources[plugin];
    };

    function standardProperties() {
      return [
        'name',
        'main',
        'template',
        'external',
        'listener',
        'contextMenu',
        'contextMenuTemplate',
        'noView'
      ];
    }

    // Delegates a set of standard properties to the given object to allow
    // a more direct access.
    this.delegateConf = function (obj, otherKeys) {
      var props = arethusaUtil.pushAll(standardProperties(), otherKeys);
      angular.forEach(props, function (property, i) {
        obj[property] = obj.conf[property];
      });
    };

    this.getConfAndDelegate = function (name, obj, keys) {
      obj.conf = self.configurationFor(name);
      self.delegateConf(obj, keys);
      return obj;
    };

    this.getRetrievers = function (retrievers) {
      return arethusaUtil.inject({}, retrievers, function (memo, name, conf) {
        var Retriever = self.getService(name);
        memo[name] = new Retriever(conf);
      });
    };

    this.getRetriever = function(retrievers) {
      var retrs = self.getRetrievers(retrievers);
      return retrs[Object.keys(retrs)[0]];
    };

    this.provideResource = function (name) {
      var conf = this.configuration.resources[name];
      return new Resource(conf);
    };
  }
]);
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import FileDrop from 'react-file-drop'
import ipc from 'electron-safe-ipc/guest'
import Radium from 'radium'

import SimpleStat from '../simple-stat'
import IconButton from '../icon-button'
import Header from '../header'
import Icon from '../icon'

import 'normalize.css'
import 'css-box-sizing-border-box/index.css'
import '../../../styles/common.css'
import '../../../styles/fonts.css'
import '../../../styles/file-drop.less'

@Radium
export default class ProfileScreen extends React.Component {

  static propTypes = {
    peers: PropTypes.number,
    location: PropTypes.string,
    onStopClick: PropTypes.func,
    onConsoleClick: PropTypes.func,
    onBrowserClick: PropTypes.func,
    onSettingsClick: PropTypes.func,
    onCloseClick: PropTypes.func
  }

  static defaultProps = {
    peers: 0,
    location: '',
    onStopClick () {},
    onConsoleClick () {},
    onBrowserClick () {},
    onSettingsClick () {},
    onCloseClick () {}
  }

  _onFileDrop = (files, event) => {
    const filesArray = []
    for (let i = 0; i < files.length; i++) {
      filesArray.push(files[i].path)
    }

    ipc.send('drop-files', null, filesArray)
  }

  render () {
    const styles = {
      wrapper: {
        display: 'flex',
        width: '100%',
        height: '100%',
        backgroundColor: '#19b5fe',
        color: '#FFFFFF',
        flexDirection: 'column',
        alignItems: 'center'
      },
      image: {
        display: 'flex',
        flex: '1',
        color: '#FFFFFF',
        backgroundImage: `url(${require('../../../img/stars.png')})`,
        backgroundSize: 'cover',
        backgroundPosition: '0 0',
        width: '100%',
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column'
      },
      stats: {
        display: 'flex',
        flex: '1',
        backgroundColor: '#FFFFFF',
        color: '#000000',
        width: '100%',
        height: '30%',
        justifyContent: 'space-around'
      },
      footer: {
        display: 'flex',
        height: '70px',
        justifyContent: 'space-around',
        width: '100%'
      }
    }

    return (

        <FileDrop
          onDrop={this._onFileDrop}
          />

          <Icon name='location' style={{padding: '10px 0', fontSize: '32px'}}/>

            {this.props.location}

          <SimpleStat
            name='peers'
            value={this.props.peers}
            color='#50d2c2'
            />

          <IconButton
            name='Console'
            icon='gaming'
            onClick={this.props.onConsoleClick}
            />
          <IconButton
            name='Browser'
            icon='window'
            onClick={this.props.onBrowserClick}
            />
          <IconButton
            name='Quit'
            icon='media-stop'
            onClick={this.props.onStopClick}
            />

    )
  }
}
<commit_msg>fix: Rename Quit to Stop<commit_after>import React, {PropTypes} from 'react'
import FileDrop from 'react-file-drop'
import ipc from 'electron-safe-ipc/guest'
import Radium from 'radium'

import SimpleStat from '../simple-stat'
import IconButton from '../icon-button'
import Header from '../header'
import Icon from '../icon'

import 'normalize.css'
import 'css-box-sizing-border-box/index.css'
import '../../../styles/common.css'
import '../../../styles/fonts.css'
import '../../../styles/file-drop.less'

@Radium
export default class ProfileScreen extends React.Component {

  static propTypes = {
    peers: PropTypes.number,
    location: PropTypes.string,
    onStopClick: PropTypes.func,
    onConsoleClick: PropTypes.func,
    onBrowserClick: PropTypes.func,
    onSettingsClick: PropTypes.func,
    onCloseClick: PropTypes.func
  }

  static defaultProps = {
    peers: 0,
    location: '',
    onStopClick () {},
    onConsoleClick () {},
    onBrowserClick () {},
    onSettingsClick () {},
    onCloseClick () {}
  }

  _onFileDrop = (files, event) => {
    const filesArray = []
    for (let i = 0; i < files.length; i++) {
      filesArray.push(files[i].path)
    }

    ipc.send('drop-files', null, filesArray)
  }

  render () {
    const styles = {
      wrapper: {
        display: 'flex',
        width: '100%',
        height: '100%',
        backgroundColor: '#19b5fe',
        color: '#FFFFFF',
        flexDirection: 'column',
        alignItems: 'center'
      },
      image: {
        display: 'flex',
        flex: '1',
        color: '#FFFFFF',
        backgroundImage: `url(${require('../../../img/stars.png')})`,
        backgroundSize: 'cover',
        backgroundPosition: '0 0',
        width: '100%',
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column'
      },
      stats: {
        display: 'flex',
        flex: '1',
        backgroundColor: '#FFFFFF',
        color: '#000000',
        width: '100%',
        height: '30%',
        justifyContent: 'space-around'
      },
      footer: {
        display: 'flex',
        height: '70px',
        justifyContent: 'space-around',
        width: '100%'
      }
    }

    return (

        <FileDrop
          onDrop={this._onFileDrop}
          />

          <Icon name='location' style={{padding: '10px 0', fontSize: '32px'}}/>

            {this.props.location}

          <SimpleStat
            name='peers'
            value={this.props.peers}
            color='#50d2c2'
            />

          <IconButton
            name='Console'
            icon='gaming'
            onClick={this.props.onConsoleClick}
            />
          <IconButton
            name='Browser'
            icon='window'
            onClick={this.props.onBrowserClick}
            />
          <IconButton
            name='Stop'
            icon='media-stop'
            onClick={this.props.onStopClick}
            />

    )
  }
}
<|endoftext|>