<commit_before>import {assign, joinParams} from './helpers';

export const HTTP_METHOD_GET = 'GET';
export const HTTP_METHOD_PUT = 'PUT';
export const HTTP_METHOD_POST = 'POST';
export const HTTP_METHOD_DELETE = 'DELETE';
export const ALLOWED_METHODS = [
    HTTP_METHOD_GET,
    HTTP_METHOD_PUT,
    HTTP_METHOD_POST,
    HTTP_METHOD_DELETE
];

export const CONTENT_TYPE_JSON = 'application/json';
export const MIN_SUCCESSFUL_HTTP_CODE = 200;
export const MAX_SUCCESSFUL_HTTP_CODE = 299;

export const ERROR_XHR_NOT_FOUND = 'Constructor XMLHttpRequest not found';
export const ERROR_PROMISE_NOT_FOUND = 'Constructor Promise not found';
export const ERROR_UNKNOWN_HTTP_METHOD = 'Unknown HTTP method';
export const ERROR_UNACCEPTABLE_HTTP_CODE = 'Unacceptable HTTP code';
export const ERROR_INVALID_DATA = 'Invalid data for sending';
export const ERROR_JSON_PARSE = 'Invalid JSON';

export const ERROR_CONNECTION_TIMEOUT = 'Connection timeout';
export const ERROR_RESOURCE_ABORTED = 'Resource has been aborted';
export const ERROR_RESOURCE_FAILED = 'Resource failed to load';

/**
 * @class HTTP-transport based on XHR
 * @property {String} path Common path
 * @property {Number} timeout Timeout (in milliseconds)
 * @property {Object} headers Common headers
 * @property {Array} opened List of opened requests
 * @property {XMLHttpRequest} XMLHttpRequest XHR interface
 * @property {Boolean} cors ```true``` if supported Cross-Origin Resource Sharing
 * @see https://xhr.spec.whatwg.org/
 * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
 * @see http://www.html5rocks.com/en/tutorials/cors/
 */
class FetchPlease {
    /**
     * @param {String} path Common path for all requests
     * @param {Object} [settings] Object with settings
     * @param {Number} [settings.timeout = 0]
     * @param {Object} [settings.XMLHttpRequest = global.XMLHttpRequest]
     * @param {Object|Function} [settings.headers = {}]
     */
    constructor(path = '', settings = {}) {
        this.path = path;
        this.opened = [];

        assign(this, {
            timeout: 0,
            headers: {},
            XMLHttpRequest: global.XMLHttpRequest
        }, settings);

        let {XMLHttpRequest} = this;
        if (XMLHttpRequest) {
            this.cors = 'withCredentials' in (new XMLHttpRequest()) && settings.cors;
        }
    }

    /**
     * Creates XHR instance and Promise instance
     * @param {String} method HTTP method
     * @param {String} path
     * @param {Object} data
     * @param {Object} settings
     * @return {Object}
     */
    request(method, path, data = null, settings = null) {
        if (!this.XMLHttpRequest) {
            throw new Error(ERROR_XHR_NOT_FOUND);
        }

        if (!global.Promise) {
            throw new Error(ERROR_PROMISE_NOT_FOUND);
        }

        if (ALLOWED_METHODS.indexOf(method) === -1) {
            throw new Error(ERROR_UNKNOWN_HTTP_METHOD);
        }

        let xhr = new this.XMLHttpRequest(),
            promise = new Promise(function (resolve, reject) {
                xhr.addEventListener('error', () => reject(ERROR_RESOURCE_FAILED));
                xhr.addEventListener('abort', () => reject(ERROR_RESOURCE_ABORTED));
                xhr.addEventListener('timeout', () => reject(ERROR_CONNECTION_TIMEOUT));
                xhr.addEventListener('load', () => {
                    if (xhr.status) {
                        resolve(xhr);
                    }
                });
            });

        settings = settings || {};

        let handleJson = settings.handleJson || this.handleJson,
            handleError = settings.handleError || this.handleError,
            handleResponse = settings.handleResponse || this.handleResponse;

        promise = promise
            // Remove request from list of opened requests
            .then(
                () => this.close(xhr),
                (reason) => {
                    this.close(xhr);
                    throw new Error(reason);
                }
            )
            // Handle response
            .then(handleResponse)
            // Handle JSON in response
            .then(handleJson)
            // Handle errors
            .catch(handleError);

        // Add optinal `onProgress` handler
        if (settings.onProgress) {
            xhr.addEventListener('progress', settings.onProgress);
        }

        // Form URL without normalizing and open request
        let url = this.path + path;
        xhr.open(method, url);

        // Set headers
        // Order of method's calls is important
        // You must call setRequestHeader() after open(), but before send()
        // @see: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#setRequestHeader()
        let headers = assign(
            {},
            typeof this.headers === 'function' ? this.headers() : this.headers,
            settings.headers
        );
        this.setHeaders(xhr, headers);

        // Set timeout (before send() too)
        // @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#Example_using_a_timeout
        let timeout = this.timeout || settings.timeout || 0;
        xhr.timeout = timeout;

        // Serialize data and send request
        data = this.serialize(data);

        if (this.cors) {
            xhr.withCredentials = true;
        }

        xhr.send(data);

        // Add request into list of opened requests
        this.add(xhr);

        return {xhr, promise};
    }

    /**
     * Serializes data for request
     * @param {*} data
     * @return {String|FormData|Blob}
     */
    serialize(data) {
        // Do nothing with FormData instance
        if (global.FormData && data instanceof FormData) {
            return data;
        }

        // Do nothing with Blob instance
        if (global.Blob && data instanceof Blob) {
            return data;
        }

        // Do nothing with string or null
        if (data === null || typeof data === 'string') {
            return data;
        }

        try {
            // Serialize other data as JSON
            return JSON.stringify(data);
        } catch (e) {
            throw new Error(ERROR_INVALID_DATA);
        }
    }

    /**
     * Iterates given headers and set them for XHR object
     * @param {XMLHttpRequest} xhr
     * @param {Object} headers
     * @return {XMLHttpRequest}
     */
    setHeaders(xhr, headers = {}) {
        return Object.keys(headers).reduce((xhr, header) => {
            let value = headers[header];

            if (value !== false && value !== null && value !== undefined) {
                xhr.setRequestHeader(header, value);
            }

            return xhr;
        }, xhr);
    }

    /**
     * Aborts all opened requests
     */
    abort() {
        this.opened.forEach((xhr) => xhr.abort());
    }

    /**
     * Adds request to list of opened requests
     * @param {XMLHttpRequest} xhr
     */
    add(xhr) {
        this.opened.push(xhr);
    }

    /**
     * Deletes request from list of opened requests
     * @param {XMLHttpRequest} xhr
     * @return {XMLHtpRequest}
     */
    close(xhr) {
        let idx = this.opened.indexOf(xhr);
        if (idx !== -1) {
            this.opened.splice(idx, 1);
        }
        return xhr;
    }

    /**
     * Sends GET request
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Promise}
     */
    get(url, params, settings) {
        let {promise} = this.getRequest(url, params, settings);
        return promise;
    }

    /**
     * Sends PUT request
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Promise}
     */
    put(url, data, settings) {
        let {promise} = this.putRequest(url, data, settings);
        return promise;
    }

    /**
     * Sends POST request
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Promise}
     */
    post(url, data, settings) {
        let {promise} = this.postRequest(url, data, settings);
        return promise;
    }

    /**
     * Sends DELETE request
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Promise}
     */
    delete(url, params, settings) {
        let {promise} = this.deleteRequest(url, params, settings);
        return promise;
    }

    /**
     * Sends same GET request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Object}
     */
    getRequest(url, params = null, settings = null) {
        url = joinParams(url, params);
        return this.request(HTTP_METHOD_GET, url, null, settings);
    }

    /**
     * Sends same PUT request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Object}
     */
    putRequest(url, data, settings = null) {
        return this.request(HTTP_METHOD_PUT, url, data, settings);
    }

    /**
     * Sends same POST request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Object}
     */
    postRequest(url, data, settings = null) {
        return this.request(HTTP_METHOD_POST, url, data, settings);
    }

    /**
     * Sends same DELETE request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Object}
     */
    deleteRequest(url, params = null, settings = null) {
        url = joinParams(url, params);
        return this.request(HTTP_METHOD_DELETE, url, null, settings);
    }

    /**
     * Handles JSON in response
     * @param {XMLHttpRequest} xhr
     * @param {String} xhr.responseText
     * @return {Object}
     */
    handleJson(xhr) {
        let {responseText} = xhr,
            contentType = xhr.getResponseHeader('Content-Type') || '';

        contentType = contentType.toLowerCase();

        // If header Content-Type has value "application/json" then parse text as JSON
        if (contentType.indexOf(CONTENT_TYPE_JSON) !== -1) {
            try {
                return JSON.parse(responseText);
            } catch (e) {
                throw new Error(ERROR_JSON_PARSE);
            }
        }

        // Just return given xhr
        return xhr;
    }

    /**
     * Handles XHR response
     * @param {XMLHttpRequest} xhr
     * @param {Number} xhr.status
     * @see https://xhr.spec.whatwg.org/#interface-xmlhttprequest
     * @return {XMLHttpRequest}
     */
    handleResponse(xhr) {
        let {status} = xhr;

        // If status < 200 or status >= 200 then return error
        if (status < MIN_SUCCESSFUL_HTTP_CODE || status > MAX_SUCCESSFUL_HTTP_CODE) {
            let error = new Error(ERROR_UNACCEPTABLE_HTTP_CODE);
            error.statusCode = status;
            throw error;
        }

        // Just return given xhr
        return xhr;
    }

    /**
     * Handles any occured errors
     * @param {Error} error
     * @return {Error}
     */
    handleError(error) {
        // Really do nothing
        throw error;
    }
}

export default FetchPlease;
<commit_msg>Document better the `cors` option<commit_after>import {assign, joinParams} from './helpers';

export const HTTP_METHOD_GET = 'GET';
export const HTTP_METHOD_PUT = 'PUT';
export const HTTP_METHOD_POST = 'POST';
export const HTTP_METHOD_DELETE = 'DELETE';
export const ALLOWED_METHODS = [
    HTTP_METHOD_GET,
    HTTP_METHOD_PUT,
    HTTP_METHOD_POST,
    HTTP_METHOD_DELETE
];

export const CONTENT_TYPE_JSON = 'application/json';
export const MIN_SUCCESSFUL_HTTP_CODE = 200;
export const MAX_SUCCESSFUL_HTTP_CODE = 299;

export const ERROR_XHR_NOT_FOUND = 'Constructor XMLHttpRequest not found';
export const ERROR_PROMISE_NOT_FOUND = 'Constructor Promise not found';
export const ERROR_UNKNOWN_HTTP_METHOD = 'Unknown HTTP method';
export const ERROR_UNACCEPTABLE_HTTP_CODE = 'Unacceptable HTTP code';
export const ERROR_INVALID_DATA = 'Invalid data for sending';
export const ERROR_JSON_PARSE = 'Invalid JSON';

export const ERROR_CONNECTION_TIMEOUT = 'Connection timeout';
export const ERROR_RESOURCE_ABORTED = 'Resource has been aborted';
export const ERROR_RESOURCE_FAILED = 'Resource failed to load';

/**
 * @class HTTP-transport based on XHR
 * @property {String} path Common path
 * @property {Number} timeout Timeout (in milliseconds)
 * @property {Object} headers Common headers
 * @property {Array} opened List of opened requests
 * @property {Boolean} cors  Use `withCredentials`
 * @property {XMLHttpRequest} XMLHttpRequest XHR interface
 * @property {Boolean} cors ```true``` if supported Cross-Origin Resource Sharing
 * @see https://xhr.spec.whatwg.org/
 * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
 * @see http://www.html5rocks.com/en/tutorials/cors/
 */
class FetchPlease {
    /**
     * @param {String} path Common path for all requests
     * @param {Object} [settings] Object with settings
     * @param {Number} [settings.timeout = 0]
     * @param {Object} [settings.XMLHttpRequest = global.XMLHttpRequest]
     * @param {Object} [settings.cors = false]
     * @param {Object|Function} [settings.headers = {}]
     */
    constructor(path = '', settings = {}) {
        this.path = path;
        this.opened = [];

        assign(this, {
            timeout: 0,
            headers: {},
            cors: false
            XMLHttpRequest: global.XMLHttpRequest
        }, settings);

        let {XMLHttpRequest} = this;
        if (XMLHttpRequest) {
            this.cors = 'withCredentials' in (new XMLHttpRequest()) && settings.cors;
        }
    }

    /**
     * Creates XHR instance and Promise instance
     * @param {String} method HTTP method
     * @param {String} path
     * @param {Object} data
     * @param {Object} settings
     * @return {Object}
     */
    request(method, path, data = null, settings = null) {
        if (!this.XMLHttpRequest) {
            throw new Error(ERROR_XHR_NOT_FOUND);
        }

        if (!global.Promise) {
            throw new Error(ERROR_PROMISE_NOT_FOUND);
        }

        if (ALLOWED_METHODS.indexOf(method) === -1) {
            throw new Error(ERROR_UNKNOWN_HTTP_METHOD);
        }

        let xhr = new this.XMLHttpRequest(),
            promise = new Promise(function (resolve, reject) {
                xhr.addEventListener('error', () => reject(ERROR_RESOURCE_FAILED));
                xhr.addEventListener('abort', () => reject(ERROR_RESOURCE_ABORTED));
                xhr.addEventListener('timeout', () => reject(ERROR_CONNECTION_TIMEOUT));
                xhr.addEventListener('load', () => {
                    if (xhr.status) {
                        resolve(xhr);
                    }
                });
            });

        settings = settings || {};

        let handleJson = settings.handleJson || this.handleJson,
            handleError = settings.handleError || this.handleError,
            handleResponse = settings.handleResponse || this.handleResponse;

        promise = promise
            // Remove request from list of opened requests
            .then(
                () => this.close(xhr),
                (reason) => {
                    this.close(xhr);
                    throw new Error(reason);
                }
            )
            // Handle response
            .then(handleResponse)
            // Handle JSON in response
            .then(handleJson)
            // Handle errors
            .catch(handleError);

        // Add optinal `onProgress` handler
        if (settings.onProgress) {
            xhr.addEventListener('progress', settings.onProgress);
        }

        // Form URL without normalizing and open request
        let url = this.path + path;
        xhr.open(method, url);

        // Set headers
        // Order of method's calls is important
        // You must call setRequestHeader() after open(), but before send()
        // @see: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#setRequestHeader()
        let headers = assign(
            {},
            typeof this.headers === 'function' ? this.headers() : this.headers,
            settings.headers
        );
        this.setHeaders(xhr, headers);

        // Set timeout (before send() too)
        // @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#Example_using_a_timeout
        let timeout = this.timeout || settings.timeout || 0;
        xhr.timeout = timeout;

        // Serialize data and send request
        data = this.serialize(data);

        if (this.cors) {
            xhr.withCredentials = true;
        }

        xhr.send(data);

        // Add request into list of opened requests
        this.add(xhr);

        return {xhr, promise};
    }

    /**
     * Serializes data for request
     * @param {*} data
     * @return {String|FormData|Blob}
     */
    serialize(data) {
        // Do nothing with FormData instance
        if (global.FormData && data instanceof FormData) {
            return data;
        }

        // Do nothing with Blob instance
        if (global.Blob && data instanceof Blob) {
            return data;
        }

        // Do nothing with string or null
        if (data === null || typeof data === 'string') {
            return data;
        }

        try {
            // Serialize other data as JSON
            return JSON.stringify(data);
        } catch (e) {
            throw new Error(ERROR_INVALID_DATA);
        }
    }

    /**
     * Iterates given headers and set them for XHR object
     * @param {XMLHttpRequest} xhr
     * @param {Object} headers
     * @return {XMLHttpRequest}
     */
    setHeaders(xhr, headers = {}) {
        return Object.keys(headers).reduce((xhr, header) => {
            let value = headers[header];

            if (value !== false && value !== null && value !== undefined) {
                xhr.setRequestHeader(header, value);
            }

            return xhr;
        }, xhr);
    }

    /**
     * Aborts all opened requests
     */
    abort() {
        this.opened.forEach((xhr) => xhr.abort());
    }

    /**
     * Adds request to list of opened requests
     * @param {XMLHttpRequest} xhr
     */
    add(xhr) {
        this.opened.push(xhr);
    }

    /**
     * Deletes request from list of opened requests
     * @param {XMLHttpRequest} xhr
     * @return {XMLHtpRequest}
     */
    close(xhr) {
        let idx = this.opened.indexOf(xhr);
        if (idx !== -1) {
            this.opened.splice(idx, 1);
        }
        return xhr;
    }

    /**
     * Sends GET request
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Promise}
     */
    get(url, params, settings) {
        let {promise} = this.getRequest(url, params, settings);
        return promise;
    }

    /**
     * Sends PUT request
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Promise}
     */
    put(url, data, settings) {
        let {promise} = this.putRequest(url, data, settings);
        return promise;
    }

    /**
     * Sends POST request
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Promise}
     */
    post(url, data, settings) {
        let {promise} = this.postRequest(url, data, settings);
        return promise;
    }

    /**
     * Sends DELETE request
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Promise}
     */
    delete(url, params, settings) {
        let {promise} = this.deleteRequest(url, params, settings);
        return promise;
    }

    /**
     * Sends same GET request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Object}
     */
    getRequest(url, params = null, settings = null) {
        url = joinParams(url, params);
        return this.request(HTTP_METHOD_GET, url, null, settings);
    }

    /**
     * Sends same PUT request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Object}
     */
    putRequest(url, data, settings = null) {
        return this.request(HTTP_METHOD_PUT, url, data, settings);
    }

    /**
     * Sends same POST request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} data
     * @param {Object} [settings]
     * @return {Object}
     */
    postRequest(url, data, settings = null) {
        return this.request(HTTP_METHOD_POST, url, data, settings);
    }

    /**
     * Sends same DELETE request but returns object with instances of XHR and Promise
     * @param {String} url
     * @param {Object} [params]
     * @param {Object} [settings]
     * @return {Object}
     */
    deleteRequest(url, params = null, settings = null) {
        url = joinParams(url, params);
        return this.request(HTTP_METHOD_DELETE, url, null, settings);
    }

    /**
     * Handles JSON in response
     * @param {XMLHttpRequest} xhr
     * @param {String} xhr.responseText
     * @return {Object}
     */
    handleJson(xhr) {
        let {responseText} = xhr,
            contentType = xhr.getResponseHeader('Content-Type') || '';

        contentType = contentType.toLowerCase();

        // If header Content-Type has value "application/json" then parse text as JSON
        if (contentType.indexOf(CONTENT_TYPE_JSON) !== -1) {
            try {
                return JSON.parse(responseText);
            } catch (e) {
                throw new Error(ERROR_JSON_PARSE);
            }
        }

        // Just return given xhr
        return xhr;
    }

    /**
     * Handles XHR response
     * @param {XMLHttpRequest} xhr
     * @param {Number} xhr.status
     * @see https://xhr.spec.whatwg.org/#interface-xmlhttprequest
     * @return {XMLHttpRequest}
     */
    handleResponse(xhr) {
        let {status} = xhr;

        // If status < 200 or status >= 200 then return error
        if (status < MIN_SUCCESSFUL_HTTP_CODE || status > MAX_SUCCESSFUL_HTTP_CODE) {
            let error = new Error(ERROR_UNACCEPTABLE_HTTP_CODE);
            error.statusCode = status;
            throw error;
        }

        // Just return given xhr
        return xhr;
    }

    /**
     * Handles any occured errors
     * @param {Error} error
     * @return {Error}
     */
    handleError(error) {
        // Really do nothing
        throw error;
    }
}

export default FetchPlease;
<|endoftext|>


<commit_before>/*jslint node: true */
/*global require */

module.exports = function (util, events, http, querystring, url, elastic) {
    "use strict";

    var exports = {};

    /**
     * Filter the posts to only query the public posts available
     * @param query
     * @returns modified query
     */
    var filterPublicPosts = function (query) {
        query.status = "active";

        if (query.user === undefined) {
            query.user = {};
        }

        query.user.active = "1";

        return query;
    };

    var searchRequest = function (request, response) {
        request.setEncoding("utf8");

        var body = "";

        request.on("data", function (chunk) {
            body += chunk;
        });
        request.on("end", function () {
            if (request.url.length > 400) {
                response.writeHead(403, {"Content-Type": "text/plain"});
                response.end("Search string is too long.");
                return;
            }

            var requestUrl = url.parse(request.url.toLowerCase(), true);

            var search = new elastic.search();

            search.query(requestUrl.query);

            search.on("success", function (searchResponse) {
                var result;
                var resultJson;

                response.writeHead(200, {"Content-Type": "application/json"});

                if (requestUrl.query.suggestion !== undefined) {
                    if (searchResponse.facets !== undefined &&
                        searchResponse.facets.title_suggestions !== undefined &&
                        searchResponse.facets.title_suggestions.terms !== undefined
                        ) {
                        var terms = searchResponse.facets.title_suggestions.terms;

                        var suggestions = [];

                        for (var suggestionPos in terms) {
                            if (terms.hasOwnProperty(suggestionPos) && terms[suggestionPos].term !== undefined) {
                                suggestions.push(terms[suggestionPos].term);
                            }
                        }

                        result = suggestions;
                    } else {
                        response.writeHead(404, {"Content-Type": "text/plain"});
                        response.end("Missing data for the suggestions response.");
                    }
                } else {
                    result = searchResponse;
                }

                resultJson = JSON.stringify(result);

                response.end(resultJson);
            });

            search.on("failure", function (error) {
                response.writeHead(404, {"Content-Type": "text/plain"});
                response.end("Search failure.");
            });
        });
    };

    var notFoundRequest = function (request, response) {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.end("Not found.");
    };

    exports.load = function (request, response) {
        var requestUrl = url.parse(request.url);

        if (requestUrl.pathname === "/") {
            return searchRequest(request, response);
        } else {
            return notFoundRequest(request, response);
        }
    };

    return exports;
};
<commit_msg>add filter so it is only possible to query for the public available posts<commit_after>/*jslint node: true */
/*global require */

module.exports = function (util, events, http, querystring, url, elastic) {
    "use strict";

    var exports = {};

    /**
     * Filter the posts to only query the public posts available
     * @param query
     * @returns modified query
     */
    var filterPublicPosts = function (query) {
        query.status = "active";

        if (query.user === undefined) {
            query.user = {};
        }

        query.user.active = "1";

        return query;
    };

    var searchRequest = function (request, response) {
        request.setEncoding("utf8");

        var body = "";

        request.on("data", function (chunk) {
            body += chunk;
        });
        request.on("end", function () {
            if (request.url.length > 400) {
                response.writeHead(403, {"Content-Type": "text/plain"});
                response.end("Search string is too long.");
                return;
            }

            var requestUrl = url.parse(request.url.toLowerCase(), true);

            var search = new elastic.search();

            search.query(filterPublicPosts(requestUrl.query));

            search.on("success", function (searchResponse) {
                var result;
                var resultJson;

                response.writeHead(200, {"Content-Type": "application/json"});

                if (requestUrl.query.suggestion !== undefined) {
                    if (searchResponse.facets !== undefined &&
                        searchResponse.facets.title_suggestions !== undefined &&
                        searchResponse.facets.title_suggestions.terms !== undefined
                        ) {
                        var terms = searchResponse.facets.title_suggestions.terms;

                        var suggestions = [];

                        for (var suggestionPos in terms) {
                            if (terms.hasOwnProperty(suggestionPos) && terms[suggestionPos].term !== undefined) {
                                suggestions.push(terms[suggestionPos].term);
                            }
                        }

                        result = suggestions;
                    } else {
                        response.writeHead(404, {"Content-Type": "text/plain"});
                        response.end("Missing data for the suggestions response.");
                    }
                } else {
                    result = searchResponse;
                }

                resultJson = JSON.stringify(result);

                response.end(resultJson);
            });

            search.on("failure", function (error) {
                response.writeHead(404, {"Content-Type": "text/plain"});
                response.end("Search failure.");
            });
        });
    };

    var notFoundRequest = function (request, response) {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.end("Not found.");
    };

    exports.load = function (request, response) {
        var requestUrl = url.parse(request.url);

        if (requestUrl.pathname === "/") {
            return searchRequest(request, response);
        } else {
            return notFoundRequest(request, response);
        }
    };

    return exports;
};
<|endoftext|>


<commit_before>const DocumentDBClient = require('documentdb').DocumentClient;

const config = {
    DatabaseId: "mean-dev",
    CollectionId: "users",
    Host: process.env.DocDb_Host,
    AuthKey: process.env.DocDb_AuthKey,
};

config.CollLink = 'dbs/' + config.DatabaseId + '/colls/' + config.CollectionId

module.exports = function (context, message) {

    let err = null;

    const docDbClient = new DocumentDBClient(config.Host, { masterKey: config.AuthKey });
    const query = 'SELECT * FROM c WHERE (c.twitter[\'$id\'] = \'' + message.userid + '\' AND \'twitter\' = \'' + message.platform + '\') OR (c.instagram[\'$id\'] = \'' + message.userid + '\' AND \'instagram\' = \'' + message.platform + '\') OR (c.facebook[\'$id\'] = \'' + message.userid + '\' AND \'facebook\' = \'' + message.platform + '\')';

    docDbClient.queryDocuments(config.CollLink, query).toArray(function (err, results) {

        let userdata = results[0];

        if (!userdata || userdata == undefined) {
            context.log('Not tracking user');

            err = new Error('Not tracking user');
            return;
        }

        if ((!userdata.twitter || userdata.twitter == undefined)
                && (!userdata.facebook || userdata.facebook == undefined)
                || (!userdata.instagram || userdata.instagram == undefined)) {
            context.log('No social profiles');

            err = new Error('No social profiles');
            return;
        }

        if (!userdata.id) {
            context.log('No user id');

            err = new Error('No social profiles');
            return;
        }

        context.bindings.out = {
            id: guid(),
            user: userdata,
            request: message
        };

        context.done(err);
    });

    function guid() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        }
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }
};<commit_msg>db name<commit_after>const DocumentDBClient = require('documentdb').DocumentClient;

const config = {
    DatabaseId: "users",
    CollectionId: "social",
    Host: process.env.DocDb_Host,
    AuthKey: process.env.DocDb_AuthKey,
};

config.CollLink = 'dbs/' + config.DatabaseId + '/colls/' + config.CollectionId

module.exports = function (context, message) {

    let err = null;

    const docDbClient = new DocumentDBClient(config.Host, { masterKey: config.AuthKey });
    const query = 'SELECT * FROM c WHERE (c.twitter[\'$id\'] = \'' + message.userid + '\' AND \'twitter\' = \'' + message.platform + '\') OR (c.instagram[\'$id\'] = \'' + message.userid + '\' AND \'instagram\' = \'' + message.platform + '\') OR (c.facebook[\'$id\'] = \'' + message.userid + '\' AND \'facebook\' = \'' + message.platform + '\')';

    docDbClient.queryDocuments(config.CollLink, query).toArray(function (err, results) {

        let userdata = results[0];

        if (!userdata || userdata == undefined) {
            context.log('Not tracking user');

            err = new Error('Not tracking user');
            return;
        }

        if ((!userdata.twitter || userdata.twitter == undefined)
                && (!userdata.facebook || userdata.facebook == undefined)
                || (!userdata.instagram || userdata.instagram == undefined)) {
            context.log('No social profiles');

            err = new Error('No social profiles');
            return;
        }

        if (!userdata.id) {
            context.log('No user id');

            err = new Error('No social profiles');
            return;
        }

        context.bindings.out = {
            id: guid(),
            user: userdata,
            request: message
        };

        context.done(err);
    });

    function guid() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        }
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }
};<|endoftext|>


<commit_before>/* globals $ */
$(function () {
  'use strict'
/*
  const score = function ($form, ponderation) {
    const ret = { ponderation: ponderation }
    $form.serializeArray().forEach(function (x) { ret[x.name] = x.value })
    ret.note = parseFloat(ret.note)
    ret.ponderation = parseFloat(ret.ponderation)
    ret.createdAt = new Date().toISOString()
    return ret
  }
*/

  const $body = $('body')
  const bodyData = $body.data()
  const $score = $('#json-score')

  const submitJpeg = function (bodyData, self, rev, ev) {
    // see http://stackoverflow.com/a/11910333/1154755
    const $self = $(self)
    const exid = bodyData.exercice
    const docUrl = $self.parents('form').attr('action')
    const file = $self[0].files[0]
    const putRequest = new window.XMLHttpRequest()
    const fileReader = new window.FileReader()
    putRequest.open('PUT', docUrl + '/' + exid + '.jpg' + '?rev=' + rev, true)
    putRequest.setRequestHeader('Content-Type', file.type)
    fileReader.readAsArrayBuffer(file)
    fileReader.onload = function (readerEvent) { putRequest.send(readerEvent.target.result) }
    putRequest.onreadystatechange = function (response) {
      if (putRequest.readyState === 4 && putRequest.status === 201) {
        $('label[for="fichier-label"]').addClass('success').text('Merci!')
        $self.prop('disabled', true)
      } else {
        $('label[for="fichier-label"]').addClass('warning').text('Erreur #1...')
      }
    }
  }

  const showUpload = function (student, $score) {
    $score.after([
      '<form id="upload-jpeg" action="/_users/org.couchdb.user:' + student + '">',
      '  <fieldset class="fieldset callout primary">',
      '
Téléverser jpeg
',
      '
',
      '
',
      '      Choisir le fichier',
      '      <input type="file" id="fichier-label" class="show-for-sr" name="jpeg" required>',
      '
',
      '
',
      '  </fieldset>',
      '</form>'
    ].join())
  }

  const showJpeg = function (exercice, userDoc) {
    $score.after('')
  }

  const makeHtml = function (score2) {
    return ('
' + score2.percent + '%' + ' ' + score2.note + '/' + score2.ponderation + '
' +
     '
' + score2.createdAt.split('T')[0] + ' ' + score2.commentaires + '
')
  }

  const showScore = function (bodyData, userDoc, $score, score3) {
    score3.percent = Math.round(1000 * score3.note / score3.ponderation) / 10
    $score.addClass('success').html(makeHtml(score3))
    if (userDoc._attachments && userDoc._attachments[bodyData.exercice + '.jpg']) {
      showJpeg(bodyData.exercice, userDoc)
    } else {
      showUpload(bodyData.student, $score)
    }
    $body.on('change', 'input#fichier-label', submitJpeg.bind(null, bodyData, 'input#fichier-label', userDoc._rev))
  }

  $.getJSON('/_users/org.couchdb.user:' + bodyData.student, function (userDoc) {
    const score4 = userDoc.corrections && userDoc.corrections[bodyData.exercice]
    if (score4) {
      showScore(bodyData, userDoc, $score, score4)
    } else {
      $score.addClass('warning').text('Aucun résultat disponible.')
    }
  })
})
<commit_msg>Touchups.<commit_after>/* globals $ */
$(function () {
  'use strict'
  const $body = $('body')
  const bodyData = $body.data()
  const $score = $('#json-score')

  const submitJpeg = function (bodyData, self, rev, ev) {
    // see http://stackoverflow.com/a/11910333/1154755
    const $self = $(self)
    const exid = bodyData.exercice
    const docUrl = $self.parents('form').attr('action')
    const file = $self[0].files[0]
    const putRequest = new window.XMLHttpRequest()
    const fileReader = new window.FileReader()
    putRequest.open('PUT', docUrl + '/' + exid + '.jpg' + '?rev=' + rev, true)
    putRequest.setRequestHeader('Content-Type', file.type)
    fileReader.readAsArrayBuffer(file)
    fileReader.onload = function (readerEvent) { putRequest.send(readerEvent.target.result) }
    putRequest.onreadystatechange = function (response) {
      if (putRequest.readyState === 4 && putRequest.status === 201) {
        $('label[for="fichier-label"]').addClass('success').text('Merci!')
        $self.prop('disabled', true)
      } else {
        $('label[for="fichier-label"]').addClass('warning').text('Erreur #1...')
      }
    }
  }

  const showUpload = function (student, $score) {
    $score.after([
      '<form id="upload-jpeg" action="/_users/org.couchdb.user:' + student + '">',
      '  <fieldset class="fieldset callout primary">',
      '
Téléverser jpeg
',
      '
',
      '
',
      '      Choisir le fichier',
      '      <input type="file" id="fichier-label" class="show-for-sr" name="jpeg" required>',
      '
',
      '
',
      '  </fieldset>',
      '</form>'
    ].join())
  }

  const showJpeg = function (exercice, userDoc) {
    $score.after('')
  }

  const makeHtml = function (score) {
    return ('
' + score.percent + '%' + ' ' + score.note + '/' + score.ponderation + '
' +
     '
' + score.createdAt.split('T')[0] + ' ' + score.commentaires + '
')
  }

  const showScore = function (bodyData, userDoc, $score, score) {
    score.percent = Math.round(1000 * score.note / score.ponderation) / 10
    $score.addClass('success').html(makeHtml(score))
    if (userDoc._attachments && userDoc._attachments[bodyData.exercice + '.jpg']) {
      showJpeg(bodyData.exercice, userDoc)
    } else {
      showUpload(bodyData.student, $score)
    }
    $body.on('change', 'input#fichier-label', submitJpeg.bind(null, bodyData, 'input#fichier-label', userDoc._rev))
  }

  $.getJSON('/_users/org.couchdb.user:' + bodyData.student, function (userDoc) {
    const score = userDoc.corrections && userDoc.corrections[bodyData.exercice]
    if (score) {
      showScore(bodyData, userDoc, $score, score)
    } else {
      $score.addClass('warning').text('Aucun résultat disponible.')
    }
  })
})
<|endoftext|>


<commit_before>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    // Register this filter
    global.addFilter(
        // Filter name
        'skin',
        // Vertex Shader (null uses default)
        null,
        // Fragment Shader (null uses default)
        '\
        uniform sampler2D texture;\
        varying vec2 texCoord;\
        void main() {\
            vec4 color = texture2D(texture, texCoord);\
            float r = color.r;\
            float g = color.g;\
            float b = color.b;\
            \
            if ((r>45.0/255.0)&&(g>40.0/255.0)&&(b>20.0/255.0)\
                &&(r>g)&&(r>b)\
                &&(r-min(g,b)>15.0/255.0)\
                &&(abs(r-g)>15.0/255.0)){\
                gl_FragColor = color;\
            } else {\
                gl_FragColor = vec4(0.0,0.0,0.0,color.a);\
            }\
        }\
        ',
        // Uniforms callback
        null
    );

})(glimp);
<commit_msg>skin filter: explicit code optimization on branch condition that was anyway applied by the GLSL compiler<commit_after>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    // Register this filter
    global.addFilter(
        // Filter name
        'skin',
        // Vertex Shader (null uses default)
        null,
        // Fragment Shader (null uses default)
        '\
        uniform sampler2D texture;\
        varying vec2 texCoord;\
        void main() {\
            vec4 color = texture2D(texture, texCoord);\
            float r = color.r;\
            float g = color.g;\
            float b = color.b;\
            \
            if ((r<=45.0/255.0)||(g<=40.0/255.0)||(b<=20.0/255.0)\
                ||(r<=g)||(r<=b)\
                ||((r-min(g,b))<=15.0/255.0)\
                ||(abs(r-g)<=15.0/255.0)){\
                gl_FragColor = vec4(0.0,0.0,0.0,color.a);\
            } else {\
                gl_FragColor = color;\
            }\
        }\
        ',
        // Uniforms callback
        null
    );

})(glimp);
<|endoftext|>


<commit_before>var FiltersPanel = function(container, model) {

  var self = this,

      NAMES = {
        'image': 'Image',
        'text': 'Text File'
      },

      getScreenName = function(contentType) {
        if (NAMES.hasOwnProperty(contentType))
          return NAMES[contentType];
        else
          return contentType;
      },

      createDropdown = function() {
        var select = jQuery(
          '<select>' +
            '<option selected="true" value=""></option>' +
          '</select>');

        model.getFilterValues().map(function(val) {
          select.append('<option value="' + val + '">' + getScreenName(val) + '</val>');
        });

        select.change(function(e) {
          var val = select.val();
          if (val === '')
            self.fireEvent('change');
          else
            self.fireEvent('change', val);
        });

        return select;
      },

      render = function() {
        container.html(createDropdown());
      };

  this.render = render;

  HasEvents.apply(this);
};

FiltersPanel.prototype = Object.create(HasEvents.prototype);
<commit_msg>Minor edits<commit_after>var FiltersPanel = function(container, model) {

  var self = this,

      NAMES = {
        'image': 'Image',
        'text': 'Text File',
        'html': 'HTML Document',
        'other': 'Other...'
      },

      getScreenName = function(contentType) {
        if (NAMES.hasOwnProperty(contentType))
          return NAMES[contentType];
        else
          return contentType;
      },

      createDropdown = function() {
        var select = jQuery(
          '<select>' +
            '<option selected="true" value=""></option>' +
          '</select>');

        model.getFilterValues().map(function(val) {
          select.append('<option value="' + val + '">' + getScreenName(val) + '</val>');
        });

        select.change(function(e) {
          var val = select.val();
          if (val === '')
            self.fireEvent('change');
          else
            self.fireEvent('change', val);
        });

        return select;
      },

      render = function() {
        container.html(createDropdown());
      };

  this.render = render;

  HasEvents.apply(this);
};

FiltersPanel.prototype = Object.create(HasEvents.prototype);
<|endoftext|>


<commit_before>define(function (require, exports, module) {

  const Events = require('plug/core/Events');
  const ApplicationView = require('plug/views/app/ApplicationView');

  const settings = require('./store/settings');
  const RoomSettings = require('./models/RoomSettings');
  const PluginMeta = require('./models/PluginMeta');
  const PluginsCollection = require('./collections/PluginsCollection');
  const Plugin = require('./Plugin');
  const loadPlugin = require('./load-plugin');

  const VersionPlugin = require('./plugins/version');
  const SettingsTabPlugin = require('./plugins/settings-tab');
  const ChatTypePlugin = require('./plugins/custom-chat-type');
  const ChatClassesPlugin = require('./plugins/chat-classes');

  const _package = require('./package');

  const $ = require('jquery');
  const _ = require('underscore');
  const Backbone = require('backbone');
  const meld = require('meld');

  let hooks = [
    require('./hooks/api-early'),
    require('./hooks/chat'),
    require('./hooks/playback'),
    require('./hooks/settings'),
    require('./hooks/popout-style')
  ];

  // LocalStorage key name for extplug
  const LS_NAME = 'extPlugins';

  // Try to parse as JSON, defaulting to an empty object.
  function jsonParse(str) {
    try { return JSON.parse(str) || {}; }
    catch (e) {}
    return {};
  }

  // compare semver version numbers
  function semvercmp(a, b) {
    a = a.split('.').map(n => parseInt(n, 10));
    b = b.split('.').map(n => parseInt(n, 10));
    for (let i = 0; i < 3; i++) {
      if (a[i] > b[i]) return 1;
      if (a[i] < b[i]) return -1;
    }
    return 0;
  }

  /**
   * Gets a reference to the main Plug.DJ ApplicationView instance.
   *
   * The ApplicationView is not stored anywhere public, it just
   * exists as a variable in a require() closure, where we cannot
   * directly retrieve it.
   * However, it adds some events to the global Events channel on render,
   * one of them being "show:room", so that's where we can find a reference.
   *
   * @return {ApplicationView} The ApplicationView instance of this page.
   */
  function getApplicationView() {
    let evts = Events._events['show:room'];
    // Backbone event handlers have a .ctx property, containing what they will be bound to.
    // And ApplicationView adds a handler that's bound to itself!
    let appView;
    if (evts) {
      appView = _.find(evts, event => event.ctx instanceof ApplicationView);
    }
    return appView && appView.ctx;
  }

  /**
   * Main ExtPlug extension class.
   *
   * This will be instantiated by ExtPlug later, and can then be accessed
   * on `window.ext`.
   *
   * @constructor
   */
  const ExtPlug = Plugin.extend({
    name: 'ExtPlug',
    settings: {
      corsProxy: { type: 'boolean', default: true, label: 'Use CORS proxy' }
    },
    init() {
      this._super('extplug', this);

      this._core = [
        new VersionPlugin('version', this),
        new SettingsTabPlugin('settings-tab', this),
        new ChatTypePlugin('custom-chat-type', this),
        new ChatClassesPlugin('chat-classes', this)
      ];

    },

    /**
     * Register an ExtPlug plugin by require.js module name.
     * This can be anything that is accepted by require.js, including
     * modules using require.js plugins or modules on remote URLs.
     */
    registerPlugin(id, cb) {
      require(
        [ `extplug/load-plugin!${id}` ],
        plugin => {
          let meta = new PluginMeta({
            id: plugin.id,
            name: plugin.name,
            instance: plugin
          });
          this._plugins.add(meta);
          let settings = this._getPluginSettings(plugin.id);
          plugin.settings.set(settings.settings);
          plugin.settings.on('change', () => {
            this._savePluginSettings(plugin.id);
          });
          if (settings.enabled) {
            _.defer(() => {
              meta.enable();
            });
          }
          if (cb) cb(null);
        },
        (err) => {
          if (cb) cb(err);
        }
      );
      return this;
    },

    /**
     * Disables and removes an ExtPlug plugin.
     */
    unregisterPlugin(id) {
      let plugin = this._plugins.findWhere({ id: id });
      if (plugin) {
        plugin.disable();
        this._plugins.remove(plugin);
      }
    },

    getPlugin(id) {
      let meta = this._plugins.get(id);
      return meta ? meta.get('instance') : null;
    },

    /**
     * Installs a plugin. This is basically registerPlugin(), but it also
     * remembers the plugin name so it can be loaded again automatically
     * on following ExtPlug runs.
     */
    install(id, cb) {
      this.registerPlugin(id, (e) => {
        if (e) return cb(e);
        let json = jsonParse(localStorage.getItem(LS_NAME));
        json.installed = (json.installed || []).concat([ id ]);
        localStorage.setItem(LS_NAME, JSON.stringify(json));
        cb(null);
      });
    },

    /**
     * Disables and removes a plugin forever.
     */
    uninstall(id) {
      this.unregisterPlugin(id);
      let json = jsonParse(localStorage.getItem(LS_NAME));
      if (json.installed) {
        let i = json.installed.indexOf(id);
        if (i !== -1) {
          json.installed.splice(i, 1);
          localStorage.setItem(LS_NAME, JSON.stringify(json));
        }
      }
    },

    /**
     * Loads installed plugins.
     */
    _loadInstalled() {
      let { installed } = jsonParse(localStorage.getItem(LS_NAME));
      if (_.isArray(installed)) {
        let l = installed.length;
        let i = 0;
        let errors = [];
        const done = () => {
          if (errors.length) {
            errors.forEach(e => {
              Events.trigger('notify', 'icon-chat-system',
                             `Plugin error: ${e.message}`);
            });
          }
          else if (i > 0) {
            Events.trigger('notify', 'icon-plug-dj',
                           `ExtPlug: loaded ${i} plugins.`);
          }
        };
        installed.forEach(name => {
          this.registerPlugin(name, e => {
            if (e) errors.push(e);
            if (++i >= l) {
              done();
            }
          });
        });
      }
    },

    /**
     * Checks if ExtPlug has been initialised before.
     */
    isFirstRun() {
      return localStorage.getItem(LS_NAME) == null;
    },
    /**
     * Things that should only happen the first time ExtPlug
     * is initialised.
     */
    onFirstRun() {
      localStorage.setItem(LS_NAME, JSON.stringify({
        version: _package.version,
        installed: [
          'autowoot/build/autowoot.js;extplug/autowoot/main',
          'chat-notifications/build/chat-notifications.js;' +
            'extplug/chat-notifications/main',
          'compact-history/build/compact-history.js;' +
            'extplug/compact-history/main',
          'hide-badges/build/hide-badges.js;extplug/hide-badges/main',
          'meh-icons/build/meh-icons.js;extplug/meh-icons/main',
          'room-styles/build/room-styles.js;extplug/room-styles/main'
        ].map(path => `https://extplug.github.io/${path}`),
        plugins: {}
      }));
    },

    /**
     * Initializes ExtPlug.
     *
     * This attaches events and finds some common DOM elements. Also, adds
     * the ExtPlug tab to the user settings area.
     *
     * @return {ExtPlug} `this`.
     */
    enable() {
      this._super();

      /**
       * Internal map of registered plugins.
       */
      this._plugins = new PluginsCollection();
      this._plugins.on('change:enabled', (plugin, enabled) => {
        this._savePluginSettings(plugin.get('id'));
      });

      if (this.isFirstRun()) this.onFirstRun();

      this.upgrade();

      settings.update();
      this.appView = getApplicationView();

      // ExtPlug styles
      this.Style()
        .set(require('./styles/badge'))
        .set(require('./styles/inline-chat'))
        .set(require('./styles/settings-pane'))
        .set(require('./styles/install-plugin-dialog'));

      // install extra events
      hooks.forEach(hook => {
        hook.install();
      });

      this._core.forEach(plugin => {
        plugin.enable();
      });

      // room settings
      this.roomSettings = new RoomSettings(this);

      this._loadInstalled();
      Events.trigger('notify', 'icon-plug-dj', `ExtPlug v${_package.version} loaded`);

      return this;
    },

    /**
     * Deinitializes and cleans up ExtPlug.
     *
     * Everything should be unloaded here, so the Plug.DJ page looks like nothing ever happened.
     */
    disable() {
      this._plugins.off().forEach(mod => {
        mod.disable();
      });
      this._core.forEach(plugin => {
        plugin.disable();
      });
      hooks.forEach(hook => {
        hook.uninstall();
      });

      // remove room settings handling
      this.roomSettings.dispose();
      this.trigger('deinit');
      this._super();
    },

    /**
     * Persists plugin settings to localStorage.
     * @private
     */
    _savePluginSettings(id) {
      let json = jsonParse(localStorage.getItem(LS_NAME));
      let plugin = this._plugins.findWhere({ id: id });
      let settings = plugin.get('instance').settings;
      if (!json.plugins) json.plugins = {};
      json.plugins[id] = { enabled: plugin.get('enabled'), settings: settings };
      localStorage.setItem(LS_NAME, JSON.stringify(json));
    },

    /**
     * Retrieves plugin settings from localStorage.
     */
    _getPluginSettings(id) {
      let settings = jsonParse(localStorage.getItem(LS_NAME)).plugins;
      if (settings && id in settings) {
        return settings[id];
      }
      return { enabled: false, settings: {} };
    },

    /**
     * Upgrades old ExtPlug version settings.
     */
    upgrade() {
      let stored = jsonParse(localStorage.getItem(LS_NAME));

      // "hide-badges" was added in 0.10.0
      if (semvercmp(stored.version, '0.10.0') < 0) {
        stored.version = '0.10.0';
        const plugin = 'extplug/plugins/hide-badges/main';
        if (stored.installed.indexOf(plugin) === -1) {
          stored.installed.push(plugin);
        }
      }

      // "rollover-blurbs" was removed from core in 0.12.0
      if (semvercmp(stored.version, '0.12.0') < 0) {
        stored.version = '0.12.0';
        replace(
          'extplug/plugins/rollover-blurbs/main',
          'https://extplug.github.io/rollover-blurb/build/rollover-blurb.js',
          'extplug/rollover-blurb/main'
        );
      }

      if (semvercmp(stored.version, '0.13.0') < 0) {
        stored.version = '0.13.0';
        replace(
          'extplug/plugins/autowoot/main',
          'https://extplug.github.io/autowoot/build/autowoot.js',
          'extplug/autowoot/main'
        );
        replace(
          'extplug/plugins/chat-notifications/main',
          'https://extplug.github.io/chat-notifications/build/chat-notifications.js',
          'extplug/chat-notifications/main'
        );
        replace(
          'extplug/plugins/compact-history/main',
          'https://extplug.github.io/compact-history/build/compact-history.js',
          'extplug/compact-history/main'
        );
        replace(
          'extplug/plugins/hide-badges/main',
          'https://extplug.github.io/hide-badges/build/hide-badges.js',
          'extplug/hide-badges/main'
        );
        replace(
          'extplug/plugins/meh-icon/main',
          'https://extplug.github.io/meh-icons/build/meh-icons.js;' +
          'extplug/meh-icons/main'
        );
        replace(
          'extplug/plugins/room-styles/main',
          'https://extplug.github.io/room-styles/build/room-styles.js',
          'extplug/room-styles/main'
        );

        // full-size video was removed in favour of plug's Video Only mode
        let fullSizeVideo = 'extplug/plugins/full-size-video/main';
        stored.installed = _.without(stored.installed, fullSizeVideo);
        delete stored.plugins[fullSizeVideo];
      }

      localStorage.setItem(LS_NAME, JSON.stringify(stored));

      function replace(oldPlugin, url, name) {
        let i = stored.installed.indexOf(oldPlugin);
        if (i !== -1) {
          stored.installed.splice(i, 1, `${url};${name}`);
          // move settings
          stored.plugins[name] = stored.plugins[oldPlugin];
          delete stored.plugins[oldPlugin];
        }
      }
    }
  });

  module.exports = ExtPlug;

});
<commit_msg>add show-deleted to default plugins<commit_after>define(function (require, exports, module) {

  const Events = require('plug/core/Events');
  const ApplicationView = require('plug/views/app/ApplicationView');

  const settings = require('./store/settings');
  const RoomSettings = require('./models/RoomSettings');
  const PluginMeta = require('./models/PluginMeta');
  const PluginsCollection = require('./collections/PluginsCollection');
  const Plugin = require('./Plugin');
  const loadPlugin = require('./load-plugin');

  const VersionPlugin = require('./plugins/version');
  const SettingsTabPlugin = require('./plugins/settings-tab');
  const ChatTypePlugin = require('./plugins/custom-chat-type');
  const ChatClassesPlugin = require('./plugins/chat-classes');

  const _package = require('./package');

  const $ = require('jquery');
  const _ = require('underscore');
  const Backbone = require('backbone');
  const meld = require('meld');

  let hooks = [
    require('./hooks/api-early'),
    require('./hooks/chat'),
    require('./hooks/playback'),
    require('./hooks/settings'),
    require('./hooks/popout-style')
  ];

  // LocalStorage key name for extplug
  const LS_NAME = 'extPlugins';

  // Try to parse as JSON, defaulting to an empty object.
  function jsonParse(str) {
    try { return JSON.parse(str) || {}; }
    catch (e) {}
    return {};
  }

  // compare semver version numbers
  function semvercmp(a, b) {
    a = a.split('.').map(n => parseInt(n, 10));
    b = b.split('.').map(n => parseInt(n, 10));
    for (let i = 0; i < 3; i++) {
      if (a[i] > b[i]) return 1;
      if (a[i] < b[i]) return -1;
    }
    return 0;
  }

  /**
   * Gets a reference to the main Plug.DJ ApplicationView instance.
   *
   * The ApplicationView is not stored anywhere public, it just
   * exists as a variable in a require() closure, where we cannot
   * directly retrieve it.
   * However, it adds some events to the global Events channel on render,
   * one of them being "show:room", so that's where we can find a reference.
   *
   * @return {ApplicationView} The ApplicationView instance of this page.
   */
  function getApplicationView() {
    let evts = Events._events['show:room'];
    // Backbone event handlers have a .ctx property, containing what they will be bound to.
    // And ApplicationView adds a handler that's bound to itself!
    let appView;
    if (evts) {
      appView = _.find(evts, event => event.ctx instanceof ApplicationView);
    }
    return appView && appView.ctx;
  }

  /**
   * Main ExtPlug extension class.
   *
   * This will be instantiated by ExtPlug later, and can then be accessed
   * on `window.ext`.
   *
   * @constructor
   */
  const ExtPlug = Plugin.extend({
    name: 'ExtPlug',
    settings: {
      corsProxy: { type: 'boolean', default: true, label: 'Use CORS proxy' }
    },
    init() {
      this._super('extplug', this);

      this._core = [
        new VersionPlugin('version', this),
        new SettingsTabPlugin('settings-tab', this),
        new ChatTypePlugin('custom-chat-type', this),
        new ChatClassesPlugin('chat-classes', this)
      ];

    },

    /**
     * Register an ExtPlug plugin by require.js module name.
     * This can be anything that is accepted by require.js, including
     * modules using require.js plugins or modules on remote URLs.
     */
    registerPlugin(id, cb) {
      require(
        [ `extplug/load-plugin!${id}` ],
        plugin => {
          let meta = new PluginMeta({
            id: plugin.id,
            name: plugin.name,
            instance: plugin
          });
          this._plugins.add(meta);
          let settings = this._getPluginSettings(plugin.id);
          plugin.settings.set(settings.settings);
          plugin.settings.on('change', () => {
            this._savePluginSettings(plugin.id);
          });
          if (settings.enabled) {
            _.defer(() => {
              meta.enable();
            });
          }
          if (cb) cb(null);
        },
        (err) => {
          if (cb) cb(err);
        }
      );
      return this;
    },

    /**
     * Disables and removes an ExtPlug plugin.
     */
    unregisterPlugin(id) {
      let plugin = this._plugins.findWhere({ id: id });
      if (plugin) {
        plugin.disable();
        this._plugins.remove(plugin);
      }
    },

    getPlugin(id) {
      let meta = this._plugins.get(id);
      return meta ? meta.get('instance') : null;
    },

    /**
     * Installs a plugin. This is basically registerPlugin(), but it also
     * remembers the plugin name so it can be loaded again automatically
     * on following ExtPlug runs.
     */
    install(id, cb) {
      this.registerPlugin(id, (e) => {
        if (e) return cb(e);
        let json = jsonParse(localStorage.getItem(LS_NAME));
        json.installed = (json.installed || []).concat([ id ]);
        localStorage.setItem(LS_NAME, JSON.stringify(json));
        cb(null);
      });
    },

    /**
     * Disables and removes a plugin forever.
     */
    uninstall(id) {
      this.unregisterPlugin(id);
      let json = jsonParse(localStorage.getItem(LS_NAME));
      if (json.installed) {
        let i = json.installed.indexOf(id);
        if (i !== -1) {
          json.installed.splice(i, 1);
          localStorage.setItem(LS_NAME, JSON.stringify(json));
        }
      }
    },

    /**
     * Loads installed plugins.
     */
    _loadInstalled() {
      let { installed } = jsonParse(localStorage.getItem(LS_NAME));
      if (_.isArray(installed)) {
        let l = installed.length;
        let i = 0;
        let errors = [];
        const done = () => {
          if (errors.length) {
            errors.forEach(e => {
              Events.trigger('notify', 'icon-chat-system',
                             `Plugin error: ${e.message}`);
            });
          }
          else if (i > 0) {
            Events.trigger('notify', 'icon-plug-dj',
                           `ExtPlug: loaded ${i} plugins.`);
          }
        };
        installed.forEach(name => {
          this.registerPlugin(name, e => {
            if (e) errors.push(e);
            if (++i >= l) {
              done();
            }
          });
        });
      }
    },

    /**
     * Checks if ExtPlug has been initialised before.
     */
    isFirstRun() {
      return localStorage.getItem(LS_NAME) == null;
    },
    /**
     * Things that should only happen the first time ExtPlug
     * is initialised.
     */
    onFirstRun() {
      localStorage.setItem(LS_NAME, JSON.stringify({
        version: _package.version,
        installed: [
          'autowoot/build/autowoot.js;extplug/autowoot/main',
          'chat-notifications/build/chat-notifications.js;' +
            'extplug/chat-notifications/main',
          'compact-history/build/compact-history.js;' +
            'extplug/compact-history/main',
          'hide-badges/build/hide-badges.js;extplug/hide-badges/main',
          'meh-icons/build/meh-icons.js;extplug/meh-icons/main',
          'room-styles/build/room-styles.js;extplug/room-styles/main',
          'show-deleted/build/show-deleted.js;extplug/show-deleted/main'
        ].map(path => `https://extplug.github.io/${path}`),
        plugins: {}
      }));
    },

    /**
     * Initializes ExtPlug.
     *
     * This attaches events and finds some common DOM elements. Also, adds
     * the ExtPlug tab to the user settings area.
     *
     * @return {ExtPlug} `this`.
     */
    enable() {
      this._super();

      /**
       * Internal map of registered plugins.
       */
      this._plugins = new PluginsCollection();
      this._plugins.on('change:enabled', (plugin, enabled) => {
        this._savePluginSettings(plugin.get('id'));
      });

      if (this.isFirstRun()) this.onFirstRun();

      this.upgrade();

      settings.update();
      this.appView = getApplicationView();

      // ExtPlug styles
      this.Style()
        .set(require('./styles/badge'))
        .set(require('./styles/inline-chat'))
        .set(require('./styles/settings-pane'))
        .set(require('./styles/install-plugin-dialog'));

      // install extra events
      hooks.forEach(hook => {
        hook.install();
      });

      this._core.forEach(plugin => {
        plugin.enable();
      });

      // room settings
      this.roomSettings = new RoomSettings(this);

      this._loadInstalled();
      Events.trigger('notify', 'icon-plug-dj', `ExtPlug v${_package.version} loaded`);

      return this;
    },

    /**
     * Deinitializes and cleans up ExtPlug.
     *
     * Everything should be unloaded here, so the Plug.DJ page looks like nothing ever happened.
     */
    disable() {
      this._plugins.off().forEach(mod => {
        mod.disable();
      });
      this._core.forEach(plugin => {
        plugin.disable();
      });
      hooks.forEach(hook => {
        hook.uninstall();
      });

      // remove room settings handling
      this.roomSettings.dispose();
      this.trigger('deinit');
      this._super();
    },

    /**
     * Persists plugin settings to localStorage.
     * @private
     */
    _savePluginSettings(id) {
      let json = jsonParse(localStorage.getItem(LS_NAME));
      let plugin = this._plugins.findWhere({ id: id });
      let settings = plugin.get('instance').settings;
      if (!json.plugins) json.plugins = {};
      json.plugins[id] = { enabled: plugin.get('enabled'), settings: settings };
      localStorage.setItem(LS_NAME, JSON.stringify(json));
    },

    /**
     * Retrieves plugin settings from localStorage.
     */
    _getPluginSettings(id) {
      let settings = jsonParse(localStorage.getItem(LS_NAME)).plugins;
      if (settings && id in settings) {
        return settings[id];
      }
      return { enabled: false, settings: {} };
    },

    /**
     * Upgrades old ExtPlug version settings.
     */
    upgrade() {
      let stored = jsonParse(localStorage.getItem(LS_NAME));

      // "hide-badges" was added in 0.10.0
      if (semvercmp(stored.version, '0.10.0') < 0) {
        stored.version = '0.10.0';
        const plugin = 'extplug/plugins/hide-badges/main';
        if (stored.installed.indexOf(plugin) === -1) {
          stored.installed.push(plugin);
        }
      }

      // "rollover-blurbs" was removed from core in 0.12.0
      if (semvercmp(stored.version, '0.12.0') < 0) {
        stored.version = '0.12.0';
        replace(
          'extplug/plugins/rollover-blurbs/main',
          'https://extplug.github.io/rollover-blurb/build/rollover-blurb.js',
          'extplug/rollover-blurb/main'
        );
      }

      if (semvercmp(stored.version, '0.13.0') < 0) {
        stored.version = '0.13.0';
        replace(
          'extplug/plugins/autowoot/main',
          'https://extplug.github.io/autowoot/build/autowoot.js',
          'extplug/autowoot/main'
        );
        replace(
          'extplug/plugins/chat-notifications/main',
          'https://extplug.github.io/chat-notifications/build/chat-notifications.js',
          'extplug/chat-notifications/main'
        );
        replace(
          'extplug/plugins/compact-history/main',
          'https://extplug.github.io/compact-history/build/compact-history.js',
          'extplug/compact-history/main'
        );
        replace(
          'extplug/plugins/hide-badges/main',
          'https://extplug.github.io/hide-badges/build/hide-badges.js',
          'extplug/hide-badges/main'
        );
        replace(
          'extplug/plugins/meh-icon/main',
          'https://extplug.github.io/meh-icons/build/meh-icons.js;' +
          'extplug/meh-icons/main'
        );
        replace(
          'extplug/plugins/room-styles/main',
          'https://extplug.github.io/room-styles/build/room-styles.js',
          'extplug/room-styles/main'
        );

        // full-size video was removed in favour of plug's Video Only mode
        let fullSizeVideo = 'extplug/plugins/full-size-video/main';
        stored.installed = _.without(stored.installed, fullSizeVideo);
        delete stored.plugins[fullSizeVideo];
      }
      if (semvercmp(stored.version, '0.13.1') < 0) {
        stored.version = '0.13.1'
        // show-deleted was added to core in 0.13
        let showDeleted =
            'https://extplug.github.io/show-deleted/build/show-deleted.js;' +
            'extplug/show-deleted/main';
        if (stored.installed.indexOf(showDeleted) === -1) {
          stored.installed.push(showDeleted);
        }
      }

      localStorage.setItem(LS_NAME, JSON.stringify(stored));

      function replace(oldPlugin, url, name) {
        let i = stored.installed.indexOf(oldPlugin);
        if (i !== -1) {
          stored.installed.splice(i, 1, `${url};${name}`);
          // move settings
          stored.plugins[name] = stored.plugins[oldPlugin];
          delete stored.plugins[oldPlugin];
        }
      }
    }
  });

  module.exports = ExtPlug;

});
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var url = require("url");
var async = require("async");
var request = require("request");

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by Brian White to use Array.isArray instead of the custom isArray method
 */
exports.extend = function() {
  // copy reference to target object
  var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && typeof target !== 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (typeof copy !== "undefined")
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};

// Found on http://bonsaiden.github.com/JavaScript-Garden/#types.typeof
exports.is = function(type, obj) {
    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    return obj !== undefined && obj !== null && clas === type;
};

exports.addAll = function(thisArray, anotherArray) {
    if(!(thisArray && anotherArray && anotherArray.length))
        return;
    for(var i = 0; i < anotherArray.length; i++)
        thisArray.push(anotherArray[i]);
};

exports.getPropertyInObject = function(jsonObject, propertyName, callback) {

    var foundValues = [];

    function recurseObject(jsonObject, propertyName) {
        if (exports.is("Object", jsonObject)) {
            for (var m in jsonObject) {
                if (jsonObject.hasOwnProperty(m)) {
                    if (m === propertyName) {
                        foundValues.push(jsonObject[m]);
                    }
                    else if (exports.is("Object", jsonObject[m])) {
                        recurseObject(jsonObject[m], propertyName);
                    }
                    else if (exports.is("Array", jsonObject[m])) {
                        for (var n=0; n<jsonObject[m].length; n++) {
                            recurseObject(jsonObject[m][n], propertyName);
                        }
                    }
                }
            }
        }
    }
    recurseObject(jsonObject, propertyName);
    callback(foundValues);
};

// quick/dirty sanitization ripped from the Jade template engine
exports.sanitize = function(term){
    return String(term)
        .replace(/&(?!\w+;)/g, '&')
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/"/g, '"');
};

exports.trim = function(stringToTrim) {
  return stringToTrim.replace(/^\s+|\s+$/g,"");
};
exports.ltrim = function(stringToTrim) {
  return stringToTrim.replace(/^\s+/,"");
};
exports.rtrim = function(stringToTrim) {
  return stringToTrim.replace(/\s+$/,"");
};

exports.atomicWriteFileSync = function(dest, data) {
    var tmp = dest + '.tmp';
    var bkp = dest + '.bkp';
    var stat = null;

    try {
        stat = fs.statSync(dest);
    } catch (err) {
    }

    // make a backup iff the destination file already exists
    if (stat)
        fs.writeFileSync(bkp, fs.readFileSync(dest));

    // write out the new contents to a temp file
    fs.writeFileSync(tmp, data);

    // check if it worked
    if(data.length && fs.statSync(tmp).size !== Buffer.byteLength(data, 'utf8')) throw new Error('atomic write error! file size !== data.length');

    // atomically rename the temp file into place
    fs.renameSync(tmp, dest);
}

// processes a json newline stream, cbEach(json, callback) and cbDone(err) when done
exports.streamFromUrl = function(url, cbEach, cbDone) {
    var ended = false;
    var q = async.queue(function(chunk, cb){
        if(chunk == "") return cb();
        try{ var js = JSON.parse(chunk); }catch(E){ return cb(); }
        cbEach(js, cb);
    },1);
    var error;
    var req = request.get({uri:url}, function(err){
        if(err) error = err;
        ended = true;
        q.push(""); // this triggers the drain if there was no data, GOTCHA
    });
    var buff = "";
    req.on("data",function(data){
        buff += data.toString();
        var chunks = buff.split('\n');
        buff = chunks.pop(); // if was end \n, == '', if mid-stream it'll be a not-yet-complete chunk of json
        chunks.forEach(q.push);
    });
    q.drain = function(){
        if(!ended) return; // drain can be called many times, we only care when it's after data is done coming in
        cbDone(error);
    };
    req.on("end",function(){
        ended = true;
        q.push(""); // this triggers the drain if there was no data, GOTCHA
    });
}
// creates an idr, type://network/context?id=account#id
// context and account are optional
exports.idrNew = function(type, network, id, context, account)
{
    var r = {slashes:true};
    r.host = network;
    r.pathname = (context) ? context : '/';
    if(account) r.query = {id: account.toString()};
    r.protocol = type;
    if(id) r.hash = id.toString();
    return url.format(r);
}
/*
IDR

A simple way to store a rich extensible id structure as a parseable/serializeable string key, examples:

require('url').parse('tweet://twitter/mention?id=jeremie#103976138702983168',true)
{ protocol: 'tweet:',
  slashes: true,
  host: 'twitter',
  hostname: 'twitter',
  href: 'tweet://twitter/mention?id=jeremie#103976138702983168',
  hash: '#103976138702983168',
  search: '?id=jeremie',
  query: { id: 'jeremie' },
  pathname: '/mention' }

require('url').parse('post://facebook/wall?id=630347951#630347951_10150351352017952',true)
{ protocol: 'post:',
  slashes: true,
  host: 'facebook',
  hostname: 'facebook',
  href: 'post://facebook/wall?id=630347951#630347951_10150351352017952',
  hash: '#630347951_10150351352017952',
  search: '?id=630347951',
  query: { id: '630347951' },
  pathname: '/wall' }

IDR - TL;DR

There are an innumerable amount of things that need to be "addressed" within the locker, and not just externally, but strong internal references to the actual local storage identifiers as well.  These addressible entities are not simple GUIDs either, they have a critical set of metadata that makes up their identity, most importantly the originating network, the type of entity it is on that network, and the unique identifier assigned to it by that network.  Often equally important is the context in which it was discovered from that network, the common example being a tweet, from twitter, encountered as a mention.  Other locally important attributes sometimes need to be tracked as well, such as the account id that the entity originated from.

All of these attributes are required to uniquely resolve a reference to an entity to the actual data, either locally (requiring the context and account bits) or globally (just the type, network, and id bits).  While programmatically each of these is independently important, as identifiers they need to be stored in a consistent way as a unique string for simple KV lookups/matching.  There is a standard and built-in library perfect for this job, URLs! They're also very familiar to read and the tools handle all the encoding, parsing, etc.

*/
<commit_msg>Temas says I should use undefined instead of null<commit_after>var fs = require("fs");
var path = require("path");
var url = require("url");
var async = require("async");
var request = require("request");

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by Brian White to use Array.isArray instead of the custom isArray method
 */
exports.extend = function() {
  // copy reference to target object
  var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

  // Handle a deep copy situation
  if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if (typeof target !== "object" && typeof target !== 'function')
    target = {};

  var isPlainObject = function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval)
      return false;

    var has_own_constructor = hasOwnProperty.call(obj, "constructor");
    var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf");
    // Not own constructor property must be Object
    if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
      return false;

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var last_key;
    for (var key in obj)
      last_key = key;

    return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key);
  };

  for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) !== null) {
      // Extend the base object
      for (name in options) {
        src = target[name];
        copy = options[name];

        // Prevent never-ending loop
        if (target === copy)
            continue;

        // Recurse if we're merging object literal values or arrays
        if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
          var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

          // Never move original objects, clone them
          target[name] = exports.extend(deep, clone, copy);

        // Don't bring in undefined values
        } else if (typeof copy !== "undefined")
          target[name] = copy;
      }
    }
  }

  // Return the modified object
  return target;
};

// Found on http://bonsaiden.github.com/JavaScript-Garden/#types.typeof
exports.is = function(type, obj) {
    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    return obj !== undefined && obj !== null && clas === type;
};

exports.addAll = function(thisArray, anotherArray) {
    if(!(thisArray && anotherArray && anotherArray.length))
        return;
    for(var i = 0; i < anotherArray.length; i++)
        thisArray.push(anotherArray[i]);
};

exports.getPropertyInObject = function(jsonObject, propertyName, callback) {

    var foundValues = [];

    function recurseObject(jsonObject, propertyName) {
        if (exports.is("Object", jsonObject)) {
            for (var m in jsonObject) {
                if (jsonObject.hasOwnProperty(m)) {
                    if (m === propertyName) {
                        foundValues.push(jsonObject[m]);
                    }
                    else if (exports.is("Object", jsonObject[m])) {
                        recurseObject(jsonObject[m], propertyName);
                    }
                    else if (exports.is("Array", jsonObject[m])) {
                        for (var n=0; n<jsonObject[m].length; n++) {
                            recurseObject(jsonObject[m][n], propertyName);
                        }
                    }
                }
            }
        }
    }
    recurseObject(jsonObject, propertyName);
    callback(foundValues);
};

// quick/dirty sanitization ripped from the Jade template engine
exports.sanitize = function(term){
    return String(term)
        .replace(/&(?!\w+;)/g, '&')
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/"/g, '"');
};

exports.trim = function(stringToTrim) {
  return stringToTrim.replace(/^\s+|\s+$/g,"");
};
exports.ltrim = function(stringToTrim) {
  return stringToTrim.replace(/^\s+/,"");
};
exports.rtrim = function(stringToTrim) {
  return stringToTrim.replace(/\s+$/,"");
};

exports.atomicWriteFileSync = function(dest, data) {
    var tmp = dest + '.tmp';
    var bkp = dest + '.bkp';
    var stat = undefined;

    try {
        stat = fs.statSync(dest);
    } catch (err) {
    }

    // make a backup iff the destination file already exists
    if (stat)
        fs.writeFileSync(bkp, fs.readFileSync(dest));

    // write out the new contents to a temp file
    fs.writeFileSync(tmp, data);

    // check if it worked
    if(data.length && fs.statSync(tmp).size !== Buffer.byteLength(data, 'utf8')) throw new Error('atomic write error! file size !== data.length');

    // atomically rename the temp file into place
    fs.renameSync(tmp, dest);
}

// processes a json newline stream, cbEach(json, callback) and cbDone(err) when done
exports.streamFromUrl = function(url, cbEach, cbDone) {
    var ended = false;
    var q = async.queue(function(chunk, cb){
        if(chunk == "") return cb();
        try{ var js = JSON.parse(chunk); }catch(E){ return cb(); }
        cbEach(js, cb);
    },1);
    var error;
    var req = request.get({uri:url}, function(err){
        if(err) error = err;
        ended = true;
        q.push(""); // this triggers the drain if there was no data, GOTCHA
    });
    var buff = "";
    req.on("data",function(data){
        buff += data.toString();
        var chunks = buff.split('\n');
        buff = chunks.pop(); // if was end \n, == '', if mid-stream it'll be a not-yet-complete chunk of json
        chunks.forEach(q.push);
    });
    q.drain = function(){
        if(!ended) return; // drain can be called many times, we only care when it's after data is done coming in
        cbDone(error);
    };
    req.on("end",function(){
        ended = true;
        q.push(""); // this triggers the drain if there was no data, GOTCHA
    });
}
// creates an idr, type://network/context?id=account#id
// context and account are optional
exports.idrNew = function(type, network, id, context, account)
{
    var r = {slashes:true};
    r.host = network;
    r.pathname = (context) ? context : '/';
    if(account) r.query = {id: account.toString()};
    r.protocol = type;
    if(id) r.hash = id.toString();
    return url.format(r);
}
/*
IDR

A simple way to store a rich extensible id structure as a parseable/serializeable string key, examples:

require('url').parse('tweet://twitter/mention?id=jeremie#103976138702983168',true)
{ protocol: 'tweet:',
  slashes: true,
  host: 'twitter',
  hostname: 'twitter',
  href: 'tweet://twitter/mention?id=jeremie#103976138702983168',
  hash: '#103976138702983168',
  search: '?id=jeremie',
  query: { id: 'jeremie' },
  pathname: '/mention' }

require('url').parse('post://facebook/wall?id=630347951#630347951_10150351352017952',true)
{ protocol: 'post:',
  slashes: true,
  host: 'facebook',
  hostname: 'facebook',
  href: 'post://facebook/wall?id=630347951#630347951_10150351352017952',
  hash: '#630347951_10150351352017952',
  search: '?id=630347951',
  query: { id: '630347951' },
  pathname: '/wall' }

IDR - TL;DR

There are an innumerable amount of things that need to be "addressed" within the locker, and not just externally, but strong internal references to the actual local storage identifiers as well.  These addressible entities are not simple GUIDs either, they have a critical set of metadata that makes up their identity, most importantly the originating network, the type of entity it is on that network, and the unique identifier assigned to it by that network.  Often equally important is the context in which it was discovered from that network, the common example being a tweet, from twitter, encountered as a mention.  Other locally important attributes sometimes need to be tracked as well, such as the account id that the entity originated from.

All of these attributes are required to uniquely resolve a reference to an entity to the actual data, either locally (requiring the context and account bits) or globally (just the type, network, and id bits).  While programmatically each of these is independently important, as identifiers they need to be stored in a consistent way as a unique string for simple KV lookups/matching.  There is a standard and built-in library perfect for this job, URLs! They're also very familiar to read and the tools handle all the encoding, parsing, etc.

*/
<|endoftext|>


<commit_before>(function(root) {

  'use strict';

  function bindToClick(selector, handler) {
    var elems = document.querySelectorAll(selector);
    var i = -1;
    var len = elems.length;
    while (++i < len) {
      elems[i].addEventListener('click', handler);
    }
  }

  function TinyModal(modal, opts) {

    // Allow `TinyModal` to be called without the `new` keyword.
    var self = this;
    if (!(self instanceof TinyModal)) {
      return new TinyModal(modal, opts);
    }

    // Parse `opts`.
    opts = opts || {};
    self.scrollTop = opts.scrollTop != null ? opts.scrollTop : true;
    self.onShown = opts.onShown || function() {};
    self.onHidden = opts.onHidden || function() {};
    self.isShown = false;
    self.modal = modal;

    // Set default styles on the `modal`.
    modal.style.cssText += 'display:none;position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;will-change:transform;';

    // Bind the `close` and `open` methods to the click event of elements that
    // match the `opts.closeButtonClass` and `opts.openButtonClass` selectors.
    bindToClick(opts.closeButtonClass || '.modal-hide', function() {
      self.hide();
    });
    bindToClick(opts.openButtonClass || '.modal-show', function() {
      self.show();
    });

    // Bind the `close` method to the `click` event on the `modal`. Only close
    // the modal if we had clicked directly on the `modal` (ie. the overlay).
    modal.addEventListener('click', function(e) {
      if (e.target === modal) {
        self.hide();
      }
    });

    // Bind the `close` method to the `keydown` event. Only close the modal if
    // we had pressed the `escape` key.
    document.addEventListener('keydown', function(e) {
      if (e.keyCode === 27) {
        self.hide();
      }
    });

  }

  TinyModal.prototype.show = function() {
    var self = this;
    var modal = self.modal;
    if (!self.isShown) {
      // Disable scrolling on the window, and show the `modal`.
      document.body.style.overflow = 'hidden';
      modal.style.display = 'block';
      if (self.scrollTop) {
        // Scroll to the top of the modal.
        modal.scrollTop = 0;
      }
      self.isShown = true;
      self.onShown(modal);
    }
  };

  TinyModal.prototype.hide = function() {
    var self = this;
    if (self.isShown) {
      // Enable scrolling on the window, and hide the `modal`.
      document.body.style.overflow = '';
      self.modal.style.display = 'none';
      self.isShown = false;
      self.onHidden(self.modal);
    }
  };

  /* istanbul ignore else */
  if (typeof module === 'object') {
    module.exports = TinyModal;
  } else {
    root.tinyModal = TinyModal;
  }

})(this);
<commit_msg>Rename keys<commit_after>(function(root) {

  'use strict';

  function bindToClick(selector, handler) {
    var elems = document.querySelectorAll(selector);
    var i = -1;
    var len = elems.length;
    while (++i < len) {
      elems[i].addEventListener('click', handler);
    }
  }

  function TinyModal(modal, opts) {

    // Allow `TinyModal` to be called without the `new` keyword.
    var self = this;
    if (!(self instanceof TinyModal)) {
      return new TinyModal(modal, opts);
    }

    // Parse `opts`.
    opts = opts || {};
    self.scrollTop = opts.scrollTop != null ? opts.scrollTop : true;
    self.onShown = opts.onShown || function() {};
    self.onHidden = opts.onHidden || function() {};
    self.isShown = false;
    self.modal = modal;

    // Set default styles on the `modal`.
    modal.style.cssText += 'display:none;position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;will-change:transform;';

    // Bind the `close` and `open` methods to the click event of elements that
    // match `opts.closeButtonSelector` and `opts.openButtonSelector`.
    bindToClick(opts.closeButtonSelector || '.modal-hide', function() {
      self.hide();
    });
    bindToClick(opts.openButtonSelector || '.modal-show', function() {
      self.show();
    });

    // Bind the `close` method to the `click` event on the `modal`. Only close
    // the modal if we had clicked directly on the `modal` (ie. the overlay).
    modal.addEventListener('click', function(e) {
      if (e.target === modal) {
        self.hide();
      }
    });

    // Bind the `close` method to the `keydown` event. Only close the modal if
    // we had pressed the `escape` key.
    document.addEventListener('keydown', function(e) {
      if (e.keyCode === 27) {
        self.hide();
      }
    });

  }

  TinyModal.prototype.show = function() {
    var self = this;
    var modal = self.modal;
    if (!self.isShown) {
      // Disable scrolling on the window, and show the `modal`.
      document.body.style.overflow = 'hidden';
      modal.style.display = 'block';
      if (self.scrollTop) {
        // Scroll to the top of the modal.
        modal.scrollTop = 0;
      }
      self.isShown = true;
      self.onShown(modal);
    }
  };

  TinyModal.prototype.hide = function() {
    var self = this;
    if (self.isShown) {
      // Enable scrolling on the window, and hide the `modal`.
      document.body.style.overflow = '';
      self.modal.style.display = 'none';
      self.isShown = false;
      self.onHidden(self.modal);
    }
  };

  /* istanbul ignore else */
  if (typeof module === 'object') {
    module.exports = TinyModal;
  } else {
    root.tinyModal = TinyModal;
  }

})(this);
<|endoftext|>


<commit_before>import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
import {Link} from 'react-router';

export default class SubmitRecord extends React.Component {
    state = {
        code: '',
    };

    handleChange = (event) => {
        this.setState({code: event.target.value});
    };

    render = () => {
        const flexStyle = {
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            height: '80%',
        };

        return (

                <TextField  hintText="Enter Patient Code"
                            value={this.state.code}
                            onChange={this.handleChange} />
                <RaisedButton label="Find Records" primary={true} />
                <Link to={`/records`}>skip</Link>

        );
    }
}<commit_msg>button now logs patient code from state<commit_after>import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
import {Link} from 'react-router';

export default class SubmitRecord extends React.Component {
    state = {
        code: '',
    };

    lookup = () => {
        console.log(this.state.code);
    };

    handleChange = (event) => {
        this.setState({code: event.target.value});
    };

    render = () => {
        const flexStyle = {
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            height: '80%',
        };

        return (

                <TextField  hintText="Enter Patient Code"
                            value={this.state.code}
                            onChange={this.handleChange} />
                <RaisedButton label="Find Records" primary={true} onClick={this.lookup}/>
                <Link to={`/records`}>skip</Link>

        );
    }
}<|endoftext|>