<commit_before>angular.module('n52.core.interface', [])
    .service('interfaceService', ['$http', '$q', 'interfaceServiceUtils', 'utils',
        function($http, $q, interfaceServiceUtils, utils) {

            getPlatforms = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'platforms/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getMobilePlatforms = function(id, apiUrl, params) {
                interfaceServiceUtils.extendParams(params, {
                    platformTypes: 'mobile'
                });
                return getPlatforms(id, apiUrl, params);
            };

            this.getStationaryPlatforms = function(id, apiUrl, params) {
                interfaceServiceUtils.extendParams(params, {
                    platformTypes: 'stationary'
                });
                return getPlatforms(id, apiUrl, params);
            };

            this.getServices = function(apiUrl, id) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'services/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs({
                            expanded: true
                        }))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getStations = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    this.getServices(apiUrl).then(response => {
                        if (isNaN(response[0].quantities.platforms)) {
                            $http.get(apiUrl + 'stations/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                                .then(response => {
                                    resolve(response.data);
                                }, error => {
                                    interfaceServiceUtils.errorCallback(error, reject);
                                });
                        } else {
                            interfaceServiceUtils.extendParams(params, {
                                expanded: true
                            });
                            this.getStationaryPlatforms(id, apiUrl, params)
                                .then(response => {
                                    if (isNaN(response.length)) {
                                        response.properties = {
                                            id: response.id
                                        };
                                    } else {
                                        response.forEach(entry => {
                                            entry.properties = {
                                                id: entry.id
                                            };
                                        });
                                    }
                                    resolve(response);
                                });
                        }
                    });
                });
            };

            this.getPhenomena = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'phenomena/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getCategories = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'categories/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getFeatures = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'features/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getProcedures = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'procedures/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.search = function(apiUrl, arrayParams) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'search', interfaceServiceUtils.createRequestConfigs({
                            q: arrayParams.join(',')
                        }))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getTimeseries = function(id, apiUrl, params) {
                if (angular.isUndefined(params))
                    params = {};
                params.expanded = true;
                params.force_latest_values = true;
                params.status_intervals = true;
                params.rendering_hints = true;
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            if (angular.isArray(response.data)) {
                                var array = [];
                                angular.forEach(response.data, ts => {
                                    array.push(interfaceServiceUtils.pimpTs(ts, apiUrl));
                                });
                                resolve(array);
                            } else {
                                resolve(interfaceServiceUtils.pimpTs(response.data, apiUrl));
                            }
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getExtras = function(tsId, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(tsId) + '/extras', interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getTsData = function(id, apiUrl, timespan, extendedData, generalizeData) {
                var params = {
                    timespan: utils.createRequestTimespan(timespan.start, timespan.end),
                    generalize: generalizeData || false,
                    expanded: true,
                    format: 'flot'
                };
                if (extendedData) {
                    angular.extend(params, extendedData);
                }
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(id) + "/getData", interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            interfaceServiceUtils.revampTimeseriesData(response.data, id);
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getDatasets = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'datasets/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getDatasetData = function(id, apiUrl, timespan, extendedParams) {
                var params = {
                    timespan: utils.createRequestTimespan(timespan.start, timespan.end)
                };
                if (extendedParams) {
                    angular.extend(params, extendedParams);
                }
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'datasets/' + interfaceServiceUtils.createIdString(id) + '/data', interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };
        }
    ])
    .service('interfaceServiceUtils', ['settingsService', 'utils', '$log',
        function(settingsService, utils, $log) {

            this.extendParams = function(params, extendParams) {
                if (!params) {
                    return extendParams;
                } else {
                    return angular.extend(params, extendParams);
                }
            };

            this.createRequestConfigs = function(params) {
                if (angular.isUndefined(params)) {
                    params = settingsService.additionalParameters;
                } else {
                    angular.extend(params, settingsService.additionalParameters);
                }
                return {
                    params: params,
                    cache: true
                };
            };

            this.errorCallback = function(error, reject) {
                if (error.data && error.data.userMessage)
                    $log.error(error.data.userMessage);
                reject(error);
            };

            this.createIdString = function(id) {
                return (id === null || angular.isUndefined(id) ? "" : id);
            };

            this.pimpTs = function(ts, url) {
                ts.apiUrl = url;
                ts.internalId = utils.createInternalId(ts.id, url);
                if (ts.uom === settingsService.undefinedUomString) {
                    delete ts.uom;
                }
                return ts;
            };

            this.revampTimeseriesData = function(data, id) {
                if (data[id].values.length > 0 && data[id].values[0].timestamp) {
                    var temp = [];
                    angular.forEach(data[id].values, entry => {
                        temp.push([entry.timestamp, entry.value]);
                    });
                    data[id].values = temp;
                }
            };

        }
    ]);
<commit_msg>change checking for new API version<commit_after>angular.module('n52.core.interface', [])
    .service('interfaceService', ['$http', '$q', 'interfaceServiceUtils', 'utils',
        function($http, $q, interfaceServiceUtils, utils) {

            isNewApi = function(apiUrl) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl).then(response => {
                        if (response && response.data && !isNaN(response.data.length)) {
                            response.data.forEach(entry => {
                                if (entry.id === 'platforms') {
                                    resolve(true);
                                }
                            });
                        }
                        resolve(false);
                    });
                });
            };

            getPlatforms = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'platforms/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getMobilePlatforms = function(id, apiUrl, params) {
                interfaceServiceUtils.extendParams(params, {
                    platformTypes: 'mobile'
                });
                return getPlatforms(id, apiUrl, params);
            };

            this.getStationaryPlatforms = function(id, apiUrl, params) {
                interfaceServiceUtils.extendParams(params, {
                    platformTypes: 'stationary'
                });
                return getPlatforms(id, apiUrl, params);
            };

            this.getServices = function(apiUrl, id) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'services/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs({
                            expanded: true
                        }))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getStations = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    isNewApi(apiUrl).then(isNew => {
                        if (isNew) {
                            interfaceServiceUtils.extendParams(params, {
                                expanded: true
                            });
                            this.getStationaryPlatforms(id, apiUrl, params)
                                .then(response => {
                                    if (isNaN(response.length)) {
                                        response.properties = {
                                            id: response.id
                                        };
                                    } else {
                                        response.forEach(entry => {
                                            entry.properties = {
                                                id: entry.id
                                            };
                                        });
                                    }
                                    resolve(response);
                                });
                        } else {
                            $http.get(apiUrl + 'stations/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                                .then(response => {
                                    resolve(response.data);
                                }, error => {
                                    interfaceServiceUtils.errorCallback(error, reject);
                                });
                        }
                    });
                });
            };

            this.getPhenomena = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'phenomena/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getCategories = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'categories/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getFeatures = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'features/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getProcedures = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'procedures/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.search = function(apiUrl, arrayParams) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'search', interfaceServiceUtils.createRequestConfigs({
                            q: arrayParams.join(',')
                        }))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getTimeseries = function(id, apiUrl, params) {
                if (angular.isUndefined(params))
                    params = {};
                params.expanded = true;
                params.force_latest_values = true;
                params.status_intervals = true;
                params.rendering_hints = true;
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            if (angular.isArray(response.data)) {
                                var array = [];
                                angular.forEach(response.data, ts => {
                                    array.push(interfaceServiceUtils.pimpTs(ts, apiUrl));
                                });
                                resolve(array);
                            } else {
                                resolve(interfaceServiceUtils.pimpTs(response.data, apiUrl));
                            }
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getExtras = function(tsId, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(tsId) + '/extras', interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getTsData = function(id, apiUrl, timespan, extendedData, generalizeData) {
                var params = {
                    timespan: utils.createRequestTimespan(timespan.start, timespan.end),
                    generalize: generalizeData || false,
                    expanded: true,
                    format: 'flot'
                };
                if (extendedData) {
                    angular.extend(params, extendedData);
                }
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'timeseries/' + interfaceServiceUtils.createIdString(id) + "/getData", interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            interfaceServiceUtils.revampTimeseriesData(response.data, id);
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getDatasets = function(id, apiUrl, params) {
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'datasets/' + interfaceServiceUtils.createIdString(id), interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };

            this.getDatasetData = function(id, apiUrl, timespan, extendedParams) {
                var params = {
                    timespan: utils.createRequestTimespan(timespan.start, timespan.end)
                };
                if (extendedParams) {
                    angular.extend(params, extendedParams);
                }
                return $q((resolve, reject) => {
                    $http.get(apiUrl + 'datasets/' + interfaceServiceUtils.createIdString(id) + '/data', interfaceServiceUtils.createRequestConfigs(params))
                        .then(response => {
                            resolve(response.data);
                        }, error => {
                            interfaceServiceUtils.errorCallback(error, reject);
                        });
                });
            };
        }
    ])
    .service('interfaceServiceUtils', ['settingsService', 'utils', '$log',
        function(settingsService, utils, $log) {

            this.extendParams = function(params, extendParams) {
                if (!params) {
                    return extendParams;
                } else {
                    return angular.extend(params, extendParams);
                }
            };

            this.createRequestConfigs = function(params) {
                if (angular.isUndefined(params)) {
                    params = settingsService.additionalParameters;
                } else {
                    angular.extend(params, settingsService.additionalParameters);
                }
                return {
                    params: params,
                    cache: true
                };
            };

            this.errorCallback = function(error, reject) {
                if (error.data && error.data.userMessage)
                    $log.error(error.data.userMessage);
                reject(error);
            };

            this.createIdString = function(id) {
                return (id === null || angular.isUndefined(id) ? "" : id);
            };

            this.pimpTs = function(ts, url) {
                ts.apiUrl = url;
                ts.internalId = utils.createInternalId(ts.id, url);
                if (ts.uom === settingsService.undefinedUomString) {
                    delete ts.uom;
                }
                return ts;
            };

            this.revampTimeseriesData = function(data, id) {
                if (data[id].values.length > 0 && data[id].values[0].timestamp) {
                    var temp = [];
                    angular.forEach(data[id].values, entry => {
                        temp.push([entry.timestamp, entry.value]);
                    });
                    data[id].values = temp;
                }
            };

        }
    ]);
<|endoftext|>


<commit_before>/*
 * Namespace: lb.base.template.i18n
 * Base Module for Internationalization Templates (i18n)
 *
 * This module defines methods that manipulate string and HTML templates and
 * replace parameters with values from dynamic data or language properties:
 *   o <getString(key[,data[,languageCode]]): string>
 *   o <filterHtml(htmlNode[,data[,languageCode]])>
 *
 * The module includes also individual filters to be applied to HTML nodes
 * and more generally helpers for use in i18n templates:
 *   o <filterByLanguage(languageCode): function>
 *   o <setLanguage(htmlElement)>
 *   o <withValuesFromDataOrLanguageProperties([data[,languageCode]]): function>
 *
 * Author:
 * Eric Bréchemier <legalbox@eric.brechemier.name>
 *
 * Copyright:
 * Legal-Box SAS (c) 2010-2011, All Rights Reserved
 *
 * License:
 * BSD License
 * http://creativecommons.org/licenses/BSD/
 *
 * Version:
 * 2011-04-08
 */
/*requires lb.base.template.js */
/*jslint white:false, plusplus:false */
/*global lb */
// preserve the module, if already loaded
lb.base.template.i18n = lb.base.template.i18n || (function() {
  // Builder of
  // Closure for lb.base.template.i18n module

  // Declare aliases

  var /*requires lb.base.object.js */
      has = lb.base.object.has,
      /*requires lb.base.type.js */
      is = lb.base.type.is,
      /*requires lb.base.dom.js */
      dom = lb.base.dom,
      ELEMENT_NODE = dom.ELEMENT_NODE,
      hasAttribute = dom.hasAttribute,
      /*requires lb.base.i18n.js */
      i18n = lb.base.i18n,
      /*requires lb.base.i18n.data.js */
      i18nData = i18n.data,
      getDefaultLanguageCode = i18n.data.getDefaultLanguageCode,
      get = i18nData.get,
      /*requires lb.base.template.js */
      template = lb.base.template,
      applyFilters = template.applyFilters,
      /*requires lb.base.template.string.js */
      stringTemplates = template.string,
      withValuesFrom = stringTemplates.withValuesFrom,
      replaceParamsInString = stringTemplates.replaceParams,
      /*requires lb.base.template.html.js */
      topDownParsing = template.html.topDownParsing,
      replaceParams = template.html.replaceParams,
      // hack to let JSLint accept mutual recursion
      withValuesFromDataOrLanguageProperties2;

  function getString(key,data,languageCode){
    // Function: getString(key[,data[,languageCode]]): string
    // Get a string computed by replacing data values in the most specific
    // value found for given key, used as a string template.
    //
    // When a function is found for the given key instead of a string template,
    // it is called with the key, data and language code, replaced with their
    // default values when omitted, and its return value is used as string
    // template instead.
    //
    // The parameters to replace are surrounded by '#' characters,
    // e.g. '#param-to-replace#'. No space can appear in the name;
    // only characters in the range [a-zA-Z0-9_\-\.] are allowed.
    //
    // Replacement values are provided as properties of the data object, with
    // the same name as the parameter:
    // | {
    // |   'param-to-replace': 'value'
    // | }
    //
    // Dotted parameter names, e.g. '#section.subsection.name#', are replaced
    // with values nested within sections and subsections of the data object:
    // | {
    // |   section: {
    // |     subsection: {
    // |       name: 'value'
    // |     }
    // |   }
    // | }
    //
    // In case a property is not found in the given data object, getString()
    // is called recursively to get the string value of the property for
    // parameter replacement.
    //
    // To summarize:
    //
    //   1. the key is looked up in language properties of selected language.
    //      A string is expected. If no value is found, null is returned.
    //      If a function is found, its return value is used instead
    //
    //   2. any parameter found in the string value is looked up, first in the
    //      given data, then in language properties of selected language, by
    //      calling getString() recursively. A string is expected for parameter
    //      replacement.
    //
    //   3. the resulting string, with parameters replaced, is returned.
    //
    // Parameters:
    //   key - string or array, the key identifiying the property:
    //         * a property name: 'name' (at top level of language properties)
    //         * a dotted name: 'section.subsection.name' (nested property)
    //         * an array: ['section','subsection','name'] (alternate form for
    //                                                      nested properties)
    //   data - object, optional, replacement values for parameters, which may
    //          be nested within sections and subsections. Defaults to an empty
    //          object, leaving all parameters unreplaced.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Returns:
    //   * string, the value of corresponding property, in the most specific
    //     language available, with parameters replaced with the value of
    //     corresponding properties found in data object or as a fallback in the
    //     language properties of the most specific language where available
    //   * or null if the property is not found
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    var value = get(key,languageCode);
    if ( !is(value) ){
      return value;
    }
    if ( is(value,'function') ){
      value = value(key,data,languageCode);
    }
    return replaceParamsInString(
      withValuesFromDataOrLanguageProperties2(data,languageCode)
    )(value);
  }

  function withValuesFromDataOrLanguageProperties(data,languageCode){
    // Function: withValuesFromDataOrLanguageProperties([data[,languageCode]]): function
    // Get a closure function that gets values of properties in data or, as a
    // fallback, from language properties available for given language code.
    //
    // This method is intended for use in combination with replaceParams(),
    // to get a filter to replace parameters in a string or an HTML template
    // with values from given data (first) or from language properties (then):
    // | var filter = replaceParams(
    // |   withValuesFromDataOrLanguageProperties(data,languageCode)
    // | );
    //
    // It calls getString() to retrieve values from language properties, which
    // are treated as string templates where parameters are replaced with
    // values from data or language properties.
    // See <getString(key[,data[,languageCode]]): string> for details.
    //
    // Parameter:
    //   data - object, optional, properties for parameter replacement, which
    //          may be nested in sections and subsections. Defaults to {}.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Returns:
    //   function, a closure wrapped around the given data and language code,
    //   with the following signature:
    //   | Function: getDataOrLanguagePropertiesValue(key): any
    //   | Get the value of a property, possibly nested, in wrapped data or,
    //   | as a fallback, from language properties of wrapped language code.
    //   |
    //   | Parameter:
    //   |   key - string, the key identifying a property, which may be:
    //   |     * a string refering to the name of a property: 'name'
    //   |     * a dotted string for a nested property: 'section.name'
    //   |
    //   | Returns:
    //   |   * any, the value of corresponding property, if found in data
    //   |   * any, the value of corresponding language property found in the
    //   |     most specific language available, as a fallback
    //   |   * null if neither is available
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    var getDataValue = withValuesFrom(data);
    return function(key){
      var value = getDataValue(key);
      if ( is(value) ){
        return value;
      } else {
        return getString(key,data,languageCode);
      }
    };
  }
  // hack to have JSLint allow mutual recursion
  withValuesFromDataOrLanguageProperties2 =
    withValuesFromDataOrLanguageProperties;

  function filterByLanguage(languageCode){
    // Function: filterByLanguage(languageCode): function
    // Return a filter function that removes HTML elements that do not match
    // the given language code.
    //
    // The signature of filter functions is filter(htmlElement).
    // In filter functions, the 'lang' attribute of HTML elements is compared
    // to the given language code. If the 'lang' is not an hyphenated substring
    // of the given language code (case-insensitive), the element is removed
    // from its parent. Nothing happens in case the element has no parent.
    //
    // No processing is done in filter functions to discover or set the
    // language of elements without a 'lang' attribute. These filters should be
    // used in conjunction with <setLanguage(htmlElement)>, which is intended
    // for this purpose.
    //
    // Parameter:
    //   languageCode - string, the language code identifying the language,
    //                  as defined in RFC5646 "Tags for Identifying Languages"
    //
    // Returns:
    //   function, a filter function for the given language code,
    //   or null if the language code was missing or not a string.
    if ( !is(languageCode,'string') ){
      return null;
    }

    return function(htmlElement){
      // anonymous(htmlElement)
      // Closure generated by filterByLanguage(languageCode).
      // Remove the HTML element from the tree if it does not match the language
      // in the context of this closure.
      //
      // Closure Context:
      //   languageCode - string, the language code identifying the language
      //                  used for filtering, as defined in RFC5646 "Tags for
      //                  Identifying Languages"
      //
      // Parameter:
      //   htmlElement - DOM Element, the DOM Element to check.
      //                 Other types of DOM nodes and other values are ignored.
      if ( !has(htmlElement) ||
           htmlElement.nodeType !== ELEMENT_NODE ||
           is(htmlElement,'parentNode',null) ){
        return;
      }

      if ( !i18n.contains(languageCode,htmlElement.lang) ){
        htmlElement.parentNode.removeChild(htmlElement);
      }
    };
  }

  function setLanguage(htmlElement){
    // Function: setLanguage(htmlElement)
    // Compute and set the language of given HTML element.
    //
    // This filter sets the 'lang' attribute of HTML elements explicitly.
    // When a 'lang' attribute is already specified, it is preserved. When it
    // is missing, the language inherited from ancestors is computed and set
    // to the 'lang' property/attribute.
    //
    // Parameter:
    //   htmlElement - DOM Element, a DOM element with or without parent.
    //                 Other types of DOM nodes and other values are ignored.
    //
    // Note:
    // In current implementation, only the 'lang' attribute is set, not the
    // 'xml:lang' attribute. This may be added in a future implementation.
    if ( !has(htmlElement) ||
         htmlElement.nodeType !== ELEMENT_NODE ){
      return;
    }

    if ( !hasAttribute(htmlElement,'lang') ){
      // Compute and set the language explicitly
      i18n.setLanguage(i18n.getLanguage(htmlElement), htmlElement);
    }
  }

  function filterHtml(htmlNode,data,languageCode){
    // Function: filterHtml(htmlNode[,data[,languageCode]])
    // Replace parameters and trim nodes based on html 'lang' attribute.
    //
    // This is a higher level filter, that applies a predefined selection of
    // filters to the given HTML node:
    //   * topDownParsing (from base HTML templates)
    //   * filterByLanguage
    //   * setLanguage
    //   * replaceParams (from base HTML templates)
    //
    // The given HTML node is modified in place. You should clone it beforehand
    // if you wish to preserve the original version.
    //
    // The HTML node is filtered according to the languageCode argument, or
    // if it is omitted, the language code of the application as returned by
    // getSelectedLanguage(). Multiple translations may be included
    // and only relevant translations will be kept, based on 'lang' attribute:
    // |

    // |   Hallo #user.firstName#!
    // |   Hi #user.firstName#!
    // |   Salut #user.firstName# !
    // |   こんにちは#user.lastName#!
    // |

    //
    // Filtering the HTML from the above example for the
    // language 'en-GB' would result in:
    // |

    // |   Hi #user.firstName#!
    // |

    //
    // The 'lang' attribute is inherited from ancestors, including ancestors
    // of the given HTML node, unless it has a 'lang' attribute itself. The
    // root element of the HTML node will be removed from its parent as well
    // if its language does not match the language code used for filtering.
    // Elements within the scope of the empty language '' or in the scope of
    // no language attribute are preserved by the filtering.
    //
    // Parameters of the form #param# found in text and attribute nodes are
    // replaced in the same way as using lb.base.i18n.data.getString():
    // - the parameter format is based on following regular expression:
    //   /#([a-zA-Z0-9_\-\.]+)#/g
    // - data object contains values for the parameters to replace, which may
    //   be nested:
    //   | {
    //   |   user: {
    //   |     firstName: 'Jane',
    //   |     lastName: 'Doe'
    //   |   }
    //   | }
    // - when no property is found in data for the replacement of a parameter,
    //   a lookup is performed in language properties instead
    //
    // After parameter replacement, the HTML node of the above example would
    // end up as:
    // |

    // |   Hi Jane!
    // |

    //
    // Parameters:
    //   htmlNode - DOM node, the node to apply the i18n filters to.
    //   data - object, optional, replacement values for parameters found in
    //          attributes and text of the HTML node. Defaults to an empty
    //          object.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Reference:
    //   Specifying the language of content: the lang attribute
    //   o http://www.w3.org/TR/html401/struct/dirlang.html#h-8.1
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    applyFilters(
      htmlNode,
      [
        topDownParsing,
        filterByLanguage(languageCode),
        setLanguage,
        replaceParams(
          withValuesFromDataOrLanguageProperties(data,languageCode)
        )
      ]
    );
  }

  return { // public API
    getString: getString,
    withValuesFromDataOrLanguageProperties:
      withValuesFromDataOrLanguageProperties,
    filterByLanguage: filterByLanguage,
    setLanguage: setLanguage,
    filterHtml: filterHtml
  };
}());
<commit_msg>Updated version<commit_after>/*
 * Namespace: lb.base.template.i18n
 * Base Module for Internationalization Templates (i18n)
 *
 * This module defines methods that manipulate string and HTML templates and
 * replace parameters with values from dynamic data or language properties:
 *   o <getString(key[,data[,languageCode]]): string>
 *   o <filterHtml(htmlNode[,data[,languageCode]])>
 *
 * The module includes also individual filters to be applied to HTML nodes
 * and more generally helpers for use in i18n templates:
 *   o <filterByLanguage(languageCode): function>
 *   o <setLanguage(htmlElement)>
 *   o <withValuesFromDataOrLanguageProperties([data[,languageCode]]): function>
 *
 * Author:
 * Eric Bréchemier <legalbox@eric.brechemier.name>
 *
 * Copyright:
 * Legal-Box SAS (c) 2010-2011, All Rights Reserved
 *
 * License:
 * BSD License
 * http://creativecommons.org/licenses/BSD/
 *
 * Version:
 * 2011-04-12
 */
/*requires lb.base.template.js */
/*jslint white:false, plusplus:false */
/*global lb */
// preserve the module, if already loaded
lb.base.template.i18n = lb.base.template.i18n || (function() {
  // Builder of
  // Closure for lb.base.template.i18n module

  // Declare aliases

  var /*requires lb.base.object.js */
      has = lb.base.object.has,
      /*requires lb.base.type.js */
      is = lb.base.type.is,
      /*requires lb.base.dom.js */
      dom = lb.base.dom,
      ELEMENT_NODE = dom.ELEMENT_NODE,
      hasAttribute = dom.hasAttribute,
      /*requires lb.base.i18n.js */
      i18n = lb.base.i18n,
      /*requires lb.base.i18n.data.js */
      i18nData = i18n.data,
      getDefaultLanguageCode = i18n.data.getDefaultLanguageCode,
      get = i18nData.get,
      /*requires lb.base.template.js */
      template = lb.base.template,
      applyFilters = template.applyFilters,
      /*requires lb.base.template.string.js */
      stringTemplates = template.string,
      withValuesFrom = stringTemplates.withValuesFrom,
      replaceParamsInString = stringTemplates.replaceParams,
      /*requires lb.base.template.html.js */
      topDownParsing = template.html.topDownParsing,
      replaceParams = template.html.replaceParams,
      // hack to let JSLint accept mutual recursion
      withValuesFromDataOrLanguageProperties2;

  function getString(key,data,languageCode){
    // Function: getString(key[,data[,languageCode]]): string
    // Get a string computed by replacing data values in the most specific
    // value found for given key, used as a string template.
    //
    // When a function is found for the given key instead of a string template,
    // it is called with the key, data and language code, replaced with their
    // default values when omitted, and its return value is used as string
    // template instead.
    //
    // The parameters to replace are surrounded by '#' characters,
    // e.g. '#param-to-replace#'. No space can appear in the name;
    // only characters in the range [a-zA-Z0-9_\-\.] are allowed.
    //
    // Replacement values are provided as properties of the data object, with
    // the same name as the parameter:
    // | {
    // |   'param-to-replace': 'value'
    // | }
    //
    // Dotted parameter names, e.g. '#section.subsection.name#', are replaced
    // with values nested within sections and subsections of the data object:
    // | {
    // |   section: {
    // |     subsection: {
    // |       name: 'value'
    // |     }
    // |   }
    // | }
    //
    // In case a property is not found in the given data object, getString()
    // is called recursively to get the string value of the property for
    // parameter replacement.
    //
    // To summarize:
    //
    //   1. the key is looked up in language properties of selected language.
    //      A string is expected. If no value is found, null is returned.
    //      If a function is found, its return value is used instead
    //
    //   2. any parameter found in the string value is looked up, first in the
    //      given data, then in language properties of selected language, by
    //      calling getString() recursively. A string is expected for parameter
    //      replacement.
    //
    //   3. the resulting string, with parameters replaced, is returned.
    //
    // Parameters:
    //   key - string or array, the key identifiying the property:
    //         * a property name: 'name' (at top level of language properties)
    //         * a dotted name: 'section.subsection.name' (nested property)
    //         * an array: ['section','subsection','name'] (alternate form for
    //                                                      nested properties)
    //   data - object, optional, replacement values for parameters, which may
    //          be nested within sections and subsections. Defaults to an empty
    //          object, leaving all parameters unreplaced.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Returns:
    //   * string, the value of corresponding property, in the most specific
    //     language available, with parameters replaced with the value of
    //     corresponding properties found in data object or as a fallback in the
    //     language properties of the most specific language where available
    //   * or null if the property is not found
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    var value = get(key,languageCode);
    if ( !is(value) ){
      return value;
    }
    if ( is(value,'function') ){
      value = value(key,data,languageCode);
    }
    return replaceParamsInString(
      withValuesFromDataOrLanguageProperties2(data,languageCode)
    )(value);
  }

  function withValuesFromDataOrLanguageProperties(data,languageCode){
    // Function: withValuesFromDataOrLanguageProperties([data[,languageCode]]): function
    // Get a closure function that gets values of properties in data or, as a
    // fallback, from language properties available for given language code.
    //
    // This method is intended for use in combination with replaceParams(),
    // to get a filter to replace parameters in a string or an HTML template
    // with values from given data (first) or from language properties (then):
    // | var filter = replaceParams(
    // |   withValuesFromDataOrLanguageProperties(data,languageCode)
    // | );
    //
    // It calls getString() to retrieve values from language properties, which
    // are treated as string templates where parameters are replaced with
    // values from data or language properties.
    // See <getString(key[,data[,languageCode]]): string> for details.
    //
    // Parameter:
    //   data - object, optional, properties for parameter replacement, which
    //          may be nested in sections and subsections. Defaults to {}.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Returns:
    //   function, a closure wrapped around the given data and language code,
    //   with the following signature:
    //   | Function: getDataOrLanguagePropertiesValue(key): any
    //   | Get the value of a property, possibly nested, in wrapped data or,
    //   | as a fallback, from language properties of wrapped language code.
    //   |
    //   | Parameter:
    //   |   key - string, the key identifying a property, which may be:
    //   |     * a string refering to the name of a property: 'name'
    //   |     * a dotted string for a nested property: 'section.name'
    //   |
    //   | Returns:
    //   |   * any, the value of corresponding property, if found in data
    //   |   * any, the value of corresponding language property found in the
    //   |     most specific language available, as a fallback
    //   |   * null if neither is available
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    var getDataValue = withValuesFrom(data);
    return function(key){
      var value = getDataValue(key);
      if ( is(value) ){
        return value;
      } else {
        return getString(key,data,languageCode);
      }
    };
  }
  // hack to have JSLint allow mutual recursion
  withValuesFromDataOrLanguageProperties2 =
    withValuesFromDataOrLanguageProperties;

  function filterByLanguage(languageCode){
    // Function: filterByLanguage(languageCode): function
    // Return a filter function that removes HTML elements that do not match
    // the given language code.
    //
    // The signature of filter functions is filter(htmlElement).
    // In filter functions, the 'lang' attribute of HTML elements is compared
    // to the given language code. If the 'lang' is not an hyphenated substring
    // of the given language code (case-insensitive), the element is removed
    // from its parent. Nothing happens in case the element has no parent.
    //
    // No processing is done in filter functions to discover or set the
    // language of elements without a 'lang' attribute. These filters should be
    // used in conjunction with <setLanguage(htmlElement)>, which is intended
    // for this purpose.
    //
    // Parameter:
    //   languageCode - string, the language code identifying the language,
    //                  as defined in RFC5646 "Tags for Identifying Languages"
    //
    // Returns:
    //   function, a filter function for the given language code,
    //   or null if the language code was missing or not a string.
    if ( !is(languageCode,'string') ){
      return null;
    }

    return function(htmlElement){
      // anonymous(htmlElement)
      // Closure generated by filterByLanguage(languageCode).
      // Remove the HTML element from the tree if it does not match the language
      // in the context of this closure.
      //
      // Closure Context:
      //   languageCode - string, the language code identifying the language
      //                  used for filtering, as defined in RFC5646 "Tags for
      //                  Identifying Languages"
      //
      // Parameter:
      //   htmlElement - DOM Element, the DOM Element to check.
      //                 Other types of DOM nodes and other values are ignored.
      if ( !has(htmlElement) ||
           htmlElement.nodeType !== ELEMENT_NODE ||
           is(htmlElement,'parentNode',null) ){
        return;
      }

      if ( !i18n.contains(languageCode,htmlElement.lang) ){
        htmlElement.parentNode.removeChild(htmlElement);
      }
    };
  }

  function setLanguage(htmlElement){
    // Function: setLanguage(htmlElement)
    // Compute and set the language of given HTML element.
    //
    // This filter sets the 'lang' attribute of HTML elements explicitly.
    // When a 'lang' attribute is already specified, it is preserved. When it
    // is missing, the language inherited from ancestors is computed and set
    // to the 'lang' property/attribute.
    //
    // Parameter:
    //   htmlElement - DOM Element, a DOM element with or without parent.
    //                 Other types of DOM nodes and other values are ignored.
    //
    // Note:
    // In current implementation, only the 'lang' attribute is set, not the
    // 'xml:lang' attribute. This may be added in a future implementation.
    if ( !has(htmlElement) ||
         htmlElement.nodeType !== ELEMENT_NODE ){
      return;
    }

    if ( !hasAttribute(htmlElement,'lang') ){
      // Compute and set the language explicitly
      i18n.setLanguage(i18n.getLanguage(htmlElement), htmlElement);
    }
  }

  function filterHtml(htmlNode,data,languageCode){
    // Function: filterHtml(htmlNode[,data[,languageCode]])
    // Replace parameters and trim nodes based on html 'lang' attribute.
    //
    // This is a higher level filter, that applies a predefined selection of
    // filters to the given HTML node:
    //   * topDownParsing (from base HTML templates)
    //   * filterByLanguage
    //   * setLanguage
    //   * replaceParams (from base HTML templates)
    //
    // The given HTML node is modified in place. You should clone it beforehand
    // if you wish to preserve the original version.
    //
    // The HTML node is filtered according to the languageCode argument, or
    // if it is omitted, the language code of the application as returned by
    // getSelectedLanguage(). Multiple translations may be included
    // and only relevant translations will be kept, based on 'lang' attribute:
    // |

    // |   Hallo #user.firstName#!
    // |   Hi #user.firstName#!
    // |   Salut #user.firstName# !
    // |   こんにちは#user.lastName#!
    // |

    //
    // Filtering the HTML from the above example for the
    // language 'en-GB' would result in:
    // |

    // |   Hi #user.firstName#!
    // |

    //
    // The 'lang' attribute is inherited from ancestors, including ancestors
    // of the given HTML node, unless it has a 'lang' attribute itself. The
    // root element of the HTML node will be removed from its parent as well
    // if its language does not match the language code used for filtering.
    // Elements within the scope of the empty language '' or in the scope of
    // no language attribute are preserved by the filtering.
    //
    // Parameters of the form #param# found in text and attribute nodes are
    // replaced in the same way as using lb.base.i18n.data.getString():
    // - the parameter format is based on following regular expression:
    //   /#([a-zA-Z0-9_\-\.]+)#/g
    // - data object contains values for the parameters to replace, which may
    //   be nested:
    //   | {
    //   |   user: {
    //   |     firstName: 'Jane',
    //   |     lastName: 'Doe'
    //   |   }
    //   | }
    // - when no property is found in data for the replacement of a parameter,
    //   a lookup is performed in language properties instead
    //
    // After parameter replacement, the HTML node of the above example would
    // end up as:
    // |

    // |   Hi Jane!
    // |

    //
    // Parameters:
    //   htmlNode - DOM node, the node to apply the i18n filters to.
    //   data - object, optional, replacement values for parameters found in
    //          attributes and text of the HTML node. Defaults to an empty
    //          object.
    //   languageCode - string, optional, language code for lookup in a
    //                  specific language. Defaults to the value of
    //                  <lb.base.i18n.data.getDefaultLanguageCode(): string>.
    //
    // Reference:
    //   Specifying the language of content: the lang attribute
    //   o http://www.w3.org/TR/html401/struct/dirlang.html#h-8.1
    data = has(data)? data : {};
    if ( !is(languageCode,'string') ){
      languageCode = getDefaultLanguageCode();
    }

    applyFilters(
      htmlNode,
      [
        topDownParsing,
        filterByLanguage(languageCode),
        setLanguage,
        replaceParams(
          withValuesFromDataOrLanguageProperties(data,languageCode)
        )
      ]
    );
  }

  return { // public API
    getString: getString,
    withValuesFromDataOrLanguageProperties:
      withValuesFromDataOrLanguageProperties,
    filterByLanguage: filterByLanguage,
    setLanguage: setLanguage,
    filterHtml: filterHtml
  };
}());
<|endoftext|>


<commit_before>const departments = {
	AR: 'ART',
	AS: 'ASIAN',
	BI: 'BIO',
	CH: 'CHEM',
	CS: 'CSCI',
	EC: 'ECON',
	ES: 'ENVST',
	HI: 'HIST',
	MU: 'MUSIC',
	PH: 'PHIL',
	PS: 'PSCI',
	RE: 'REL',
	SA: 'SOAN',
}

export default function expandDepartment(dept) {
	if (dept in departments) {
		return departments[dept]
	}
	else {
		throw new TypeError(`expandDepartment(): "${dept}" is not a valid department shorthand`)
	}
}
<commit_msg>switch to a Map in expandDepartment<commit_after>const departments = new Map(Object.entries({
	AR: 'ART',
	AS: 'ASIAN',
	BI: 'BIO',
	CH: 'CHEM',
	CS: 'CSCI',
	EC: 'ECON',
	ES: 'ENVST',
	HI: 'HIST',
	MU: 'MUSIC',
	PH: 'PHIL',
	PS: 'PSCI',
	RE: 'REL',
	SA: 'SOAN',
}))

export default function expandDepartment(dept) {
	if (departments.has(dept)) {
		return departments.get(dept)
	}
	else {
		throw new TypeError(`expandDepartment(): "${dept}" is not a valid department shorthand`)
	}
}
<|endoftext|>


<commit_before>function debuglog(thing) {
    // console.log(thing);
}

// encapsulation, what's that?
// note: this is NOT pure RPC!  When generating the patch,
// the produced data will be sent back here asynchronously.
function SSync() {
    var self = this;

    self.idCtr = 0;
    self.worker = new Worker("all.js");
    self.callbacks = {};

    self.worker.onmessage = function(ev) {
        debuglog("Recieved " + ev.data.command + " for " + ev.data.id);
        debuglog(ev.data);
        if(!self.callbacks[ev.data.id]) {
            debuglog("There was no callback!");
        } else {
            self.callbacks[ev.data.id](ev.data);
        }
    }

    self.send = function(msg) {
        debuglog(msg);
        self.worker.postMessage(msg);
    }

    self.kill = function(job) {
        debuglog("Killing " + job);
        delete self.callbacks[job];
        self.send({id:job, command: "TERM"});
    }
}

var ssync = new SSync();

function AsyncBox(name, size) {
    if(!size) size = 1;
    var self = this;
    self.waitingForEmpty = [];
    self.waitingForFull = [];
    self.values = [];
    self.closed = false;

    var putIntl = function(v, continuation, blocked) {
        if(self.closed) {
            continuation({ok : false});
        } else if(self.values.length >= size) {
            debuglog("PUT: blocking in " + name);
            self.waitingForEmpty.push(function() {
                debuglog("PUT: unblocked in " + name);
                putIntl(v, continuation, true);
            });
        } else {
            self.values.push(v);
            if(self.waitingForFull.length != 0) {
                debuglog("PUT: unblocking a thread in " + name);
                var waiterCont = self.waitingForFull.shift();
                waiterCont();
            }
            debuglog("PUT: successfully put into " + name);
            continuation({ ok : true, blocked : blocked });
        }
    }

    self.put = function(v, continuation) {
        putIntl(v, continuation, false);
    }

    var takeIntl = function(continuation, blocked) {
        if(self.closed) {
            continuation({ ok : false });
        } else if(self.values.length != 0) {
            var result = self.values.shift();
            if(self.waitingForEmpty.length != 0) {
                debuglog("take: unblocking a thread in " + name);
                var waiterCont = self.waitingForEmpty.shift();
                waiterCont();
            }
            debuglog("take: successfully took from " + name);
            continuation({ ok : true, blocked : blocked, result : result });
        } else {
            debuglog("take: blocking in " + name);
            self.waitingForFull.push(function() {
                debuglog("take: unblocked in " + name);
                takeIntl(continuation, true);
            });
        }
    }

    self.take = function(continuation) {
        takeIntl(continuation, false);
    }

    self.close = function() {
        self.closed = true;
        while(self.waitingForEmpty.length != 0) {
            var waiterCont = self.waitingForEmpty.shift();
            waiterCont();
        }
        while(self.waitingForFull.length != 0) {
            var waiterCont = self.waitingForFull.shift();
            waiterCont();
        }
    }
}

function parseSig2(job, oncomplete) {
    return function(result) {
        if(result.command == "SIGCOMPLETE") {
            debuglog("Parsed sig for job " + job);
            ssync.callbacks[job] = undefined;
            oncomplete({ok : true, continuation: job});
        } else {
            ssync.kill(job)
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig1(job, oncomplete) {
    return function(result) {
        if(result.command == "CHUNKACCEPTED") {
            debuglog("Sent sig for job " + job);
            ssync.callbacks[job] = parseSig2(job, oncomplete);
            ssync.send({id: job, command: "DONE"})
        } else {
            ssync.kill(job)
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig0(job, bs, oncomplete) {
    return function(result) {
        if(result.command == "CREATED") {
            debuglog("Created job " + job + " with " + bs.length + " bytes");
            ssync.callbacks[job] = parseSig1(job, oncomplete);
            ssync.send({id: job, command: "DATA", bytes: bs});
        } else {
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig(targetSize, bs, oncomplete) {
    var job = ssync.idCtr++;
    debuglog("Creating job " + job + " with " + bs.length + " bytes");
    ssync.callbacks[job] = parseSig0(job, bs, oncomplete);
    ssync.send({id: job, command: "NEW", size: targetSize});
}

function fakeSig(oncomplete) {
    var job = ssync.idCtr++;
    debuglog("Creating job " + job);
    ssync.callbacks[job] = function(result) {
        if(result.command == "CREATED") {
            oncomplete({ok: true, continuation : job})
        } else {
            oncomplete({ok: false, error: result});
        }
    };
    ssync.send({id: job, command: "NEWFAKESIG"});
}

function msg(s) {
    $("#message").text(s);
}

function ds_status(s) {
    $("#status").text(s);
}

function ds_log(s) {
    $("#log").text(s); // append(document.createTextNode(s));
}

function readFile_progress(state) {
    state.progress(state.currentOffset, state.endOffset, state.uploadedSize);
}

function readFile_dataFromFile(state) {
    return function(e) {
        state.fromReader.put({done:false, bytes: e.target.result}, function(res) {
            debuglog("putting bytes in fromReader blocked: " + res.blocked);
            if(res.ok) {
                readFile_readNextChunk(state);
            } else {
                // process was terminated; just stop doing things
            }
        });
    }
}

function readFile_readNextChunk(state) {
    if(state.currentOffset >= state.endOffset) {
        state.fromReader.put({done:true}, function(res) {
            debuglog("putting done in fromReader blocked: " + res.blocked);
        });
        readFile_progress(state);
    } else {
        var offset = state.currentOffset;
        state.currentOffset = Math.min(state.endOffset, offset + state.chunkSize);
        state.reader.readAsArrayBuffer(state.file.slice(offset, state.currentOffset));
        readFile_progress(state);
    }
}

function readFile_awaitChunkFromFile(state) {
    return function(res) {
        debuglog("take from fromReader blocked: " + res.blocked);
        if(res.ok) {
            debuglog("***********************");
            debuglog(res);
            if(res.result.done) {
                ssync.send({id: state.job, command: "DONE"});
            } else {
                ssync.send({id: state.job, command: "DATA", bytes: new Uint8Array(res.result.bytes)});
            }
            // do NOT take again; the CHUNKACCEPTED response will do it.
        } else {
            ssync.kill(state.job);
        }
    }
}

function readFile_messageFromWorker(state) {
    return function(result) {
        debuglog("received " + result.command);
        if(result.command == "CHUNKACCEPTED") {
            state.fromReader.take(readFile_awaitChunkFromFile(state));
        } else if(result.command == "CHUNK") {
            state.fromWorker.put({ done: false, chunk : result.bytes }, function(res) {
                debuglog("putting chunk in fromWorker blocked: " + res.blocked);
                if(res.ok) {
                    // ok good
                }
            });
        } else if(result.command == "COMPLETE") {
            state.fromWorker.put({ done: true }, function(res) {
                debuglog("putting done in fromWorker blocked: " + res.blocked);
                ssync.kill(state.job);
            });
        } else {
            debuglog("error: unexpected message from worker: " + result.command);
            readFile_close(state);
        }
    }
}

function readFile_dataFromWorker(state) {
    return function(res) {
        debuglog("take from fromWorker blocked: " + res.blocked);
        if(res.ok) {
            if(res.result.done) {
                state.done(state.uploadedChunks, state.uploadedSize);
            } else {
                // TODO: this needs to retry on errors
                var chunklen = res.result.chunk.length;
                $.ajax({
                    dataType: 'json',
                    type: 'POST',
                    url: state.uploadUrl,
                    data : res.result.chunk,
                    processData: false,
                    contentType: 'application/octet-stream',
                    headers : state.authHeaders
                }).done(function(response) {
                    // TODO: this send needs to happen in all error cases too (after any retrying is done!)
                    ssync.send({ id : state.job, command : "CHUNKACCEPTED" });
                    state.uploadedChunks.push(response.blobId);
                    state.uploadedSize += chunklen;
                    readFile_progress(state);
                    state.fromWorker.take(readFile_dataFromWorker(state));
                })
            }
        } else {
            ssync.kill(state.job);
        }
    }
}

function readFile_close(state) {
    ssync.kill(state.job);
    state.fromReader.close();
    state.fromWorker.close();
}

function readFile(job, file, progress, chunk, done, uploadUrl, authHeaders) {
    // ok, here's where the control flow gets a little hairy.
    // We have three asynchronous processes -- files reading,
    // chunks processing and data uploading -- and we want to
    // manage all of them.  But we also want to exploit the
    // asynchronicity!  So we'll do anything only if we're not
    // already doing such a thing.

    var state = {
        // constant data
        job: job,
        reader: new FileReader(),
        file: file,
        chunkSize: 10240, // size of each read from the file
        uploadUrl: uploadUrl,
        authHeaders: authHeaders,

        // callbacks
        progress: progress,
        chunk: chunk,
        done: done,

        // variableData
        currentOffset: 0,
        endOffset: file.size,
        uploadedChunks: [],
        uploadedSize: 0,

        // async status tracking
        fromReader : new AsyncBox("reader"),
        fromWorker : new AsyncBox("worker")
    };
    ssync.callbacks[job] = readFile_messageFromWorker(state);
    state.reader.onload = readFile_dataFromFile(state);
    state.fromReader.take(readFile_awaitChunkFromFile(state));
    state.fromWorker.take(readFile_dataFromWorker(state));
    readFile_readNextChunk(state);
}

function watchUrl(url, authHeaders, withResult, finished) {
    $.ajax({ type: "GET", url: url, dataType: "text", headers: authHeaders }).done(function(resp) {
        withResult(resp);
        if(!finished(resp)) {
            setTimeout(function() { watchUrl(url, authHeaders, withResult, finished); }, 6000);
        }
    });
}

function watchProgress(statusUrl, logUrl, authHeaders) {
    var statusDone = false;
    var logDone = false;
    watchUrl(statusUrl, authHeaders, ds_status, function(s) {
        statusDone = (s.indexOf("SUCCESS:") === 0) || (s.indexOf("FAILURE:") === 0);
        return statusDone;
    });
    watchUrl(logUrl, authHeaders, ds_log, function(s) {
        var result = logDone;
        logDone = statusDone;
        return result;
    });
}

function findSig(base, authHeaders, cont) {
    $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
        if(resp.length == 0) {
            cont(null);
        } else {
            var year = resp[resp.length - 1];
            base += year;
            $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                var month = resp[resp.length - 1];
                base += month;
                $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                    var day = resp[resp.length - 1];
                    base += day + "signatures/";
                    $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                        var result = base + resp[resp.length - 1];
                        cont(result);
                    });
                });
            });
        }
    });
}

function go() {
    // $("#go").prop("disabled", true);
    $("#output").empty();
    var file = $("#input")[0].files[0];
    var ff = $("#fourfour").val();
    var datasyncBase = "/datasync"
    var versionUrl = datasyncBase + "/version.json"
    var datasetBase = datasyncBase + "/id/" + ff
    var serverSigPathBase = "/datasync/id/" + ff + "/completed/";
    var uploadUrl = datasetBase
    var commitUrl = datasetBase + "/commit"
    var statusUrlBase = datasetBase + "/status"
    var logUrlBase = datasetBase + "/log"
    var appToken = $("#apptoken").val();
    var authHeaders = {};
    if($.cookie('socrata-csrf-token')) {
        authHeaders["x-csrf-token"] = encodeURIComponent($.cookie('socrata-csrf-token'));
    }
    findSig(serverSigPathBase, authHeaders, function(sigurl) {
        msg("Retrieving target file size...");
        $.getJSON(versionUrl, function(j) {
            var blockSize = j['max-block-size'];

            var generatePatch = function(job) {
                msg("Yay parsed! -- next up: doing the file");
                var progress = function(read, total, sent) {
                    msg("read " + Math.round(100000*read/total)/1000 + "%; sent " + sent + " bytes");
                }
                var chunk = function(c) {
                    out(c);
                }
                var finished = function(chunks, size) {
                    msg("Uploaded CSV (" + file.size + " bytes; required " + size + " bytes to be uploaded)");
                    var commitSpec = { filename : file.name + ".sdiff", chunks : chunks, relativeTo: sigurl, expectedSize : size, control : { csv : { action: "replace" } } };
                    // TODO errors
                    $.ajax({ type: "POST", url: commitUrl, data: JSON.stringify(commitSpec), dataType: "json", headers: authHeaders }).done(function(resp) {
                        watchProgress(statusUrlBase + "/" + resp.jobId + ".txt", logUrlBase + "/" + resp.jobId + ".txt", authHeaders);
                    })
                }
                if(appToken) authHeaders['X-App-Token'] = appToken;
                readFile(job, file, progress, chunk, finished, uploadUrl, authHeaders);
            }

            if(sigurl == null) {
                msg("No signature available; using empty signature")
                fakeSig(function(result) {
                    if(result.ok) {
                        generatePatch(result.continuation);
                    } else {
                        msg(":((((( -- " + JSON.stringify(result.error));
                    }
                });
            } else {
                msg("Fetching signature from " + sigurl + " ...");
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function() {
                    if(this.readyState == 4 && this.status == 200) {
                        msg("Parsing signature...");
                        // wheeeeee!
                        var reader = new FileReader();
                        reader.addEventListener("loadend", function() {
                            parseSig(blockSize, new Uint8Array(reader.result), function(result) {
                                if(result.ok) {
                                    generatePatch(result.continuation)
                                } else {
                                    msg(":((((( -- " + JSON.stringify(result.error));
                                }
                            });
                        });
                        reader.readAsArrayBuffer(this.response);
                    }
                }
                xhr.open('GET', sigurl, true);
                xhr.responseType = 'blob';
                if(appToken) xhr.setRequestHeader('X-App-Token', appToken);
                xhr.send();
            }
        }).fail(function() {
            msg("error reading target size")
        })
    });
}
<commit_msg>Set the target size when using a fake signature<commit_after>function debuglog(thing) {
    // console.log(thing);
}

// encapsulation, what's that?
// note: this is NOT pure RPC!  When generating the patch,
// the produced data will be sent back here asynchronously.
function SSync() {
    var self = this;

    self.idCtr = 0;
    self.worker = new Worker("all.js");
    self.callbacks = {};

    self.worker.onmessage = function(ev) {
        debuglog("Recieved " + ev.data.command + " for " + ev.data.id);
        debuglog(ev.data);
        if(!self.callbacks[ev.data.id]) {
            debuglog("There was no callback!");
        } else {
            self.callbacks[ev.data.id](ev.data);
        }
    }

    self.send = function(msg) {
        debuglog(msg);
        self.worker.postMessage(msg);
    }

    self.kill = function(job) {
        debuglog("Killing " + job);
        delete self.callbacks[job];
        self.send({id:job, command: "TERM"});
    }
}

var ssync = new SSync();

function AsyncBox(name, size) {
    if(!size) size = 1;
    var self = this;
    self.waitingForEmpty = [];
    self.waitingForFull = [];
    self.values = [];
    self.closed = false;

    var putIntl = function(v, continuation, blocked) {
        if(self.closed) {
            continuation({ok : false});
        } else if(self.values.length >= size) {
            debuglog("PUT: blocking in " + name);
            self.waitingForEmpty.push(function() {
                debuglog("PUT: unblocked in " + name);
                putIntl(v, continuation, true);
            });
        } else {
            self.values.push(v);
            if(self.waitingForFull.length != 0) {
                debuglog("PUT: unblocking a thread in " + name);
                var waiterCont = self.waitingForFull.shift();
                waiterCont();
            }
            debuglog("PUT: successfully put into " + name);
            continuation({ ok : true, blocked : blocked });
        }
    }

    self.put = function(v, continuation) {
        putIntl(v, continuation, false);
    }

    var takeIntl = function(continuation, blocked) {
        if(self.closed) {
            continuation({ ok : false });
        } else if(self.values.length != 0) {
            var result = self.values.shift();
            if(self.waitingForEmpty.length != 0) {
                debuglog("take: unblocking a thread in " + name);
                var waiterCont = self.waitingForEmpty.shift();
                waiterCont();
            }
            debuglog("take: successfully took from " + name);
            continuation({ ok : true, blocked : blocked, result : result });
        } else {
            debuglog("take: blocking in " + name);
            self.waitingForFull.push(function() {
                debuglog("take: unblocked in " + name);
                takeIntl(continuation, true);
            });
        }
    }

    self.take = function(continuation) {
        takeIntl(continuation, false);
    }

    self.close = function() {
        self.closed = true;
        while(self.waitingForEmpty.length != 0) {
            var waiterCont = self.waitingForEmpty.shift();
            waiterCont();
        }
        while(self.waitingForFull.length != 0) {
            var waiterCont = self.waitingForFull.shift();
            waiterCont();
        }
    }
}

function parseSig2(job, oncomplete) {
    return function(result) {
        if(result.command == "SIGCOMPLETE") {
            debuglog("Parsed sig for job " + job);
            ssync.callbacks[job] = undefined;
            oncomplete({ok : true, continuation: job});
        } else {
            ssync.kill(job)
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig1(job, oncomplete) {
    return function(result) {
        if(result.command == "CHUNKACCEPTED") {
            debuglog("Sent sig for job " + job);
            ssync.callbacks[job] = parseSig2(job, oncomplete);
            ssync.send({id: job, command: "DONE"})
        } else {
            ssync.kill(job)
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig0(job, bs, oncomplete) {
    return function(result) {
        if(result.command == "CREATED") {
            debuglog("Created job " + job + " with " + bs.length + " bytes");
            ssync.callbacks[job] = parseSig1(job, oncomplete);
            ssync.send({id: job, command: "DATA", bytes: bs});
        } else {
            oncomplete({ok : false, error: result})
        }
    }
}

function parseSig(targetSize, bs, oncomplete) {
    var job = ssync.idCtr++;
    debuglog("Creating job " + job + " with " + bs.length + " bytes");
    ssync.callbacks[job] = parseSig0(job, bs, oncomplete);
    ssync.send({id: job, command: "NEW", size: targetSize});
}

function fakeSig(targetSize, oncomplete) {
    var job = ssync.idCtr++;
    debuglog("Creating job " + job);
    ssync.callbacks[job] = function(result) {
        if(result.command == "CREATED") {
            oncomplete({ok: true, continuation : job})
        } else {
            oncomplete({ok: false, error: result});
        }
    };
    ssync.send({id: job, command: "NEWFAKESIG", size: targetSize});
}

function msg(s) {
    $("#message").text(s);
}

function ds_status(s) {
    $("#status").text(s);
}

function ds_log(s) {
    $("#log").text(s); // append(document.createTextNode(s));
}

function readFile_progress(state) {
    state.progress(state.currentOffset, state.endOffset, state.uploadedSize);
}

function readFile_dataFromFile(state) {
    return function(e) {
        state.fromReader.put({done:false, bytes: e.target.result}, function(res) {
            debuglog("putting bytes in fromReader blocked: " + res.blocked);
            if(res.ok) {
                readFile_readNextChunk(state);
            } else {
                // process was terminated; just stop doing things
            }
        });
    }
}

function readFile_readNextChunk(state) {
    if(state.currentOffset >= state.endOffset) {
        state.fromReader.put({done:true}, function(res) {
            debuglog("putting done in fromReader blocked: " + res.blocked);
        });
        readFile_progress(state);
    } else {
        var offset = state.currentOffset;
        state.currentOffset = Math.min(state.endOffset, offset + state.chunkSize);
        state.reader.readAsArrayBuffer(state.file.slice(offset, state.currentOffset));
        readFile_progress(state);
    }
}

function readFile_awaitChunkFromFile(state) {
    return function(res) {
        debuglog("take from fromReader blocked: " + res.blocked);
        if(res.ok) {
            debuglog("***********************");
            debuglog(res);
            if(res.result.done) {
                ssync.send({id: state.job, command: "DONE"});
            } else {
                ssync.send({id: state.job, command: "DATA", bytes: new Uint8Array(res.result.bytes)});
            }
            // do NOT take again; the CHUNKACCEPTED response will do it.
        } else {
            ssync.kill(state.job);
        }
    }
}

function readFile_messageFromWorker(state) {
    return function(result) {
        debuglog("received " + result.command);
        if(result.command == "CHUNKACCEPTED") {
            state.fromReader.take(readFile_awaitChunkFromFile(state));
        } else if(result.command == "CHUNK") {
            state.fromWorker.put({ done: false, chunk : result.bytes }, function(res) {
                debuglog("putting chunk in fromWorker blocked: " + res.blocked);
                if(res.ok) {
                    // ok good
                }
            });
        } else if(result.command == "COMPLETE") {
            state.fromWorker.put({ done: true }, function(res) {
                debuglog("putting done in fromWorker blocked: " + res.blocked);
                ssync.kill(state.job);
            });
        } else {
            debuglog("error: unexpected message from worker: " + result.command);
            readFile_close(state);
        }
    }
}

function readFile_dataFromWorker(state) {
    return function(res) {
        debuglog("take from fromWorker blocked: " + res.blocked);
        if(res.ok) {
            if(res.result.done) {
                state.done(state.uploadedChunks, state.uploadedSize);
            } else {
                // TODO: this needs to retry on errors
                var chunklen = res.result.chunk.length;
                $.ajax({
                    dataType: 'json',
                    type: 'POST',
                    url: state.uploadUrl,
                    data : res.result.chunk,
                    processData: false,
                    contentType: 'application/octet-stream',
                    headers : state.authHeaders
                }).done(function(response) {
                    // TODO: this send needs to happen in all error cases too (after any retrying is done!)
                    ssync.send({ id : state.job, command : "CHUNKACCEPTED" });
                    state.uploadedChunks.push(response.blobId);
                    state.uploadedSize += chunklen;
                    readFile_progress(state);
                    state.fromWorker.take(readFile_dataFromWorker(state));
                })
            }
        } else {
            ssync.kill(state.job);
        }
    }
}

function readFile_close(state) {
    ssync.kill(state.job);
    state.fromReader.close();
    state.fromWorker.close();
}

function readFile(job, file, progress, chunk, done, uploadUrl, authHeaders) {
    // ok, here's where the control flow gets a little hairy.
    // We have three asynchronous processes -- files reading,
    // chunks processing and data uploading -- and we want to
    // manage all of them.  But we also want to exploit the
    // asynchronicity!  So we'll do anything only if we're not
    // already doing such a thing.

    var state = {
        // constant data
        job: job,
        reader: new FileReader(),
        file: file,
        chunkSize: 10240, // size of each read from the file
        uploadUrl: uploadUrl,
        authHeaders: authHeaders,

        // callbacks
        progress: progress,
        chunk: chunk,
        done: done,

        // variableData
        currentOffset: 0,
        endOffset: file.size,
        uploadedChunks: [],
        uploadedSize: 0,

        // async status tracking
        fromReader : new AsyncBox("reader"),
        fromWorker : new AsyncBox("worker")
    };
    ssync.callbacks[job] = readFile_messageFromWorker(state);
    state.reader.onload = readFile_dataFromFile(state);
    state.fromReader.take(readFile_awaitChunkFromFile(state));
    state.fromWorker.take(readFile_dataFromWorker(state));
    readFile_readNextChunk(state);
}

function watchUrl(url, authHeaders, withResult, finished) {
    $.ajax({ type: "GET", url: url, dataType: "text", headers: authHeaders }).done(function(resp) {
        withResult(resp);
        if(!finished(resp)) {
            setTimeout(function() { watchUrl(url, authHeaders, withResult, finished); }, 6000);
        }
    });
}

function watchProgress(statusUrl, logUrl, authHeaders) {
    var statusDone = false;
    var logDone = false;
    watchUrl(statusUrl, authHeaders, ds_status, function(s) {
        statusDone = (s.indexOf("SUCCESS:") === 0) || (s.indexOf("FAILURE:") === 0);
        return statusDone;
    });
    watchUrl(logUrl, authHeaders, ds_log, function(s) {
        var result = logDone;
        logDone = statusDone;
        return result;
    });
}

function findSig(base, authHeaders, cont) {
    $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
        if(resp.length == 0) {
            cont(null);
        } else {
            var year = resp[resp.length - 1];
            base += year;
            $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                var month = resp[resp.length - 1];
                base += month;
                $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                    var day = resp[resp.length - 1];
                    base += day + "signatures/";
                    $.ajax({ type: "GET", url: base, dataType: "json", headers: authHeaders}).done(function(resp) {
                        var result = base + resp[resp.length - 1];
                        cont(result);
                    });
                });
            });
        }
    });
}

function go() {
    // $("#go").prop("disabled", true);
    $("#output").empty();
    var file = $("#input")[0].files[0];
    var ff = $("#fourfour").val();
    var datasyncBase = "/datasync"
    var versionUrl = datasyncBase + "/version.json"
    var datasetBase = datasyncBase + "/id/" + ff
    var serverSigPathBase = "/datasync/id/" + ff + "/completed/";
    var uploadUrl = datasetBase
    var commitUrl = datasetBase + "/commit"
    var statusUrlBase = datasetBase + "/status"
    var logUrlBase = datasetBase + "/log"
    var appToken = $("#apptoken").val();
    var authHeaders = {};
    if($.cookie('socrata-csrf-token')) {
        authHeaders["x-csrf-token"] = encodeURIComponent($.cookie('socrata-csrf-token'));
    }
    findSig(serverSigPathBase, authHeaders, function(sigurl) {
        msg("Retrieving target file size...");
        $.getJSON(versionUrl, function(j) {
            var blockSize = j['max-block-size'];

            var generatePatch = function(job) {
                msg("Yay parsed! -- next up: doing the file");
                var progress = function(read, total, sent) {
                    msg("read " + Math.round(100000*read/total)/1000 + "%; sent " + sent + " bytes");
                }
                var chunk = function(c) {
                    out(c);
                }
                var finished = function(chunks, size) {
                    msg("Uploaded CSV (" + file.size + " bytes; required " + size + " bytes to be uploaded)");
                    var commitSpec = { filename : file.name + ".sdiff", chunks : chunks, relativeTo: sigurl, expectedSize : size, control : { csv : { action: "replace" } } };
                    // TODO errors
                    $.ajax({ type: "POST", url: commitUrl, data: JSON.stringify(commitSpec), dataType: "json", headers: authHeaders }).done(function(resp) {
                        watchProgress(statusUrlBase + "/" + resp.jobId + ".txt", logUrlBase + "/" + resp.jobId + ".txt", authHeaders);
                    })
                }
                if(appToken) authHeaders['X-App-Token'] = appToken;
                readFile(job, file, progress, chunk, finished, uploadUrl, authHeaders);
            }

            if(sigurl == null) {
                msg("No signature available; using empty signature")
                fakeSig(blockSize, function(result) {
                    if(result.ok) {
                        generatePatch(result.continuation);
                    } else {
                        msg(":((((( -- " + JSON.stringify(result.error));
                    }
                });
            } else {
                msg("Fetching signature from " + sigurl + " ...");
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function() {
                    if(this.readyState == 4 && this.status == 200) {
                        msg("Parsing signature...");
                        // wheeeeee!
                        var reader = new FileReader();
                        reader.addEventListener("loadend", function() {
                            parseSig(blockSize, new Uint8Array(reader.result), function(result) {
                                if(result.ok) {
                                    generatePatch(result.continuation)
                                } else {
                                    msg(":((((( -- " + JSON.stringify(result.error));
                                }
                            });
                        });
                        reader.readAsArrayBuffer(this.response);
                    }
                }
                xhr.open('GET', sigurl, true);
                xhr.responseType = 'blob';
                if(appToken) xhr.setRequestHeader('X-App-Token', appToken);
                xhr.send();
            }
        }).fail(function() {
            msg("error reading target size")
        })
    });
}
<|endoftext|>


<commit_before>/*jshint loopfunc: true */

'use strict';

var app = require('../../server.js').get('address');
var request = require('supertest');

describe('Wonder API:', function () {
  var newWonder;
  var wonders;

  const inputs = [1, 43, 2, 35, 65, 36, 10, 57, 32, 45, 90, 79, 32, 47, 19, 14, 30, 15, 69, 25, 50, 38, 29];

  describe('GET /api/wonders', function () {
    beforeAll(function (done) {
      request(app)
        .get('/api/wonders')
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            done.fail(err);
          }
          wonders = res.body;
          done();
        });
    });

    it('should respond with JSON array', function () {
      expect(wonders).toEqual(jasmine.any(Array));
    });
  });

  describe('POST /api/wonders', function () {

    describe('Input Wonders', () => {
      for (var counter = 0; counter < inputs.length; counter++) {
        (function (input) {
          it('should respond back with inputted wonder: ' + input, (done) => {
            request(app)
              .post('/api/wonders')
              .send({
                name: 'wonder: ' + input
              })
              .expect(200)
              .expect('Content-Type', /json/)
              .end((err, res) => {
                if (err) {
                  done.fail(err);
                }
                expect(res.body.name).toEqual('wonder: ' + input);
                done();
              });
          });
        })(inputs[counter]);
      }
    });

    describe('Gather Wonders Array', () => {
      it('should respond back with all current wonders', (done) => {
        request(app)
          .get('/api/wonders')
          .expect(200)
          .expect('Content-Type', /json/)
          .end((err, res) => {
            if (err) {
              done.fail(err);
            }
            wonders = res.body;

            expect(wonders).toEqual(jasmine.any(Array));
            expect(wonders.length).toEqual(20);

            done();
          });
      });
    });

    describe('Check Wonders Response Array', () => {
      for (var i = 0; i < 20; i++) {
        (function (input, counter) {
          it('wonders[' + counter + '] should equal wonder: ' + input, (done) => {
            expect(wonders[counter].name).toEqual('wonder: ' + input)
            done();
          });
        })(inputs[i + 3], (i + 3) % 20)
      }
    });

  });
});
<commit_msg>condensed server side wonders test<commit_after>/*jshint loopfunc: true */

'use strict';

var app = require('../../server.js').get('address');
var request = require('supertest');

describe('Wonder API:', function () {
  var newWonder;
  var wonders;

  const inputs = [1, 43, 2, 35, 65, 36, 10, 57, 32, 45, 90, 79, 32, 47, 19, 14, 30, 15, 69, 25, 50, 38, 29];

  describe('POST /api/wonders', function () {
    for (var counter = 0; counter < inputs.length; counter++) {
      (function (input) {
        return beforeAll((done) => {
          request(app)
            .post('/api/wonders')
            .send({
              name: 'wonder: ' + input
            })
            .expect(200)
            .expect('Content-Type', /json/)
            .end((err, res) => {
              if (err) {
                done.fail(err);
              }
              expect(res.body.name).toEqual('wonder: ' + input);
              done();
            });
        });
      })(inputs[counter]);
    }

    it('should respond back each query with inputted wonder', () => {
      expect(true).toEqual(true);
    });
  });

  describe('GET /api/wonders', function () {
    beforeAll(function (done) {
      request(app)
        .get('/api/wonders')
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            done.fail(err);
          }
          wonders = res.body;
          done();
        });
    });

    it('should respond with JSON array', function () {
      expect(wonders).toEqual(jasmine.any(Array));
    });

    it('wonders should equal the original input array', () => {
      for (var i = 0; i < 20; i++) {
        (function (input, counter) {
          return expect(wonders[counter].name).toEqual('wonder: ' + input)
        })(inputs[i + 3], (i + 3) % 20)
      }
    });
  });
});
<|endoftext|>