<commit_before>/*global define,$ */
/*jslint browser:true */
/** @license
 | Copyright 2015 Esri
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |    http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
 */
//============================================================================================================================//
define(['diag'], function (diag) {
    'use strict';
    var dataAccess;
    dataAccess = {

        fixedQueryParams: "&time=&geometry=&geometryType=esriGeometryEnvelope&inSR=&spatialRel=esriSpatialRelIntersects&distance=&units=esriSRUnit_Meter&returnGeometry=true&maxAllowableOffset=&geometryPrecision=&returnExtentOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&resultOffset=&resultRecordCount=&returnZ=false&returnM=false&quantizationParameters=&f=json&outSR=4326",
        featureServiceUrl: null,
        featureServiceLayerId: null,
        objectIdField: null,
        validCandidateCondition: null,
        proxyProgram: null,
        exclusionList: {},

        //--------------------------------------------------------------------------------------------------------------------//

        /**
         * Initializes the module.
         * @param {string} featureServiceUrl URL to feature service providing data
         * @param {number} featureServiceLayerId Id that the feature layer reports as its id
         * @param {string} objectIdField Field that the feature layer reports as its objectIdField
         * @param {string} validCandidateCondition Query 'where' clause to use to get survey candidates, e.g.,
         * "SRVNAME+is+null+or+SRVNAME=''", where SRVNAME is the name of the field used to store the name of the surveyor
         * @param {string} proxyProgram Path to the app's proxy, usually defined in the js/configuration.json file, e.g.,
         * "proxy/proxy.ashx"; use null if browser doesn't need proxy to query and update feature service (at this time,
         * only IE9 needs the proxy)
         */
        init: function (featureServiceUrl, featureServiceLayerId, objectIdField, validCandidateCondition, proxyProgram) {
            dataAccess.featureServiceUrl = featureServiceUrl;
            if (dataAccess.featureServiceUrl.lastIndexOf("/") !== dataAccess.featureServiceUrl.length - 1) {
                dataAccess.featureServiceUrl += "/";
            }
            dataAccess.featureServiceLayerId = featureServiceLayerId;
            dataAccess.objectIdField = objectIdField;
            dataAccess.validCandidateCondition = validCandidateCondition;
            dataAccess.proxyProgram = proxyProgram;
        },

        /**
         * Adds an item to the to-be-skipped list.
         * @param {number} item Object to be skipped; NOP if already in the list
         */
        addItemToExclusionList: function (item) {
            dataAccess.exclusionList[item] = true;
        },

        /**
         * Checks if an item is in the to-be-skipped list.
         * @param {number} item Object to be checked
         * @return {boolean} True if item is in list
         */
        isItemInExclusionList: function (item) {
            return dataAccess.exclusionList.hasOwnProperty(item);
        },

        /**
         * Empties the to-be-skipped list.
         */
        resetExclusionList: function () {
            dataAccess.exclusionList = {};
        },

        /**
         * Returns a list that doesn't include the items in the to-be-skipped list.
         * @param {array} itemList List to be filtered
         * @return {array} Filtered list
         */
        filterList: function (itemList) {
            return $.grep(itemList, function (element) {
                return !dataAccess.isItemInExclusionList(element);
            });
        },

        /**
         * Selects an item from a list of items; selection is subject to an exclusion limitation.
         * @param {array} itemList List of items to choose from
         * @param {boolean} randomizeSelection True if pseudorandom selection should be used
         * @return {object} Selected item or null if list is empty or only contains items
         * that are also in the skip list
         */
        pickFromList: function (itemList, randomizeSelection) {
            var item = null, filteredItems;

            if (itemList.length > 0) {
                filteredItems = dataAccess.filterList(itemList);
                if (filteredItems.length > 0) {
                    // Pick a candidate from amongst the available
                    item = randomizeSelection
                        ? filteredItems[Math.floor(Math.random() * filteredItems.length)]
                        : filteredItems[0];
                }
            }

            return item;
        },

        /**
         * Gets the number of features satisfying a condition.
         * @param {string} [condition] Condition to test; if omitted, the validCandidateCondition provided to this module's
         * init() function is used; a sample condition is "SRVNAME='" + username + "'", where SRVNAME is the name of the
         * field used to store the name of the surveyor, to count how many surveys the user given by 'username' has completed
         * @return {object} Deferred to provide count when it arrives; a count of -1 is used to flag an error
         */
        getObjectCount: function (condition) {
            var deferred, url;
            deferred = $.Deferred();

            url = dataAccess.featureServiceUrl + "query?where=" + (condition || dataAccess.validCandidateCondition)
                    + "&objectIds=&returnIdsOnly=false&returnCountOnly=true&outFields=" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(url, function handleObjectCountClosure(results) {
                dataAccess.handleObjectCount(results, deferred, condition);
            });

            return deferred;
        },

        /**
         * Handles the callback for an object-count query.
         * @param {object} results Results provided by callback; .error if there's an error; .count contains count
         * otherwise
         * @param {object} deferred Deferred to receive results of interpreting results
         * @param {string} condition Condition used in query for diagnostic display
         */
        handleObjectCount: function (results, deferred, condition) {
            if (!results || results.error) {
                deferred.reject(-1);
            }
            diag.appendWithLF("surveys " + (condition
                ? "for \"" + condition + "\""
                : "available") + ": " + results.count);
            deferred.resolve(results.count);
        },

        /**
         * Gets a survey candidate from the list of available unsurveyed candidates.
         * @param {boolean} [randomizeSelection] Indicates if a feature should be selected randomly from the list of available
         * features;  if omitted or false, the first available feature in the list of object ids returned by the feature
         * service is used
         * @return {object} Deferred indicating when candidate is ready; successful resolution includes object with
         * obj and attachments properties; 'obj' contains an attributes property with the candidate's attributes and
         * attachments contains an array containing objects each of which describes an attachment using id and url properties;
         * if there are no more candidates, deferred resolves successfully but with 'obj'=null and 'attachments'=[]; if
         * the fetch fails, the deferred resolves with a failure
         */
        getCandidate: function (randomizeSelection) {
            var deferred, url;
            deferred = $.Deferred();

            // Get the ids of available unsurveyed candidates
            url = dataAccess.featureServiceUrl + "query?where=" + dataAccess.validCandidateCondition
                    + "&objectIds=&returnIdsOnly=true&returnCountOnly=false&outFields=" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(url, function handleCandidatesClosure(results) {
                dataAccess.handleCandidates(results, randomizeSelection, deferred);
            });

            return deferred;
        },

        handleCandidates: function (results, randomizeSelection, deferred) {
            var objectId;

            if (!results || results.error) {
                deferred.reject({
                    id: null,
                    obj: null,
                    attachments: []
                });
                return;
            }

            // Pick a candidate from amongst the available
            objectId = dataAccess.pickFromList(results.objectIds, randomizeSelection);

            // No more surveys!
            if (objectId === null) {
                deferred.resolve({
                    id: null,
                    obj: null,
                    attachments: []
                });
                return;
            }

            // Get its info
            dataAccess.getCandidateInfo(objectId, deferred);
        },

        getCandidateInfo: function (objectId, deferred) {
            var attributesDeferred, objectAttrsUrl, attachmentsDeferred, objectAttachmentsUrl;

            // Get the candidate's attributes
            attributesDeferred = $.Deferred();
            objectAttrsUrl = dataAccess.featureServiceUrl + "query?objectIds=" + objectId
                    + "&returnIdsOnly=false&returnCountOnly=false&outFields=*" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(objectAttrsUrl, function handleCandidateAttrsClosure(results) {
                dataAccess.handleCandidateAttrs(results, attributesDeferred);
            });

            // Get the candidate's attachments
            attachmentsDeferred = $.Deferred();
            objectAttachmentsUrl = dataAccess.featureServiceUrl + objectId + "/attachments?f=json&callback=?";
            $.getJSON(objectAttachmentsUrl, function handleCandidateAttachmentsClosure(results) {
                dataAccess.handleCandidateAttachments(objectId, results, attributesDeferred, attachmentsDeferred);
            });

            // Return the attributes and attachments
            $.when(attributesDeferred, attachmentsDeferred).then(function (attributesData, attachmentsData) {
                deferred.resolve({
                    id: objectId,
                    obj: attributesData,
                    attachments: attachmentsData
                });
            }, function () {
                deferred.reject();
            });
        },

        handleCandidateAttrs: function (results, attributesDeferred) {
            // No attributes is a problem
            if (!results || results.error || !results.features || results.features.length === 0) {
                attributesDeferred.reject();
                return;
            }

            attributesDeferred.resolve(results.features[0]);
        },

        handleCandidateAttachments: function (objectId, results, attributesDeferred, attachmentsDeferred) {
            var attachments = [];

            if (!results || results.error) {
                attachmentsDeferred.resolve(null);
                return;
            }

            // Empty list of attachments is possible
            if (results && results.attachmentInfos) {

                attributesDeferred.then(function (feature) {
                    // Watch for request to reverse order of attachments
                    var doReversal = false;
                    if (feature && feature.attributes && feature.attributes.REVERSE) {
                        doReversal = dataAccess.toBoolean(feature.attributes.REVERSE, false);
                    }

                    // Build list of attachments
                    if (doReversal) {
                        results.attachmentInfos.reverse();
                    }
                    $.each(results.attachmentInfos, function (ignore, attachment) {
                        attachments.push({
                            id: attachment.id,
                            url: dataAccess.featureServiceUrl + objectId + "/attachments/" + attachment.id
                        });
                    });
                    attachmentsDeferred.resolve(attachments);
                }, function () {
                    attachmentsDeferred.resolve(null);
                });
            } else {
                attachmentsDeferred.resolve(attachments);
            }
        },

        /**
         * Updates a candidate to its feature service.
         * @param {object} candidate Candidate to write to its feature service; contains object with obj property; 'obj'
         * contains an attributes property with the candidate's attributes; only the 'obj' property is used; any other
         * properties in 'candidate' are ignored
         * @return {object} Deferred to provide information about success or failure of update
         */
        updateCandidate: function (candidate) {
            var deferred, url, update;
            deferred = $.Deferred();

            update = "f=json&id=" + dataAccess.featureServiceLayerId + "&updates=%5B" + dataAccess.stringifyForApplyEdits(candidate.obj) + "%5D";
            url = (dataAccess.proxyProgram
                ? dataAccess.proxyProgram + "?"
                : "") + dataAccess.featureServiceUrl + "applyEdits";
            $.post(url, update, function (results, status) {
                // seek
                //   * status === "success"
                //   * results.updateResults[0].objectId === candidate.obj[dataAccess.objectIdField]
                //   * results.updateResults[0].success === true
                diag.append("update obj #" + candidate.obj.attributes[dataAccess.objectIdField] + " result: ");
                if (status === "success" && results && results.updateResults.length > 0) {
                    if (results.updateResults[0].success === true
                            && results.updateResults[0].objectId === candidate.obj.attributes[dataAccess.objectIdField]) {
                        diag.appendWithLF("OK");
                        deferred.resolve();
                    } else if (results.updateResults[0].error) {
                        diag.appendWithLF("fail #" + results.updateResults[0].error.code
                                + " (" + results.updateResults[0].error.description + ")");
                        deferred.reject();
                    } else {
                        diag.appendWithLF("unspecified fail");
                        deferred.reject();
                    }
                } else {
                    diag.appendWithLF("overall fail: " + status);
                    deferred.reject();
                }
            }, "json").fail(function (err) {
                // Unable to POST; can be IE 9 proxy problem
                diag.appendWithLF("update obj #" + candidate.obj.attributes[dataAccess.objectIdField]
                        + " POST fail: " + JSON.stringify(err) + "; failing URL: " + url);
                deferred.reject();
            });

            return deferred;
        },

        //--------------------------------------------------------------------------------------------------------------------//

        /**
         * Converts a value into the escaped form required by updates to a feature service.
         * @param {null|string|object} value Value to escape
         * @return {string} Escaped value
         * @private
         */
        stringifyForApplyEdits: function (value) {
            var isFirst = true, result = "";

            if (value === null) {
                result += 'null';
            } else if (typeof value === "string") {
                result += '%22' + value + '%22';
            } else if (typeof value === "object") {
                result += '%7B';
                $.each(value, function (part) {
                    if (value.hasOwnProperty(part)) {
                        result += (isFirst
                            ? ''
                            : '%2C') + part + '%3A' + dataAccess.stringifyForApplyEdits(value[part]);
                        isFirst = false;
                    }
                });
                result += '%7D';
            } else {
                result += value;
            }
            return result;
        },

        /** Normalizes a boolean value to true or false.
         * @param {boolean|string} boolValue A true or false value that is returned directly or a string
         * "true", "t", "yes", "y", "false", "f", "no", "n" (case-insensitive) or a number (0 for false; non-zero for true)
         * that is interpreted and returned; if neither a boolean nor a usable string nor a number, falls back to defaultValue
         * @param {boolean} [defaultValue] A true or false that is returned if boolValue can't be used; if not defined, true
         * is returned
         * @private
         */
        toBoolean: function (boolValue, defaultValue) {
            var lowercaseValue;

            // Shortcut true|false
            if (boolValue === true) {
                return true;
            }
            if (boolValue === false) {
                return false;
            }

            // Handle a true|false string
            if (typeof boolValue === "string") {
                lowercaseValue = boolValue.toLowerCase();
                if (lowercaseValue === "true" || lowercaseValue === "t" || lowercaseValue === "yes" || lowercaseValue === "y" || lowercaseValue === "1") {
                    return true;
                }
                if (lowercaseValue === "false" || lowercaseValue === "f" || lowercaseValue === "no" || lowercaseValue === "n" || lowercaseValue === "0") {
                    return false;
                }
            } else if (typeof boolValue === "number") {
                return boolValue !== 0;
            }
            // Fall back to default
            if (defaultValue === undefined) {
                return true;
            }
            return defaultValue;
        }

    };
    return dataAccess;
});
<commit_msg>Encoded text values for POSTing<commit_after>/*global define,$ */
/*jslint browser:true */
/** @license
 | Copyright 2015 Esri
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |    http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
 */
//============================================================================================================================//
define(['diag'], function (diag) {
    'use strict';
    var dataAccess;
    dataAccess = {

        fixedQueryParams: "&time=&geometry=&geometryType=esriGeometryEnvelope&inSR=&spatialRel=esriSpatialRelIntersects&distance=&units=esriSRUnit_Meter&returnGeometry=true&maxAllowableOffset=&geometryPrecision=&returnExtentOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&resultOffset=&resultRecordCount=&returnZ=false&returnM=false&quantizationParameters=&f=json&outSR=4326",
        featureServiceUrl: null,
        featureServiceLayerId: null,
        objectIdField: null,
        validCandidateCondition: null,
        proxyProgram: null,
        exclusionList: {},

        //--------------------------------------------------------------------------------------------------------------------//

        /**
         * Initializes the module.
         * @param {string} featureServiceUrl URL to feature service providing data
         * @param {number} featureServiceLayerId Id that the feature layer reports as its id
         * @param {string} objectIdField Field that the feature layer reports as its objectIdField
         * @param {string} validCandidateCondition Query 'where' clause to use to get survey candidates, e.g.,
         * "SRVNAME+is+null+or+SRVNAME=''", where SRVNAME is the name of the field used to store the name of the surveyor
         * @param {string} proxyProgram Path to the app's proxy, usually defined in the js/configuration.json file, e.g.,
         * "proxy/proxy.ashx"; use null if browser doesn't need proxy to query and update feature service (at this time,
         * only IE9 needs the proxy)
         */
        init: function (featureServiceUrl, featureServiceLayerId, objectIdField, validCandidateCondition, proxyProgram) {
            dataAccess.featureServiceUrl = featureServiceUrl;
            if (dataAccess.featureServiceUrl.lastIndexOf("/") !== dataAccess.featureServiceUrl.length - 1) {
                dataAccess.featureServiceUrl += "/";
            }
            dataAccess.featureServiceLayerId = featureServiceLayerId;
            dataAccess.objectIdField = objectIdField;
            dataAccess.validCandidateCondition = validCandidateCondition;
            dataAccess.proxyProgram = proxyProgram;
        },

        /**
         * Adds an item to the to-be-skipped list.
         * @param {number} item Object to be skipped; NOP if already in the list
         */
        addItemToExclusionList: function (item) {
            dataAccess.exclusionList[item] = true;
        },

        /**
         * Checks if an item is in the to-be-skipped list.
         * @param {number} item Object to be checked
         * @return {boolean} True if item is in list
         */
        isItemInExclusionList: function (item) {
            return dataAccess.exclusionList.hasOwnProperty(item);
        },

        /**
         * Empties the to-be-skipped list.
         */
        resetExclusionList: function () {
            dataAccess.exclusionList = {};
        },

        /**
         * Returns a list that doesn't include the items in the to-be-skipped list.
         * @param {array} itemList List to be filtered
         * @return {array} Filtered list
         */
        filterList: function (itemList) {
            return $.grep(itemList, function (element) {
                return !dataAccess.isItemInExclusionList(element);
            });
        },

        /**
         * Selects an item from a list of items; selection is subject to an exclusion limitation.
         * @param {array} itemList List of items to choose from
         * @param {boolean} randomizeSelection True if pseudorandom selection should be used
         * @return {object} Selected item or null if list is empty or only contains items
         * that are also in the skip list
         */
        pickFromList: function (itemList, randomizeSelection) {
            var item = null, filteredItems;

            if (itemList.length > 0) {
                filteredItems = dataAccess.filterList(itemList);
                if (filteredItems.length > 0) {
                    // Pick a candidate from amongst the available
                    item = randomizeSelection
                        ? filteredItems[Math.floor(Math.random() * filteredItems.length)]
                        : filteredItems[0];
                }
            }

            return item;
        },

        /**
         * Gets the number of features satisfying a condition.
         * @param {string} [condition] Condition to test; if omitted, the validCandidateCondition provided to this module's
         * init() function is used; a sample condition is "SRVNAME='" + username + "'", where SRVNAME is the name of the
         * field used to store the name of the surveyor, to count how many surveys the user given by 'username' has completed
         * @return {object} Deferred to provide count when it arrives; a count of -1 is used to flag an error
         */
        getObjectCount: function (condition) {
            var deferred, url;
            deferred = $.Deferred();

            url = dataAccess.featureServiceUrl + "query?where=" + (condition || dataAccess.validCandidateCondition)
                    + "&objectIds=&returnIdsOnly=false&returnCountOnly=true&outFields=" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(url, function handleObjectCountClosure(results) {
                dataAccess.handleObjectCount(results, deferred, condition);
            });

            return deferred;
        },

        /**
         * Handles the callback for an object-count query.
         * @param {object} results Results provided by callback; .error if there's an error; .count contains count
         * otherwise
         * @param {object} deferred Deferred to receive results of interpreting results
         * @param {string} condition Condition used in query for diagnostic display
         */
        handleObjectCount: function (results, deferred, condition) {
            if (!results || results.error) {
                deferred.reject(-1);
            }
            diag.appendWithLF("surveys " + (condition
                ? "for \"" + condition + "\""
                : "available") + ": " + results.count);
            deferred.resolve(results.count);
        },

        /**
         * Gets a survey candidate from the list of available unsurveyed candidates.
         * @param {boolean} [randomizeSelection] Indicates if a feature should be selected randomly from the list of available
         * features;  if omitted or false, the first available feature in the list of object ids returned by the feature
         * service is used
         * @return {object} Deferred indicating when candidate is ready; successful resolution includes object with
         * obj and attachments properties; 'obj' contains an attributes property with the candidate's attributes and
         * attachments contains an array containing objects each of which describes an attachment using id and url properties;
         * if there are no more candidates, deferred resolves successfully but with 'obj'=null and 'attachments'=[]; if
         * the fetch fails, the deferred resolves with a failure
         */
        getCandidate: function (randomizeSelection) {
            var deferred, url;
            deferred = $.Deferred();

            // Get the ids of available unsurveyed candidates
            url = dataAccess.featureServiceUrl + "query?where=" + dataAccess.validCandidateCondition
                    + "&objectIds=&returnIdsOnly=true&returnCountOnly=false&outFields=" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(url, function handleCandidatesClosure(results) {
                dataAccess.handleCandidates(results, randomizeSelection, deferred);
            });

            return deferred;
        },

        handleCandidates: function (results, randomizeSelection, deferred) {
            var objectId;

            if (!results || results.error) {
                deferred.reject({
                    id: null,
                    obj: null,
                    attachments: []
                });
                return;
            }

            // Pick a candidate from amongst the available
            objectId = dataAccess.pickFromList(results.objectIds, randomizeSelection);

            // No more surveys!
            if (objectId === null) {
                deferred.resolve({
                    id: null,
                    obj: null,
                    attachments: []
                });
                return;
            }

            // Get its info
            dataAccess.getCandidateInfo(objectId, deferred);
        },

        getCandidateInfo: function (objectId, deferred) {
            var attributesDeferred, objectAttrsUrl, attachmentsDeferred, objectAttachmentsUrl;

            // Get the candidate's attributes
            attributesDeferred = $.Deferred();
            objectAttrsUrl = dataAccess.featureServiceUrl + "query?objectIds=" + objectId
                    + "&returnIdsOnly=false&returnCountOnly=false&outFields=*" + dataAccess.fixedQueryParams
                    + "&callback=?";
            $.getJSON(objectAttrsUrl, function handleCandidateAttrsClosure(results) {
                dataAccess.handleCandidateAttrs(results, attributesDeferred);
            });

            // Get the candidate's attachments
            attachmentsDeferred = $.Deferred();
            objectAttachmentsUrl = dataAccess.featureServiceUrl + objectId + "/attachments?f=json&callback=?";
            $.getJSON(objectAttachmentsUrl, function handleCandidateAttachmentsClosure(results) {
                dataAccess.handleCandidateAttachments(objectId, results, attributesDeferred, attachmentsDeferred);
            });

            // Return the attributes and attachments
            $.when(attributesDeferred, attachmentsDeferred).then(function (attributesData, attachmentsData) {
                deferred.resolve({
                    id: objectId,
                    obj: attributesData,
                    attachments: attachmentsData
                });
            }, function () {
                deferred.reject();
            });
        },

        handleCandidateAttrs: function (results, attributesDeferred) {
            // No attributes is a problem
            if (!results || results.error || !results.features || results.features.length === 0) {
                attributesDeferred.reject();
                return;
            }

            attributesDeferred.resolve(results.features[0]);
        },

        handleCandidateAttachments: function (objectId, results, attributesDeferred, attachmentsDeferred) {
            var attachments = [];

            if (!results || results.error) {
                attachmentsDeferred.resolve(null);
                return;
            }

            // Empty list of attachments is possible
            if (results && results.attachmentInfos) {

                attributesDeferred.then(function (feature) {
                    // Watch for request to reverse order of attachments
                    var doReversal = false;
                    if (feature && feature.attributes && feature.attributes.REVERSE) {
                        doReversal = dataAccess.toBoolean(feature.attributes.REVERSE, false);
                    }

                    // Build list of attachments
                    if (doReversal) {
                        results.attachmentInfos.reverse();
                    }
                    $.each(results.attachmentInfos, function (ignore, attachment) {
                        attachments.push({
                            id: attachment.id,
                            url: dataAccess.featureServiceUrl + objectId + "/attachments/" + attachment.id
                        });
                    });
                    attachmentsDeferred.resolve(attachments);
                }, function () {
                    attachmentsDeferred.resolve(null);
                });
            } else {
                attachmentsDeferred.resolve(attachments);
            }
        },

        /**
         * Updates a candidate to its feature service.
         * @param {object} candidate Candidate to write to its feature service; contains object with obj property; 'obj'
         * contains an attributes property with the candidate's attributes; only the 'obj' property is used; any other
         * properties in 'candidate' are ignored
         * @return {object} Deferred to provide information about success or failure of update
         */
        updateCandidate: function (candidate) {
            var deferred, url, update;
            deferred = $.Deferred();

            update = "f=json&id=" + dataAccess.featureServiceLayerId + "&updates=%5B" + dataAccess.stringifyForApplyEdits(candidate.obj) + "%5D";
            url = (dataAccess.proxyProgram
                ? dataAccess.proxyProgram + "?"
                : "") + dataAccess.featureServiceUrl + "applyEdits";
            $.post(url, update, function (results, status) {
                // seek
                //   * status === "success"
                //   * results.updateResults[0].objectId === candidate.obj[dataAccess.objectIdField]
                //   * results.updateResults[0].success === true
                diag.append("update obj #" + candidate.obj.attributes[dataAccess.objectIdField] + " result: ");
                if (status === "success" && results && results.updateResults.length > 0) {
                    if (results.updateResults[0].success === true
                            && results.updateResults[0].objectId === candidate.obj.attributes[dataAccess.objectIdField]) {
                        diag.appendWithLF("OK");
                        deferred.resolve();
                    } else if (results.updateResults[0].error) {
                        diag.appendWithLF("fail #" + results.updateResults[0].error.code
                                + " (" + results.updateResults[0].error.description + ")");
                        deferred.reject();
                    } else {
                        diag.appendWithLF("unspecified fail");
                        deferred.reject();
                    }
                } else {
                    diag.appendWithLF("overall fail: " + status);
                    deferred.reject();
                }
            }, "json").fail(function (err) {
                // Unable to POST; can be IE 9 proxy problem
                diag.appendWithLF("update obj #" + candidate.obj.attributes[dataAccess.objectIdField]
                        + " POST fail: " + JSON.stringify(err) + "; failing URL: " + url);
                deferred.reject();
            });

            return deferred;
        },

        //--------------------------------------------------------------------------------------------------------------------//

        /**
         * Converts a value into the escaped form required by updates to a feature service.
         * @param {null|string|object} value Value to escape
         * @return {string} Escaped value
         * @private
         */
        stringifyForApplyEdits: function (value) {
            var isFirst = true, result = "";

            if (value === null) {
                result += 'null';
            } else if (typeof value === "string") {
                result += '%22' + encodeURIComponent(value) + '%22';
            } else if (typeof value === "object") {
                result += '%7B';
                $.each(value, function (part) {
                    if (value.hasOwnProperty(part)) {
                        result += (isFirst
                            ? ''
                            : '%2C') + part + '%3A' + dataAccess.stringifyForApplyEdits(value[part]);
                        isFirst = false;
                    }
                });
                result += '%7D';
            } else {
                result += value;
            }
            return result;
        },

        /** Normalizes a boolean value to true or false.
         * @param {boolean|string} boolValue A true or false value that is returned directly or a string
         * "true", "t", "yes", "y", "false", "f", "no", "n" (case-insensitive) or a number (0 for false; non-zero for true)
         * that is interpreted and returned; if neither a boolean nor a usable string nor a number, falls back to defaultValue
         * @param {boolean} [defaultValue] A true or false that is returned if boolValue can't be used; if not defined, true
         * is returned
         * @private
         */
        toBoolean: function (boolValue, defaultValue) {
            var lowercaseValue;

            // Shortcut true|false
            if (boolValue === true) {
                return true;
            }
            if (boolValue === false) {
                return false;
            }

            // Handle a true|false string
            if (typeof boolValue === "string") {
                lowercaseValue = boolValue.toLowerCase();
                if (lowercaseValue === "true" || lowercaseValue === "t" || lowercaseValue === "yes" || lowercaseValue === "y" || lowercaseValue === "1") {
                    return true;
                }
                if (lowercaseValue === "false" || lowercaseValue === "f" || lowercaseValue === "no" || lowercaseValue === "n" || lowercaseValue === "0") {
                    return false;
                }
            } else if (typeof boolValue === "number") {
                return boolValue !== 0;
            }
            // Fall back to default
            if (defaultValue === undefined) {
                return true;
            }
            return defaultValue;
        }

    };
    return dataAccess;
});
<|endoftext|>


<commit_before>var doTest = require ('./');

doTest.add ('Module interface', function () {
  var test = doTest.test ();

  test
    .isObject ('fail', 'exports', doTest)
    .isFunction ('fail', '.add', doTest.add)
    .isFunction ('fail', '.run', doTest.run)
    .isFunction ('fail', '.log', doTest.log)
    .isFunction ('fail', '.exit', doTest.exit)
    .isFunction ('fail', '.test', doTest.test)
    .isFunction ('fail', '.test.done', test.done)
    .isFunction ('fail', '.test.good', test.good)
    .isFunction ('fail', '.test.warn', test.warn)
    .isFunction ('fail', '.test.fail', test.fail)
    .isFunction ('fail', '.test.error', test.error)
    .isFunction ('fail', '.test.info', test.info)
    .isFunction ('fail', '.test.exit', test.exit)
    .done ();
});

doTest.add ('test() shortcut', function (test) {
  doTest.test ()
    .isFunction ('fail', 'test', test)
    .done ();
});

doTest.add ('Methods', function (test, fake) {
  doTest.test ()
    .isError ('fail', '.isError', new Error ())
    .isObject ('fail', '.isObject', {})
    .isArray ('fail', '.isArray', [])
    .isString ('fail', '.isString', '')
    .isNumber ('fail', '.isNumber', 1)
    .isUndefined ('fail', '.isUndefined', fake)
    .isNull ('fail', '.isNull', null)
    .isNaN ('fail', '.isNaN', NaN)
    .isBoolean ('fail', '.isBoolean', true)
    .isFunction ('fail', '.isFunction', function () {})
    .isDate ('fail', '.isDate', new Date ())
    .isExactly ('fail', '.isExactly', ':)', ':)')
    .isNot ('fail', '.isNot', 'a', 'b')
    .isRegexp ('fail', '.isRegexp', /^\w$/)
    .isRegexpMatch ('fail', '.isRegexpMatch', 'a', /^\w$/)
    .isCondition ('fail', '.isCondition', 1, '<', 2)
    .isEmpty ('fail', '.isEmpty', '')
    .isNotEmpty ('fail', '.isNotEmpty', 'text')
    .warn ('This is a warn message')
    .good ('This is a good message')
    .info ({ hello: 'world' })
    .done ();
});

doTest.run ();
<commit_msg>Non-empty string for isString() check<commit_after>var doTest = require ('./');

doTest.add ('Module interface', function () {
  var test = doTest.test ();

  test
    .isObject ('fail', 'exports', doTest)
    .isFunction ('fail', '.add', doTest.add)
    .isFunction ('fail', '.run', doTest.run)
    .isFunction ('fail', '.log', doTest.log)
    .isFunction ('fail', '.exit', doTest.exit)
    .isFunction ('fail', '.test', doTest.test)
    .isFunction ('fail', '.test.done', test.done)
    .isFunction ('fail', '.test.good', test.good)
    .isFunction ('fail', '.test.warn', test.warn)
    .isFunction ('fail', '.test.fail', test.fail)
    .isFunction ('fail', '.test.error', test.error)
    .isFunction ('fail', '.test.info', test.info)
    .isFunction ('fail', '.test.exit', test.exit)
    .done ();
});

doTest.add ('test() shortcut', function (test) {
  doTest.test ()
    .isFunction ('fail', 'test', test)
    .done ();
});

doTest.add ('Methods', function (test, fake) {
  doTest.test ()
    .isError ('fail', '.isError', new Error ())
    .isObject ('fail', '.isObject', {})
    .isArray ('fail', '.isArray', [])
    .isString ('fail', '.isString', 'hello')
    .isNumber ('fail', '.isNumber', 1)
    .isUndefined ('fail', '.isUndefined', fake)
    .isNull ('fail', '.isNull', null)
    .isNaN ('fail', '.isNaN', NaN)
    .isBoolean ('fail', '.isBoolean', true)
    .isFunction ('fail', '.isFunction', function () {})
    .isDate ('fail', '.isDate', new Date ())
    .isExactly ('fail', '.isExactly', ':)', ':)')
    .isNot ('fail', '.isNot', 'a', 'b')
    .isRegexp ('fail', '.isRegexp', /^\w$/)
    .isRegexpMatch ('fail', '.isRegexpMatch', 'a', /^\w$/)
    .isCondition ('fail', '.isCondition', 1, '<', 2)
    .isEmpty ('fail', '.isEmpty', '')
    .isNotEmpty ('fail', '.isNotEmpty', 'text')
    .warn ('This is a warn message')
    .good ('This is a good message')
    .info ({ hello: 'world' })
    .done ();
});

doTest.run ();
<|endoftext|>


<commit_before>const knex = require('../../../../app/lib/db');
const chai = require('chai');
const EventController = require('../../../../app/controllers/event');
const MockExpressResponse = require('mock-express-response');

const should = chai.should();
const assert = chai.assert;

// Timeout to be used for checking controller responses
const timeout = 250;

function getMockCreateEventReq() {
  return {
    body: {
      name: 'Hey! Im testin here!',
      description: 'New event for testing!',
      default_damage: 100,
      disabled: false,
    },
  };
}

describe('Event Controller Tests', () => {
  describe('event controller createEvent', () => {
    before((done) => {
      knex.migrate.rollback()
        .then(() => {
          knex.migrate.latest()
            .then(() => {
              knex.seed.run()
                .then(() => {
                  done();
                });
            });
        });
    });
    after((done) => {
      knex.migrate.rollback()
        .then(() => {
          knex.migrate.latest()
            .then(() => {
              knex.seed.run()
                .then(() => {
                  done();
                });
            });
        });
    });

    describe('event controller createEvent success', () => {
      it('should succeed createEvent', (done) => {
        const mockReq = getMockCreateEventReq();
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(200);
          mockRes.statusMessage.should.equal('OK');
          done();
        }, timeout);
      });
    });

    describe('event controller createEvent fail', () => {
      it('should fail createEvent missing name', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.name;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
      it('should fail createEvent missing description', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.description;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
      it('should fail createEvent missing default_damage', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.default_damage;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
    });
  });

  describe('event controller getEvents', () => {
    describe('event Controller getEvents success', () => {
      it('should succeed getEvents', (done) => {
        const mockReq = {
          body: { },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEvents(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(200);
          const resJSON = mockRes._getJSON();
          resJSON.should.be.an('array');
          done();
        }, timeout);
      });
    });
  });

  describe('event controller getEventById', () => {
    describe('event Controller getEventById success', () => {
      it('should succeed getEventById', (done) => {
        const mockReq = {
          params: { id: 1 },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEventById(mockReq, mockRes);
        setTimeout(() => {
          console.log(mockRes._getJSON());
          mockRes.statusCode.should.equal(200);
          const resJSON = mockRes._getJSON();
          resJSON.id.should.equal(1);
          done();
        }, timeout);
      });
    });
    describe('event Controller getEventById fail', () => {
      it('should fail getEventById', (done) => {
        const mockReq = {
          params: { id: 100 },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEventById(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(404);
          const resJSON = mockRes._getJSON();
          resJSON.error.should.equal('An Event with that ID could not be found.');
          assert.deepEqual(resJSON.request, mockReq);
          done();
        }, timeout);
      });
    });
  });
});
<commit_msg>Removed a console.log output in a test<commit_after>const knex = require('../../../../app/lib/db');
const chai = require('chai');
const EventController = require('../../../../app/controllers/event');
const MockExpressResponse = require('mock-express-response');

const should = chai.should();
const assert = chai.assert;

// Timeout to be used for checking controller responses
const timeout = 250;

function getMockCreateEventReq() {
  return {
    body: {
      name: 'Hey! Im testin here!',
      description: 'New event for testing!',
      default_damage: 100,
      disabled: false,
    },
  };
}

describe('Event Controller Tests', () => {
  describe('event controller createEvent', () => {
    before((done) => {
      knex.migrate.rollback()
        .then(() => {
          knex.migrate.latest()
            .then(() => {
              knex.seed.run()
                .then(() => {
                  done();
                });
            });
        });
    });
    after((done) => {
      knex.migrate.rollback()
        .then(() => {
          knex.migrate.latest()
            .then(() => {
              knex.seed.run()
                .then(() => {
                  done();
                });
            });
        });
    });

    describe('event controller createEvent success', () => {
      it('should succeed createEvent', (done) => {
        const mockReq = getMockCreateEventReq();
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(200);
          mockRes.statusMessage.should.equal('OK');
          done();
        }, timeout);
      });
    });

    describe('event controller createEvent fail', () => {
      it('should fail createEvent missing name', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.name;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
      it('should fail createEvent missing description', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.description;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
      it('should fail createEvent missing default_damage', (done) => {
        const mockReq = getMockCreateEventReq();
        delete mockReq.body.default_damage;
        const mockRes = new MockExpressResponse();
        EventController.createEvent(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(400);
          mockRes.statusMessage.should.equal('Bad Request');
          done();
        }, timeout);
      });
    });
  });

  describe('event controller getEvents', () => {
    describe('event Controller getEvents success', () => {
      it('should succeed getEvents', (done) => {
        const mockReq = {
          body: { },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEvents(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(200);
          const resJSON = mockRes._getJSON();
          resJSON.should.be.an('array');
          done();
        }, timeout);
      });
    });
  });

  describe('event controller getEventById', () => {
    describe('event Controller getEventById success', () => {
      it('should succeed getEventById', (done) => {
        const mockReq = {
          params: { id: 1 },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEventById(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(200);
          const resJSON = mockRes._getJSON();
          resJSON.id.should.equal(1);
          done();
        }, timeout);
      });
    });
    describe('event Controller getEventById fail', () => {
      it('should fail getEventById', (done) => {
        const mockReq = {
          params: { id: 100 },
        };
        const mockRes = new MockExpressResponse();
        EventController.getEventById(mockReq, mockRes);
        setTimeout(() => {
          mockRes.statusCode.should.equal(404);
          const resJSON = mockRes._getJSON();
          resJSON.error.should.equal('An Event with that ID could not be found.');
          assert.deepEqual(resJSON.request, mockReq);
          done();
        }, timeout);
      });
    });
  });
});
<|endoftext|>


<commit_before>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global gladius: false, document: false, window: false, module: false, start,
  test: false, expect: false, ok: false, notEqual: false, stop, QUnit: false,
  equal: false */

(function() {

    var engine = null,
    TestComponent1 = null,
    TestComponent2 = null,
    TestComponent3 = null;

    module( 'core/Space', {
        setup: function () {
            stop();

            gladius.create( { debug: true }, function( instance ) {
                engine = instance;

                TestComponent1 = engine.base.Component({
                  type: 'Apple'
                },
                function( options ) {
                });

                TestComponent2 = engine.base.Component({
                  type: 'Apple'
                },
                function( options ) {
                });

                TestComponent3 = engine.base.Component({
                  type: 'Orange'
                },
                function( options ) {
                });

                start();
            });
        },

        teardown: function () {
            engine = null;
        }
    });

    test( 'Construction', function () {
        expect( 2 );

        var space = new engine.core.Space();
        ok(
                space,
                'New space is constructed.'
        );
        ok(
                space.size === 0,
                'Initial size is 0.'
        );
    });

    test( 'Entity', function() {
        expect( 5 );

        var space = new engine.core.Space();

        ok(
                space.size === 0,
                'Initial space size is 0.'
        );

        var entity = new space.Entity({ name: 'TestEntity' });

        ok(
                entity,
                'New entity is constructed.'
        );
        equal(
                entity.manager,
                space,
                'Entity manager is the space.'
        );
        ok(
                space.size === 1,
                'Size after entity creation is 1.'
        );

        space.remove( entity );

        ok(
                space.size === 0,
                'Size afer entity removal is 0.'
        );
    });

}());
<commit_msg>Cleanup test name<commit_after>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global gladius: false, document: false, window: false, module: false, start,
  test: false, expect: false, ok: false, notEqual: false, stop, QUnit: false,
  equal: false */

(function() {

    var engine = null,
    TestComponent1 = null,
    TestComponent2 = null,
    TestComponent3 = null;

    module( 'core/Space', {
        setup: function () {
            stop();

            gladius.create( { debug: true }, function( instance ) {
                engine = instance;

                TestComponent1 = engine.base.Component({
                  type: 'Apple'
                },
                function( options ) {
                });

                TestComponent2 = engine.base.Component({
                  type: 'Apple'
                },
                function( options ) {
                });

                TestComponent3 = engine.base.Component({
                  type: 'Orange'
                },
                function( options ) {
                });

                start();
            });
        },

        teardown: function () {
            engine = null;
        }
    });

    test( 'Construction', function () {
        expect( 2 );

        var space = new engine.core.Space();
        ok(
                space,
                'New space is constructed.'
        );
        ok(
                space.size === 0,
                'Initial size is 0.'
        );
    });

    test( 'new Entity creation', function() {
        expect( 5 );

        var space = new engine.core.Space();

        ok(
                space.size === 0,
                'Initial space size is 0.'
        );

        var entity = new space.Entity({ name: 'TestEntity' });

        ok(
                entity,
                'New entity is constructed.'
        );
        equal(
                entity.manager,
                space,
                'Entity manager is the space.'
        );
        ok(
                space.size === 1,
                'Size after entity creation is 1.'
        );

        space.remove( entity );

        ok(
                space.size === 0,
                'Size afer entity removal is 0.'
        );
    });

}());
<|endoftext|>


<commit_before>/* exported introspect_module */

const GObject = imports.gi.GObject;

function _introspect_gtype(gtype) {
    let typename = gtype.name;

    /* Exceptional cases */
    switch (typename) {
    case 'gint':
    case 'guint':
    case 'gdouble':
        return 'Number';
    case 'gchararray':
        return 'String';
    case 'gboolean':
        return 'Boolean';
    }

    return typename;
}

function _introspect_param_spec(pspec) {
    let flags = pspec.flags;
    return {
        name: pspec.get_name(),
        short_desc: pspec.get_nick(),
        long_desc: pspec.get_blurb(),
        default: pspec.get_default_value(),
        readable: !!(flags & GObject.ParamFlags.READABLE),
        writable: !!(flags & GObject.ParamFlags.WRITABLE),
        construct: !!(flags & GObject.ParamFlags.CONSTRUCT),
        construct_only: !!(flags & GObject.ParamFlags.CONSTRUCT_ONLY),
        type: _introspect_gtype(pspec.value_type),
        origin: _introspect_gtype(pspec.owner_type),
    };
}

function _introspect_slots(slots_obj) {
    return Object.keys(slots_obj).map(slot_name => {
        let slot = slots_obj[slot_name];
        return {
            name: slot_name,
            array: !!slot.array,
            multi: !!slot.multi,
        };
    });
}

function _introspect_references(references_obj) {
    return Object.keys(references_obj).map(reference_name => {
        return {
            name: reference_name,
        };
    });
}

function introspect_module(module) {
    return {
        name: module.prototype.__name__,
        gtype: _introspect_gtype(module.$gtype),
        parent: _introspect_gtype(module.__super__.$gtype),
        interfaces: module.prototype.__interfaces__
            .map(iface => _introspect_gtype(iface.$gtype)),
        properties: GObject.Object.list_properties.call(module)
            .map(_introspect_param_spec),
        slots: _introspect_slots(module.__slots__),
        references: _introspect_references(module.__references__),
    };
}
<commit_msg>introspect: Process 'allowed' and 'optional' slot attributes<commit_after>/* exported introspect_module */

const GObject = imports.gi.GObject;

function _introspect_gtype(gtype) {
    let typename = gtype.name;

    /* Exceptional cases */
    switch (typename) {
    case 'gint':
    case 'guint':
    case 'gdouble':
        return 'Number';
    case 'gchararray':
        return 'String';
    case 'gboolean':
        return 'Boolean';
    }

    return typename;
}

function _introspect_param_spec(pspec) {
    let flags = pspec.flags;
    return {
        name: pspec.get_name(),
        short_desc: pspec.get_nick(),
        long_desc: pspec.get_blurb(),
        default: pspec.get_default_value(),
        readable: !!(flags & GObject.ParamFlags.READABLE),
        writable: !!(flags & GObject.ParamFlags.WRITABLE),
        construct: !!(flags & GObject.ParamFlags.CONSTRUCT),
        construct_only: !!(flags & GObject.ParamFlags.CONSTRUCT_ONLY),
        type: _introspect_gtype(pspec.value_type),
        origin: _introspect_gtype(pspec.owner_type),
    };
}

function _introspect_slots(slots_obj) {
    return Object.keys(slots_obj).map(slot_name => {
        let slot = slots_obj[slot_name];
        return {
            name: slot_name,
            array: !!slot.array,
            multi: !!slot.multi,
            optional: !!slot.optional,
            allowed: slot.allowed || [],
        };
    });
}

function _introspect_references(references_obj) {
    return Object.keys(references_obj).map(reference_name => {
        return {
            name: reference_name,
        };
    });
}

function introspect_module(module) {
    return {
        name: module.prototype.__name__,
        gtype: _introspect_gtype(module.$gtype),
        parent: _introspect_gtype(module.__super__.$gtype),
        interfaces: module.prototype.__interfaces__
            .map(iface => _introspect_gtype(iface.$gtype)),
        properties: GObject.Object.list_properties.call(module)
            .map(_introspect_param_spec),
        slots: _introspect_slots(module.__slots__),
        references: _introspect_references(module.__references__),
    };
}
<|endoftext|>


<commit_before>var assert = require('assert');
var fs = require('fs');
var pf = require('./index');

describe('prepend-file', function() {
  var tmp = '.temp';
  it('should create the file if it not yet exists.', function(cb) {
    pf(tmp, 'Hello', function(err) {
      if (err) {
        cb(err);
      }
      var content = fs.readFileSync(tmp).toString();
      assert.equal(content, 'Hello');
      cb();
    });
  });

  it('should prepend the file if it exists', function(cb) {
    pf(tmp, 'What', function(err) {
      if (err) {
        cb(err);
      }
      var content = fs.readFileSync(tmp).toString();
      assert.equal(content, 'WhatHello');
      fs.unlink(tmp, cb);
    });
  });
});
<commit_msg>Use `after` block to cleanup tests<commit_after>var assert = require('assert');
var fs = require('fs');
var pf = require('./index');

describe('prepend-file', function() {
  var tmp = '.temp';

  after(function(cb) {
    fs.unlink(tmp, cb);
  });

  it('should create the file if it not yet exists.', function(cb) {
    pf(tmp, 'Hello', function(err) {
      if (err) {
        cb(err);
      }
      var content = fs.readFileSync(tmp).toString();
      assert.equal(content, 'Hello');
      cb();
    });
  });

  it('should prepend the file if it exists', function(cb) {
    pf(tmp, 'What', function(err) {
      if (err) {
        cb(err);
      }
      var content = fs.readFileSync(tmp).toString();
      assert.equal(content, 'WhatHello');
      cb();
    });
  });
});
<|endoftext|>


<commit_before>var test = require('tape');
var ss = require('../');

test('cumulative_std_normal_probability', function(t) {
    // https://en.wikipedia.org/wiki/Standard_normal_table#Examples_of_use
    test('wikipedia test example works', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554);
        }
        t.end();
    });
    test('nondecreasing', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(ss.cumulative_std_normal_probability(i / 100) >= ss.cumulative_std_normal_probability((i - 1) / 100), true);
        }
        t.end();
    });
    test('matches error_function', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(Math.abs(ss.cumulative_std_normal_probability(i / 100) - (.5 + .5 * ss.error_function(i / 100 / Math.sqrt(2)))) < ss.epsilon, true);
        }
        t.end();
    });
    t.end();
});
<commit_msg>Add a symmetry test for cumulative_std_normal_probability for coverage<commit_after>var test = require('tape');
var ss = require('../');

test('cumulative_std_normal_probability', function(t) {
    // https://en.wikipedia.org/wiki/Standard_normal_table#Examples_of_use
    test('wikipedia test example works', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554);
        }
        t.end();
    });
    test('nondecreasing', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(ss.cumulative_std_normal_probability(i / 100) >= ss.cumulative_std_normal_probability((i - 1) / 100), true);
        }
        t.end();
    });
    test('matches error_function', function(t) {
        for (var i = 0; i < ss.standard_normal_table.length; i++) {
            t.equal(Math.abs(ss.cumulative_std_normal_probability(i / 100) - (.5 + .5 * ss.error_function(i / 100 / Math.sqrt(2)))) < ss.epsilon, true);
        }
        t.end();
    });
    test('symmetry', function(t) {
        t.equal(Math.abs(ss.cumulative_std_normal_probability(-1) - (1 - ss.cumulative_std_normal_probability(1))) < ss.epsilon, true);
        t.end();
    });
    t.end();
});
<|endoftext|>


<commit_before>var Lab = require('lab');
var lab = exports.lab = Lab.script();

var describe = lab.describe;
var it = lab.it;
var Code = require('code');
var expect = Code.expect;

var restParam = require('../rest-param');

describe('rest-param', function () {
  it('should append an array with the rest params to a function', function(done) {
    restParam(function(rest) {
      expect(rest).to.deep.equal([1, 2, 3]);
    })(1, 2, 3);

    restParam(function(a, rest) {
      expect(a).to.equal(1);
      expect(rest).to.deep.equal([2, 3]);
    })(1, 2, 3);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(2);
      expect(rest).to.deep.equal([3, 4]);
    })(1, 2, 3, 4);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(2);
      expect(rest).to.deep.equal([]);
    })(1, 2);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(undefined);
      expect(rest).to.deep.equal([]);
    })(1);

    restParam(function(a, b, rest) {
      expect(a).to.equal(undefined);
      expect(b).to.equal(undefined);
      expect(rest).to.deep.equal([]);
    })();

    done();
  });
});
<commit_msg>fixing stuff<commit_after>var Lab = require('lab');
var lab = exports.lab = Lab.script();

var describe = lab.describe;
var it = lab.it;
var Code = require('code');
var expect = Code.expect;

var restParam = require('./');

describe('rest-param', function () {
  it('should append an array with the rest params to a function', function(done) {
    restParam(function(rest) {
      expect(rest).to.deep.equal([1, 2, 3]);
    })(1, 2, 3);

    restParam(function(a, rest) {
      expect(a).to.equal(1);
      expect(rest).to.deep.equal([2, 3]);
    })(1, 2, 3);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(2);
      expect(rest).to.deep.equal([3, 4]);
    })(1, 2, 3, 4);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(2);
      expect(rest).to.deep.equal([]);
    })(1, 2);

    restParam(function(a, b, rest) {
      expect(a).to.equal(1);
      expect(b).to.equal(undefined);
      expect(rest).to.deep.equal([]);
    })(1);

    restParam(function(a, b, rest) {
      expect(a).to.equal(undefined);
      expect(b).to.equal(undefined);
      expect(rest).to.deep.equal([]);
    })();

    done();
  });
});
<|endoftext|>


<commit_before>var util = require('./test-util');

describe('`@define` notation', function() {
  describe('empty CSS', function() {
    it('must be ignored', function() {
      util.assertSuccess('');
    });
  });

  describe('CSS that lacks a definition', function() {
    it('must be ignored', function() {
      util.assertSuccess('.horse { color: pink; }');
    });
  });

  describe('CSS with a malformed definition', function() {
    it('must be ignored', function() {
      util.assertSuccess('/** @deforne Foo */ .Foo { color: pink; }');
    });
  });

  describe('CSS with a definition violating the `componentName` pattern', function() {
    it('must complain', function() {
      util.assertSingleFailure('/** @define my-_-__Component */ .my-_-__Component { color: pink; }');
    });
  });
});

describe('Implicit @define', function() {
  describe('based on filename', function() {
    var filename = process.cwd() + '/css/c/implicit-component.css';
    var css = '.implicit-component-broken {}';

    it('must complain when true', function() {
      util.assertSingleFailure(css, {implicitComponents: true, preset: 'bem'}, null, filename);
    });

    it('must complain when string', function() {
      util.assertSingleFailure(css, {implicitComponents: 'css/**/*.css', preset: 'bem'}, null, filename);
    });

    it('must complain when array', function() {
      util.assertSingleFailure(css, {implicitComponents: ['css/c/*.css'], preset: 'bem'}, null, filename);
    });

    it('must complain about component name', function() {
      util.assertSingleFailure(
        css,
        {
          implicitComponents: true,
          componentName: /[A-Z]+/,
          componentSelectors: function() { return /.*/; },
        },
        null,
        filename
      );
    });
  });

  describe('utilities', function() {
    it('must complain', function() {
      util.assertSingleFailure('.foo-bar {}', {implicitUtilities: ['utils/*.css'], preset: 'suit'}, null, 'utils/foo-bar.css');
    });
  });
});
<commit_msg>Test implicit components for underscore-prefixed files<commit_after>var util = require('./test-util');

describe('`@define` notation', function() {
  describe('empty CSS', function() {
    it('must be ignored', function() {
      util.assertSuccess('');
    });
  });

  describe('CSS that lacks a definition', function() {
    it('must be ignored', function() {
      util.assertSuccess('.horse { color: pink; }');
    });
  });

  describe('CSS with a malformed definition', function() {
    it('must be ignored', function() {
      util.assertSuccess('/** @deforne Foo */ .Foo { color: pink; }');
    });
  });

  describe('CSS with a definition violating the `componentName` pattern', function() {
    it('must complain', function() {
      util.assertSingleFailure('/** @define my-_-__Component */ .my-_-__Component { color: pink; }');
    });
  });
});

describe('Implicit @define', function() {
  describe('based on filename', function() {
    var filename = process.cwd() + '/css/c/implicit-component.css';
    var filenameWithUnderscore = process.cwd() + '/css/c/_implicit-component.scss';
    var css = '.implicit-component-broken {}';

    it('must complain when true', function() {
      util.assertSingleFailure(css, {implicitComponents: true, preset: 'bem'}, null, filename);
      util.assertSingleFailure(css, {implicitComponents: true, preset: 'bem'}, null, filenameWithUnderscore);
    });

    it('must complain when string', function() {
      util.assertSingleFailure(css, {implicitComponents: 'css/**/*.css', preset: 'bem'}, null, filename);
      util.assertSingleFailure(css, {implicitComponents: 'css/**/*.scss', preset: 'bem'}, null, filenameWithUnderscore);
    });

    it('must complain when array', function() {
      util.assertSingleFailure(css, {implicitComponents: ['css/c/*.css'], preset: 'bem'}, null, filename);
      util.assertSingleFailure(css, {implicitComponents: ['css/c/*.scss'], preset: 'bem'}, null, filenameWithUnderscore);
    });

    it('must complain about component name', function() {
      util.assertSingleFailure(
        css,
        {
          implicitComponents: true,
          componentName: /[A-Z]+/,
          componentSelectors: function() { return /.*/; },
        },
        null,
        filename
      );
      util.assertSingleFailure(
        css,
        {
          implicitComponents: true,
          componentName: /[A-Z]+/,
          componentSelectors: function() { return /.*/; },
        },
        null,
        filenameWithUnderscore
      );
    });
  });

  describe('utilities', function() {
    it('must complain', function() {
      util.assertSingleFailure('.foo-bar {}', {implicitUtilities: ['utils/*.css'], preset: 'suit'}, null, 'utils/foo-bar.css');
    });
  });
});
<|endoftext|>


<commit_before>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length > 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<commit_msg>🤖(break): 2017-7-24 00:36:21<commit_after>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length < 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<|endoftext|>


<commit_before>"use strict";

//-----------------------------------------------------------------------------

const CryptoJS = require ('crypto-js')
    , qs       = require ('qs') // querystring

//-----------------------------------------------------------------------------

const { RequestTimeout } = require ('./errors')

//-----------------------------------------------------------------------------
// converts timeframe to ms
const parseTimeframe = (timeframe) => {
    let amount = timeframe.slice(0, -1);
    let unit = timeframe.slice(-1);
    let scale;
    switch (unit) {
        case 'm':
            scale = 60;
            break;
        case 'h':
            scale = 60 * 60;
            break;
        case 'd':
            scale = 60 * 60 * 24;
            break;
        case 'M':
            scale = 60 * 60 * 24 * 30;
            break;
        default:
            throw Error(`unknown timeframe unit: '${unit}'`);
    }
    return amount * scale;
}

// given a sorted arrays of trades (recent first) and a timeframe builds an array of OHLCV candles
const buildOHLCV = (trades, since = -Infinity, limits = Infinity, timeframe = '1m') => {
    let ms = parseTimeframe (timeframe) * 1000;
    let ohlcvs = [];
    const [timestamp, /* open */, high, low, close, volume, count] = [0, 1, 2, 3, 4, 5, 6];
    let oldest = Math.min(trades.length - 1, limits);

    for (let i = oldest; i >= 0; i--) {
        let trade = trades[i];
        if (trade.timestamp < since) continue;
        let openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M)
        let j = ohlcvs.length;

        if (j == 0 || openingTime >= ohlcvs[j-1][timestamp] + ms) {
            // moved to a new timeframe -> create a new candle from opening trade
            ohlcvs.push([openingTime, trade.price, trade.price, trade.price, trade.price, trade.amount, 1]);
        } else {
            // still processing the same timeframe -> update opening trade
            ohlcvs[j-1][high] = Math.max (ohlcvs[j-1][high], trade.price);
            ohlcvs[j-1][low] = Math.min (ohlcvs[j-1][low], trade.price);
            ohlcvs[j-1][close] = trade.price;
            ohlcvs[j-1][volume] += trade.amount;
            ohlcvs[j-1][count]++;
        } // if
    } // for
    return ohlcvs;
}

// utility helpers

const setTimeout_original = setTimeout

// setTimeout can fire earlier than specified, so we need to ensure it does not happen...

const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = Date.now () + ms) => {

    let clearInnerTimeout = () => {}
    let active = true

    let id = setTimeout (() => {
        active = true
        const rest = targetTime - Date.now ()
        if (rest > 0) {
            clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more
        } else {
            done ()
        }
    }, ms)

    return function clear () {
        if (active) {
            active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion)
            clearTimeout (id)
        }
        clearInnerTimeout ()
    }
}

const sleep = ms => new Promise (resolve => setTimeout_safe (resolve, ms))

const decimal = float => parseFloat (float).toString ()

const timeout = async (ms, promise) => {

    let clear = () => {}
    const timeout = new Promise (resolve => (clear = setTimeout_safe (resolve, ms)))

    try {
        return await Promise.race ([promise, timeout.then (() => { throw new RequestTimeout ('request timed out') })])
    } finally {
        clear () // fixes https://github.com/ccxt/ccxt/issues/749
    }
}

const capitalize = string => string.length ? (string.charAt (0).toUpperCase () + string.slice (1)) : string

const keysort = object => {
    const result = {}
    Object.keys (object).sort ().forEach (key => result[key] = object[key])
    return result
}

const extend = (...args) => Object.assign ({}, ...args)

const deepExtend = function (...args) {

    // if (args.length < 1)
    //     return args
    // else if (args.length < 2)
    //     return args[0]

    let result = undefined

    for (const arg of args) {

        if (arg && (typeof arg == 'object') && (arg.constructor === Object || !('constructor' in arg))) {

            if (typeof result != 'object') {
                result = {}
            }

            for (const key in arg) {
                result[key] = deepExtend (result[key], arg[key])
            }

        } else {

            result = arg
        }
    }

    return result
}

const omit = (object, ...args) => {
    const result = extend (object)
    for (const x of args) {
        if (typeof x === 'string') {
            delete result[x]
        } else if (Array.isArray (x)) {
            for (const k of x)
                delete result[k]
        }
    }
    return result
}

const groupBy = (array, key) => {
    const result = {}
    Object
        .values (array)
        .filter (entry => entry[key] != 'undefined')
        .forEach (entry => {
            if (typeof result[entry[key]] == 'undefined')
                result[entry[key]] = []
            result[entry[key]].push (entry)
        })
    return result
}

const filterBy = (array, key, value = undefined) => {
    if (value) {
        let grouped = groupBy (array, key)
        if (value in grouped)
            return grouped[value]
        return []
    }
    return array
}

const indexBy = (array, key) => {
    const result = {}
    Object
        .values (array)
        .filter (entry => entry[key] != 'undefined')
        .forEach (entry => {
            result[entry[key]] = entry
        })
    return result
}

const sortBy = (array, key, descending = false) => {
    descending = descending ? -1 : 1
    return array.sort ((a, b) => ((a[key] < b[key]) ? -descending : ((a[key] > b[key]) ? descending : 0)))
}

const flatten = (array, result = []) => {
    for (let i = 0, length = array.length; i < length; i++) {
        const value = array[i]
        if (Array.isArray (value)) {
            flatten (value, result)
        } else {
            result.push (value)
        }
    }
    return result
}

const unique = array => array.filter ((value, index, self) => (self.indexOf (value) == index))

const pluck = (array, key) => array
                                .filter (element => (typeof element[key] != 'undefined'))
                                .map (element => element[key])

const urlencode = object => qs.stringify (object)
const rawencode = object => qs.stringify (object, { encode: false })

const sum = (...args) => {
    const result = args.filter (arg => typeof arg != 'undefined')
    return (result.length > 0) ?
        result.reduce ((sum, value) => sum + value, 0) : undefined
}

const safeFloat = (object, key, defaultValue = undefined) => {
    if (key in object) {
        if (typeof object[key] == 'number')
            return object[key]
        else if ((typeof object[key] == 'string') && object[key])
            return parseFloat (object[key])
    }
    return defaultValue
}

const safeString = (object, key, defaultValue = undefined) => {
    return (object && (key in object) && object[key]) ? object[key].toString () : defaultValue
}

const safeInteger = (object, key, defaultValue = undefined) => {
    return ((key in object) && object[key]) ? parseInt (object[key]) : defaultValue
}

const safeValue = (object, key, defaultValue = undefined) => {
    return ((key in object) && object[key]) ? object[key] : defaultValue
}

const uuid = a => a ?
    (a ^ Math.random () * 16 >> a / 4).toString (16) :
    ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid)

// See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion

function toFixed (x) { // avoid scientific notation for too large and too small numbers

    if (Math.abs (x) < 1.0) {
        const e = parseInt (x.toString ().split ('e-')[1])
        if (e) {
            x *= Math.pow (10, e-1)
            x = '0.' + (new Array (e)).join ('0') + x.toString ().substring (2)
        }
    } else {
        let e = parseInt (x.toString ().split ('+')[1])
        if (e > 20) {
            e -= 20
            x /= Math.pow (10, e)
            x += (new Array (e+1)).join ('0')
        }
    }
    return x
}

// See https://stackoverflow.com/questions/4912788/truncate-not-round-off-decimal-numbers-in-javascript for discussion

// > So, after all it turned out, rounding bugs will always haunt you, no matter how hard you try to compensate them.
// > Hence the problem should be attacked by representing numbers exactly in decimal notation.

const truncate_regExpCache = []
    , truncate = (num, precision = 0) => {
        num = toFixed (num)
        const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp("([-]*\\d+\\.\\d{" + precision + "})(\\d)"))
        const [,result] = num.toString ().match (re) || [null, num]
        return parseFloat (result)
    }

const precisionFromString = (string) => {
    const split = string.replace (/0+$/g, '').split ('.')
    return (split.length > 1) ? (split[1].length) : 0
}

const ordered = x => x // a stub to keep assoc keys in order, in JS it does nothing, it's mostly for Python

const aggregate = function (bidasks) {

    let result = {}

    bidasks.forEach (([ price, volume ]) => {
        result[price] = (result[price] || 0) + volume
    })

    return Object.keys (result).map (price => [
        parseFloat (price),
        parseFloat (result[price]),
    ])
}

//-----------------------------------------------------------------------------
// string ←→ binary ←→ base64 conversion routines

const stringToBinary = str => {
    const arr = new Uint8Array (str.length)
    for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt(i); }
    return CryptoJS.lib.WordArray.create (arr)
}

const stringToBase64 = string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64)
    , utf16ToBase64  = string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64)
    , base64ToBinary = string => CryptoJS.enc.Base64.parse (string)
    , base64ToString = string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8)
    , binaryToString = string => string

const binaryConcat = (...args) => args.reduce ((a, b) => a.concat (b))

// url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores
const urlencodeBase64 = base64string => base64string.replace (/[=]+$/, '')
                                                    .replace (/\+/g, '-')
                                                    .replace (/\//g, '_')

//-----------------------------------------------------------------------------
// cryptography

const hash = (request, hash = 'md5', digest = 'hex') => {
    const result = CryptoJS[hash.toUpperCase ()] (request)
    return (digest == 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)])
}

const hmac = (request, secret, hash = 'sha256', digest = 'hex') => {
    const encoding = (digest == 'binary') ? 'Latin1' : capitalize (digest)
    return CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret).toString (CryptoJS.enc[capitalize (encoding)])
}

//-----------------------------------------------------------------------------
// a JSON Web Token authentication method

const jwt = (request, secret, alg = 'HS256', hash = 'sha256') => {
    const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' })))
        , encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request)))
        , token = [ encodedHeader, encodedData ].join ('.')
        , signature = urlencodeBase64 (utf16ToBase64 (hmac (token, secret, hash, 'utf16')))
    return [ token, signature ].join ('.')
}

//-----------------------------------------------------------------------------

module.exports = {

    buildOHLCV,

    setTimeout_safe,

    // common utility functions

    sleep,
    timeout,
    capitalize,
    keysort,
    extend,
    deepExtend,
    omit,
    groupBy,
    indexBy,
    sortBy,
    filterBy,
    flatten,
    unique,
    pluck,
    urlencode,
    rawencode,
    sum,
    decimal,
    safeFloat,
    safeString,
    safeInteger,
    safeValue,
    ordered,
    aggregate,
    truncate,
    uuid,
    precisionFromString,

    // underscore aliases

    index_by: indexBy,
    sort_by: sortBy,
    group_by: groupBy,
    filter_by: filterBy,
    safe_float: safeFloat,
    safe_string: safeString,
    safe_integer: safeInteger,
    safe_value: safeValue,

    // crypto functions

    binaryConcat,
    stringToBinary,
    binaryToString,
    stringToBase64,
    utf16ToBase64,
    base64ToBinary,
    base64ToString,
    urlencodeBase64,
    hash,
    hmac,
    jwt,

    // json
    json:   JSON.stringify,
    unjson: JSON.parse
}
<commit_msg>OHLCV: removed trades counter<commit_after>"use strict";

//-----------------------------------------------------------------------------

const CryptoJS = require ('crypto-js')
    , qs       = require ('qs') // querystring

//-----------------------------------------------------------------------------

const { RequestTimeout } = require ('./errors')

//-----------------------------------------------------------------------------
// converts timeframe to ms
const parseTimeframe = (timeframe) => {
    let amount = timeframe.slice(0, -1);
    let unit = timeframe.slice(-1);
    let scale;
    switch (unit) {
        case 'm':
            scale = 60;
            break;
        case 'h':
            scale = 60 * 60;
            break;
        case 'd':
            scale = 60 * 60 * 24;
            break;
        case 'M':
            scale = 60 * 60 * 24 * 30;
            break;
        default:
            throw Error(`unknown timeframe unit: '${unit}'`);
    }
    return amount * scale;
}

// given a sorted arrays of trades (recent first) and a timeframe builds an array of OHLCV candles
const buildOHLCV = (trades, since = -Infinity, limits = Infinity, timeframe = '1m') => {
    let ms = parseTimeframe (timeframe) * 1000;
    let ohlcvs = [];
    const [timestamp, /* open */, high, low, close, volume, count] = [0, 1, 2, 3, 4, 5, 6];
    let oldest = Math.min(trades.length - 1, limits);

    for (let i = oldest; i >= 0; i--) {
        let trade = trades[i];
        if (trade.timestamp < since) continue;
        let openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M)
        let j = ohlcvs.length;

        if (j == 0 || openingTime >= ohlcvs[j-1][timestamp] + ms) {
            // moved to a new timeframe -> create a new candle from opening trade
            ohlcvs.push([openingTime, trade.price, trade.price, trade.price, trade.price, trade.amount]);
        } else {
            // still processing the same timeframe -> update opening trade
            ohlcvs[j-1][high] = Math.max (ohlcvs[j-1][high], trade.price);
            ohlcvs[j-1][low] = Math.min (ohlcvs[j-1][low], trade.price);
            ohlcvs[j-1][close] = trade.price;
            ohlcvs[j-1][volume] += trade.amount;
        } // if
    } // for
    return ohlcvs;
}

// utility helpers

const setTimeout_original = setTimeout

// setTimeout can fire earlier than specified, so we need to ensure it does not happen...

const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = Date.now () + ms) => {

    let clearInnerTimeout = () => {}
    let active = true

    let id = setTimeout (() => {
        active = true
        const rest = targetTime - Date.now ()
        if (rest > 0) {
            clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more
        } else {
            done ()
        }
    }, ms)

    return function clear () {
        if (active) {
            active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion)
            clearTimeout (id)
        }
        clearInnerTimeout ()
    }
}

const sleep = ms => new Promise (resolve => setTimeout_safe (resolve, ms))

const decimal = float => parseFloat (float).toString ()

const timeout = async (ms, promise) => {

    let clear = () => {}
    const timeout = new Promise (resolve => (clear = setTimeout_safe (resolve, ms)))

    try {
        return await Promise.race ([promise, timeout.then (() => { throw new RequestTimeout ('request timed out') })])
    } finally {
        clear () // fixes https://github.com/ccxt/ccxt/issues/749
    }
}

const capitalize = string => string.length ? (string.charAt (0).toUpperCase () + string.slice (1)) : string

const keysort = object => {
    const result = {}
    Object.keys (object).sort ().forEach (key => result[key] = object[key])
    return result
}

const extend = (...args) => Object.assign ({}, ...args)

const deepExtend = function (...args) {

    // if (args.length < 1)
    //     return args
    // else if (args.length < 2)
    //     return args[0]

    let result = undefined

    for (const arg of args) {

        if (arg && (typeof arg == 'object') && (arg.constructor === Object || !('constructor' in arg))) {

            if (typeof result != 'object') {
                result = {}
            }

            for (const key in arg) {
                result[key] = deepExtend (result[key], arg[key])
            }

        } else {

            result = arg
        }
    }

    return result
}

const omit = (object, ...args) => {
    const result = extend (object)
    for (const x of args) {
        if (typeof x === 'string') {
            delete result[x]
        } else if (Array.isArray (x)) {
            for (const k of x)
                delete result[k]
        }
    }
    return result
}

const groupBy = (array, key) => {
    const result = {}
    Object
        .values (array)
        .filter (entry => entry[key] != 'undefined')
        .forEach (entry => {
            if (typeof result[entry[key]] == 'undefined')
                result[entry[key]] = []
            result[entry[key]].push (entry)
        })
    return result
}

const filterBy = (array, key, value = undefined) => {
    if (value) {
        let grouped = groupBy (array, key)
        if (value in grouped)
            return grouped[value]
        return []
    }
    return array
}

const indexBy = (array, key) => {
    const result = {}
    Object
        .values (array)
        .filter (entry => entry[key] != 'undefined')
        .forEach (entry => {
            result[entry[key]] = entry
        })
    return result
}

const sortBy = (array, key, descending = false) => {
    descending = descending ? -1 : 1
    return array.sort ((a, b) => ((a[key] < b[key]) ? -descending : ((a[key] > b[key]) ? descending : 0)))
}

const flatten = (array, result = []) => {
    for (let i = 0, length = array.length; i < length; i++) {
        const value = array[i]
        if (Array.isArray (value)) {
            flatten (value, result)
        } else {
            result.push (value)
        }
    }
    return result
}

const unique = array => array.filter ((value, index, self) => (self.indexOf (value) == index))

const pluck = (array, key) => array
                                .filter (element => (typeof element[key] != 'undefined'))
                                .map (element => element[key])

const urlencode = object => qs.stringify (object)
const rawencode = object => qs.stringify (object, { encode: false })

const sum = (...args) => {
    const result = args.filter (arg => typeof arg != 'undefined')
    return (result.length > 0) ?
        result.reduce ((sum, value) => sum + value, 0) : undefined
}

const safeFloat = (object, key, defaultValue = undefined) => {
    if (key in object) {
        if (typeof object[key] == 'number')
            return object[key]
        else if ((typeof object[key] == 'string') && object[key])
            return parseFloat (object[key])
    }
    return defaultValue
}

const safeString = (object, key, defaultValue = undefined) => {
    return (object && (key in object) && object[key]) ? object[key].toString () : defaultValue
}

const safeInteger = (object, key, defaultValue = undefined) => {
    return ((key in object) && object[key]) ? parseInt (object[key]) : defaultValue
}

const safeValue = (object, key, defaultValue = undefined) => {
    return ((key in object) && object[key]) ? object[key] : defaultValue
}

const uuid = a => a ?
    (a ^ Math.random () * 16 >> a / 4).toString (16) :
    ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid)

// See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion

function toFixed (x) { // avoid scientific notation for too large and too small numbers

    if (Math.abs (x) < 1.0) {
        const e = parseInt (x.toString ().split ('e-')[1])
        if (e) {
            x *= Math.pow (10, e-1)
            x = '0.' + (new Array (e)).join ('0') + x.toString ().substring (2)
        }
    } else {
        let e = parseInt (x.toString ().split ('+')[1])
        if (e > 20) {
            e -= 20
            x /= Math.pow (10, e)
            x += (new Array (e+1)).join ('0')
        }
    }
    return x
}

// See https://stackoverflow.com/questions/4912788/truncate-not-round-off-decimal-numbers-in-javascript for discussion

// > So, after all it turned out, rounding bugs will always haunt you, no matter how hard you try to compensate them.
// > Hence the problem should be attacked by representing numbers exactly in decimal notation.

const truncate_regExpCache = []
    , truncate = (num, precision = 0) => {
        num = toFixed (num)
        const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp("([-]*\\d+\\.\\d{" + precision + "})(\\d)"))
        const [,result] = num.toString ().match (re) || [null, num]
        return parseFloat (result)
    }

const precisionFromString = (string) => {
    const split = string.replace (/0+$/g, '').split ('.')
    return (split.length > 1) ? (split[1].length) : 0
}

const ordered = x => x // a stub to keep assoc keys in order, in JS it does nothing, it's mostly for Python

const aggregate = function (bidasks) {

    let result = {}

    bidasks.forEach (([ price, volume ]) => {
        result[price] = (result[price] || 0) + volume
    })

    return Object.keys (result).map (price => [
        parseFloat (price),
        parseFloat (result[price]),
    ])
}

//-----------------------------------------------------------------------------
// string ←→ binary ←→ base64 conversion routines

const stringToBinary = str => {
    const arr = new Uint8Array (str.length)
    for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt(i); }
    return CryptoJS.lib.WordArray.create (arr)
}

const stringToBase64 = string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64)
    , utf16ToBase64  = string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64)
    , base64ToBinary = string => CryptoJS.enc.Base64.parse (string)
    , base64ToString = string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8)
    , binaryToString = string => string

const binaryConcat = (...args) => args.reduce ((a, b) => a.concat (b))

// url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores
const urlencodeBase64 = base64string => base64string.replace (/[=]+$/, '')
                                                    .replace (/\+/g, '-')
                                                    .replace (/\//g, '_')

//-----------------------------------------------------------------------------
// cryptography

const hash = (request, hash = 'md5', digest = 'hex') => {
    const result = CryptoJS[hash.toUpperCase ()] (request)
    return (digest == 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)])
}

const hmac = (request, secret, hash = 'sha256', digest = 'hex') => {
    const encoding = (digest == 'binary') ? 'Latin1' : capitalize (digest)
    return CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret).toString (CryptoJS.enc[capitalize (encoding)])
}

//-----------------------------------------------------------------------------
// a JSON Web Token authentication method

const jwt = (request, secret, alg = 'HS256', hash = 'sha256') => {
    const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' })))
        , encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request)))
        , token = [ encodedHeader, encodedData ].join ('.')
        , signature = urlencodeBase64 (utf16ToBase64 (hmac (token, secret, hash, 'utf16')))
    return [ token, signature ].join ('.')
}

//-----------------------------------------------------------------------------

module.exports = {

    buildOHLCV,

    setTimeout_safe,

    // common utility functions

    sleep,
    timeout,
    capitalize,
    keysort,
    extend,
    deepExtend,
    omit,
    groupBy,
    indexBy,
    sortBy,
    filterBy,
    flatten,
    unique,
    pluck,
    urlencode,
    rawencode,
    sum,
    decimal,
    safeFloat,
    safeString,
    safeInteger,
    safeValue,
    ordered,
    aggregate,
    truncate,
    uuid,
    precisionFromString,

    // underscore aliases

    index_by: indexBy,
    sort_by: sortBy,
    group_by: groupBy,
    filter_by: filterBy,
    safe_float: safeFloat,
    safe_string: safeString,
    safe_integer: safeInteger,
    safe_value: safeValue,

    // crypto functions

    binaryConcat,
    stringToBinary,
    binaryToString,
    stringToBase64,
    utf16ToBase64,
    base64ToBinary,
    base64ToString,
    urlencodeBase64,
    hash,
    hmac,
    jwt,

    // json
    json:   JSON.stringify,
    unjson: JSON.parse
}
<|endoftext|>


<commit_before>var test = require('tap').test
var crdt = require('..')

function Model () {
  if (!(this instanceof Model)) return new Model()
}

Model.prototype.set = function (key, val) {
  console.log(key, val)
  this['_'+key] = val
  return this
}

Model.prototype.get = function (key) {
  return this['_'+key]
}

test('dependant object', function (t) {

  var model = new Model()

  var obj = new crdt.Obj('key', model, function (key, val) {
    this.set(key,val)
  })

  obj.set({a: 1, b: 2})
  obj.flush()

  t.equal(model.get('a'), 1)
  t.equal(model.get('b'), 2)

  t.end()

})

test('dependant set', function (t) {
  var model = []

  var set = new crdt.GSet('set', model, function (key) {
    var obj = new crdt.Obj(key)
    model.push(obj.get())
    //now you may wish to setup event listeners
    //to update the model...
    return obj
  })

  set.set(['a'], {a: 1, b: 2})
  set.set(['b'], {x: 3, y: 3})
  set.flush()

  console.log(set.get())

  t.deepEqual([ {a: 1, b: 2},  {x: 3, y: 3} ], set.get())
  t.end()
})
<commit_msg>easier to use injection to sync objects<commit_after>var test = require('tap').test
var crdt = require('..')

function Model () {
  if (!(this instanceof Model)) return new Model()
}

Model.prototype.set = function (key, val) {
  console.log(key, val)
  this['_'+key] = val
  return this
}

Model.prototype.get = function (key) {
  return this['_'+key]
}

test('dependant object', function (t) {

  var model = new Model()
  var obj = new crdt.Obj('key')
  .on('update', function (changes) {
    for (var k in changes)
      model.set(k, changes[k])
  })

  obj.set({a: 1, b: 2})
  obj.flush()

  t.equal(model.get('a'), 1)
  t.equal(model.get('b'), 2)

  t.end()

})

test('dependant set', function (t) {
  var model = []

  var set = new crdt.GSet('set')
  .on('new', function (obj) {
    model.push(obj.get())
  })

  set.set(['a'], {a: 1, b: 2})
  set.set(['b'], {x: 3, y: 3})
  set.flush()

  t.deepEqual(model, [ {a: 1, b: 2},  {x: 3, y: 3} ])
  t.end()
})
<|endoftext|>