<commit_before>/**
 * Controller for the search tab. pops in sub controllers for geographic/cultural/etc
 *
 * @param $scope
 * @param colorMapService
 * @param searchModelService
 * @param FindSocieties
 * @constructor
 */
function SearchCtrl($scope, $window, $location, colorMapService, searchModelService, FindSocieties) {
    $scope.setActive('search');
    $scope.searchModel = searchModelService.getModel();
    $scope.searchBySocietyButton = {text: 'Search', disabled: false};
    $scope.selectedButton = $scope.searchModel.selectedButton;
    var rP = 'radioPlaces';
    var rL = 'radioLanguage';
    var rC = 'radioCulture';
    var rE = 'radioEnv';
    // preserve active button style if any
    if($scope.selectedButton) {
        switch($scope.selectedButton.radioClass) {
        case 'radioPlaces':
            rP += ' active';
            break;
        case 'radioLanguage':
            rL += ' active';
            break;
        case 'radioCulture':
            rC += ' active';
            break;
        case 'radioEnv':
            rE += ' active';
            break;
        }
    }
    $scope.buttons = [
        {radioClass: rP, value:'geographic', name:'PLACES', badgeValue:
            function() { return $scope.searchModel.getGeographicRegions().badgeValue; }
        },
        {radioClass: rL, value:'language', name:'LANGUAGE', badgeValue:
            function() { return $scope.searchModel.getLanguageClassifications().badgeValue; }
        },
        {radioClass: rC, value:'cultural', name:'CULTURE', badgeValue:
            function() { return $scope.searchModel.getCulturalTraits().badgeValue; }
        },
        {radioClass: rE, value:'environmental', name:'ENVIRONMENT', badgeValue:
            function() { return $scope.searchModel.getEnvironmentalData().badgeValue; }
        }
    ];

    $scope.buttonChanged = function(selectedButton) {
        $scope.selectedButton = selectedButton;
        $scope.searchModel.selectedButton = selectedButton;
    };

    // All of this needs to move into model
    $scope.disableSearchButton = function () {
        $scope.searchButton.disabled = true;
        $scope.searchButton.text = 'Working...';
        $scope.searchBySocietyButton = {text: 'Working...', disabled: true};

    };

    $scope.enableSearchButton = function () {
        $scope.searchButton.disabled = false;
        $scope.searchButton.text = 'Search';
        $scope.searchBySocietyButton = {text: 'Search', disabled: false};

    };

    $scope.isEmpty = function(object, searchType) {
        if (searchType == 'environmental') {
            for (var i = 0; i < object.length; i++) {
                if (object[i].selectedVariable) return false;
            }
        } else {
            for (var key in object) {
                if (object[key].length > 0)
                    return false;
            }
        }
        return true;
    };
    $scope.searchCriteria = "View selected search criteria";

    $scope.showCriteria = function() {
        $("#selected-criteria").toggleClass('hidden');
        $("#search-panel").toggleClass('col-md-9', 'col-md-12');

        if (!$("#selected-criteria").hasClass('hidden')) $scope.searchCriteria = "Hide selected search criteria";
        else $scope.searchCriteria = "View selected search criteria";
    };

    $scope.checkIfSelected = function() {
        if ($scope.searchModel.getGeographicRegions().selectedRegions.length > 0) return true;
        if ($scope.searchModel.getEnvironmentalData().selectedVariables.length > 0) {
            for (var i = 0; i < $scope.searchModel.getEnvironmentalData().selectedVariables.length; i++) {
                if ($scope.searchModel.getEnvironmentalData().selectedVariables[i].selectedVariable) return true;
            }
        }
        if (!$scope.isEmpty($scope.searchModel.getCulturalTraits().selected)) return true;
        if (!$scope.isEmpty($scope.searchModel.getLanguageClassifications().selected)) return true;
        return false;
    }

    //removes a variable, language, or region from search parameters
    $scope.removeFromSearch = function(object, searchType) {
        var index = -1;
        switch(searchType) {
            case 'geographic':
                index = $scope.searchModel.getGeographicRegions().selectedRegions.indexOf(object);
                $scope.searchModel.getGeographicRegions().selectedRegions.splice(index, 1);
                $scope.searchModel.getGeographicRegions().badgeValue = $scope.searchModel.getGeographicRegions().selectedRegions.length;
                break;
            case 'environmental':
                index = $scope.searchModel.getEnvironmentalData().selectedVariables.indexOf(object);
                $scope.searchModel.getEnvironmentalData().selectedVariables.splice(index, 1);
                $scope.searchModel.getEnvironmentalData().badgeValue = $scope.searchModel.getEnvironmentalData().selectedVariables.length;
                break;
            case 'family':
                var langSelectedObjs = $scope.searchModel.getLanguageClassifications().selected;
                if (object in langSelectedObjs) {
                    for (var i = 0; i < langSelectedObjs[object].length; i++) {
                        langSelectedObjs[object][i].isSelected = false;
                        $scope.searchModel.getLanguageClassifications().badgeValue -= langSelectedObjs[object][i].societies.length
                    }
                    delete langSelectedObjs[object];
                    $scope.$broadcast('classificationSelectionChanged');
                }
                break;
            case 'language':
                var langSelectedObjs = $scope.searchModel.getLanguageClassifications().selected;
                if (object.family.name in langSelectedObjs) {
                    for (var i = 0; i < langSelectedObjs[object.family.name].length; i++) {
                        if (langSelectedObjs[object.family.name][i].id == object.id) {
                            langSelectedObjs[object.family.name][i].isSelected = false;
                            $scope.searchModel.getLanguageClassifications().badgeValue -= langSelectedObjs[object.family.name][i].societies.length
                            index = i;
                            break;
                        }
                    }
                    if (index > -1) {
                        langSelectedObjs[object.family.name].splice(index, 1);
                        // sync Select all checkbox in language.(html|js)
                        $scope.$broadcast('classificationSelectionChanged');
                    }
                }
                break;
            case 'culture':
                if (object.variable in $scope.searchModel.getCulturalTraits().selected) {
                    for (var i = 0; i < $scope.searchModel.getCulturalTraits().selected[object.variable].length; i++) {
                        if (($scope.searchModel.getCulturalTraits().selected[object.variable][i].id == object.id) || ($scope.searchModel.getCulturalTraits().selected[object.variable][i].description == object.description)) {
                            $scope.searchModel.getCulturalTraits().selected[object.variable][i].isSelected = false;
                            index = i;
                            break;
                        }
                    }
                    if (index > -1) {
                        $scope.searchModel.getCulturalTraits().selected[object.variable].splice(index, 1);
                        $scope.searchModel.getCulturalTraits().selected[object.variable].allSelected = false;
                        $scope.searchModel.getCulturalTraits().badgeValue--;
                        $scope.$broadcast('variableCheck');
                    }

                }
            case 'variable':
                while(object.length > 0) {
                    $scope.removeFromSearch(object[0], 'culture')
                }
        }
        if (!$scope.checkIfSelected()) {
            d3.select("#selected-criteria").classed("hidden", true);
            d3.select("#search-panel").classed("col-md-12", true);
            d3.select("#search-panel").classed("col-md-9", false);
            if (!$("#selected-criteria").hasClass('hidden')) $scope.searchCriteria = "Hide selected search criteria";
            else $scope.searchCriteria = "View selected search criteria";
        }
    };

    var errorCallBack = function() {
        $scope.errors = "Invalid input.";
        $scope.enableSearchButton();
    };

    var searchCompletedCallback = function() {
        $scope.enableSearchButton();
        $scope.searchModel.results = searchModelService.getCodeIDs($scope.searchModel.results, $scope.searchModel.query);
        searchModelService.assignColors($scope.searchModel.results);
        $scope.searchModel.results.searched = true;
        $scope.switchToResults();
    };

    var searchBySocietyCallBack = function() {
        $scope.enableSearchButton();
        $scope.searchModel.results.searchedByName = true;
        if ($scope.searchModel.results.societies.length == 1)
            $window.location.href = '/society/'+$scope.searchModel.results.societies[0].society.ext_id;
        else
            $scope.switchToResults();
    };

    $scope.searchSocieties = function() {
        $scope.disableSearchButton();
        var query = $scope.searchModel.query;
        $scope.searchModel.results = FindSocieties.find(query, searchCompletedCallback, errorCallBack);
    };

    $scope.searchBySociety = function() {
        $scope.disableSearchButton();
        $scope.searchModel.query = {'name': $scope.model.societyQuery}
        $scope.searchModel.results = FindSocieties.find($scope.searchModel.query, searchBySocietyCallBack);
    };

    $scope.search = function() {
        var i, pruned;
        searchModel = searchModelService.getModel();
        searchParams = searchModel.params;
        searchQuery = {};
        for (var propertyName in searchParams) {
            //get selected cultural traits/codes
            if (propertyName == 'culturalTraits') {
                var codes = [];
                for (var variable in searchParams[propertyName].selected) {
                    searchParams[propertyName].selected[variable].forEach(function(code) { if (code.isSelected) codes.push(code); });
                };
                if (codes.length > 0) {
                    searchQuery['c'] = [];
                    for (i = 0; i < codes.length; i++) {
                        pruned = codes[i].variable;
                        if ('id' in codes[i]) {
                            pruned += '-' + codes[i].id;
                        }
                        if ('min' in codes[i]) {
                            pruned += '-' + codes[i].min;
                        }
                        if ('max' in codes[i]) {
                            pruned += '-' + codes[i].max;
                        }
                        searchQuery['c'].push(pruned.toString());
                    }
                }
            }
            //get selected regions
            if (propertyName == 'geographicRegions') {
                var selectedRegions = searchParams[propertyName].selectedRegions;
                selectedRegions.forEach(function (selectedRegion) {
                    searchParams[propertyName].allRegions.forEach(function(region) {
                        if (region.tdwg_code == selectedRegion.code)
                            selectedRegion.id = region.id;
                    });
                });
                if (selectedRegions.length > 0) {
                    searchQuery['p'] = [];
                    for (i = 0; i < selectedRegions.length; i++) {
                        searchQuery['p'].push(selectedRegions[i].id);
                    }
                }
            }
            //get selected environmental variable and search parameters
            if (propertyName == 'environmentalData') {
                searchParams[propertyName].selectedVariables.forEach(function(variable) {
                    if (variable.selectedVariable) {
                        filters = []
                        if (variable.selectedVariable.data_type == 'Continuous') {
                            filters = [
                                variable.selectedVariable.id,
                                variable.selectedFilter.operator,
                                variable.vals
                            ];

                        } else {
                            if (variable.selectedVariable.selected.length>0) {
                                filters = [
                                    variable.selectedVariable.id,
                                    'categorical',
                                    variable.selectedVariable.selected
                                ]
                            } else filters = []
                        }
                        if (filters.length > 0){
                            if ('e' in searchQuery) {
                                searchQuery['e'].push(filters);
                            } else {
                                searchQuery['e'] = [filters];
                            }
                        }
                    }
                });
            }

            //get selected languages
            if (propertyName == 'languageClassifications') {
                var classifications = [];
                for (var family in searchParams[propertyName].selected) {
                    searchParams[propertyName].selected[family].forEach(function(language) { if (language.isSelected) classifications.push(language); });
                }
                if (classifications.length > 0) {
                    searchQuery['l'] = [];
                    for (i = 0; i < classifications.length; i++) {
                        searchQuery['l'].push(classifications[i].id);
                    }
                }
           }
        }
        searchModelService.updateSearchQuery(searchQuery);
        console.log(searchQuery);
        $scope.searchSocieties();

    };

    // resets this object state and the search query.
    $scope.resetSearch = function() {
        $scope.errors = "";
        $scope.enableSearchButton();
        $scope.searchModel.reset();
        // send a notification so that the individual controllers reload their state
        $scope.$broadcast('searchModelReset');

        // hide selected search criteria div if shown
        if (!$("#selected-criteria").hasClass('hidden')) {
            $("#selected-criteria").addClass('hidden');
            $scope.searchCriteria = "View selected search criteria";
            $("#search-panel").toggleClass('col-md-9', 'col-md-12');
        }
    };

}<commit_msg>removing console.log<commit_after>/**
 * Controller for the search tab. pops in sub controllers for geographic/cultural/etc
 *
 * @param $scope
 * @param colorMapService
 * @param searchModelService
 * @param FindSocieties
 * @constructor
 */
function SearchCtrl($scope, $window, $location, colorMapService, searchModelService, FindSocieties) {
    $scope.setActive('search');
    $scope.searchModel = searchModelService.getModel();
    $scope.searchBySocietyButton = {text: 'Search', disabled: false};
    $scope.selectedButton = $scope.searchModel.selectedButton;
    var rP = 'radioPlaces';
    var rL = 'radioLanguage';
    var rC = 'radioCulture';
    var rE = 'radioEnv';
    // preserve active button style if any
    if($scope.selectedButton) {
        switch($scope.selectedButton.radioClass) {
        case 'radioPlaces':
            rP += ' active';
            break;
        case 'radioLanguage':
            rL += ' active';
            break;
        case 'radioCulture':
            rC += ' active';
            break;
        case 'radioEnv':
            rE += ' active';
            break;
        }
    }
    $scope.buttons = [
        {radioClass: rP, value:'geographic', name:'PLACES', badgeValue:
            function() { return $scope.searchModel.getGeographicRegions().badgeValue; }
        },
        {radioClass: rL, value:'language', name:'LANGUAGE', badgeValue:
            function() { return $scope.searchModel.getLanguageClassifications().badgeValue; }
        },
        {radioClass: rC, value:'cultural', name:'CULTURE', badgeValue:
            function() { return $scope.searchModel.getCulturalTraits().badgeValue; }
        },
        {radioClass: rE, value:'environmental', name:'ENVIRONMENT', badgeValue:
            function() { return $scope.searchModel.getEnvironmentalData().badgeValue; }
        }
    ];

    $scope.buttonChanged = function(selectedButton) {
        $scope.selectedButton = selectedButton;
        $scope.searchModel.selectedButton = selectedButton;
    };

    // All of this needs to move into model
    $scope.disableSearchButton = function () {
        $scope.searchButton.disabled = true;
        $scope.searchButton.text = 'Working...';
        $scope.searchBySocietyButton = {text: 'Working...', disabled: true};

    };

    $scope.enableSearchButton = function () {
        $scope.searchButton.disabled = false;
        $scope.searchButton.text = 'Search';
        $scope.searchBySocietyButton = {text: 'Search', disabled: false};

    };

    $scope.isEmpty = function(object, searchType) {
        if (searchType == 'environmental') {
            for (var i = 0; i < object.length; i++) {
                if (object[i].selectedVariable) return false;
            }
        } else {
            for (var key in object) {
                if (object[key].length > 0)
                    return false;
            }
        }
        return true;
    };
    $scope.searchCriteria = "View selected search criteria";

    $scope.showCriteria = function() {
        $("#selected-criteria").toggleClass('hidden');
        $("#search-panel").toggleClass('col-md-9', 'col-md-12');

        if (!$("#selected-criteria").hasClass('hidden')) $scope.searchCriteria = "Hide selected search criteria";
        else $scope.searchCriteria = "View selected search criteria";
    };

    $scope.checkIfSelected = function() {
        if ($scope.searchModel.getGeographicRegions().selectedRegions.length > 0) return true;
        if ($scope.searchModel.getEnvironmentalData().selectedVariables.length > 0) {
            for (var i = 0; i < $scope.searchModel.getEnvironmentalData().selectedVariables.length; i++) {
                if ($scope.searchModel.getEnvironmentalData().selectedVariables[i].selectedVariable) return true;
            }
        }
        if (!$scope.isEmpty($scope.searchModel.getCulturalTraits().selected)) return true;
        if (!$scope.isEmpty($scope.searchModel.getLanguageClassifications().selected)) return true;
        return false;
    }

    //removes a variable, language, or region from search parameters
    $scope.removeFromSearch = function(object, searchType) {
        var index = -1;
        switch(searchType) {
            case 'geographic':
                index = $scope.searchModel.getGeographicRegions().selectedRegions.indexOf(object);
                $scope.searchModel.getGeographicRegions().selectedRegions.splice(index, 1);
                $scope.searchModel.getGeographicRegions().badgeValue = $scope.searchModel.getGeographicRegions().selectedRegions.length;
                break;
            case 'environmental':
                index = $scope.searchModel.getEnvironmentalData().selectedVariables.indexOf(object);
                $scope.searchModel.getEnvironmentalData().selectedVariables.splice(index, 1);
                $scope.searchModel.getEnvironmentalData().badgeValue = $scope.searchModel.getEnvironmentalData().selectedVariables.length;
                break;
            case 'family':
                var langSelectedObjs = $scope.searchModel.getLanguageClassifications().selected;
                if (object in langSelectedObjs) {
                    for (var i = 0; i < langSelectedObjs[object].length; i++) {
                        langSelectedObjs[object][i].isSelected = false;
                        $scope.searchModel.getLanguageClassifications().badgeValue -= langSelectedObjs[object][i].societies.length
                    }
                    delete langSelectedObjs[object];
                    $scope.$broadcast('classificationSelectionChanged');
                }
                break;
            case 'language':
                var langSelectedObjs = $scope.searchModel.getLanguageClassifications().selected;
                if (object.family.name in langSelectedObjs) {
                    for (var i = 0; i < langSelectedObjs[object.family.name].length; i++) {
                        if (langSelectedObjs[object.family.name][i].id == object.id) {
                            langSelectedObjs[object.family.name][i].isSelected = false;
                            $scope.searchModel.getLanguageClassifications().badgeValue -= langSelectedObjs[object.family.name][i].societies.length
                            index = i;
                            break;
                        }
                    }
                    if (index > -1) {
                        langSelectedObjs[object.family.name].splice(index, 1);
                        // sync Select all checkbox in language.(html|js)
                        $scope.$broadcast('classificationSelectionChanged');
                    }
                }
                break;
            case 'culture':
                if (object.variable in $scope.searchModel.getCulturalTraits().selected) {
                    for (var i = 0; i < $scope.searchModel.getCulturalTraits().selected[object.variable].length; i++) {
                        if (($scope.searchModel.getCulturalTraits().selected[object.variable][i].id == object.id) || ($scope.searchModel.getCulturalTraits().selected[object.variable][i].description == object.description)) {
                            $scope.searchModel.getCulturalTraits().selected[object.variable][i].isSelected = false;
                            index = i;
                            break;
                        }
                    }
                    if (index > -1) {
                        $scope.searchModel.getCulturalTraits().selected[object.variable].splice(index, 1);
                        $scope.searchModel.getCulturalTraits().selected[object.variable].allSelected = false;
                        $scope.searchModel.getCulturalTraits().badgeValue--;
                        $scope.$broadcast('variableCheck');
                    }

                }
            case 'variable':
                while(object.length > 0) {
                    $scope.removeFromSearch(object[0], 'culture')
                }
        }
        if (!$scope.checkIfSelected()) {
            d3.select("#selected-criteria").classed("hidden", true);
            d3.select("#search-panel").classed("col-md-12", true);
            d3.select("#search-panel").classed("col-md-9", false);
            if (!$("#selected-criteria").hasClass('hidden')) $scope.searchCriteria = "Hide selected search criteria";
            else $scope.searchCriteria = "View selected search criteria";
        }
    };

    var errorCallBack = function() {
        $scope.errors = "Invalid input.";
        $scope.enableSearchButton();
    };

    var searchCompletedCallback = function() {
        $scope.enableSearchButton();
        $scope.searchModel.results = searchModelService.getCodeIDs($scope.searchModel.results, $scope.searchModel.query);
        searchModelService.assignColors($scope.searchModel.results);
        $scope.searchModel.results.searched = true;
        $scope.switchToResults();
    };

    var searchBySocietyCallBack = function() {
        $scope.enableSearchButton();
        $scope.searchModel.results.searchedByName = true;
        if ($scope.searchModel.results.societies.length == 1)
            $window.location.href = '/society/'+$scope.searchModel.results.societies[0].society.ext_id;
        else
            $scope.switchToResults();
    };

    $scope.searchSocieties = function() {
        $scope.disableSearchButton();
        var query = $scope.searchModel.query;
        $scope.searchModel.results = FindSocieties.find(query, searchCompletedCallback, errorCallBack);
    };

    $scope.searchBySociety = function() {
        $scope.disableSearchButton();
        $scope.searchModel.query = {'name': $scope.model.societyQuery}
        $scope.searchModel.results = FindSocieties.find($scope.searchModel.query, searchBySocietyCallBack);
    };

    $scope.search = function() {
        var i, pruned;
        searchModel = searchModelService.getModel();
        searchParams = searchModel.params;
        searchQuery = {};
        for (var propertyName in searchParams) {
            //get selected cultural traits/codes
            if (propertyName == 'culturalTraits') {
                var codes = [];
                for (var variable in searchParams[propertyName].selected) {
                    searchParams[propertyName].selected[variable].forEach(function(code) { if (code.isSelected) codes.push(code); });
                };
                if (codes.length > 0) {
                    searchQuery['c'] = [];
                    for (i = 0; i < codes.length; i++) {
                        pruned = codes[i].variable;
                        if ('id' in codes[i]) {
                            pruned += '-' + codes[i].id;
                        }
                        if ('min' in codes[i]) {
                            pruned += '-' + codes[i].min;
                        }
                        if ('max' in codes[i]) {
                            pruned += '-' + codes[i].max;
                        }
                        searchQuery['c'].push(pruned.toString());
                    }
                }
            }
            //get selected regions
            if (propertyName == 'geographicRegions') {
                var selectedRegions = searchParams[propertyName].selectedRegions;
                selectedRegions.forEach(function (selectedRegion) {
                    searchParams[propertyName].allRegions.forEach(function(region) {
                        if (region.tdwg_code == selectedRegion.code)
                            selectedRegion.id = region.id;
                    });
                });
                if (selectedRegions.length > 0) {
                    searchQuery['p'] = [];
                    for (i = 0; i < selectedRegions.length; i++) {
                        searchQuery['p'].push(selectedRegions[i].id);
                    }
                }
            }
            //get selected environmental variable and search parameters
            if (propertyName == 'environmentalData') {
                searchParams[propertyName].selectedVariables.forEach(function(variable) {
                    if (variable.selectedVariable) {
                        filters = []
                        if (variable.selectedVariable.data_type == 'Continuous') {
                            filters = [
                                variable.selectedVariable.id,
                                variable.selectedFilter.operator,
                                variable.vals
                            ];

                        } else {
                            if (variable.selectedVariable.selected.length>0) {
                                filters = [
                                    variable.selectedVariable.id,
                                    'categorical',
                                    variable.selectedVariable.selected
                                ]
                            } else filters = []
                        }
                        if (filters.length > 0){
                            if ('e' in searchQuery) {
                                searchQuery['e'].push(filters);
                            } else {
                                searchQuery['e'] = [filters];
                            }
                        }
                    }
                });
            }

            //get selected languages
            if (propertyName == 'languageClassifications') {
                var classifications = [];
                for (var family in searchParams[propertyName].selected) {
                    searchParams[propertyName].selected[family].forEach(function(language) { if (language.isSelected) classifications.push(language); });
                }
                if (classifications.length > 0) {
                    searchQuery['l'] = [];
                    for (i = 0; i < classifications.length; i++) {
                        searchQuery['l'].push(classifications[i].id);
                    }
                }
           }
        }
        searchModelService.updateSearchQuery(searchQuery);
        $scope.searchSocieties();

    };

    // resets this object state and the search query.
    $scope.resetSearch = function() {
        $scope.errors = "";
        $scope.enableSearchButton();
        $scope.searchModel.reset();
        // send a notification so that the individual controllers reload their state
        $scope.$broadcast('searchModelReset');

        // hide selected search criteria div if shown
        if (!$("#selected-criteria").hasClass('hidden')) {
            $("#selected-criteria").addClass('hidden');
            $scope.searchCriteria = "View selected search criteria";
            $("#search-panel").toggleClass('col-md-9', 'col-md-12');
        }
    };

}<|endoftext|>


<commit_before>var sw = require("swagger-node-express");
var url = require("url");
var msh = require('msh');
var request = require("superagent");
var transformer = require('./payloadTransformer.js');
var assert = require('assert');

var swe = sw.errors;

var dockerPort = process.env.SP_DOCKER_PORT || 4321;
var dockerIp = process.env.SP_DOCKER_HOST || '172.17.42.1';
var nucleusPort = process.env.SP_NUCLEUS_PORT || 8080;
var nucleusHost = process.env.SP_NUCLEUS_HOST || 'localhost';

var dockerUrl = 'http://' + dockerIp + ':' + dockerPort;
var nucleusUrl = 'http://' + nucleusHost + ':' + nucleusPort;

var pokemonPath = '/service/pokemon/substore/muon'

exports.containers = {
  'spec': {
    description : "Return all docker containers for this control-plane",
    path : "/container",
    method: "GET",
    summary : "Return all containers",
    notes : "Returns all containers records in docker",
    type : "array",
    nickname : "containersList",
    produces : ["application/json"],
    parameters : [],
    responseMessages : [swe.notFound('container')]
  },
  'action': function (req,res) {
        console.log('resources.js containers() listing from ' + dockerUrl + '/containers/json');

        var req = request.get(dockerUrl + '/containers/json');
        req.end(function(dockerRes){
            console.log("resources.js containers() GET /containers/json req.end() res.text=" + dockerRes.text);
            var json = dockerRes.text;
            res.send(json);
        });

  }
};

exports.getContainer = {
  'spec': {
    description : "Find nucleus stored item by id",
    path : "/container/{containerId}",
    method: "GET",
    summary : "Find container ID",
    notes : "Returns meta data for docker container based on ID",
    type : "Record",
    nickname : "getContainerId",
    produces : ["application/json"],
    parameters : [sw.pathParam("containerId", "ID of container to fetch", "string")],
    responseMessages : [swe.invalid('containerId'), swe.notFound('container')]
  },
  'action': function (req,res) {
     console.log('resources.js getContainer()');
    if (! req.params.containerId) {
      throw swe.invalid('containerId');
    }

    var containerId = req.params.containerId;

      console.log('resources.js containers()');

        var req = request.get(dockerUrl + '/containers/' + containerId + '/json');
        req.end(function(dockerRes){
            console.log("resources.js getContainer() GET /containers/" + containerId + "/json req.end() res.text=" + dockerRes.text);
            var json = dockerRes.text;
            if (json) {
                res.send(json);
            } else {
                throw swe.notFound('container',res);
            }

        });
  }
};

exports.postContainer = {
  'spec': {
    description : "Create docker  container via control-plane",
    path : "/container",
    method: "POST",
    summary : "Create container",
    notes : "Create docker containter suing json meta data",
    type : "Message",
    nickname : "postContainer",
    produces : ["application/json"],
    parameters : [sw.bodyParam("Payload", "Container meta data to be created in docker", "Payload")],
    responseMessages : [swe.notFound('payload'), swe.invalid('payload')]
  },
  'action': function (req,res) {

    console.log('resources.js postContainer() req.body=' + JSON.stringify(req.body));
    var payload = req.body;

      if (! payload) {
           throw swe.invalid('payload');
      }

    createAndStartDockerContainer(req, res, payload);

  }
};

function createAndStartDockerContainer(req, res, payload)  {

      var imageUrl = '/images/create?fromImage=' + payload.imageId;
      var containerUrl = '/containers/create?name=' + payload.name;

      //console.log('resources.js postContainer() imageUrl=' + imageUrl);
      //console.log('resources.js postContainer() containerUrl=' + containerUrl);

      var callback = function(actions) {
          //console.log('resources.js postContainer()->callback()');
          // after we've created the docker container and get it's ID, we can start the container

          if (actions[0].statusCode != '201') {
                 sendServerError(res, actions[0], actions, 'error creating new image via docker api'  );
          } else if (actions[1].statusCode != '201') {
              sendServerError(res, actions[0], actions, 'error creating new container via docker api'  );
          } else if (! actions.allOk() ) {
              res.send(500, {message: 'error during msh callbacks', logs: actions});
          }
          var dockerReply =  actions[1].response;
          //console.log('resources.js postContainer() dockerReply=' + dockerReply);

          startContainer(req, res, dockerReply, payload);
      }

      var errCallback = function(error) {
          //console.log('resources.js postContainer()->errCallback()');
          res.send(500, {message: 'error 500', desc: error});
      }

      var dockerPayload = transformer.muonToDocker(payload);
      //console.log('resources.js postContainer() msh.init() dockerPayload=' + JSON.stringify(dockerPayload));

      msh.init(callback, errCallback)
      //.post(dockerIp, dockerPort, imageUrl, {})
      .post(dockerIp, dockerPort, containerUrl, dockerPayload)
      .end();
}

function sendServerError(res, action, actions, message) {
    console.log("*********** UNEXPECTED ERROR: NON 200-300 HTTP STAUS CODE RETURNED **************************************");
    console.log("message=" + message);
    console.log("action=" + JSON.stringify(action));
    console.dir(actions);
    console.log("*********** END UNEXPECTED ERROR ************************************************************************");
    res.send(500, {message: message, error: 'request error in action ' + action.counter, action: JSON.stringify(action) });
}

function startContainer(req, res, dockerReply, payload)  {

     var dockerStartJson =  createDockerStartJson(dockerStartJson, payload);

      var startUrl = '/containers/' + dockerReply.Id + '/start';

      var callback = function(actions) {
           console.log('resources.js postContainer()->callback() actions:');
          //console.dir(actions);
          if (actions[0].statusCode != '201') {
              sendServerError(res, actions[0], actions, 'while starting docker container with id  ' +  dockerReply.Id);
          } else if (actions[2].statusCode != '201') {
               sendServerError(res, actions[2], actions, 'while posting docker container info to nucleus' );
          } else if (! actions.allOk() ) {
               sendServerError(res, actions[2], actions, 'unknown http error status code returned during msh execution ' );
          } else {
              res.send(201, {message: 'Container created', id: dockerReply.Id});
          }

      }

      var errCallback = function(err) {
           //console.log('resources.js postContainer()->callback()->innerErrCallback()');
          res.send(500, {message: err});
      }

       msh.init(callback, errCallback)
       .post(dockerIp, dockerPort, startUrl, dockerStartJson)
       .pipe()
       .put(nucleusHost, nucleusPort, pokemonPath + '/record/' + dockerReply.Id)
       .end();

}

function createDockerStartJson(dockerStartJson, payload) {
      var dockerStartJson = {};

    // Expose proxy on port 80 if this is the sp_proxy container being created
        if (payload.imageId.indexOf("sp_proxy") > 0 || payload.name.indexOf("sp_proxy") > 0) {
          dockerStartJson = {
                "PortBindings": { "8888/tcp": [{ "HostPort": "80" }] },
                "Privileged": false,
                "PublishAllPorts": false
           }
        }
        return dockerStartJson;
}

exports.deleteContainer = {
  'spec': {
    path : "/container/{containerId}",
    notes : "delete a container",
    summary : "Delete a containter",
    method: "DELETE",
    nickname: "deleteContainer",
    parameters : [sw.pathParam("containerId", "Id of of containter to destroy", "string")],
    responseMessages : [swe.invalid('containerId'), swe.notFound('containter')]
  },
  'action': function (req,res) {
        console.log('resources.js deleteContainer()');
        if (! req.params.containerId) {
          throw swe.invalid('containerId');
        }

        var containerId = req.params.containerId;
        deleteContainer(req, res, containerId);

  }
};

function deleteContainer(req, res, containerId) {
      var killUrl = '/containers/' + containerId + '/kill';
      var deleteUrl = '/containers/' + containerId;

      /*
        def dockerRet = dockerApi.post("/containers/${id}/kill")
        dockerRet = dockerApi.delete("/containers/${id}")

        [message: "Container Destroyed"]
      */

      var callback = function(actions) {
          //console.log('resources.js deleteContainer()->callback() actions:');
          //console.dir(actions);
          res.send(200, {message: "Container Destroyed"});
      }

      var errCallback = function(err) {
          res.send(500, {message: err});
      }

      msh.init(callback, errCallback)
      .post(dockerIp, dockerPort, killUrl)
      .del(dockerIp, dockerPort, deleteUrl)
      .del(nucleusHost, nucleusPort,  pokemonPath + '/record/' + containerId)
      .end();
}

<commit_msg>minor fix<commit_after>var sw = require("swagger-node-express");
var url = require("url");
var msh = require('msh');
var request = require("superagent");
var transformer = require('./payloadTransformer.js');
var assert = require('assert');

var swe = sw.errors;

var dockerPort = process.env.SP_DOCKER_PORT || 4321;
var dockerIp = process.env.SP_DOCKER_HOST || '172.17.42.1';
var nucleusPort = process.env.SP_NUCLEUS_PORT || 8080;
var nucleusHost = process.env.SP_NUCLEUS_HOST || 'localhost';

var dockerUrl = 'http://' + dockerIp + ':' + dockerPort;
var nucleusUrl = 'http://' + nucleusHost + ':' + nucleusPort;

var pokemonPath = '/service/pokemon/substore/muon'

exports.containers = {
  'spec': {
    description : "Return all docker containers for this control-plane",
    path : "/container",
    method: "GET",
    summary : "Return all containers",
    notes : "Returns all containers records in docker",
    type : "array",
    nickname : "containersList",
    produces : ["application/json"],
    parameters : [],
    responseMessages : [swe.notFound('container')]
  },
  'action': function (req,res) {
        console.log('resources.js containers() listing from ' + dockerUrl + '/containers/json');

        var req = request.get(dockerUrl + '/containers/json');
        req.end(function(dockerRes){
            console.log("resources.js containers() GET /containers/json req.end() res.text=" + dockerRes.text);
            var json = dockerRes.text;
            res.send(json);
        });

  }
};

exports.getContainer = {
  'spec': {
    description : "Find nucleus stored item by id",
    path : "/container/{containerId}",
    method: "GET",
    summary : "Find container ID",
    notes : "Returns meta data for docker container based on ID",
    type : "Record",
    nickname : "getContainerId",
    produces : ["application/json"],
    parameters : [sw.pathParam("containerId", "ID of container to fetch", "string")],
    responseMessages : [swe.invalid('containerId'), swe.notFound('container')]
  },
  'action': function (req,res) {
     console.log('resources.js getContainer()');
    if (! req.params.containerId) {
      throw swe.invalid('containerId');
    }

    var containerId = req.params.containerId;

      console.log('resources.js containers()');

        var req = request.get(dockerUrl + '/containers/' + containerId + '/json');
        req.end(function(dockerRes){
            console.log("resources.js getContainer() GET /containers/" + containerId + "/json req.end() res.text=" + dockerRes.text);
            var json = dockerRes.text;
            if (json) {
                res.send(json);
            } else {
                throw swe.notFound('container',res);
            }

        });
  }
};

exports.postContainer = {
  'spec': {
    description : "Create docker  container via control-plane",
    path : "/container",
    method: "POST",
    summary : "Create container",
    notes : "Create docker containter suing json meta data",
    type : "Message",
    nickname : "postContainer",
    produces : ["application/json"],
    parameters : [sw.bodyParam("Payload", "Container meta data to be created in docker", "Payload")],
    responseMessages : [swe.notFound('payload'), swe.invalid('payload')]
  },
  'action': function (req,res) {

    console.log('resources.js postContainer() req.body=' + JSON.stringify(req.body));
    var payload = req.body;

      if (! payload) {
           throw swe.invalid('payload');
      }

    createAndStartDockerContainer(req, res, payload);

  }
};

function createAndStartDockerContainer(req, res, payload)  {

      var imageUrl = '/images/create?fromImage=' + payload.imageId;
      var containerUrl = '/containers/create?name=' + payload.name;

      //console.log('resources.js postContainer() imageUrl=' + imageUrl);
      //console.log('resources.js postContainer() containerUrl=' + containerUrl);

      var callback = function(actions) {
          //console.log('resources.js postContainer()->callback()');
          // after we've created the docker container and get it's ID, we can start the container

          //if (actions[0].statusCode != '201') {
        //         sendServerError(res, actions[0], actions, 'error creating new image via docker api'  );
        //  }
          if (actions[0].statusCode != '201') {
              sendServerError(res, actions[0], actions, 'error creating new container via docker api'  );
          }
          else if (! actions.allOk() ) {
              res.send(500, {message: 'error during msh callbacks', logs: actions});
          }
          var dockerReply =  actions[0].response;
          //console.log('resources.js postContainer() dockerReply=' + dockerReply);

          startContainer(req, res, dockerReply, payload);
      }

      var errCallback = function(error) {
          //console.log('resources.js postContainer()->errCallback()');
          res.send(500, {message: 'error 500', desc: error});
      }

      var dockerPayload = transformer.muonToDocker(payload);
      //console.log('resources.js postContainer() msh.init() dockerPayload=' + JSON.stringify(dockerPayload));

      msh.init(callback, errCallback)
      //.post(dockerIp, dockerPort, imageUrl, {})
      .post(dockerIp, dockerPort, containerUrl, dockerPayload)
      .end();
}

function sendServerError(res, action, actions, message) {
    console.log("*********** UNEXPECTED ERROR: NON 200-300 HTTP STAUS CODE RETURNED **************************************");
    console.log("message=" + message);
    console.log("action=" + JSON.stringify(action));
    console.dir(actions);
    console.log("*********** END UNEXPECTED ERROR ************************************************************************");
    res.send(500, {message: message, error: 'request error in action ' + action.counter, action: JSON.stringify(action) });
}

function startContainer(req, res, dockerReply, payload)  {

     var dockerStartJson =  createDockerStartJson(dockerStartJson, payload);

      var startUrl = '/containers/' + dockerReply.Id + '/start';

      var callback = function(actions) {
           console.log('resources.js postContainer()->callback() actions:');
          //console.dir(actions);
          if (actions[0].statusCode != '201') {
              sendServerError(res, actions[0], actions, 'while starting docker container with id  ' +  dockerReply.Id);
          } else if (actions[2].statusCode != '201') {
               sendServerError(res, actions[2], actions, 'while posting docker container info to nucleus' );
          } else if (! actions.allOk() ) {
               sendServerError(res, actions[2], actions, 'unknown http error status code returned during msh execution ' );
          } else {
              res.send(201, {message: 'Container created', id: dockerReply.Id});
          }

      }

      var errCallback = function(err) {
           //console.log('resources.js postContainer()->callback()->innerErrCallback()');
          res.send(500, {message: err});
      }

       msh.init(callback, errCallback)
       .post(dockerIp, dockerPort, startUrl, dockerStartJson)
       .pipe()
       .put(nucleusHost, nucleusPort, pokemonPath + '/record/' + dockerReply.Id)
       .end();

}

function createDockerStartJson(dockerStartJson, payload) {
      var dockerStartJson = {};

    // Expose proxy on port 80 if this is the sp_proxy container being created
        if (payload.imageId.indexOf("sp_proxy") > 0 || payload.name.indexOf("sp_proxy") > 0) {
          dockerStartJson = {
                "PortBindings": { "8888/tcp": [{ "HostPort": "80" }] },
                "Privileged": false,
                "PublishAllPorts": false
           }
        }
        return dockerStartJson;
}

exports.deleteContainer = {
  'spec': {
    path : "/container/{containerId}",
    notes : "delete a container",
    summary : "Delete a containter",
    method: "DELETE",
    nickname: "deleteContainer",
    parameters : [sw.pathParam("containerId", "Id of of containter to destroy", "string")],
    responseMessages : [swe.invalid('containerId'), swe.notFound('containter')]
  },
  'action': function (req,res) {
        console.log('resources.js deleteContainer()');
        if (! req.params.containerId) {
          throw swe.invalid('containerId');
        }

        var containerId = req.params.containerId;
        deleteContainer(req, res, containerId);

  }
};

function deleteContainer(req, res, containerId) {
      var killUrl = '/containers/' + containerId + '/kill';
      var deleteUrl = '/containers/' + containerId;

      /*
        def dockerRet = dockerApi.post("/containers/${id}/kill")
        dockerRet = dockerApi.delete("/containers/${id}")

        [message: "Container Destroyed"]
      */

      var callback = function(actions) {
          //console.log('resources.js deleteContainer()->callback() actions:');
          //console.dir(actions);
          res.send(200, {message: "Container Destroyed"});
      }

      var errCallback = function(err) {
          res.send(500, {message: err});
      }

      msh.init(callback, errCallback)
      .post(dockerIp, dockerPort, killUrl)
      .del(dockerIp, dockerPort, deleteUrl)
      .del(nucleusHost, nucleusPort,  pokemonPath + '/record/' + containerId)
      .end();
}

<|endoftext|>


<commit_before>// Copyright (c) 2012 UX Productivity Pty Ltd. All rights reserved.
// If you're successfully able to decipher all this and figure out what it
// does, please send a copy of your resume to peter@uxproductivity.com.

var trace;

(function() {

    function valueString(arg)
    {
        try {
            if (typeof(arg) == "object") {
                var str = arg.toString();
                return "[object "+arg.constructor.name+"]";
            }
            else if (typeof(arg) == "string") {
                return JSON.stringify(arg);
            }
            else if (typeof(arg) == "function") {
                if (arg.name)
                    return "<function "+arg.name+">";
                else
                    return "<anonymous function>";
            }
            else {
                return arg;
            }
        }
        catch (e) {
            return "?"; // in case object or array element's toString() fails
        }
    }

    function stackEntryString(fun,args,thisObject)
    {
        var components = new Array();
        if ((thisObject != null) && (thisObject.constructor.name != null)) {
            components.push(thisObject.constructor.name+".");
        }
        components.push(fun.name+"(");
        for (var argno = 0; argno < args.length; argno++) {
            var arg = args[argno];
            if (argno > 0)
                components.push(",");
            components.push(valueString(arg));
        }
        components.push("): this = "+valueString(thisObject));
        return components.join("");
    }

    trace = function(fun)
    {
        var result = function() {
            try {
                return fun.apply(this,arguments);
            }
            catch (e) {
                var error = e;
                if (!error.custom) {
                    error = new Error(e.toString()+"\n");
                    error.custom = true;
                }
                error.message += stackEntryString(fun,arguments,this)+"\n";
                throw error;
            }
        }
        result.wrappedName = fun.name;
        return result;
    }

})();
<commit_msg>When reporting exceptions, show null arguments, as well as node names in case of nodes<commit_after>// Copyright (c) 2012 UX Productivity Pty Ltd. All rights reserved.
// If you're successfully able to decipher all this and figure out what it
// does, please send a copy of your resume to peter@uxproductivity.com.

var trace;

(function() {

    function valueString(arg)
    {
        try {
            if (arg == null) {
                return "null";
            }
            else if (typeof(arg) == "object") {
                if (arg instanceof Node) {
                    return "[node "+arg.nodeName+"]";
                }
                else {
                    var str = arg.toString();
                    return "[object "+arg.constructor.name+"]";
                }
            }
            else if (typeof(arg) == "string") {
                return JSON.stringify(arg);
            }
            else if (typeof(arg) == "function") {
                if (arg.name)
                    return "<function "+arg.name+">";
                else
                    return "<anonymous function>";
            }
            else {
                return arg;
            }
        }
        catch (e) {
            return "?"; // in case object or array element's toString() fails
        }
    }

    function stackEntryString(fun,args,thisObject)
    {
        var components = new Array();
        if ((thisObject != null) && (thisObject.constructor.name != null)) {
            components.push(thisObject.constructor.name+".");
        }
        components.push(fun.name+"(");
        for (var argno = 0; argno < args.length; argno++) {
            var arg = args[argno];
            if (argno > 0)
                components.push(",");
            components.push(valueString(arg));
        }
        components.push("): this = "+valueString(thisObject));
        return components.join("");
    }

    trace = function(fun)
    {
        var result = function() {
            try {
                return fun.apply(this,arguments);
            }
            catch (e) {
                var error = e;
                if (!error.custom) {
                    error = new Error(e.toString()+"\n");
                    error.custom = true;
                }
                error.message += stackEntryString(fun,arguments,this)+"\n";
                throw error;
            }
        }
        result.wrappedName = fun.name;
        return result;
    }

})();
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Flock - JavaScript Key-Value Cache
//
// In-memory key-value store. Stores entire tree structure in memory.
// Use for complex lookups, as memcache on top of a persistence layer,
// or as an in-memory DHT node.
//
// https://github.com/wwidd/flock
////////////////////////////////////////////////////////////////////////////////
var	exports,

flock = function () {
	var RE_PATH_VALIDATOR = /^(\.{3})*([^\.,]+(\.{1,3}|,))*[^\.,]+$/,
			RE_PATH_SKIPPER = /\.{2,}/,

	// - root: root object for datastore
	flock = function (root) {
		// creating default root object
		if (typeof root === 'undefined') {
			root = {};
		}

		var self = {
			//////////////////////////////
			// Getters, setters

			root: function () {
				return root;
			},

			//////////////////////////////
			// Control

			// retrieves node at the given path
			// - path: string or array representing path
			// returns node on path
			// raises TypeError on invalid path
			get: function (path) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						tmp = root,
						i;

				// walking nodes along path
				for (i = 0; i < tpath.length; i++) {
					tmp = tmp[tpath[i]];
				}

				// returning value on end node
				return tmp;
			},

			// sets node at the given path
			// - path: array representing path
			// - value: value to be set at path
			// returns the node on the input path
			set: function (path, value) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						last = tpath.length - 1,
						key, node, child,
						i;

				if (!tpath.length) {
					throw "flock.set: empty path '" + path + "'";
				}

				// walking nodes along path
				for (i = 0, node = root; i < last; i++) {
					key = tpath[i];
					child = node[key];
					if (typeof child === 'undefined') {
						// creating node
						child = node[key] = {};
					}
					node = child;
				}

				// setting value on end node
				node[tpath[i]] = value;

				return node;
			},

			// removes a datastore element from the given path
			// - path: array representing element path
			// returns
			// - true: elem was present and deleted
			// - false: elem was not present
			unset: function (path) {
				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						key = tpath.pop(),
						parent = self.get(tpath);

				// deleting node when exists
				if (parent && parent.hasOwnProperty(key)) {
					delete parent[key];
					return true;
				} else {
					return false;
				}
			},

			// collects or modifies end nodes specified by a wildcard path
			// - path: path to end nodes, may contain wildcards "*"
			// - options:
			//	 - limit: max number of entries to retrieve, default: unlimited
			//	 - mode: type of return value is Object or Array (flock.key/flock.values/flock.both), default: flock.array
			//	 - loopback: whether to traverse loopbacks, default: false
			//	 - undef: whether to collect undefined entries, default: false
			// - custom: value to set, or callback function to execute on nodes
			//	 when undefined, function returns collected values
			many: function (path, options, custom) {
				options = options || {};

				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						last = tpath.length - 1,
						limit = options.limit || 0,
						loopback = options.loopback || false,
						result = options.mode === flock.both ? {} : [],
						stack = options.loopback ? null : [];

				// default case
				if (!tpath.length) {
					return root;
				}

				// collects end nodes
				// must be class level, creating a function on each call
				// to .multiget() may impact performance significantly
				// - obj: node data
				// - i: current position in path
				// - depth: current depth in tree
				(function walk(obj, i, depth) {
					var key, j;

					// detecting loopback
					if (!loopback) {
						for (j = 0; j < depth; j++) {
							if (obj === stack[j]) {
								return;
							}
						}
						// putting current object on the stack
						stack[depth] = obj;
					}

					// processes one node
					// - key: key in object to proceed to
					// returns flag whether to terminate traversal
					function node(key) {
						var value = obj[key];
						if (i < last) {
							walk(value, i + 1, depth + 1);
						} else if (options.undef || typeof value !== 'undefined') {
							switch (typeof custom) {
							case 'undefined':
								// no handler, just collecting values
								switch (options.mode) {
								case flock.both:
									result[key] = value;
									break;
								case flock.keys:
									result.push(key);
									break;
								default:
								case flock.values:
									result.push(value);
									break;
								}
								break;
							case 'function':
								// calling custom handler on node
								value = custom(value);
								if (typeof value !== undefined) {
									obj[key] = value;
								}
								break;
							default:
								// assigning custom value to key
								obj[key] = custom;
								break;
							}
							if (--limit === 0) {
								return true;
							}
						}
						return false;
					}

					// processing next key in path
					key = tpath[i];
					if (key === '*') {
						// processing wildcard node
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (node(key)) {
									return;
								}
							}
						}
					} else if ((key === '' || key === null) && typeof obj === 'object') {
						// processing skipper node
						// must be object type as strings have indexes, too
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (key === tpath[i + 1]) {
									// current key matches next key in path
									// re-walking current object but leving skipper key
									walk(obj, i + 1, depth);
								} else {
									// current key doesn't match next key in path
									// walking next level, but staying on skipper key
									walk(obj[key], i, depth + 1);
								}
							}
						}
					} else if (key instanceof Array) {
						// processing list of nodes
						for (j = 0; j < key.length; j++) {
							if (node(key[j])) {
								return;
							}
						}
					} else {
						// processing single node
						key = tpath[i];
						if (!obj.hasOwnProperty(key) || node(key)) {
							return;
						}
					}
				}(root, 0, 0));

				return result;
			}
		};

		return self;
	};

	//////////////////////////////
	// Static variables

	flock.keys = 0;
	flock.values = 1;
	flock.both = 2;

	//////////////////////////////
	// Static methods

	// returns an array representation of the passed string
	// path format is: keys separated with dots
	// wildcards:
	// - '*': for one level
	// - '.' or '': until the adjacent key in path matches
	// example: 'contacts.smith.*.ancestors...name'
	// 					will get the names of all ancestor names for contacts w/ last name 'smith'
	flock.resolve = function (path) {
		// processing path
		if (typeof path === 'string') {
			// validating path
			if (path.length && !RE_PATH_VALIDATOR.test(path)) {
				throw "flock.resolve: invalid path";
			}

			var keys,
					i, key;

			// splitting along dots
			keys = path.length ? path.replace(RE_PATH_SKIPPER, function (match, offset) {
				return offset ? '..' : '.';
			}).split('.') : [];

			// splitting along commas to form multiple choice keys
			for (i = 0; i < keys.length; i++) {
				key = keys[i];
				if (key.indexOf(',') > -1) {
					keys[i] = key.split(',');
				}
			}

			return keys;
		} else {
			throw "flock.resolve: invalid argument";
		}
	};

	return flock;
}();

(exports || {}).flock = flock;

<commit_msg>refactored .many()<commit_after>////////////////////////////////////////////////////////////////////////////////
// Flock - JavaScript Key-Value Cache
//
// In-memory key-value store. Stores entire tree structure in memory.
// Use for complex lookups, as memcache on top of a persistence layer,
// or as an in-memory DHT node.
//
// https://github.com/wwidd/flock
////////////////////////////////////////////////////////////////////////////////
var	exports,

flock = function () {
	var RE_PATH_VALIDATOR = /^(\.{3})*([^\.,]+(\.{1,3}|,))*[^\.,]+$/,
			RE_PATH_SKIPPER = /\.{2,}/,

	// - root: root object for datastore
	flock = function (root) {
		// creating default root object
		if (typeof root === 'undefined') {
			root = {};
		}

		var self = {
			//////////////////////////////
			// Getters, setters

			root: function () {
				return root;
			},

			//////////////////////////////
			// Control

			// retrieves node at the given path
			// - path: string or array representing path
			// returns node on path
			// raises TypeError on invalid path
			get: function (path) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						tmp = root,
						i;

				// walking nodes along path
				for (i = 0; i < tpath.length; i++) {
					tmp = tmp[tpath[i]];
				}

				// returning value on end node
				return tmp;
			},

			// sets node at the given path
			// - path: array representing path
			// - value: value to be set at path
			// returns the node on the input path
			set: function (path, value) {
				var tpath = typeof path === 'object' ? path : flock.resolve(path),
						last = tpath.length - 1,
						key, node, child,
						i;

				if (!tpath.length) {
					throw "flock.set: empty path '" + path + "'";
				}

				// walking nodes along path
				for (i = 0, node = root; i < last; i++) {
					key = tpath[i];
					child = node[key];
					if (typeof child === 'undefined') {
						// creating node
						child = node[key] = {};
					}
					node = child;
				}

				// setting value on end node
				node[tpath[i]] = value;

				return node;
			},

			// removes a datastore element from the given path
			// - path: array representing element path
			// returns
			// - true: elem was present and deleted
			// - false: elem was not present
			unset: function (path) {
				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						key = tpath.pop(),
						parent = self.get(tpath);

				// deleting node when exists
				if (parent && parent.hasOwnProperty(key)) {
					delete parent[key];
					return true;
				} else {
					return false;
				}
			},

			// collects or modifies end nodes specified by a wildcard path
			// - path: path to end nodes, may contain wildcards "*"
			// - options:
			//	 - limit: max number of entries to retrieve, default: unlimited
			//	 - mode: type of return value is Object or Array (flock.key/flock.values/flock.both/flock.del), default: flock.array
			//	 - loopback: whether to traverse loopbacks, default: false
			//	 - undef: whether to collect undefined entries, default: false
			//	 - value: value to set, or callback function to execute on nodes
			//		 when undefined, function returns collected values
			many: function (path, options) {
				options = options || {};

				// setting defaults
				if (!options.value && typeof options.mode === 'undefined') {
					options.mode = flock.values;
				}

				var tpath = typeof path === 'object' ? path.concat([]) : flock.resolve(path),
						last = tpath.length - 1,
						limit = options.limit || 0,
						loopback = options.loopback || false,
						result = {2: {}, 4: 0}[options.mode] || [],
						stack = options.loopback ? null : [];

				// default case
				if (!tpath.length) {
					return root;
				}

				// collects end nodes
				// must be class level, creating a function on each call
				// to .multiget() may impact performance significantly
				// - obj: node data
				// - i: current position in path
				// - depth: current depth in tree
				(function walk(obj, i, depth) {
					var key, j;

					// detecting loopback
					if (!loopback) {
						for (j = 0; j < depth; j++) {
							if (obj === stack[j]) {
								return;
							}
						}
						// putting current object on the stack
						stack[depth] = obj;
					}

					// processes one node
					// - key: key in object to proceed to
					// returns flag whether to terminate traversal
					function node(key) {
						var value = obj[key];
						if (i < last) {
							walk(value, i + 1, depth + 1);
						} else if (options.undef || typeof value !== 'undefined') {
							switch (options.mode) {
							case flock.values:
								result.push(value);
								break;
							case flock.keys:
								result.push(key);
								break;
							case flock.both:
								result[key] = value;
								break;
							case flock.del:
								delete result[key];
								break;
							case flock.count:
								result++;
								break;
							default:
								if (typeof options.value === 'function') {
									// calling custom handler on node
									value = options.value(value);
									if (typeof value !== undefined) {
										obj[key] = value;
									}
								} else {
									// assigning custom value to key
									obj[key] = options.value;
								}
								break;
							}
							if (--limit === 0) {
								return true;
							}
						}
						return false;
					}

					// processing next key in path
					key = tpath[i];
					if (key === '*') {
						// processing wildcard node
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (node(key)) {
									return;
								}
							}
						}
					} else if ((key === '' || key === null) && typeof obj === 'object') {
						// processing skipper node
						// must be object type as strings have indexes, too
						for (key in obj) {
							if (obj.hasOwnProperty(key)) {
								if (key === tpath[i + 1]) {
									// current key matches next key in path
									// re-walking current object but leving skipper key
									walk(obj, i + 1, depth);
								} else {
									// current key doesn't match next key in path
									// walking next level, but staying on skipper key
									walk(obj[key], i, depth + 1);
								}
							}
						}
					} else if (key instanceof Array) {
						// processing list of nodes
						for (j = 0; j < key.length; j++) {
							if (node(key[j])) {
								return;
							}
						}
					} else {
						// processing single node
						key = tpath[i];
						if (!obj.hasOwnProperty(key) || node(key)) {
							return;
						}
					}
				}(root, 0, 0));

				return result;
			}
		};

		return self;
	};

	//////////////////////////////
	// Static variables

	flock.keys = 0;
	flock.values = 1;
	flock.both = 2;
	flock.del = 3;
	flock.count = 4;

	//////////////////////////////
	// Static methods

	// returns an array representation of the passed string
	// path format is: keys separated with dots
	// wildcards:
	// - '*': for one level
	// - '.' or '': until the adjacent key in path matches
	// example: 'contacts.smith.*.ancestors...name'
	// 					will get the names of all ancestor names for contacts w/ last name 'smith'
	flock.resolve = function (path) {
		// processing path
		if (typeof path === 'string') {
			// validating path
			if (path.length && !RE_PATH_VALIDATOR.test(path)) {
				throw "flock.resolve: invalid path";
			}

			var keys,
					i, key;

			// splitting along dots
			keys = path.length ? path.replace(RE_PATH_SKIPPER, function (match, offset) {
				return offset ? '..' : '.';
			}).split('.') : [];

			// splitting along commas to form multiple choice keys
			for (i = 0; i < keys.length; i++) {
				key = keys[i];
				if (key.indexOf(',') > -1) {
					keys[i] = key.split(',');
				}
			}

			return keys;
		} else {
			throw "flock.resolve: invalid argument";
		}
	};

	return flock;
}();

(exports || {}).flock = flock;

<|endoftext|>


<commit_before>var DateTime = require('dateutils').DateTime
var DateFormat = require('dateutils').DateFormat
var DateLocale = require('dateutils').DateLocale
var attachFastClick = require('fastclick')
var navigation = require('./navigation')
attachFastClick(document.body)
var markupForDateRange = require('./markupForDateRange')
var locations = require('../generated/locations')
const format = require('./format')
var didScroll = false
var alreadyLoadingMoreResults = false
var today = DateTime.fromIsoDate(window.serverDate)
var activeDate = today.plusDays(2)
const body = document.body
window.addEventListener('scroll', () => { didScroll = true })
setInterval(() => {
    if (didScroll) {
        didScroll = false
        if (!alreadyLoadingMoreResults && body.scrollTop + window.innerHeight > body.scrollHeight - 400) {
            loadMoreResults(5)
            ga('send', 'event', 'Scroll to end', today.distanceInDays(activeDate))
        }
    }
}, 250)
var schedule = document.getElementById('schedule')
navigation.init()
listAvailabilityForActiveDate(30)
var reservationModal = document.querySelector('.reservationModal')
reservationModal.addEventListener('click', e => {
    var clickArea = e.target
    if (clickArea.classList.contains('close')) {
        reservationModal.style.display = 'none'
    }
})
schedule.addEventListener('click', e => {
    var clickArea = e.target
    var openAction = clickArea.classList.contains('locationLabel')
    if (openAction) {
        var locationBoxes = clickArea.parentNode
        var fields = JSON.parse(locationBoxes.getAttribute('data-fields'))
        reservationModal.innerHTML = reservationModalMarkup(fields)
        reservationModal.style.display = 'block'
        var distance = today.distanceInDays(DateTime.fromIsoDate(fields.date))
        ga('send', 'event', 'Reservation', distance)
    }
})

elems('.locationMap .close, .information .close')
    .forEach(el => el.addEventListener('click', e => e.target.parentNode.style.display = 'none'))

function loadMoreResults(days) {
    if (!alreadyLoadingMoreResults) {
        alreadyLoadingMoreResults = true
        listAvailabilityForActiveDate(days)
    }
}

function reservationModalMarkup(obj) {
    var dateTime = DateTime.fromIsoDate(obj.date)
    var currentLocation = obj.location
    var locationObject = locations.find(location => location.title === currentLocation)
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel

    return `
${currentLocation}

${format.formatDate(dateTime)} klo ${obj.time}

Lisää kalenteriin

${obj.fields.map(toButtonMarkup).join('')}

Tiedot

        ${linksMarkup(locationObject)}

×
`

    function toButtonMarkup(field) {
        const urlParams = encodeUrl({
            location: currentLocation,
            field:    field.field,
            price:    field.price,
            tel:      tel,
            date:     obj.date,
            time:     obj.time,
            address:  address,
            url:      url
        })
        const classes = [
            'button',
            'fieldLabel',
            obj.location,
            field.type,
            format.durationClass(field.doubleLesson)
        ].join(' ')
        return `<a href="/calendar?${urlParams}" class="${classes}">${field.field}<br/>${format.formatPrice(field.price)}</a>`
    }
}

function linksMarkup(locationObject) {
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel
    return `

${tel}

${address}
` +
        (url ? `<div><a target="_blank" onclick="ga('send', 'event', 'Booking', '${locationObject.title}'); return true;" href="${url}">Siirry varausjärjestelmään</a></div>` : '') + '
'
}

function listAvailabilityForActiveDate(days) {
    var requestedDate = activeDate.toISODateString()
    activeDate = activeDate.plusDays(days - 1)
    schedule.classList.add('loading')
    alreadyLoadingMoreResults = true
    const urlParams = encodeUrl({
        date:    requestedDate,
        days:    days,
        refresh: window.refresh
    })
    getJson(`/courts?${urlParams}`, allDataWithDates => {
        schedule.classList.remove('loading')
        schedule.innerHTML += markupForDateRange(allDataWithDates, today)
        alreadyLoadingMoreResults = false
    })
}

function getJson(url, cb) {
    var request = new XMLHttpRequest()
    request.open('GET', url, true)
    request.onload = () => {
        if (request.status >= 200 && request.status < 400) {
            cb(JSON.parse(request.responseText))
        } else {
            console.error('Error with ajax request')
        }
    }
    request.onerror = () => console.error('There was a connection error of some sort')
    request.send()
}

function encodeUrl(obj) {
    return encodeURI(Object.keys(obj).map(k=>k + '=' + obj[k]).join('&'))
}

function elems(selector) { return Array.prototype.slice.call(document.querySelectorAll(selector)) }
<commit_msg>Remove unused dependency<commit_after>var DateTime = require('dateutils').DateTime
var attachFastClick = require('fastclick')
var navigation = require('./navigation')
attachFastClick(document.body)
var markupForDateRange = require('./markupForDateRange')
var locations = require('../generated/locations')
const format = require('./format')
var didScroll = false
var alreadyLoadingMoreResults = false
var today = DateTime.fromIsoDate(window.serverDate)
var activeDate = today.plusDays(2)
const body = document.body
window.addEventListener('scroll', () => { didScroll = true })
setInterval(() => {
    if (didScroll) {
        didScroll = false
        if (!alreadyLoadingMoreResults && body.scrollTop + window.innerHeight > body.scrollHeight - 400) {
            loadMoreResults(5)
            ga('send', 'event', 'Scroll to end', today.distanceInDays(activeDate))
        }
    }
}, 250)
var schedule = document.getElementById('schedule')
navigation.init()
listAvailabilityForActiveDate(30)
var reservationModal = document.querySelector('.reservationModal')
reservationModal.addEventListener('click', e => {
    var clickArea = e.target
    if (clickArea.classList.contains('close')) {
        reservationModal.style.display = 'none'
    }
})
schedule.addEventListener('click', e => {
    var clickArea = e.target
    var openAction = clickArea.classList.contains('locationLabel')
    if (openAction) {
        var locationBoxes = clickArea.parentNode
        var fields = JSON.parse(locationBoxes.getAttribute('data-fields'))
        reservationModal.innerHTML = reservationModalMarkup(fields)
        reservationModal.style.display = 'block'
        var distance = today.distanceInDays(DateTime.fromIsoDate(fields.date))
        ga('send', 'event', 'Reservation', distance)
    }
})

elems('.locationMap .close, .information .close')
    .forEach(el => el.addEventListener('click', e => e.target.parentNode.style.display = 'none'))

function loadMoreResults(days) {
    if (!alreadyLoadingMoreResults) {
        alreadyLoadingMoreResults = true
        listAvailabilityForActiveDate(days)
    }
}

function reservationModalMarkup(obj) {
    var dateTime = DateTime.fromIsoDate(obj.date)
    var currentLocation = obj.location
    var locationObject = locations.find(location => location.title === currentLocation)
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel

    return `
${currentLocation}

${format.formatDate(dateTime)} klo ${obj.time}

Lisää kalenteriin

${obj.fields.map(toButtonMarkup).join('')}

Tiedot

        ${linksMarkup(locationObject)}

×
`

    function toButtonMarkup(field) {
        const urlParams = encodeUrl({
            location: currentLocation,
            field:    field.field,
            price:    field.price,
            tel:      tel,
            date:     obj.date,
            time:     obj.time,
            address:  address,
            url:      url
        })
        const classes = [
            'button',
            'fieldLabel',
            obj.location,
            field.type,
            format.durationClass(field.doubleLesson)
        ].join(' ')
        return `<a href="/calendar?${urlParams}" class="${classes}">${field.field}<br/>${format.formatPrice(field.price)}</a>`
    }
}

function linksMarkup(locationObject) {
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel
    return `

${tel}

${address}
` +
        (url ? `<div><a target="_blank" onclick="ga('send', 'event', 'Booking', '${locationObject.title}'); return true;" href="${url}">Siirry varausjärjestelmään</a></div>` : '') + '
'
}

function listAvailabilityForActiveDate(days) {
    var requestedDate = activeDate.toISODateString()
    activeDate = activeDate.plusDays(days - 1)
    schedule.classList.add('loading')
    alreadyLoadingMoreResults = true
    const urlParams = encodeUrl({
        date:    requestedDate,
        days:    days,
        refresh: window.refresh
    })
    getJson(`/courts?${urlParams}`, allDataWithDates => {
        schedule.classList.remove('loading')
        schedule.innerHTML += markupForDateRange(allDataWithDates, today)
        alreadyLoadingMoreResults = false
    })
}

function getJson(url, cb) {
    var request = new XMLHttpRequest()
    request.open('GET', url, true)
    request.onload = () => {
        if (request.status >= 200 && request.status < 400) {
            cb(JSON.parse(request.responseText))
        } else {
            console.error('Error with ajax request')
        }
    }
    request.onerror = () => console.error('There was a connection error of some sort')
    request.send()
}

function encodeUrl(obj) {
    return encodeURI(Object.keys(obj).map(k=>k + '=' + obj[k]).join('&'))
}

function elems(selector) { return Array.prototype.slice.call(document.querySelectorAll(selector)) }
<|endoftext|>


<commit_before>var DateTime = require('dateutils').DateTime
var DateFormat = require('dateutils').DateFormat
var DateLocale = require('dateutils').DateLocale
var attachFastClick = require('fastclick')
var navigation = require('./navigation')
attachFastClick(document.body)
var markupForDateRange = require('./markupForDateRange')
var locations = require('../generated/locations')
const format = require('./format')
var didScroll = false
var alreadyLoadingMoreResults = false
var today = DateTime.fromIsoDate(window.serverDate)
var activeDate = today.plusDays(2)
const body = document.body
window.addEventListener('scroll', () => { didScroll = true })
setInterval(() => {
    if (didScroll) {
        didScroll = false
        if (!alreadyLoadingMoreResults && body.scrollTop + window.innerHeight > body.scrollHeight - 400) {
            loadMoreResults(5)
            ga('send', 'event', 'Scroll to end', today.distanceInDays(activeDate))
        }
    }
}, 250)
var schedule = document.getElementById('schedule')
navigation.init()
listAvailabilityForActiveDate(30)
var reservationModal = document.querySelector('.reservationModal')
reservationModal.addEventListener('click', e => {
    var clickArea = e.target
    if (clickArea.classList.contains('close')) {
        reservationModal.style.display = 'none'
    }
})
schedule.addEventListener('click', e => {
    var clickArea = e.target
    var openAction = clickArea.classList.contains('locationLabel')
    if (openAction) {
        var locationBoxes = clickArea.parentNode
        var fields = JSON.parse(locationBoxes.getAttribute('data-fields'))
        reservationModal.innerHTML = reservationModalMarkup(fields)
        reservationModal.style.display = 'block'
    }
    ga('send', 'event', 'Reservation', openAction ? 'open' : 'close')
})

document.querySelector('.locationMap .close').addEventListener('click', e => e.target.parentNode.style.display = 'none')
document.querySelector('.information .close').addEventListener('click', e => e.target.parentNode.style.display = 'none')

function loadMoreResults(days) {
    if (!alreadyLoadingMoreResults) {
        alreadyLoadingMoreResults = true
        listAvailabilityForActiveDate(days)
    }
}

function reservationModalMarkup(obj) {
    var dateTime = DateTime.fromIsoDate(obj.date)
    var currentLocation = obj.location
    var locationObject = locations.find(location => location.title === currentLocation)
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel

    return `
${currentLocation}

${format.formatDate(dateTime)} klo ${obj.time}

Lisää kalenteriin

${obj.fields.map(toButtonMarkup).join('')}

Tiedot

        ${linksMarkup(locationObject)}

×
`

    function toButtonMarkup(field) {
        const urlParams = encodeUrl({
            location: currentLocation,
            field:    field.field,
            price:    field.price,
            tel:      tel,
            date:     obj.date,
            time:     obj.time,
            address:  address,
            url:      url
        })
        return `    ${field.field}
${format.formatPrice(field.price)}`
    }
}

function linksMarkup(locationObject) {
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel
    return `

${tel}

${address}
` +
        (url ? `<div><a target="_blank" onclick="ga('send', 'event', 'Booking', '${locationObject.title}'); return true;" href="${url}">Siirry varausjärjestelmään</a></div>` : '') + '
'
}

function listAvailabilityForActiveDate(days) {
    var requestedDate = activeDate.toISODateString()
    activeDate = activeDate.plusDays(days - 1)
    schedule.classList.add('loading')
    alreadyLoadingMoreResults = true
    getJson(`/courts?date=${requestedDate}&days=${days}&refresh=${window.refresh}`, allDataWithDates => {
        schedule.classList.remove('loading')
        schedule.innerHTML += markupForDateRange(allDataWithDates, today)
        alreadyLoadingMoreResults = false
    })
}

function getJson(url, cb) {
    var request = new XMLHttpRequest()
    request.open('GET', url, true)
    request.onload = () => {
        if (request.status >= 200 && request.status < 400) {
            cb(JSON.parse(request.responseText))
        } else {
            console.error('Error with ajax request')
        }
    }
    request.onerror = () => console.error('There was a connection error of some sort')
    request.send()
}

function encodeUrl(obj) {
    return encodeURI(Object.keys(obj).map(k=>k + '=' + obj[k]).join('&'))
}
<commit_msg>Make class list more readable<commit_after>var DateTime = require('dateutils').DateTime
var DateFormat = require('dateutils').DateFormat
var DateLocale = require('dateutils').DateLocale
var attachFastClick = require('fastclick')
var navigation = require('./navigation')
attachFastClick(document.body)
var markupForDateRange = require('./markupForDateRange')
var locations = require('../generated/locations')
const format = require('./format')
var didScroll = false
var alreadyLoadingMoreResults = false
var today = DateTime.fromIsoDate(window.serverDate)
var activeDate = today.plusDays(2)
const body = document.body
window.addEventListener('scroll', () => { didScroll = true })
setInterval(() => {
    if (didScroll) {
        didScroll = false
        if (!alreadyLoadingMoreResults && body.scrollTop + window.innerHeight > body.scrollHeight - 400) {
            loadMoreResults(5)
            ga('send', 'event', 'Scroll to end', today.distanceInDays(activeDate))
        }
    }
}, 250)
var schedule = document.getElementById('schedule')
navigation.init()
listAvailabilityForActiveDate(30)
var reservationModal = document.querySelector('.reservationModal')
reservationModal.addEventListener('click', e => {
    var clickArea = e.target
    if (clickArea.classList.contains('close')) {
        reservationModal.style.display = 'none'
    }
})
schedule.addEventListener('click', e => {
    var clickArea = e.target
    var openAction = clickArea.classList.contains('locationLabel')
    if (openAction) {
        var locationBoxes = clickArea.parentNode
        var fields = JSON.parse(locationBoxes.getAttribute('data-fields'))
        reservationModal.innerHTML = reservationModalMarkup(fields)
        reservationModal.style.display = 'block'
    }
    ga('send', 'event', 'Reservation', openAction ? 'open' : 'close')
})

document.querySelector('.locationMap .close').addEventListener('click', e => e.target.parentNode.style.display = 'none')
document.querySelector('.information .close').addEventListener('click', e => e.target.parentNode.style.display = 'none')

function loadMoreResults(days) {
    if (!alreadyLoadingMoreResults) {
        alreadyLoadingMoreResults = true
        listAvailabilityForActiveDate(days)
    }
}

function reservationModalMarkup(obj) {
    var dateTime = DateTime.fromIsoDate(obj.date)
    var currentLocation = obj.location
    var locationObject = locations.find(location => location.title === currentLocation)
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel

    return `
${currentLocation}

${format.formatDate(dateTime)} klo ${obj.time}

Lisää kalenteriin

${obj.fields.map(toButtonMarkup).join('')}

Tiedot

        ${linksMarkup(locationObject)}

×
`

    function toButtonMarkup(field) {
        const urlParams = encodeUrl({
            location: currentLocation,
            field:    field.field,
            price:    field.price,
            tel:      tel,
            date:     obj.date,
            time:     obj.time,
            address:  address,
            url:      url
        })
        const classes = [
            'button',
            'fieldLabel',
            obj.location,
            field.type,
            format.durationClass(field.doubleLesson)
        ].join(' ')
        return `<a href="/calendar?${urlParams}" class="${classes}">${field.field}<br/>${format.formatPrice(field.price)}</a>`
    }
}

function linksMarkup(locationObject) {
    var address = locationObject.address
    var url = locationObject.url
    var tel = locationObject.tel
    return `

${tel}

${address}
` +
        (url ? `<div><a target="_blank" onclick="ga('send', 'event', 'Booking', '${locationObject.title}'); return true;" href="${url}">Siirry varausjärjestelmään</a></div>` : '') + '
'
}

function listAvailabilityForActiveDate(days) {
    var requestedDate = activeDate.toISODateString()
    activeDate = activeDate.plusDays(days - 1)
    schedule.classList.add('loading')
    alreadyLoadingMoreResults = true
    getJson(`/courts?date=${requestedDate}&days=${days}&refresh=${window.refresh}`, allDataWithDates => {
        schedule.classList.remove('loading')
        schedule.innerHTML += markupForDateRange(allDataWithDates, today)
        alreadyLoadingMoreResults = false
    })
}

function getJson(url, cb) {
    var request = new XMLHttpRequest()
    request.open('GET', url, true)
    request.onload = () => {
        if (request.status >= 200 && request.status < 400) {
            cb(JSON.parse(request.responseText))
        } else {
            console.error('Error with ajax request')
        }
    }
    request.onerror = () => console.error('There was a connection error of some sort')
    request.send()
}

function encodeUrl(obj) {
    return encodeURI(Object.keys(obj).map(k=>k + '=' + obj[k]).join('&'))
}
<|endoftext|>