<commit_before>/**
 * Helper method to handle List operations, e.g. creating items, deleting items,
 * getting information about those lists, etc.
 */

const listToArray = require('list-to-array');
const qs = require('qs');
const xhr = require('xhr');

/**
 * Get the columns of a list, structured by fields and headings
 *
 * @param  {Object} list The list we want the columns of
 *
 * @return {Array}       The columns
 */
function getColumns (list) {
	return list.uiElements.map((col) => {
		if (col.type === 'heading') {
			return { type: 'heading', content: col.content };
		} else {
			var field = list.fields[col.field];
			return field ? { type: 'field', field: field, title: field.label, path: field.path } : null;
		}
	}).filter(i => i);
}

/**
 * Make an array of filters an object keyed by the filtering path
 *
 * @param  {Array} filterArray The array of filters
 *
 * @return {Object}            The corrected filters, keyed by path
 */
function getFilters (filterArray) {
	var filters = {};
	filterArray.forEach((filter) => {
		filters[filter.field.path] = filter.value;
	});
	return filters;
};

/**
 * Get the sorting string for the URI
 *
 * @param  {Array} sort.paths The paths we want to sort
 *
 * @return {String}           All the sorting queries we want as a string
 */
function getSortString (sort) {
	return sort.paths.map(i => {
		return i.invert ? '-' + i.path : i.path;
	}).filter(i => i).join(',');
};

/**
 * Build a query string from a bunch of options
 */
function buildQueryString (options) {
	const query = {};
	if (options.search) query.search = options.search;
	if (options.filters.length) query.filters = JSON.stringify(getFilters(options.filters));
	if (options.columns) query.select = options.columns.map(i => i.path).join(',');
	if (options.page && options.page.size) query.limit = options.page.size;
	if (options.page && options.page.index > 1) query.skip = (options.page.index - 1) * options.page.size;
	if (options.sort) query.sort = getSortString(options.sort);
	query.expandRelationshipFields = true;
	return '?' + qs.stringify(query);
};

/**
 * The main list helper class
 *
 * @param {Object} options
 */
const List = function (options) {
	Object.assign(this, options);
	this.columns = getColumns(this);
	this.expandedDefaultColumns = this.expandColumns(this.defaultColumns);
	this.defaultColumnPaths = this.expandedDefaultColumns.map(i => i.path).join(',');
};

/**
 * Create an item via the API
 *
 * @param  {FormData} formData The submitted form data
 * @param  {Function} callback Called after the API call
 */
List.prototype.createItem = function (formData, callback) {
	xhr({
		url: `${Keystone.adminPath}/api/${this.path}/create`,
		responseType: 'json',
		method: 'POST',
		headers: Keystone.csrf.header,
		body: formData,
	}, (err, resp, data) => {
		if (err) callback(err);
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			// NOTE: xhr callback will be called with an Error if
			//  there is an error in the browser that prevents
			//  sending the request. A HTTP 500 response is not
			//  going to cause an error to be returned.
			callback(data, null);
		}
	});
};

/**
 * Update a specific item
 *
 * @param  {String}   id       The id of the item we want to update
 * @param  {FormData} formData The submitted form data
 * @param  {Function} callback Called after the API call
 */
List.prototype.updateItem = function (id, formData, callback) {
	xhr({
		url: `${Keystone.adminPath}/api/${this.path}/${id}`,
		responseType: 'json',
		method: 'POST',
		headers: Keystone.csrf.header,
		body: formData,
	}, (err, resp, data) => {
		if (err) callback(err);
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

List.prototype.expandColumns = function (input) {
	let nameIncluded = false;
	const cols = listToArray(input).map(i => {
		const split = i.split('|');
		let path = split[0];
		if (path === '__name__') {
			path = this.namePath;
		}
		if (path === this.namePath) {
			nameIncluded = true;
		}
		const field = this.fields[path];
		if (!field) {
			// TODO: Support arbitary document paths
			console.warn('Invalid Column specified:', i);
			return;
		}
		return {
			field: field,
			type: field.type,
			label: field.label,
			path: field.path,
		};
	}).filter(i => i);
	if (!nameIncluded) {
		cols.unshift({
			type: 'id',
			label: 'ID',
			path: 'id',
		});
	}
	return cols;
};

List.prototype.expandSort = function (input) {
	const sort = {
		rawInput: input || this.defaultSort,
		isDefaultSort: false,
	};
	sort.input = sort.rawInput;
	if (sort.input === '__default__') {
		sort.isDefaultSort = true;
		sort.input = this.sortable ? 'sortOrder' : this.namePath;
	}
	sort.paths = listToArray(sort.input).map(path => {
		let invert = false;
		if (path.charAt(0) === '-') {
			invert = true;
			path = path.substr(1);
		}
		const field = this.fields[path];
		if (!field) {
			// TODO: Support arbitary document paths
			console.warn('Invalid Sort specified:', path);
			return;
		}
		return {
			field: field,
			type: field.type,
			label: field.label,
			path: field.path,
			invert: invert,
		};
	}).filter(i => i);
	return sort;
};

/**
 * Load a specific item via the API
 *
 * @param  {String}   itemId   The id of the item we want to load
 * @param  {Object}   options
 * @param  {Function} callback
 */
List.prototype.loadItem = function (itemId, options, callback) {
	if (arguments.length === 2 && typeof options === 'function') {
		callback = options;
		options = null;
	}
	let url = Keystone.adminPath + '/api/' + this.path + '/' + itemId;
	const query = qs.stringify(options);
	if (query.length) url += '?' + query;
	xhr({
		url: url,
		responseType: 'json',
	}, (err, resp, data) => {
		if (err) return callback(err);
		// Pass the data as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

/**
 * Load all items of a list, optionally passing objects to build a query string
 * for sorting or searching
 *
 * @param  {Object}   options
 * @param  {Function} callback
 */
List.prototype.loadItems = function (options, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + buildQueryString(options);
	xhr({
		url: url,
		responseType: 'json',
	}, (err, resp, data) => {
		if (err) callback(err);
		// Pass the data as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

/**
 * Constructs a download URL to download a list with the current sorting, filtering,
 * selection and searching options
 *
 * @param  {Object} options
 *
 * @return {String}         The download URL
 */
List.prototype.getDownloadURL = function (options) {
	const url = Keystone.adminPath + '/api/' + this.path;
	const parts = [];
	if (options.format !== 'json') {
		options.format = 'csv';
	}
	parts.push(options.search ? 'search=' + options.search : '');
	parts.push(options.filters.length ? 'filters=' + JSON.stringify(getFilters(options.filters)) : '');
	parts.push(options.columns ? 'select=' + options.columns.map(i => i.path).join(',') : '');
	parts.push(options.sort ? 'sort=' + getSortString(options.sort) : '');
	parts.push('expandRelationshipFields=true');
	return url + '/export.' + options.format + '?' + parts.filter(i => i).join('&');
};

/**
 * Delete a specific item via the API
 *
 * @param  {String}   itemId   The id of the item we want to delete
 * @param  {Function} callback
 */
List.prototype.deleteItem = function (itemId, callback) {
	this.deleteItems([itemId], callback);
};

/**
 * Delete multiple items at once via the API
 *
 * @param  {Array}   itemIds  An array of ids of items we want to delete
 * @param  {Function} callback
 */
List.prototype.deleteItems = function (itemIds, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + '/' + itemIds.join(',') + '/delete';
	xhr({
		url: url,
		method: 'POST',
		headers: Keystone.csrf.header,
	}, (err, resp, body) => {
		if (err) return callback(err);
		try {
			body = JSON.parse(body);
		} catch (e) {
			console.log('Error parsing results json:', e, body);
			return callback(e);
		}
		// Pass the body as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, body);
		} else {
			callback(body);
		}
	});
};

List.prototype.reorderItems = function (item, oldSortOrder, newSortOrder, pageOptions, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + '/' + item.id + '/sortOrder/' + oldSortOrder + '/' + newSortOrder + '/' + buildQueryString(pageOptions);
	xhr({
		url: url,
		method: 'POST',
		headers: Keystone.csrf.header,
	}, (err, resp, body) => {
		if (err) return callback(err);
		try {
			body = JSON.parse(body);
		} catch (e) {
			console.log('Error parsing results json:', e, body);
			return callback(e);
		}
		// Pass the body as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, body);
		} else {
			callback(body);
		}
	});
};

module.exports = List;
<commit_msg>Explanation what .filter(i => i) does<commit_after>/**
 * Helper method to handle List operations, e.g. creating items, deleting items,
 * getting information about those lists, etc.
 */

const listToArray = require('list-to-array');
const qs = require('qs');
const xhr = require('xhr');
// Filters for truthy elements in an array
const truthy = (i) => i;

/**
 * Get the columns of a list, structured by fields and headings
 *
 * @param  {Object} list The list we want the columns of
 *
 * @return {Array}       The columns
 */
function getColumns (list) {
	return list.uiElements.map((col) => {
		if (col.type === 'heading') {
			return { type: 'heading', content: col.content };
		} else {
			var field = list.fields[col.field];
			return field ? { type: 'field', field: field, title: field.label, path: field.path } : null;
		}
	}).filter(truthy);
}

/**
 * Make an array of filters an object keyed by the filtering path
 *
 * @param  {Array} filterArray The array of filters
 *
 * @return {Object}            The corrected filters, keyed by path
 */
function getFilters (filterArray) {
	var filters = {};
	filterArray.forEach((filter) => {
		filters[filter.field.path] = filter.value;
	});
	return filters;
};

/**
 * Get the sorting string for the URI
 *
 * @param  {Array} sort.paths The paths we want to sort
 *
 * @return {String}           All the sorting queries we want as a string
 */
function getSortString (sort) {
	return sort.paths.map(i => {
		return i.invert ? '-' + i.path : i.path;
	}).filter(truthy).join(',');
};

/**
 * Build a query string from a bunch of options
 */
function buildQueryString (options) {
	const query = {};
	if (options.search) query.search = options.search;
	if (options.filters.length) query.filters = JSON.stringify(getFilters(options.filters));
	if (options.columns) query.select = options.columns.map(i => i.path).join(',');
	if (options.page && options.page.size) query.limit = options.page.size;
	if (options.page && options.page.index > 1) query.skip = (options.page.index - 1) * options.page.size;
	if (options.sort) query.sort = getSortString(options.sort);
	query.expandRelationshipFields = true;
	return '?' + qs.stringify(query);
};

/**
 * The main list helper class
 *
 * @param {Object} options
 */
const List = function (options) {
	Object.assign(this, options);
	this.columns = getColumns(this);
	this.expandedDefaultColumns = this.expandColumns(this.defaultColumns);
	this.defaultColumnPaths = this.expandedDefaultColumns.map(i => i.path).join(',');
};

/**
 * Create an item via the API
 *
 * @param  {FormData} formData The submitted form data
 * @param  {Function} callback Called after the API call
 */
List.prototype.createItem = function (formData, callback) {
	xhr({
		url: `${Keystone.adminPath}/api/${this.path}/create`,
		responseType: 'json',
		method: 'POST',
		headers: Keystone.csrf.header,
		body: formData,
	}, (err, resp, data) => {
		if (err) callback(err);
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			// NOTE: xhr callback will be called with an Error if
			//  there is an error in the browser that prevents
			//  sending the request. A HTTP 500 response is not
			//  going to cause an error to be returned.
			callback(data, null);
		}
	});
};

/**
 * Update a specific item
 *
 * @param  {String}   id       The id of the item we want to update
 * @param  {FormData} formData The submitted form data
 * @param  {Function} callback Called after the API call
 */
List.prototype.updateItem = function (id, formData, callback) {
	xhr({
		url: `${Keystone.adminPath}/api/${this.path}/${id}`,
		responseType: 'json',
		method: 'POST',
		headers: Keystone.csrf.header,
		body: formData,
	}, (err, resp, data) => {
		if (err) callback(err);
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

List.prototype.expandColumns = function (input) {
	let nameIncluded = false;
	const cols = listToArray(input).map(i => {
		const split = i.split('|');
		let path = split[0];
		if (path === '__name__') {
			path = this.namePath;
		}
		if (path === this.namePath) {
			nameIncluded = true;
		}
		const field = this.fields[path];
		if (!field) {
			// TODO: Support arbitary document paths
			console.warn('Invalid Column specified:', i);
			return;
		}
		return {
			field: field,
			type: field.type,
			label: field.label,
			path: field.path,
		};
	}).filter(truthy);
	if (!nameIncluded) {
		cols.unshift({
			type: 'id',
			label: 'ID',
			path: 'id',
		});
	}
	return cols;
};

List.prototype.expandSort = function (input) {
	const sort = {
		rawInput: input || this.defaultSort,
		isDefaultSort: false,
	};
	sort.input = sort.rawInput;
	if (sort.input === '__default__') {
		sort.isDefaultSort = true;
		sort.input = this.sortable ? 'sortOrder' : this.namePath;
	}
	sort.paths = listToArray(sort.input).map(path => {
		let invert = false;
		if (path.charAt(0) === '-') {
			invert = true;
			path = path.substr(1);
		}
		const field = this.fields[path];
		if (!field) {
			// TODO: Support arbitary document paths
			console.warn('Invalid Sort specified:', path);
			return;
		}
		return {
			field: field,
			type: field.type,
			label: field.label,
			path: field.path,
			invert: invert,
		};
	}).filter(truthy);
	return sort;
};

/**
 * Load a specific item via the API
 *
 * @param  {String}   itemId   The id of the item we want to load
 * @param  {Object}   options
 * @param  {Function} callback
 */
List.prototype.loadItem = function (itemId, options, callback) {
	if (arguments.length === 2 && typeof options === 'function') {
		callback = options;
		options = null;
	}
	let url = Keystone.adminPath + '/api/' + this.path + '/' + itemId;
	const query = qs.stringify(options);
	if (query.length) url += '?' + query;
	xhr({
		url: url,
		responseType: 'json',
	}, (err, resp, data) => {
		if (err) return callback(err);
		// Pass the data as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

/**
 * Load all items of a list, optionally passing objects to build a query string
 * for sorting or searching
 *
 * @param  {Object}   options
 * @param  {Function} callback
 */
List.prototype.loadItems = function (options, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + buildQueryString(options);
	xhr({
		url: url,
		responseType: 'json',
	}, (err, resp, data) => {
		if (err) callback(err);
		// Pass the data as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, data);
		} else {
			callback(data);
		}
	});
};

/**
 * Constructs a download URL to download a list with the current sorting, filtering,
 * selection and searching options
 *
 * @param  {Object} options
 *
 * @return {String}         The download URL
 */
List.prototype.getDownloadURL = function (options) {
	const url = Keystone.adminPath + '/api/' + this.path;
	const parts = [];
	if (options.format !== 'json') {
		options.format = 'csv';
	}
	parts.push(options.search ? 'search=' + options.search : '');
	parts.push(options.filters.length ? 'filters=' + JSON.stringify(getFilters(options.filters)) : '');
	parts.push(options.columns ? 'select=' + options.columns.map(i => i.path).join(',') : '');
	parts.push(options.sort ? 'sort=' + getSortString(options.sort) : '');
	parts.push('expandRelationshipFields=true');
	return url + '/export.' + options.format + '?' + parts.filter(truthy).join('&');
};

/**
 * Delete a specific item via the API
 *
 * @param  {String}   itemId   The id of the item we want to delete
 * @param  {Function} callback
 */
List.prototype.deleteItem = function (itemId, callback) {
	this.deleteItems([itemId], callback);
};

/**
 * Delete multiple items at once via the API
 *
 * @param  {Array}   itemIds  An array of ids of items we want to delete
 * @param  {Function} callback
 */
List.prototype.deleteItems = function (itemIds, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + '/' + itemIds.join(',') + '/delete';
	xhr({
		url: url,
		method: 'POST',
		headers: Keystone.csrf.header,
	}, (err, resp, body) => {
		if (err) return callback(err);
		try {
			body = JSON.parse(body);
		} catch (e) {
			console.log('Error parsing results json:', e, body);
			return callback(e);
		}
		// Pass the body as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, body);
		} else {
			callback(body);
		}
	});
};

List.prototype.reorderItems = function (item, oldSortOrder, newSortOrder, pageOptions, callback) {
	const url = Keystone.adminPath + '/api/' + this.path + '/' + item.id + '/sortOrder/' + oldSortOrder + '/' + newSortOrder + '/' + buildQueryString(pageOptions);
	xhr({
		url: url,
		method: 'POST',
		headers: Keystone.csrf.header,
	}, (err, resp, body) => {
		if (err) return callback(err);
		try {
			body = JSON.parse(body);
		} catch (e) {
			console.log('Error parsing results json:', e, body);
			return callback(e);
		}
		// Pass the body as result or error, depending on the statusCode
		if (resp.statusCode === 200) {
			callback(null, body);
		} else {
			callback(body);
		}
	});
};

module.exports = List;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50, browser: true */
/*global _onValueChange: false */

(function () {
    "use strict";

        // Hash with available editors for given properties.
        // @see _registerProvider()
    var _providers = {},
        // current active editor for model.property
        _activeEditor,
        // element matched by model.selector
        _target = null,
        /*
            Hash with selector, CSS property and value.
            Will be updated by _setup() and _onValueChange()
            Will be synced to Brackets via _getModel() to update text in code editor.
            @example {selector: 'body', property: 'shape-inside', value: 'circle()' }
        */
        _model = null,
        // regular expression for shape values with no coordinates
        _emptyShapeRE = /(polygon|circle|rectangle|ellipse)\(\s*\)/i;

    /*
        Setup an editor for a specific CSS property of an element using data in model.
        Editors must be registered with _registerProvider()

        @param {Object} model Hash with data:
            {
                // selector to match an element for editing
                selector: {String},

                // CSS property to edit
                property: {String},

                // Initial value for editor
                value: {String}
            }
    */
    function _setup(model) {
        if (!model || !model.property) {
            throw new TypeError("Invalid input model or missing property.");
        }

        if (!_providers[model.property]) {
            console.warn("No editor provided for property: " + model.property);
            return;
        }

        // find the first matching element from the given selector
        // TODO: implement querySelectorAll() navigation through multiple results
        _target = document.querySelector(model.selector);

        if (!_target) {
            console.warn("No element matching selector: " + model.selector);
            return;
        }

        // reset everything
        // _remove();

        // store the data from Brackets editor
        _model = model;

        _model.forceUpdate = _emptyShapeRE.test(_model.value);

        // get an editor that can handle the property
        _activeEditor = new _providers[model.property]();
        _activeEditor.setup(_target, model);

        // sync the element's style and the model value
        _activeEditor.onValueChange(_onValueChange);
    }

    function _onValueChange(value) {
        if (!_target || !value) {
            return;
        }

        // update the selector target's style
        _target.style[_model.property] = value;

        /*
           If the previous shape value coordinates are missing, ex: `polygon()`, like auto-suggested by Brackets hinting,
           the CSSShapesEditor will automatically infer coordintates from the element and return a usable shape value.

           Here, we set a flag to force the code editor to accept this inferred default shape value.
           By default, the code editor ignores shape values coming from the live editor if the user is still typing.
           Setting this flag to true circumvents that behavior.
        */
        _model.forceUpdate = _emptyShapeRE.test(_model.value);

        // update the model. will be requested by Brackets to sync code editor
        _model.value = value;
    }

    function _remove() {
        if (_activeEditor) {
            _activeEditor.remove();
            _activeEditor = undefined;
        }

        _target.style[_model.property] = '';
        _model = null;
    }

    function _update(model) {
        _activeEditor.update(model);
    }

    function _getModel() {
        return JSON.stringify(_model);
    }

    /*
        Register an editor for the given CSS property.
        This allows support for custom editors for any CSS property.

        Editor will be invoked if the given property
        matches model.property in _LD_CSS_EDITOR.setup(model).

        @param {String} property CSS property
        @param {Object} editor Handler for the property.

        Provided editors MUST implement the follwing interface:
        {
            // turn on editor on specified target HTMLElement.
            // picks-up necessary args from model
            setup: function (target, model) {},

            // update the editor state given the provided model
            update: function (model) {},

            // turn off the editor and remove any scaffolding
            remove: function () {},

            // sets a callback to be called with the new value
            onValueChange: function (callback) {}
        }
    */
    function _registerProvider(property, editor) {
        // TODO: check for interface conformity
        _providers[property] = editor;
    }

    window._LD_CSS_EDITOR = {
        setup:              _setup,
        remove:             _remove,
        update:             _update,
        getModel:           _getModel,
        registerProvider:   _registerProvider
    };

}());
<commit_msg>check style of target element; do not invoke editor on style mismatch<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50, browser: true */
/*global _onValueChange: false */

(function () {
    "use strict";

        // Hash with available editors for given properties.
        // @see _registerProvider()
    var _providers = {},
        // current active editor for model.property
        _activeEditor,
        // element matched by model.selector
        _target = null,
        /*
            Hash with selector, CSS property and value.
            Will be updated by _setup() and _onValueChange()
            Will be synced to Brackets via _getModel() to update text in code editor.
            @example {selector: 'body', property: 'shape-inside', value: 'circle()' }
        */
        _model = null,
        // regular expression for shape values with no coordinates
        _emptyShapeRE = /(polygon|circle|rectangle|ellipse)\(\s*\)/i;

    /*
        Returns true if the element's computed style
        contains the provided property/value CSS rule.

        @param {HTMLElement} element
        @param {String} property CSS property
        @param {String} valuem CSS property value

        @return {Boolean}
    */
    function _hasPropertyValue(element, property, value) {
        var result = false,
            style, test, testStyle;

        /*
            Using a dummy test element with the given CSS rule to get an accurate
            computed style value because the browser automatically expands shorthands.

            @example:
            circle() -> circle(closest-side at 50% 50%)
            ellipse() -> ellipse(closest-side closest-side at 50% 50%)

            Also helps avoid false negatives due to whitespace in value.

            @example:
            circle(50%) === circle(  50%  )
        */
        test = document.createElement('div');
        test.style.position = 'absolute';
        test.style.display = 'none';
        test.style[property] = value;
        document.body.appendChild(test);

        testStyle = window.getComputedStyle(test);
        style = window.getComputedStyle(element, null);

        if (style[property] && (style[property] === testStyle[property])) {
            result = true;
        }

        // cleanup
        test.parentNode.removeChild(test);

        return result;
    }

    /*
        Setup an editor for a specific CSS property of an element using data in model.
        Editors must be registered with _registerProvider()

        @param {Object} model Hash with data:
            {
                // selector to match an element for editing
                selector: {String},

                // CSS property to edit
                property: {String},

                // Initial value for editor
                value: {String}
            }
    */
    function _setup(model) {
        if (!model || !model.property) {
            throw new TypeError("Invalid input model or missing property.");
        }

        if (!_providers[model.property]) {
            console.warn("No editor provided for property: " + model.property);
            return;
        }

        // find the first matching element from the given selector
        // TODO: implement querySelectorAll() navigation through multiple results
        _target = document.querySelector(model.selector);

        if (!_target) {
            console.warn("No element matching selector: " + model.selector);
            return;
        }

        /*
            Naively checks if the given css value exists on the element matched by the selector

            Migitates problem scenarios:
            - selector is found in media query which does not match current page view
            - multiple duplicate selectors in the origin stylesheet, but not editing the one which applies last on the page
        */
        if (!_hasPropertyValue(_target, model.property, model.value)){
            console.error('style mismatch!');
            console.log('expected: '+ model.value);
            console.log('actual: '+ window.getComputedStyle(_target, null)[model.property]);
            _remove();
            return;
        }

        // reset everything
        // _remove();

        // store the data from Brackets editor
        _model = model;

        _model.forceUpdate = _emptyShapeRE.test(_model.value);

        // get an editor that can handle the property
        _activeEditor = new _providers[model.property]();
        _activeEditor.setup(_target, model);

        // sync the element's style and the model value
        _activeEditor.onValueChange(_onValueChange);
    }

    function _onValueChange(value) {
        if (!_target || !value) {
            return;
        }

        // update the selector target's style
        _target.style[_model.property] = value;

        /*
           If the previous shape value coordinates are missing, ex: `polygon()`, like auto-suggested by Brackets hinting,
           the CSSShapesEditor will automatically infer coordintates from the element and return a usable shape value.

           Here, we set a flag to force the code editor to accept this inferred default shape value.
           By default, the code editor ignores shape values coming from the live editor if the user is still typing.
           Setting this flag to true circumvents that behavior.
        */
        _model.forceUpdate = _emptyShapeRE.test(_model.value);

        // update the model. will be requested by Brackets to sync code editor
        _model.value = value;
    }

    function _remove() {
        if (_activeEditor) {
            _activeEditor.remove();
            _activeEditor = undefined;
        }

        _target.style[_model.property] = '';
        _model = null;
    }

    function _update(model) {
        _activeEditor.update(model);
    }

    function _getModel() {
        return JSON.stringify(_model);
    }

    /*
        Register an editor for the given CSS property.
        This allows support for custom editors for any CSS property.

        Editor will be invoked if the given property
        matches model.property in _LD_CSS_EDITOR.setup(model).

        @param {String} property CSS property
        @param {Object} editor Handler for the property.

        Provided editors MUST implement the follwing interface:
        {
            // turn on editor on specified target HTMLElement.
            // picks-up necessary args from model
            setup: function (target, model) {},

            // update the editor state given the provided model
            update: function (model) {},

            // turn off the editor and remove any scaffolding
            remove: function () {},

            // sets a callback to be called with the new value
            onValueChange: function (callback) {}
        }
    */
    function _registerProvider(property, editor) {
        // TODO: check for interface conformity
        _providers[property] = editor;
    }

    window._LD_CSS_EDITOR = {
        setup:              _setup,
        remove:             _remove,
        update:             _update,
        getModel:           _getModel,
        registerProvider:   _registerProvider
    };

}());
<|endoftext|>


<commit_before>export { InputsOutputs };

import * as FloatValue from "../../Unpacker/FloatValue.js";

/**
 * Element value bounds (per channel) for inputs and outputs of an operation.
 *
 *
 * @member {FloatValue.BoundsArray[]} inputs
 *   The element value bounds (per channel) of all inputs (i.e. the domain of the operation). This array (which is past into
 * constructor) will be kept (not cloned) directly. So caller should not modify them.
 *
 * @member {number} inputTensorCount
 *   How many input tensors (i.e. this.inputs.length).
 *
 * @member {FloatValue.BoundsArray} input0
 *   The element value bounds (per channel) of 1st input (i.e. this.inputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} input1
 *   The element value bounds (per channel) of 2nd input (i.e. this.inputs[ 1 ]).
 *
 * @member {number} inputChannelCount0
 *   The channel count of 1st input (i.e. this.input0.length).
 *
 * @member {number} inputChannelCount1
 *   The channel count of 2nd input (i.e. this.input1.length).
 *
 * @member {FloatValue.BoundsArray[]} outputs
 *   The element value bounds (per channel) of all outputs (i.e. the range of the operation). The array is created by constructor
 * according to outputChannelCount0 and outputChannelCount1.
 *
 * @member {number} outputTensorCount
 *   How many output tensors (i.e. this.outputs.length).
 *
 * @member {FloatValue.BoundsArray} output0
 *   The element value bounds (per channel) of 1st output (i.e. this.outputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} output1
 *   The element value bounds (per channel) of 2nd output (i.e. this.outputs[ 1 ]).
 *
 * @member {number} outputChannelCount0
 *   The channel count of 1st output (i.e. this.output0.length).
 *
 * @member {number} outputChannelCount1
 *   The channel count of 2nd output (i.e. this.output1.length).
 */
class InputsOutputs {

  /**
   */
  constructor( inputs, outputChannelCount0, outputChannelCount1 ) {
    this.inputs = inputs;

    // Determine outputs array.
    if ( outputChannelCount0 > 0 ) {
      if ( outputChannelCount1 > 0 ) { // Two outputs.
        this.outputs = [ new FloatValue.BoundsArray( outputChannelCount0 ), new FloatValue.BoundsArray( outputChannelCount1 ) ];

      } else { // ( outputChannelCount1 <= 0 ), One output.
        this.outputs = [ new FloatValue.BoundsArray( outputChannelCount0 ) ];
      }

    } else { // ( outputChannelCount0 <= 0 ), Illegal.

      tf.util.assert( ( ( outputChannelCount0 <= 0 ) && ( outputChannelCount1 <= 0 ) ),
        `BoundsArraySet.InputsOutputs.constructor(): `
          + `output0 must exist (i.e. outputChannelCount0 ( ${outputChannelCount0} ) must > 0 ).`
      );
    }
  }

  /**
   * @return {InputsOutputs}
   *   Return a newly created InputsOutputs which is a copy of this InputsOutputs. The this.inputs will just past
   * to new InputsOutputs (i.e. NOT copied). But the this.outputs will be copied.
   */
  clone() {
    let result = new InputsOutputs( this.inputs, this.outputChannelCount0, this.outputChannelCount1 );
    result.set_outputs_all_byBoundsArraySet( this );
    return result;
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set all .outputs[] to the same as the specified aBounds.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBounds( aBounds ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBounds( aBounds );
    }
    return this;
  }

  /**
   * @param {BoundsArray} outputBoundsArray
   *   The BoundsArray to be copied to all .outputs[].
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray( outputBoundsArray ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBoundsArray( outputBoundsArray );
    }
    return this;
  }

  /**
   * @param {BoundsArraySet.InputsOutputs} aBoundsArraySet
   *   The BoundsArraySet to be copied. Precondition: ( this.outputs.length == aBoundsArraySet.outputs.length ).
   *
   * @return {InputsOutputs}
   *   Return this (modified) object which is copied from aBoundsArraySet.
   */
  set_outputs_all_byBoundsArraySet( aBoundsArraySet ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_outputs_all_byBoundsArray( aBoundsArraySet.outputs[ outTensorIndex ] );
    }
    return this;
  }

  /**
   * Set .outputs[] by .input[ 0 ].
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_input0() {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBoundsArray( this.input[ 0 ] );
    }
    return this;
  }

  /**
   * Set .outputs[] by concatenating .input[ 0 ] and .input[ 1 ]. The length of .outputs[] will be modified.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_concat_input0_input1() {
    let rLength = this.inputs[ 0 ].length + this.inputs[ 1 ].length;
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].length = rLength;
      this.outputs[ outTensorIndex ].set_all_byBoundsArray_concat_input0_input1( this.inputs[ 0 ], this.inputs[ 1 ] );
    }
    return this;
  }

  /**
   * Set .outputs[ 0 ] and .outputs[ 1 ] by splitting .input[ 0 ]. If .outputs[ 1 ] does not exist, it will be created. The length
   * of both .outputs[ 0 ] and .outputs[ 1 ] will be modified.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_split_input0() {

    if ( !this.outputs[ 1 ] )
      this.outputs[ 1 ] = new FloatValue.BoundsArray( 0 );

    this.inputs[ 0 ].split_to_lowerHalf_higherHalf( this.outputs[ 0 ], this.outputs[ 1 ] );
    return this;
  }

  get inputTensorCount() { return this.inputs.length; }

  get input0() { return this.inputs[ 0 ]; }
  get input1() { return this.inputs[ 1 ]; }

  get inputChannelCount0() { return this.input0.length; }
  get inputChannelCount1() { return this.input1.length; }

  get outputTensorCount() { return this.outputs.length; }

  get output0() { return this.outputs[ 0 ]; }
  get output1() { return this.outputs[ 1 ]; }

  get outputChannelCount0() { return this.output0.length; }
  get outputChannelCount1() { return this.output1.length; }

}

<commit_msg>Update InputsOutputs.js<commit_after>export { InputsOutputs };

import * as FloatValue from "../../Unpacker/FloatValue.js";

/**
 * Element value bounds (per channel) for inputs and outputs of an operation.
 *
 *
 * @member {FloatValue.BoundsArray[]} inputs
 *   The element value bounds (per channel) of all inputs (i.e. the domain of the operation). This array (which is past into
 * constructor) will be kept (not cloned) directly. So caller should not modify them.
 *
 * @member {number} inputTensorCount
 *   How many input tensors (i.e. this.inputs.length).
 *
 * @member {FloatValue.BoundsArray} input0
 *   The element value bounds (per channel) of 1st input (i.e. this.inputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} input1
 *   The element value bounds (per channel) of 2nd input (i.e. this.inputs[ 1 ]).
 *
 * @member {number} inputChannelCount0
 *   The channel count of 1st input (i.e. this.input0.length).
 *
 * @member {number} inputChannelCount1
 *   The channel count of 2nd input (i.e. this.input1.length).
 *
 * @member {FloatValue.BoundsArray[]} outputs
 *   The element value bounds (per channel) of all outputs (i.e. the range of the operation). The array is created by constructor
 * according to outputChannelCount0 and outputChannelCount1.
 *
 * @member {number} outputTensorCount
 *   How many output tensors (i.e. this.outputs.length).
 *
 * @member {FloatValue.BoundsArray} output0
 *   The element value bounds (per channel) of 1st output (i.e. this.outputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} output1
 *   The element value bounds (per channel) of 2nd output (i.e. this.outputs[ 1 ]).
 *
 * @member {number} outputChannelCount0
 *   The channel count of 1st output (i.e. this.output0.length).
 *
 * @member {number} outputChannelCount1
 *   The channel count of 2nd output (i.e. this.output1.length).
 */
class InputsOutputs {

  /**
   */
  constructor( inputs, outputChannelCount0, outputChannelCount1 ) {
    this.inputs = inputs;

    // Determine outputs array.
    if ( outputChannelCount0 > 0 ) {
      if ( outputChannelCount1 > 0 ) { // Two outputs.
        this.outputs = [ new FloatValue.BoundsArray( outputChannelCount0 ), new FloatValue.BoundsArray( outputChannelCount1 ) ];

      } else { // ( outputChannelCount1 <= 0 ), One output.
        this.outputs = [ new FloatValue.BoundsArray( outputChannelCount0 ) ];
      }

    } else { // ( outputChannelCount0 <= 0 ), Illegal.

      tf.util.assert( ( ( outputChannelCount0 <= 0 ) && ( outputChannelCount1 <= 0 ) ),
        `BoundsArraySet.InputsOutputs.constructor(): `
          + `output0 must exist (i.e. outputChannelCount0 ( ${outputChannelCount0} ) must > 0 ).`
      );
    }
  }

  /**
   * @return {InputsOutputs}
   *   Return a newly created InputsOutputs which is a copy of this InputsOutputs. The this.inputs will just past
   * to new InputsOutputs (i.e. NOT copied). But the this.outputs will be copied.
   */
  clone() {
    let result = new InputsOutputs( this.inputs, this.outputChannelCount0, this.outputChannelCount1 );
    result.set_outputs_all_byBoundsArraySet( this );
    return result;
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set all .outputs[] to the same as the specified aBounds.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBounds( aBounds ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBounds( aBounds );
    }
    return this;
  }

  /**
   * @param {BoundsArray} outputBoundsArray
   *   The BoundsArray to be copied to all .outputs[].
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray( outputBoundsArray ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBoundsArray( outputBoundsArray );
    }
    return this;
  }

  /**
   * @param {BoundsArraySet.InputsOutputs} aBoundsArraySet
   *   The BoundsArraySet to be copied. Precondition: ( this.outputs.length == aBoundsArraySet.outputs.length ).
   *
   * @return {InputsOutputs}
   *   Return this (modified) object which is copied from aBoundsArraySet.
   */
  set_outputs_all_byBoundsArraySet( aBoundsArraySet ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_outputs_all_byBoundsArray( aBoundsArraySet.outputs[ outTensorIndex ] );
    }
    return this;
  }

  /**
   * Set .outputs[] by .input[ 0 ].
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_input0() {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].set_all_byBoundsArray( this.input[ 0 ] );
    }
    return this;
  }

  /**
   * Set .outputs[] by concatenating .input[ 0 ] and .input[ 1 ]. The length of .outputs[] will be modified.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_concat_input0_input1() {
    let rLength = this.inputs[ 0 ].length + this.inputs[ 1 ].length;
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].length = rLength;
      this.outputs[ outTensorIndex ].set_all_byBoundsArray_concat_input0_input1( this.inputs[ 0 ], this.inputs[ 1 ] );
    }
    return this;
  }

  /**
   * Set .outputs[ 0 ] and .outputs[ 1 ] by splitting .input[ 0 ]. If .outputs[ 1 ] does not exist, it will be created. The length
   * of both .outputs[ 0 ] and .outputs[ 1 ] will be modified.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byBoundsArray_split_input0() {

    if ( !this.outputs[ 1 ] )
      this.outputs[ 1 ] = new FloatValue.BoundsArray( 0 );

    this.inputs[ 0 ].split_to_lowerHalf_higherHalf( this.outputs[ 0 ], this.outputs[ 1 ] );
    return this;
  }

  /**
   * Rearrange this.outputs[] channel information by interleaving as ( groupCount == 2 ). This channel count must be even
   * (i.e. divisible by 2).
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements temporarily. Provide this array could reduce memory re-allocation
   * and improve performance.
   *
   * @return {InputsOutputs}
   *   Return this (modified) object.
   */
  set_outputs_all_byInterleave_asGrouptTwo( arrayTemp ) {
    for ( let outTensorIndex = 0; outTensorIndex < this.outputs.length; ++outTensorIndex ) {
      this.outputs[ outTensorIndex ].interleave_asGrouptTwo( arrayTemp );
    }
    return this;
  }

  get inputTensorCount() { return this.inputs.length; }

  get input0() { return this.inputs[ 0 ]; }
  get input1() { return this.inputs[ 1 ]; }

  get inputChannelCount0() { return this.input0.length; }
  get inputChannelCount1() { return this.input1.length; }

  get outputTensorCount() { return this.outputs.length; }

  get output0() { return this.outputs[ 0 ]; }
  get output1() { return this.outputs[ 1 ]; }

  get outputChannelCount0() { return this.output0.length; }
  get outputChannelCount1() { return this.output1.length; }

}

<|endoftext|>


<commit_before>tfsMonitor.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

	$urlRouterProvider.otherwise("/build")

	$stateProvider
		.state('home', {
	  		url: "/",
	  		templateUrl: "Views/Home/Home.html"
		})
		.state('home.build', {
	  		url: "build",
	  		templateUrl: "Views/BuildMonitor/BuildMonitor.html"
		 })
		.state('home.work', {
			url: "work",
			templateUrl: "Views/WorkMonitor/WorkMonitor.html"
		})
}])<commit_msg>fixing minor routing bug<commit_after>tfsMonitor.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

	$urlRouterProvider.otherwise("/build")

	$stateProvider
		.state('home', {
	  		url: "/",
	  		templateUrl: "Views/Home/Home.html",
			abstract: true //this is abstract so that if a user navigates here, it automatically routes using the .otherwise() route
		})
		.state('home.build', {
	  		url: "build",
	  		templateUrl: "Views/BuildMonitor/BuildMonitor.html"
		 })
		.state('home.work', {
			url: "work",
			templateUrl: "Views/WorkMonitor/WorkMonitor.html"
		})
}])<|endoftext|>


<commit_before>/* -*- Mode: java; tab-width: 2; c-tab-always-indent: t; indent-tabs-mode: t; c-basic-offset: 2 -*- */

String.prototype.trim = function() {
  return this.replace(/(^\s+|\s+$)/g, '');
};

String.prototype.formatted = function() {
  var newString = this;

  for (var i = 0; i < arguments.length; i++)
    newString = newString.replace("%{" + i + "}", arguments[i], "g");

  return newString;
};

String.prototype.repeat = function(count) {
  var newString = "";
  for (var i = 0; i < count; i++) {
    newString += this;
  }

  return newString;
};

String.prototype.capitalize = function() {
  return this.replace(/\w+/g,
											function(a) {
												return ( a.charAt(0).toUpperCase()
																 + a.substr(1).toLowerCase() );
											});
};

String.prototype.decodeEntities = function() {
  return this.replace(/&#(\d+);/g,
											function(wholematch, parenmatch1) {
												return String.fromCharCode(+parenmatch1);
											});
};

String.prototype.asDate = function () {
  var newDate;
  var date = this.split("/");
  if (date.length == 3)
    newDate = new Date(date[2], date[1] - 1, date[0]);
  else {
    date = this.split("-");
    if (date.length == 3)
      newDate = new Date(date[0], date[1] - 1, date[2]);
    else {
      if (this.length == 8) {
				newDate = new Date(this.substring(0, 4),
													 this.substring(4, 6) - 1,
													 this.substring(6, 8));
      }
    }
  }

  return newDate;
};

String.prototype.asCSSIdentifier = function () {
	var substitutions = { '_': '_U_',
												'.': '_D_',
												'#': '_H_',
												'@': '_A_',
												'*': '_S_',
												':': '_C_',
												',': '_CO_',
												' ': '_SP_' };
	var newString = this;

	for (var key in substitutions)
		newString = newString.replace(key, substitutions[key]);

  return newString;
}

Date.prototype.sogoDayName = function() {
  var dayName = "";

  var day = this.getDay();
  if (day == 0) {
    dayName = labels['a2_Sunday'];
  } else if (day == 1) {
    dayName = labels['a2_Monday'];
  } else if (day == 2) {
    dayName = labels['a2_Tuesday'];
  } else if (day == 3) {
    dayName = labels['a2_Wednesday'];
  } else if (day == 4) {
    dayName = labels['a2_Thursday'];
  } else if (day == 5) {
    dayName = labels['a2_Friday'];
  } else if (day == 6) {
    dayName = labels['a2_Saturday'];
  }

  return dayName;
};

Date.prototype.daysUpTo = function(otherDate) {
  var days = new Array();

  var day1 = this.getTime();
  var day2 = otherDate.getTime();
  if (day1 > day2) {
    var tmp = day1;
    day1 = day2;
    day2 = tmp;
  }
  //   var day1Date = new Date();
  //   day1Date.setTime(this.getTime());
  //   day1Date.setHours(0, 0, 0, 0);
  //   var day2Date = new Date();
  //   day2Date.setTime(otherDate.getTime());
  //   day2Date.setHours(23, 59, 59, 999);
  //   var day1 = day1Date.getTime();
  //   var day2 = day2Date.getTime();

  var nbrDays = Math.floor((day2 - day1) / 86400000) + 1;
  for (var i = 0; i < nbrDays; i++) {
    var newDate = new Date();
    newDate.setTime(day1 + (i * 86400000));
    days.push(newDate);
  }

  return days;
};

Date.prototype.getDayString = function() {
  var newString = this.getYear();
  if (newString < 1000) newString += 1900;
  var month = '' + (this.getMonth() + 1);
  if (month.length == 1)
    month = '0' + month;
  newString += month;
  var day = '' + this.getDate();
  if (day.length == 1)
    day = '0' + day;
  newString += day;

  return newString;
};

Date.prototype.getHourString = function() {
  var newString = this.getHours() + '00';
  if (newString.length == 3)
    newString = '0' + newString;

  return newString;
};

Date.prototype.getDisplayHoursString = function() {
  var hoursString = "" + this.getUTCHours();
  if (hoursString.length == 1)
    hoursString = '0' + hoursString;

  var minutesString = "" + this.getUTCMinutes();
  if (minutesString.length == 1)
    minutesString = '0' + minutesString;

  return hoursString + ":" + minutesString;
};

Date.prototype.stringWithSeparator = function(separator) {
  var month = '' + (this.getMonth() + 1);
  var day = '' + this.getDate();
  var year = this.getYear();
  if (year < 1000)
    year = '' + (year + 1900);
  if (month.length == 1)
    month = '0' + month;
  if (day.length == 1)
    day = '0' + day;

  if (separator == '-')
    str = year + '-' + month + '-' + day;
  else
    str = day + '/' + month + '/' + year;

  return str;
};

Date.prototype.sogoFreeBusyStringWithSeparator = function(separator) {
  return this.sogoDayName() + ", " + this.stringWithSeparator(separator);
};

Date.prototype.addDays = function(nbrDays) {
  var milliSeconds = this.getTime();
  milliSeconds += 86400000 * nbrDays;
  this.setTime(milliSeconds);
};

Date.prototype.earlierDate = function(otherDate) {
  var workDate = new Date();
  workDate.setTime(otherDate.getTime());
  workDate.setHours(0);
  return ((this.getTime() < workDate.getTime())
					? this : otherDate);
};

Date.prototype.laterDate = function(otherDate) {
  var workDate = new Date();
  workDate.setTime(otherDate.getTime());
  workDate.setHours(23);
  workDate.setMinutes(59);
  workDate.setSeconds(59);
  workDate.setMilliseconds(999);
  return ((this.getTime() < workDate.getTime())
					? otherDate : this);
};

Date.prototype.beginOfWeek = function() {
  var offset = firstDayOfWeek - this.getDay();
  if (offset > 0)
    offset -= 7;

  var beginOfWeek = new Date(this.getTime());
  beginOfWeek.setHours(12);
  beginOfWeek.addDays(offset);
  beginOfWeek.setMinutes(0);
  beginOfWeek.setSeconds(0);
  beginOfWeek.setMilliseconds(0);

  return beginOfWeek;
};

Date.prototype.endOfWeek = function() {
  var endOfWeek = this.beginOfWeek();
  endOfWeek.addDays(6);

  endOfWeek.setHours(23);
  endOfWeek.setMinutes(59);
  endOfWeek.setSeconds(59);
  endOfWeek.setMilliseconds(999);

  return endOfWeek;
};

String.prototype._base64_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
String.prototype.base64encode = function () {
  var output = "";
  var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  var i = 0;

  var input = this.utf8encode();

  while (i < input.length) {
    chr1 = input.charCodeAt(i++);
    chr2 = input.charCodeAt(i++);
    chr3 = input.charCodeAt(i++);

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if (isNaN(chr2)) {
      enc3 = enc4 = 64;
    } else if (isNaN(chr3)) {
      enc4 = 64;
    }

    output = output +
      this._base64_keyStr.charAt(enc1) + this._base64_keyStr.charAt(enc2) +
      this._base64_keyStr.charAt(enc3) + this._base64_keyStr.charAt(enc4);
  }

  return output;
};

String.prototype.base64decode = function() {
  var output = "";
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;

  var input = this.replace(/[^A-Za-z0-9\+\/\=]/g, "");

  while (i < input.length) {
    enc1 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc2 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc3 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc4 = this._base64_keyStr.indexOf(input.charAt(i++));

    chr1 = (enc1 << 2) | (enc2 >> 4);
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    chr3 = ((enc3 & 3) << 6) | enc4;

    output = output + String.fromCharCode(chr1);

    if (enc3 != 64) {
      output = output + String.fromCharCode(chr2);
    }
    if (enc4 != 64) {
      output = output + String.fromCharCode(chr3);
    }
  }

  return output.utf8decode();
};

String.prototype.utf8encode = function() {
  var string = this.replace(/\r\n/g,"\n");
  var utftext = "";

  for (var n = 0; n < this.length; n++) {
    var c = this.charCodeAt(n);

    if (c < 128) {
      utftext += String.fromCharCode(c);
    }
    else if((c > 127) && (c < 2048)) {
      utftext += String.fromCharCode((c >> 6) | 192);
      utftext += String.fromCharCode((c & 63) | 128);
    }
    else {
      utftext += String.fromCharCode((c >> 12) | 224);
      utftext += String.fromCharCode(((c >> 6) & 63) | 128);
      utftext += String.fromCharCode((c & 63) | 128);
    }
  }

  return utftext;
};

String.prototype.utf8decode = function() {
  var string = "";
  var i = 0;
  var c = c1 = c2 = 0;

  while (i < string.length) {
    c = utftext.charCodeAt(i);

    if (c < 128) {
      string += String.fromCharCode(c);
      i++;
    }
    else if((c > 191) && (c < 224)) {
      c2 = this.charCodeAt(i+1);
      string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
      i += 2;
    }
    else {
      c2 = this.charCodeAt(i+1);
      c3 = this.charCodeAt(i+2);
      string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
      i += 3;
    }
  }

  return string;
};

String.prototype.cssSafeString = function() {
  var newString = this.replace("#", "_", "g");
  newString = newString.replace(".", "_", "g");
  newString = newString.replace("@", "_", "g");

  return newString;
};

window.width = function() {
  if (window.innerWidth)
    return window.innerWidth;
  else if (document.body && document.body.offsetWidth)
    return document.body.offsetWidth;
  else
    return 0;
};

window.height = function() {
  if (window.innerHeight)
    return window.innerHeight;
  else if (document.body && document.body.offsetHeight)
    return document.body.offsetHeight;
  else
    return 0;
};
<commit_msg>Fix regexp to be global in special characters replacement<commit_after>/* -*- Mode: java; tab-width: 2; c-tab-always-indent: t; indent-tabs-mode: t; c-basic-offset: 2 -*- */

String.prototype.trim = function() {
  return this.replace(/(^\s+|\s+$)/g, '');
};

String.prototype.formatted = function() {
  var newString = this;

  for (var i = 0; i < arguments.length; i++)
    newString = newString.replace("%{" + i + "}", arguments[i], "g");

  return newString;
};

String.prototype.repeat = function(count) {
  var newString = "";
  for (var i = 0; i < count; i++) {
    newString += this;
  }

  return newString;
};

String.prototype.capitalize = function() {
  return this.replace(/\w+/g,
											function(a) {
												return ( a.charAt(0).toUpperCase()
																 + a.substr(1).toLowerCase() );
											});
};

String.prototype.decodeEntities = function() {
  return this.replace(/&#(\d+);/g,
											function(wholematch, parenmatch1) {
												return String.fromCharCode(+parenmatch1);
											});
};

String.prototype.asDate = function () {
  var newDate;
  var date = this.split("/");
  if (date.length == 3)
    newDate = new Date(date[2], date[1] - 1, date[0]);
  else {
    date = this.split("-");
    if (date.length == 3)
      newDate = new Date(date[0], date[1] - 1, date[2]);
    else {
      if (this.length == 8) {
				newDate = new Date(this.substring(0, 4),
													 this.substring(4, 6) - 1,
													 this.substring(6, 8));
      }
    }
  }

  return newDate;
};

String.prototype.asCSSIdentifier = function () {
	var substitutions = { '_': '_U_',
												'\\.': '_D_',
												'#': '_H_',
												'@': '_A_',
												'\\*': '_S_',
												':': '_C_',
												',': '_CO_',
												' ': '_SP_' };
	var newString = this;
	var re;

	for (var key in substitutions) {
		re = new RegExp(key, 'g');
		newString = newString.replace(re, substitutions[key]);
	}

  return newString;
}

Date.prototype.sogoDayName = function() {
  var dayName = "";

  var day = this.getDay();
  if (day == 0) {
    dayName = labels['a2_Sunday'];
  } else if (day == 1) {
    dayName = labels['a2_Monday'];
  } else if (day == 2) {
    dayName = labels['a2_Tuesday'];
  } else if (day == 3) {
    dayName = labels['a2_Wednesday'];
  } else if (day == 4) {
    dayName = labels['a2_Thursday'];
  } else if (day == 5) {
    dayName = labels['a2_Friday'];
  } else if (day == 6) {
    dayName = labels['a2_Saturday'];
  }

  return dayName;
};

Date.prototype.daysUpTo = function(otherDate) {
  var days = new Array();

  var day1 = this.getTime();
  var day2 = otherDate.getTime();
  if (day1 > day2) {
    var tmp = day1;
    day1 = day2;
    day2 = tmp;
  }
  //   var day1Date = new Date();
  //   day1Date.setTime(this.getTime());
  //   day1Date.setHours(0, 0, 0, 0);
  //   var day2Date = new Date();
  //   day2Date.setTime(otherDate.getTime());
  //   day2Date.setHours(23, 59, 59, 999);
  //   var day1 = day1Date.getTime();
  //   var day2 = day2Date.getTime();

  var nbrDays = Math.floor((day2 - day1) / 86400000) + 1;
  for (var i = 0; i < nbrDays; i++) {
    var newDate = new Date();
    newDate.setTime(day1 + (i * 86400000));
    days.push(newDate);
  }

  return days;
};

Date.prototype.getDayString = function() {
  var newString = this.getYear();
  if (newString < 1000) newString += 1900;
  var month = '' + (this.getMonth() + 1);
  if (month.length == 1)
    month = '0' + month;
  newString += month;
  var day = '' + this.getDate();
  if (day.length == 1)
    day = '0' + day;
  newString += day;

  return newString;
};

Date.prototype.getHourString = function() {
  var newString = this.getHours() + '00';
  if (newString.length == 3)
    newString = '0' + newString;

  return newString;
};

Date.prototype.getDisplayHoursString = function() {
  var hoursString = "" + this.getUTCHours();
  if (hoursString.length == 1)
    hoursString = '0' + hoursString;

  var minutesString = "" + this.getUTCMinutes();
  if (minutesString.length == 1)
    minutesString = '0' + minutesString;

  return hoursString + ":" + minutesString;
};

Date.prototype.stringWithSeparator = function(separator) {
  var month = '' + (this.getMonth() + 1);
  var day = '' + this.getDate();
  var year = this.getYear();
  if (year < 1000)
    year = '' + (year + 1900);
  if (month.length == 1)
    month = '0' + month;
  if (day.length == 1)
    day = '0' + day;

  if (separator == '-')
    str = year + '-' + month + '-' + day;
  else
    str = day + '/' + month + '/' + year;

  return str;
};

Date.prototype.sogoFreeBusyStringWithSeparator = function(separator) {
  return this.sogoDayName() + ", " + this.stringWithSeparator(separator);
};

Date.prototype.addDays = function(nbrDays) {
  var milliSeconds = this.getTime();
  milliSeconds += 86400000 * nbrDays;
  this.setTime(milliSeconds);
};

Date.prototype.earlierDate = function(otherDate) {
  var workDate = new Date();
  workDate.setTime(otherDate.getTime());
  workDate.setHours(0);
  return ((this.getTime() < workDate.getTime())
					? this : otherDate);
};

Date.prototype.laterDate = function(otherDate) {
  var workDate = new Date();
  workDate.setTime(otherDate.getTime());
  workDate.setHours(23);
  workDate.setMinutes(59);
  workDate.setSeconds(59);
  workDate.setMilliseconds(999);
  return ((this.getTime() < workDate.getTime())
					? otherDate : this);
};

Date.prototype.beginOfWeek = function() {
  var offset = firstDayOfWeek - this.getDay();
  if (offset > 0)
    offset -= 7;

  var beginOfWeek = new Date(this.getTime());
  beginOfWeek.setHours(12);
  beginOfWeek.addDays(offset);
  beginOfWeek.setMinutes(0);
  beginOfWeek.setSeconds(0);
  beginOfWeek.setMilliseconds(0);

  return beginOfWeek;
};

Date.prototype.endOfWeek = function() {
  var endOfWeek = this.beginOfWeek();
  endOfWeek.addDays(6);

  endOfWeek.setHours(23);
  endOfWeek.setMinutes(59);
  endOfWeek.setSeconds(59);
  endOfWeek.setMilliseconds(999);

  return endOfWeek;
};

String.prototype._base64_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
String.prototype.base64encode = function () {
  var output = "";
  var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  var i = 0;

  var input = this.utf8encode();

  while (i < input.length) {
    chr1 = input.charCodeAt(i++);
    chr2 = input.charCodeAt(i++);
    chr3 = input.charCodeAt(i++);

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if (isNaN(chr2)) {
      enc3 = enc4 = 64;
    } else if (isNaN(chr3)) {
      enc4 = 64;
    }

    output = output +
      this._base64_keyStr.charAt(enc1) + this._base64_keyStr.charAt(enc2) +
      this._base64_keyStr.charAt(enc3) + this._base64_keyStr.charAt(enc4);
  }

  return output;
};

String.prototype.base64decode = function() {
  var output = "";
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;

  var input = this.replace(/[^A-Za-z0-9\+\/\=]/g, "");

  while (i < input.length) {
    enc1 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc2 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc3 = this._base64_keyStr.indexOf(input.charAt(i++));
    enc4 = this._base64_keyStr.indexOf(input.charAt(i++));

    chr1 = (enc1 << 2) | (enc2 >> 4);
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    chr3 = ((enc3 & 3) << 6) | enc4;

    output = output + String.fromCharCode(chr1);

    if (enc3 != 64) {
      output = output + String.fromCharCode(chr2);
    }
    if (enc4 != 64) {
      output = output + String.fromCharCode(chr3);
    }
  }

  return output.utf8decode();
};

String.prototype.utf8encode = function() {
  var string = this.replace(/\r\n/g,"\n");
  var utftext = "";

  for (var n = 0; n < this.length; n++) {
    var c = this.charCodeAt(n);

    if (c < 128) {
      utftext += String.fromCharCode(c);
    }
    else if((c > 127) && (c < 2048)) {
      utftext += String.fromCharCode((c >> 6) | 192);
      utftext += String.fromCharCode((c & 63) | 128);
    }
    else {
      utftext += String.fromCharCode((c >> 12) | 224);
      utftext += String.fromCharCode(((c >> 6) & 63) | 128);
      utftext += String.fromCharCode((c & 63) | 128);
    }
  }

  return utftext;
};

String.prototype.utf8decode = function() {
  var string = "";
  var i = 0;
  var c = c1 = c2 = 0;

  while (i < string.length) {
    c = utftext.charCodeAt(i);

    if (c < 128) {
      string += String.fromCharCode(c);
      i++;
    }
    else if((c > 191) && (c < 224)) {
      c2 = this.charCodeAt(i+1);
      string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
      i += 2;
    }
    else {
      c2 = this.charCodeAt(i+1);
      c3 = this.charCodeAt(i+2);
      string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
      i += 3;
    }
  }

  return string;
};

String.prototype.cssSafeString = function() {
  var newString = this.replace("#", "_", "g");
  newString = newString.replace(".", "_", "g");
  newString = newString.replace("@", "_", "g");

  return newString;
};

window.width = function() {
  if (window.innerWidth)
    return window.innerWidth;
  else if (document.body && document.body.offsetWidth)
    return document.body.offsetWidth;
  else
    return 0;
};

window.height = function() {
  if (window.innerHeight)
    return window.innerHeight;
  else if (document.body && document.body.offsetHeight)
    return document.body.offsetHeight;
  else
    return 0;
};
<|endoftext|>


<commit_before>/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/* custom extensions to the DOM api */
Element.addMethods({
        addInterface: function(element, objectInterface) {
            element = $(element);
            Object.extend(element, objectInterface);
            if (element.bind)
                element.bind();
        },

        allTextContent: function(element) {
            var content = "";
            for (var i = 0; i < element.childNodes.length; i++) {
                var node = $(element.childNodes[i]);
                if (node.nodeType == Node.TEXT_NODE) {
                    content += node.nodeValue;
                }
                else if (node.nodeType == Node.ELEMENT_NODE) {
                    content += Element.allTextContent(node);
                }
            }

            return content;
        },

        childNodesWithTag: function(element, tagName) {
            element = $(element);

            var matchingNodes = new Array();
            tagName = tagName.toUpperCase();

            for (var i = 0; i < element.childNodes.length; i++) {
                var childNode = $(element.childNodes[i]);
                if (Object.isElement(childNode)
                    && childNode.tagName
                    && childNode.tagName.toUpperCase() == tagName)
                    matchingNodes.push(childNode);
            }

            return matchingNodes;
        },

        getParentWithTagName: function(element, tagName) {
            element = $(element);
            var currentElement = element;
            tagName = tagName.toUpperCase();

            currentElement = currentElement.parentNode;
            while (currentElement
                   && currentElement.tagName != tagName) {
                currentElement = currentElement.parentNode;
            }

            return currentElement;
        },

        cascadeLeftOffset: function(element) {
            element = $(element);
            var currentElement = element;

            var offset = 0;
            while (currentElement) {
                offset += currentElement.offsetLeft;
                currentElement = $(currentElement).getParentWithTagName("div");
            }

            return offset;
        },

        cascadeTopOffset: function(element) {
            element = $(element);
            var currentElement = element;
            var offset = 0;

            var i = 0;

            while (currentElement && currentElement.tagName) {
                offset += currentElement.offsetTop;
                currentElement = currentElement.parentNode;
                i++;
            }

            return offset;
        },

        dump: function(element, additionalInfo, additionalKeys) {
            element = $(element);
            var id = element.getAttribute("id");
            var nclass = element.getAttribute("class");

            var str = element.tagName;
            if (id)
                str += "; id = " + id;
            if (nclass)
                str += "; class = " + nclass;

            if (additionalInfo)
                str += "; " + additionalInfo;

            if (additionalKeys)
                for (var i = 0; i < additionalKeys.length; i++) {
                    var value = element.readAttribute(additionalKeys[i]);
                    if (value)
                        str += "; " + additionalKeys[i] + " = " + value;
                }

            log (str);
        },

        getSelectedNodes: function(element) {
            element = $(element);

            if (!element.selectedElements)
                element.selectedElements = new Array();

            return element.selectedElements;
        },

        getSelectedNodesId: function(element) {
            element = $(element);

            var selArray = null;
            if (element.selectedIds) {
                selArray = element.selectedIds;
            }
            else {
                selArray = [];
                if (element.selectedElements) {
                    for (var i = 0; i < element.selectedElements.length; i++) {
                        var node = element.selectedElements[i];
                        selArray.push(node.getAttribute("id"));
                    }
                }
            }

            return selArray;
        },

        onContextMenu: function(element, event) {
            element = $(element);
            Event.stop(event);
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);

            var popup = element.sogoContextMenu;
            var menuTop = Event.pointerY(event);
            var menuLeft = Event.pointerX(event);
            var heightDiff = (window.height()
                              - (menuTop + popup.offsetHeight));
            if (heightDiff < 0)
                menuTop += heightDiff;

            var leftDiff = (window.width()
                            - (menuLeft + popup.offsetWidth));
            if (leftDiff < 0)
                menuLeft -= popup.offsetWidth;

            var isVisible = true;
            if (popup.prepareVisibility)
                isVisible = popup.prepareVisibility();

            if (isVisible) {
                popup.setStyle( { top: menuTop + "px",
                            left: menuLeft + "px",
                            visibility: "visible" } );

                document.currentPopupMenu = popup;
                document.body.observe("click", onBodyClickMenuHandler);
            }
            else
                log ("Warning: not showing the contextual menu " + element.id);
        },

        attachMenu: function(element, menuName) {
            element = $(element);
            element.sogoContextMenu = $(menuName);
            element.observe("contextmenu",
                            element.onContextMenu.bindAsEventListener(element));
        },

        selectElement: function(element) {
            element = $(element);
            element.addClassName('_selected');

            var parent = element.up();
            if (!parent.selectedElements || !parent.selectedIds) {
                // Selected nodes are kept in a array at the
                // container level.
                parent.selectedElements = new Array();
                parent.selectedIds = new Array();
            }
            for (var i = 0; i < parent.selectedElements.length; i++)
                if (parent.selectedElements[i] == element) return;
            for (var i = 0; i < parent.selectedIds.length; i++)
                if (parent.selectedIds[i] == element.id) return;
            parent.selectedElements.push(element); // use index instead ?
            parent.selectedIds.push(element.id);
        },

        selectRange: function(element, startIndex, endIndex) {
            element = $(element);
            var s;
            var e;
            var rows;

            if (startIndex > endIndex) {
                s = endIndex;
                e = startIndex;
            }
            else {
                s = startIndex;
                e = endIndex;
            }
            if (element.tagName == 'UL')
                rows = element.getElementsByTagName('LI');
            else
                rows = element.getElementsByTagName('TR');
            while (s <= e) {
                if (rows[s].nodeType == 1)
                    $(rows[s]).selectElement();
                s++;
            }
        },

        selectAll: function(element) {
            element = $(element);
            if (element.tagName == 'UL')
                rows = element.getElementsByTagName('LI');
            else
                rows = element.select('TBODY TR');
            for (var i = 0; i < rows.length; i++)
                if (rows[i].nodeType == 1)
                    $(rows[i]).selectElement();
        },

        deselect: function(element) {
            element = $(element);
            element.removeClassName('_selected');
            var parent = element.parentNode;
            if (parent) {
                if (parent.selectedElements)
                    parent.selectedElements = parent.selectedElements.without(element);
                if (parent.selectedIds)
                    parent.selectedIds = parent.selectedIds.without(element.id);
            }
        },

        deselectAll: function(element) {
            element = $(element);
            if (element.tagName == 'TABLE') {
                var tbody = element.tBodies[0];
                if (tbody)
                    element = tbody;
            }
            var s = element.select("._selected");
            for (var i = 0; i < s.length; i++)
                s[i].removeClassName("_selected");
            element.selectedElements = null;
            element.selectedIds = null;
        },

        refreshSelectionByIds: function(element) {
            element = $(element);
            if (element.selectedIds) {
                for (var i = 0; i < element.selectedIds.length; i++) {
                    var e = element.down('#'+element.selectedIds[i]);
                    if (e) {
                        if (!e.hasClassName('_selected'))
                            e.addClassName('_selected');
                    }
                    else {
                        log ("refreshSelectionByIds Error: " + element.tagName
                             + " select by ID " + element.selectedIds[i]
                             + " not found (" + element.childNodes.length + " children)");
                        element.selectedIds.splice(i,1);
                    }
                }
            }
        },

        setCaretTo: function(element, pos) {
            element = $(element);
            if (element.setSelectionRange) {  // For Mozilla and Safari
                element.focus();
                element.setSelectionRange(pos, pos);
            }
            else if (element.createTextRange) {  // For IE
                var range = element.createTextRange();
                range.move("character", pos);
                range.select();
            }
        },

        selectText: function(element, start, end) {
            element = $(element);
            if (element.setSelectionRange) {     // For Mozilla and Safari
                element.setSelectionRange(start, end);
            }
            else if (element.createTextRange) {  // For IE
                var textRange = element.createTextRange();
                textRange.moveStart("character", start);
                textRange.moveEnd("character", end-element.value.length);
                textRange.select();
            }
            else {
                element.select();
            }
        },

        getRadioValue: function(element, radioName) {
            element = $(element);
            var radioValue;
            Form.getInputs(element, 'radio', radioName).each(function(input) {
                    if (input.checked)
                        radioValue = input.value;
                });
            return radioValue;
        },

        setRadioValue: function(element, radioName, value) {
            element = $(element);
            var i = 0;

            Form.getInputs(element, 'radio', radioName).each(function(input) {
                    if (i == value)
                        input.checked = 1;
                    i++;
                });
        },

        getCheckBoxListValues: function(element, checkboxName) {
            element = $(element);
            var values = new Array();
            var i = 0;

            Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {
                    if (input.checked)
                        values.push(i+1);

                    i++;
                });
            return values.join(",");
        },

        setCheckBoxListValues: function(element, checkboxName, values) {
            element = $(element);
            var v = values.split(',');
            var i = 1;

            Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {

                    if ($(v).indexOf(i+"") != -1)
                        input.checked = 1;
                    i++;
                });
        }
});
<commit_msg>Fixed a bug in selection of live loading tables. Can have drawbacks elsewhere.<commit_after>/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/* custom extensions to the DOM api */
Element.addMethods({
        addInterface: function(element, objectInterface) {
            element = $(element);
            Object.extend(element, objectInterface);
            if (element.bind)
                element.bind();
        },

        allTextContent: function(element) {
            var content = "";
            for (var i = 0; i < element.childNodes.length; i++) {
                var node = $(element.childNodes[i]);
                if (node.nodeType == Node.TEXT_NODE) {
                    content += node.nodeValue;
                }
                else if (node.nodeType == Node.ELEMENT_NODE) {
                    content += Element.allTextContent(node);
                }
            }

            return content;
        },

        childNodesWithTag: function(element, tagName) {
            element = $(element);

            var matchingNodes = new Array();
            tagName = tagName.toUpperCase();

            for (var i = 0; i < element.childNodes.length; i++) {
                var childNode = $(element.childNodes[i]);
                if (Object.isElement(childNode)
                    && childNode.tagName
                    && childNode.tagName.toUpperCase() == tagName)
                    matchingNodes.push(childNode);
            }

            return matchingNodes;
        },

        getParentWithTagName: function(element, tagName) {
            element = $(element);
            var currentElement = element;
            tagName = tagName.toUpperCase();

            currentElement = currentElement.parentNode;
            while (currentElement
                   && currentElement.tagName != tagName) {
                currentElement = currentElement.parentNode;
            }

            return currentElement;
        },

        cascadeLeftOffset: function(element) {
            element = $(element);
            var currentElement = element;

            var offset = 0;
            while (currentElement) {
                offset += currentElement.offsetLeft;
                currentElement = $(currentElement).getParentWithTagName("div");
            }

            return offset;
        },

        cascadeTopOffset: function(element) {
            element = $(element);
            var currentElement = element;
            var offset = 0;

            var i = 0;

            while (currentElement && currentElement.tagName) {
                offset += currentElement.offsetTop;
                currentElement = currentElement.parentNode;
                i++;
            }

            return offset;
        },

        dump: function(element, additionalInfo, additionalKeys) {
            element = $(element);
            var id = element.getAttribute("id");
            var nclass = element.getAttribute("class");

            var str = element.tagName;
            if (id)
                str += "; id = " + id;
            if (nclass)
                str += "; class = " + nclass;

            if (additionalInfo)
                str += "; " + additionalInfo;

            if (additionalKeys)
                for (var i = 0; i < additionalKeys.length; i++) {
                    var value = element.readAttribute(additionalKeys[i]);
                    if (value)
                        str += "; " + additionalKeys[i] + " = " + value;
                }

            log (str);
        },

        getSelectedNodes: function(element) {
            element = $(element);

            if (!element.selectedElements)
                element.selectedElements = new Array();

            return element.selectedElements;
        },

        getSelectedNodesId: function(element) {
            element = $(element);

            var selArray = null;
            if (element.selectedIds) {
                selArray = element.selectedIds;
            }
            else {
                selArray = [];
                if (element.selectedElements) {
                    for (var i = 0; i < element.selectedElements.length; i++) {
                        var node = element.selectedElements[i];
                        selArray.push(node.getAttribute("id"));
                    }
                }
            }

            return selArray;
        },

        onContextMenu: function(element, event) {
            element = $(element);
            Event.stop(event);
            if (document.currentPopupMenu)
                hideMenu(document.currentPopupMenu);

            var popup = element.sogoContextMenu;
            var menuTop = Event.pointerY(event);
            var menuLeft = Event.pointerX(event);
            var heightDiff = (window.height()
                              - (menuTop + popup.offsetHeight));
            if (heightDiff < 0)
                menuTop += heightDiff;

            var leftDiff = (window.width()
                            - (menuLeft + popup.offsetWidth));
            if (leftDiff < 0)
                menuLeft -= popup.offsetWidth;

            var isVisible = true;
            if (popup.prepareVisibility)
                isVisible = popup.prepareVisibility();

            if (isVisible) {
                popup.setStyle( { top: menuTop + "px",
                            left: menuLeft + "px",
                            visibility: "visible" } );

                document.currentPopupMenu = popup;
                document.body.observe("click", onBodyClickMenuHandler);
            }
            else
                log ("Warning: not showing the contextual menu " + element.id);
        },

        attachMenu: function(element, menuName) {
            element = $(element);
            element.sogoContextMenu = $(menuName);
            element.observe("contextmenu",
                            element.onContextMenu.bindAsEventListener(element));
        },

        selectElement: function(element) {
            element = $(element);
            element.addClassName('_selected');

            var parent = element.up();
            if (!parent.selectedElements || !parent.selectedIds) {
                // Selected nodes are kept in a array at the
                // container level.
                parent.selectedElements = new Array();
                parent.selectedIds = new Array();
            }
            for (var i = 0; i < parent.selectedElements.length; i++)
                if (parent.selectedElements[i] == element) return;
            for (var i = 0; i < parent.selectedIds.length; i++)
                if (parent.selectedIds[i] == element.id) return;
            parent.selectedElements.push(element); // use index instead ?
            parent.selectedIds.push(element.id);
        },

        selectRange: function(element, startIndex, endIndex) {
            element = $(element);
            var s;
            var e;
            var rows;

            if (startIndex > endIndex) {
                s = endIndex;
                e = startIndex;
            }
            else {
                s = startIndex;
                e = endIndex;
            }
            if (element.tagName == 'UL')
                rows = element.getElementsByTagName('LI');
            else
                rows = element.getElementsByTagName('TR');
            while (s <= e) {
                if (rows[s].nodeType == 1)
                    $(rows[s]).selectElement();
                s++;
            }
        },

        selectAll: function(element) {
            element = $(element);
            if (element.tagName == 'UL')
                rows = element.getElementsByTagName('LI');
            else
                rows = element.select('TBODY TR');
            for (var i = 0; i < rows.length; i++)
                if (rows[i].nodeType == 1)
                    $(rows[i]).selectElement();
        },

        deselect: function(element) {
            element = $(element);
            element.removeClassName('_selected');
            var parent = element.parentNode;
            if (parent) {
                if (parent.selectedElements)
                    parent.selectedElements = parent.selectedElements.without(element);
                if (parent.selectedIds)
                    parent.selectedIds = parent.selectedIds.without(element.id);
            }
        },

        deselectAll: function(element) {
            element = $(element);
            if (element.tagName == 'TABLE') {
                var tbody = element.tBodies[0];
                if (tbody)
                    element = tbody;
            }
            var s = element.select("._selected");
            for (var i = 0; i < s.length; i++)
                s[i].removeClassName("_selected");
            element.selectedElements = null;
            element.selectedIds = null;
        },

        refreshSelectionByIds: function(element) {
            element = $(element);
            if (element.selectedIds) {
                for (var i = 0; i < element.selectedIds.length; i++) {
                    var e = element.down('#'+element.selectedIds[i]);
                    if (e) {
                        if (!e.hasClassName('_selected'))
                            e.addClassName('_selected');
                    }
                    else {
                        log ("refreshSelectionByIds Error: " + element.tagName
                             + " select by ID " + element.selectedIds[i]
                             + " not found (" + element.childNodes.length + " children)");
                        //element.selectedIds.splice(i,1);
                    }
                }
            }
        },

        setCaretTo: function(element, pos) {
            element = $(element);
            if (element.setSelectionRange) {  // For Mozilla and Safari
                element.focus();
                element.setSelectionRange(pos, pos);
            }
            else if (element.createTextRange) {  // For IE
                var range = element.createTextRange();
                range.move("character", pos);
                range.select();
            }
        },

        selectText: function(element, start, end) {
            element = $(element);
            if (element.setSelectionRange) {     // For Mozilla and Safari
                element.setSelectionRange(start, end);
            }
            else if (element.createTextRange) {  // For IE
                var textRange = element.createTextRange();
                textRange.moveStart("character", start);
                textRange.moveEnd("character", end-element.value.length);
                textRange.select();
            }
            else {
                element.select();
            }
        },

        getRadioValue: function(element, radioName) {
            element = $(element);
            var radioValue;
            Form.getInputs(element, 'radio', radioName).each(function(input) {
                    if (input.checked)
                        radioValue = input.value;
                });
            return radioValue;
        },

        setRadioValue: function(element, radioName, value) {
            element = $(element);
            var i = 0;

            Form.getInputs(element, 'radio', radioName).each(function(input) {
                    if (i == value)
                        input.checked = 1;
                    i++;
                });
        },

        getCheckBoxListValues: function(element, checkboxName) {
            element = $(element);
            var values = new Array();
            var i = 0;

            Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {
                    if (input.checked)
                        values.push(i+1);

                    i++;
                });
            return values.join(",");
        },

        setCheckBoxListValues: function(element, checkboxName, values) {
            element = $(element);
            var v = values.split(',');
            var i = 1;

            Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {

                    if ($(v).indexOf(i+"") != -1)
                        input.checked = 1;
                    i++;
                });
        }
});
<|endoftext|>


<commit_before><commit_msg>distinguished between controller vs mouse signals<commit_after><|endoftext|>


<commit_before>  /**
   * committeeListing/index.js
   *
   * Describes a single political committee.
   **/

   (function() {
    'use strict';

    angular
    .module('committeeListing',[])
    .directive('committeeListing', committeeListingDirective);

    function committeeListingDirective() {
      return {
        restrict: 'E',
        template: require('./committeeListing.html'),
        controller: committeeListingController,
        controllerAs: '$ctrl',
        bindToController: true,
        scope: {
          committee: '=',
          contributions: '=',
        }
      };
    }

    committeeListingController.$inject = ['$filter'];
    function committeeListingController($filter) {
      var contributionsObjs,
          gridWidth;

      var ctrl = this;
      ctrl.committeeTotal = 0;
      ctrl.contributionsSearch = '';

      // ag-grid requires fixed widths in pixels. Calculating them here to take all available space
      var odcaGridElement = document.querySelectorAll('.odca-grid')[0];
      gridWidth = odcaGridElement.offsetWidth;

      // Convert resources to plain old objects to make ag-grid happy
      contributionsObjs = ctrl.contributions.map(function(resource) {
        // Total listed contributions
        ctrl.committeeTotal += resource.Tran_Amt1;

        return {
          Name: getFriendlyName(resource),
          Tran_Amt1: resource.Tran_Amt1,
          Tran_Date: resource.Tran_Date
        };
      });

      ctrl.gridOptions = {
        columnDefs: [
          {
            headerName: 'Contributor',
            field: 'Name',
            filter: 'text',
            width: Math.round(gridWidth * 0.6 * 100) / 100,
            suppressMenu: true
          },
          {
            headerName: 'Amount',
            field: 'Tran_Amt1',
            width: Math.round(gridWidth * 0.2 * 100) / 100,
            cellFormatter: function(params) {
              return $filter('currency')(params.value, '$', 0);
            },
            sort: 'desc',
            suppressMenu: true
          },
          {
            headerName: 'Date',
            field: 'Tran_Date',
            width: Math.round(gridWidth * 0.2 * 100) / 100,
            suppressMenu: true
          }
        ],
        headerHeight: 40,
        enableSorting: true,
        enableFilter: true,
        rowData: contributionsObjs,
        rowHeight: 40,
        suppressMovableColumns: true
      };

      // Combine first and last name, capitalize
      function getFriendlyName(resource) {
        var name = '';
        var names = [];
        if (resource.Tran_NamF) {
          names = names.concat(resource.Tran_NamF.split(' '));
        }

        if (resource.Tran_NamL) {
          names = names.concat(resource.Tran_NamL.split(' '));
        }

        names.forEach(function(newName) {
          name += _.capitalize(newName);
          name += ' ';
        });
        // Remove the extra space at the end
        return name.substring(0, name.length - 1);
      }

    }

})();
<commit_msg>Per comments on the pr, leaving contributor names as formatted in the docs<commit_after>  /**
   * committeeListing/index.js
   *
   * Describes a single political committee.
   **/

   (function() {
    'use strict';

    angular
    .module('committeeListing',[])
    .directive('committeeListing', committeeListingDirective);

    function committeeListingDirective() {
      return {
        restrict: 'E',
        template: require('./committeeListing.html'),
        controller: committeeListingController,
        controllerAs: '$ctrl',
        bindToController: true,
        scope: {
          committee: '=',
          contributions: '=',
        }
      };
    }

    committeeListingController.$inject = ['$filter'];
    function committeeListingController($filter) {
      var contributionsObjs,
          gridWidth;

      var ctrl = this;
      ctrl.committeeTotal = 0;
      ctrl.contributionsSearch = '';

      // ag-grid requires fixed widths in pixels. Calculating them here to take all available space
      var odcaGridElement = document.querySelectorAll('.odca-grid')[0];
      gridWidth = odcaGridElement.offsetWidth;

      // Convert resources to plain old objects to make ag-grid happy
      contributionsObjs = ctrl.contributions.map(function(resource) {
        // Total listed contributions
        ctrl.committeeTotal += resource.Tran_Amt1;

        return {
          Name: getFriendlyName(resource),
          Tran_Amt1: resource.Tran_Amt1,
          Tran_Date: resource.Tran_Date
        };
      });

      ctrl.gridOptions = {
        columnDefs: [
          {
            headerName: 'Contributor',
            field: 'Name',
            filter: 'text',
            width: Math.round(gridWidth * 0.6 * 100) / 100,
            suppressMenu: true
          },
          {
            headerName: 'Amount',
            field: 'Tran_Amt1',
            width: Math.round(gridWidth * 0.2 * 100) / 100,
            cellFormatter: function(params) {
              return $filter('currency')(params.value, '$', 0);
            },
            sort: 'desc',
            suppressMenu: true
          },
          {
            headerName: 'Date',
            field: 'Tran_Date',
            width: Math.round(gridWidth * 0.2 * 100) / 100,
            suppressMenu: true
          }
        ],
        headerHeight: 40,
        enableSorting: true,
        enableFilter: true,
        rowData: contributionsObjs,
        rowHeight: 40,
        suppressMovableColumns: true
      };

      // Combine first and last name, capitalize
      function getFriendlyName(resource) {
        var names = [];
        if (resource.Tran_NamF) {
          names = names.concat(resource.Tran_NamF.split(' '));
        }

        if (resource.Tran_NamL) {
          names = names.concat(resource.Tran_NamL.split(' '));
        }

        return names.join(' ');
      }

    }

})();
<|endoftext|>


<commit_before>import { callbacks } from '../../../callbacks';
import s from 'underscore.string';

const generateKeyDefault = (...args) => args.map((item) => JSON.stringify(item)).join('-');

const mem = (fn, tm = 500, generateKey = generateKeyDefault) => {
	const cache = {};
	const timeout = {};

	const invalidateCache = (key) => delete cache[key];
	return (...args) => {
		const key = generateKey(...args);
		if (!cache[key]) {
			cache[key] = fn(...args);
		}
		if (timeout[key]) {
			clearTimeout(timeout[key]);
		}
		timeout[key] = setTimeout(invalidateCache, tm, key);
		return cache[key];
	};
};

export const renderMessageBody = mem((message) => {

	message.html = s.trim(message.msg) ? s.escapeHTML(message.msg) : '';

	const { tokens, html } = callbacks.run('renderMessage', message);

	return (Array.isArray(tokens) ? tokens.reverse() : [])
		.reduce((html, { token, text }) => html.replace(token, () => text), html);
}, 5000, ({ _id, _updatedAt }) => (_id + _updatedAt));
<commit_msg>[FIX] renderMessageBody was caching messages in wrong scenarios #14420<commit_after>import { callbacks } from '../../../callbacks';
import s from 'underscore.string';

const generateKeyDefault = (...args) => args.map((item) => JSON.stringify(item)).join('-');

const mem = (fn, tm = 500, generateKey = generateKeyDefault) => {
	const cache = {};
	const timeout = {};

	const invalidateCache = (key) => delete cache[key];
	return (...args) => {
		const key = generateKey(...args);
		if (!key) {
			return fn(...args);
		}
		if (!cache[key]) {
			cache[key] = fn(...args);
		}
		if (timeout[key]) {
			clearTimeout(timeout[key]);
		}
		timeout[key] = setTimeout(invalidateCache, tm, key);
		return cache[key];
	};
};

export const renderMessageBody = mem((message) => {

	message.html = s.trim(message.msg) ? s.escapeHTML(message.msg) : '';

	const { tokens, html } = callbacks.run('renderMessage', message);

	return (Array.isArray(tokens) ? tokens.reverse() : [])
		.reduce((html, { token, text }) => html.replace(token, () => text), html);
}, 5000, ({ _id, _updatedAt }) => (_id && _updatedAt && _id + _updatedAt));
<|endoftext|>