<commit_before>/*
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      This file is part of the Smart Developer Hub Project:
        http://www.smartdeveloperhub.org/
      Center for Open Middleware
            http://www.centeropenmiddleware.com/
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Copyright (C) 2015 Center for Open Middleware.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
                http://www.apache.org/licenses/LICENSE-2.0
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
*/

(function() {

    var CommonWidget = function CommonWidget(extending, container) {

        if (extending === true) {
            return;
        }

        //First of all, register this widget with the dashboard
        framework.dashboard.registerWidget(this);

        this._common = {};
        this._common.isloading = 0;
        this._common.callback = null;
        this._common.secureEndTimer = null;
        this._common.disposed = false;
        this._common.container = container;
        this._common.resizeHandler = null;

        this._common.loadingContainer = document.createElement('div');
        this._common.loadingContainer.className ='loadingContainer';
        var loadingLayer = document.createElement('div');
        loadingLayer.className ='loadingLayer';
        var spinner = document.createElement('i');
        spinner.className ='fa fa-spinner fa-pulse';

        this._common.loadingContainer.appendChild(loadingLayer);
        loadingLayer.appendChild(spinner);
        this._common.container.appendChild(this._common.loadingContainer);
        this._common.loadingLayer = loadingLayer;

        this.restoreContainerHandler = function restoreContainerHandler(e) {
            clearTimeout(this._common.secureEndTimer);
            this._common.loadingLayer.removeEventListener('transitionend', this.restoreContainerHandler);
            $(this._common.container).removeClass('blurMode');
            this._common.container.style.position = oldposstyle;
            window.removeEventListener("resize", this._common.resizeHandler);
            this._common.resizeHandler = null;
            if (typeof this._common.callback == 'function' && !this._common.disposed) {
                this._common.callback();
            }
        }.bind(this);
    };

    var oldContainerClass, oldposstyle;

    // Set the global and set it to be reset on every change of dashboard
    CommonWidget.prototype.previousColors = {};
    framework.ready(function() {
        framework.dashboard.addEventListener('change', function () {
            CommonWidget.prototype.previousColors = {};
        });
    });

    CommonWidget.prototype.startLoading = function startLoading() {
        this._common.isloading += 1;
        if (this._common.isloading > 1) {
            return;
        }
        if (!oldposstyle) {
            oldposstyle = this._common.container.style.position;
        }
        this._common.container.style.position = 'relative';
        setLoadingSize.call(this);
        this._common.resizeHandler = resizeHandler.bind(this);
        window.addEventListener("resize", this._common.resizeHandler);
        $(this._common.container).addClass('blurMode');
        $(this._common.loadingLayer).addClass('on');

    };

    /*
    The transitionend event doesn't fire if the transition is aborted before
    the transition is completed because either the element is made display: none
    or the animating property's value is changed.
    */
    CommonWidget.prototype.endLoading = function endLoading(callback) {
        this._common.isloading -= 1;
        if(this._common.isloading == 0) {
            this._common.callback = callback;
            this._common.loadingLayer.addEventListener('transitionend', this.restoreContainerHandler);
            setTimeout(function() {
                $(this._common.loadingLayer).removeClass('on')
            }.bind(this), 100);
            this._common.secureEndTimer = setTimeout(function() {
                this.restoreContainerHandler();
            }.bind(this), 600);
        } else {
            console.log('discarding data...');
        }
    };

    CommonWidget.prototype.extractMetrics = function extractMetrics(framework_data) {

        var values = [];

        for(var metricId in framework_data) {

            for(var m = 0; m < framework_data[metricId].length; ++m) {

                var metricData = framework_data[metricId][m]['data'];

                if(typeof metricData === 'object' && metricData['values'] != null) {
                    for(var k = 0; k < metricData['values'].length; k++) {
                        values.push(metricData['values'][k]);
                    }
                }

            }
        }

        return values;

    };

    CommonWidget.prototype.extractData = function extractData(framework_data) {

        var values = [];

        for(var metricId in framework_data) {

            for(var m = 0; m < framework_data[metricId].length; ++m) {

                var metricData = framework_data[metricId][m]['data'];

                if(metricData instanceof Array) {
                    for(var k = 0; k < metricData.length; k++) {
                        values.push(metricData[k]);
                    }
                } else if(typeof metricData === 'object' && metricData['values'] == null) {
                    values.push(metricData);
                }

            }
        }

        return values;

    };

    CommonWidget.prototype.extractAll = function extractAll(framework_data) {

        return [].concat(this.extractData(framework_data), this.extractMetrics(framework_data));
    };

    var inArray = function inArray(str, array) {
        for(var c = 0; c < array.length; ++c) {
            if(array[c] === str) {
                return true;
            }
        }

        return false;
    };

    /**
     * Generates colors for the chart given the data received from the framework and a palette. This method must be
     * used to keep the same colors for the same resources among widget updates.
     * @param framework_data Data received from the framework
     * @param palette Palette of colors to use. It can be an array of colors or an object hash map. In case of a
     * hash map, the key refers to the id of the resource and the value refers to the color that it must have. This case
     * can be used to force colors instead of selecting them randomly from a palette as in the case of the array.
     * @returns {Array} Array of colors keeping the order of the resources in the data from the framework.
     */
    CommonWidget.prototype.generateColors = function generateColors(framework_data, palette) {

        var newPreviousColors = {};
        palette = palette || d3.scale.category20().range();

        var colors = [];
        var forcedColors = false;
        var usedColorIndexes = {};
        for(var id in CommonWidget.prototype.previousColors){
            usedColorIndexes[CommonWidget.prototype.previousColors[id]] = true;
        }

        if(!(palette instanceof Array) && palette instanceof Object) {
            forcedColors = true;
        }

        var currentColorIndex = -1;

        for(var metricId in framework_data) {

            for (var m = 0; m < framework_data[metricId].length; ++m) {

                if(forcedColors) { //Colors assigned 'by hand' depending of the resource
                    colors.push(palette[metricId]);

                } else { //Colors assigned from a palette

                    var UID = framework_data[metricId][m]['info']['UID'];

                    if(CommonWidget.prototype.previousColors[UID] != null && newPreviousColors[UID] == null) { //Use the previous color
                        colors.push(palette[CommonWidget.prototype.previousColors[UID] % palette.length]);

                    } else { //Try to assign an unused color

                        while(true) {

                            if(!usedColorIndexes[++currentColorIndex]) {
                                CommonWidget.prototype.previousColors[UID] = currentColorIndex;
                                colors.push(palette[currentColorIndex % palette.length]);
                                break;
                            }

                        }

                    }
                }

            }

        }

        return colors;

    };

    /**
     * Generic observe methods that should be used in the widget as it controls concurrency problems.
     * When new data is received, the updateData method is called.
     * @param event
     */
    CommonWidget.prototype.commonObserveCallback = function commonObserveCallback(event) {

        if(this._common.disposed) {
            return;
        }

        if(event.event === 'loading') {
            this.startLoading();
        } else if(event.event === 'data') {

            //Check if there is any metric that needs to be filled with zeros
            for(var resourceId in event.data) {
                for(var i in event.data[resourceId]){
                    var resource = event.data[resourceId][i];

                    //Only metrics need to be checked
                    if(isMetric(resource)) {
                        zeroFillMetric(resource);
                    }
                }
            }

            this.endLoading(this.updateData.bind(this, event.data));
        }

    };

    /**
     * Sets the widget as disposed.
     * @param event
     */
    CommonWidget.prototype.dispose = function dispose() {
        this._common.disposed = true;
    };

    // ---------------------------
    // ------PRIVATE METHODS------
    // ---------------------------

    var setLoadingSize = function setLoadingSize() {
        var wsize = this._common.container.getBoundingClientRect();
        // center the spinner vertically because a responsive
        // widget can change it height dynamically
        this._common.loadingLayer.style.lineHeight = wsize.height + 'px';
        this._common.loadingContainer.style.height = wsize.height + 'px';
        this._common.loadingContainer.style.width = wsize.width + 'px';
        if(this._common.loadingContainer.getBoundingClientRect().top == 0) {
            this._common.loadingContainer.style.top = wsize.top + 'px';
        }
        this._common.loadingContainer.style.left = 'auto';
    };

    var resizeHandler = function resizeHandler(e) {
        setLoadingSize.call(this);
    };

    var isNumber = function isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    };

    var isMetric = function isMetric(resource) {
        var resourceData = resource.data;
        return resourceData != null && resourceData.values instanceof Array && isNumber(resourceData.values[0]);
    };

    var zeroFillMetric = function zeroFillMetric(resource) {

        var resourceInterval = resource['data']['interval'];
        var requestedInterval = {
            from: resource['info']['request']['params']['from'],
            to: resource['info']['request']['params']['to']
        };

        var step = resource['data']['step'];
        var values = resource['data']['values'];

        //We need step to be a number
        if(!isNumber(step)){
            return;
        }
        step = Number(step);

        // Check 'from'
        if(resourceInterval['from'] != null && requestedInterval['from'] != null) {

            //Make sure they are numbers
            resourceInterval['from'] = Number(resourceInterval['from']);
            requestedInterval['from'] = Number(requestedInterval['from']);

            // We need to add zeros
            if(requestedInterval['from'] < resourceInterval['from']) {
                var diff = resourceInterval['from'] - requestedInterval['from'];
                var nZeros = Math.floor(diff/step);

                //Add the calculated number of zeros
                for(var i = nZeros; i > 0; --i) {
                    values.unshift(0);
                }

                //Update the new from
                resourceInterval['from'] -= nZeros * step;
            }

        }

        // Check 'to'
        if(resourceInterval['to'] != null && requestedInterval['to'] != null) {

            //Make sure they are numbers
            resourceInterval['to'] = Number(resourceInterval['to']);
            requestedInterval['to'] = Number(requestedInterval['to']);

            // We need to add zeros
            if(requestedInterval['to'] > resourceInterval['to']) {
                var diff = requestedInterval['to'] - resourceInterval['to'];
                var nZeros = Math.floor(diff/step);

                //Add the calculated number of zeros
                for(var i = nZeros; i > 0; --i) {
                    values.push(0);
                }

                //Update the new from
                resourceInterval['from'] += nZeros * step;
            }

        }

    };

    window.framework.widgets.CommonWidget = CommonWidget;

    // AMD compliant
    if ( typeof define === "function" && define.amd) {
        define( ['css!sdh-framework/framework.widget.common'], function () { return CommonWidget; } );
    }

})();<commit_msg>[Widget][Common] Added improved replace method that supports Javascript.<commit_after>/*
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      This file is part of the Smart Developer Hub Project:
        http://www.smartdeveloperhub.org/
      Center for Open Middleware
            http://www.centeropenmiddleware.com/
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Copyright (C) 2015 Center for Open Middleware.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
                http://www.apache.org/licenses/LICENSE-2.0
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
*/

(function() {

    var CommonWidget = function CommonWidget(extending, container) {

        if (extending === true) {
            return;
        }

        //First of all, register this widget with the dashboard
        framework.dashboard.registerWidget(this);

        this._common = {};
        this._common.isloading = 0;
        this._common.callback = null;
        this._common.secureEndTimer = null;
        this._common.disposed = false;
        this._common.container = container;
        this._common.resizeHandler = null;

        this._common.loadingContainer = document.createElement('div');
        this._common.loadingContainer.className ='loadingContainer';
        var loadingLayer = document.createElement('div');
        loadingLayer.className ='loadingLayer';
        var spinner = document.createElement('i');
        spinner.className ='fa fa-spinner fa-pulse';

        this._common.loadingContainer.appendChild(loadingLayer);
        loadingLayer.appendChild(spinner);
        this._common.container.appendChild(this._common.loadingContainer);
        this._common.loadingLayer = loadingLayer;

        this.restoreContainerHandler = function restoreContainerHandler(e) {
            clearTimeout(this._common.secureEndTimer);
            this._common.loadingLayer.removeEventListener('transitionend', this.restoreContainerHandler);
            $(this._common.container).removeClass('blurMode');
            this._common.container.style.position = oldposstyle;
            window.removeEventListener("resize", this._common.resizeHandler);
            this._common.resizeHandler = null;
            if (typeof this._common.callback == 'function' && !this._common.disposed) {
                this._common.callback();
            }
        }.bind(this);
    };

    var oldContainerClass, oldposstyle;

    // Set the global and set it to be reset on every change of dashboard
    CommonWidget.prototype.previousColors = {};
    framework.ready(function() {
        framework.dashboard.addEventListener('change', function () {
            CommonWidget.prototype.previousColors = {};
        });
    });

    CommonWidget.prototype.startLoading = function startLoading() {
        this._common.isloading += 1;
        if (this._common.isloading > 1) {
            return;
        }
        if (!oldposstyle) {
            oldposstyle = this._common.container.style.position;
        }
        this._common.container.style.position = 'relative';
        setLoadingSize.call(this);
        this._common.resizeHandler = resizeHandler.bind(this);
        window.addEventListener("resize", this._common.resizeHandler);
        $(this._common.container).addClass('blurMode');
        $(this._common.loadingLayer).addClass('on');

    };

    /*
    The transitionend event doesn't fire if the transition is aborted before
    the transition is completed because either the element is made display: none
    or the animating property's value is changed.
    */
    CommonWidget.prototype.endLoading = function endLoading(callback) {
        this._common.isloading -= 1;
        if(this._common.isloading == 0) {
            this._common.callback = callback;
            this._common.loadingLayer.addEventListener('transitionend', this.restoreContainerHandler);
            setTimeout(function() {
                $(this._common.loadingLayer).removeClass('on')
            }.bind(this), 100);
            this._common.secureEndTimer = setTimeout(function() {
                this.restoreContainerHandler();
            }.bind(this), 600);
        } else {
            console.log('discarding data...');
        }
    };

    CommonWidget.prototype.extractMetrics = function extractMetrics(framework_data) {

        var values = [];

        for(var metricId in framework_data) {

            for(var m = 0; m < framework_data[metricId].length; ++m) {

                var metricData = framework_data[metricId][m]['data'];

                if(typeof metricData === 'object' && metricData['values'] != null) {
                    for(var k = 0; k < metricData['values'].length; k++) {
                        values.push(metricData['values'][k]);
                    }
                }

            }
        }

        return values;

    };

    CommonWidget.prototype.extractData = function extractData(framework_data) {

        var values = [];

        for(var metricId in framework_data) {

            for(var m = 0; m < framework_data[metricId].length; ++m) {

                var metricData = framework_data[metricId][m]['data'];

                if(metricData instanceof Array) {
                    for(var k = 0; k < metricData.length; k++) {
                        values.push(metricData[k]);
                    }
                } else if(typeof metricData === 'object' && metricData['values'] == null) {
                    values.push(metricData);
                }

            }
        }

        return values;

    };

    CommonWidget.prototype.extractAll = function extractAll(framework_data) {

        return [].concat(this.extractData(framework_data), this.extractMetrics(framework_data));
    };

    var inArray = function inArray(str, array) {
        for(var c = 0; c < array.length; ++c) {
            if(array[c] === str) {
                return true;
            }
        }

        return false;
    };

    /**
     * Generates colors for the chart given the data received from the framework and a palette. This method must be
     * used to keep the same colors for the same resources among widget updates.
     * @param framework_data Data received from the framework
     * @param palette Palette of colors to use. It can be an array of colors or an object hash map. In case of a
     * hash map, the key refers to the id of the resource and the value refers to the color that it must have. This case
     * can be used to force colors instead of selecting them randomly from a palette as in the case of the array.
     * @returns {Array} Array of colors keeping the order of the resources in the data from the framework.
     */
    CommonWidget.prototype.generateColors = function generateColors(framework_data, palette) {

        var newPreviousColors = {};
        palette = palette || d3.scale.category20().range();

        var colors = [];
        var forcedColors = false;
        var usedColorIndexes = {};
        for(var id in CommonWidget.prototype.previousColors){
            usedColorIndexes[CommonWidget.prototype.previousColors[id]] = true;
        }

        if(!(palette instanceof Array) && palette instanceof Object) {
            forcedColors = true;
        }

        var currentColorIndex = -1;

        for(var metricId in framework_data) {

            for (var m = 0; m < framework_data[metricId].length; ++m) {

                if(forcedColors) { //Colors assigned 'by hand' depending of the resource
                    colors.push(palette[metricId]);

                } else { //Colors assigned from a palette

                    var UID = framework_data[metricId][m]['info']['UID'];

                    if(CommonWidget.prototype.previousColors[UID] != null && newPreviousColors[UID] == null) { //Use the previous color
                        colors.push(palette[CommonWidget.prototype.previousColors[UID] % palette.length]);

                    } else { //Try to assign an unused color

                        while(true) {

                            if(!usedColorIndexes[++currentColorIndex]) {
                                CommonWidget.prototype.previousColors[UID] = currentColorIndex;
                                colors.push(palette[currentColorIndex % palette.length]);
                                break;
                            }

                        }

                    }
                }

            }

        }

        return colors;

    };

    /**
     * Generic observe methods that should be used in the widget as it controls concurrency problems.
     * When new data is received, the updateData method is called.
     * @param event
     */
    CommonWidget.prototype.commonObserveCallback = function commonObserveCallback(event) {

        if(this._common.disposed) {
            return;
        }

        if(event.event === 'loading') {
            this.startLoading();
        } else if(event.event === 'data') {

            //Check if there is any metric that needs to be filled with zeros
            for(var resourceId in event.data) {
                for(var i in event.data[resourceId]){
                    var resource = event.data[resourceId][i];

                    //Only metrics need to be checked
                    if(isMetric(resource)) {
                        zeroFillMetric(resource);
                    }
                }
            }

            this.endLoading(this.updateData.bind(this, event.data));
        }

    };

    /**
     * Sets the widget as disposed.
     * @param event
     */
    CommonWidget.prototype.dispose = function dispose() {
        this._common.disposed = true;
    };

    CommonWidget.prototype.replacer = function(resourceId, resource, str) {

        //Remove the initial an trailing '%' of the string
        str = str.substring(1, str.length-1);

        //Check if it is a parameter an return its value
        if(str === "resourceId") { //Special command to indicate the name of the resource
            return resourceId;

        } else { // Obtain its value through the object given the path

            var _$ = resource;

            return eval(str);

        }

    };

    //TODO:
    CommonWidget.prototype.replace = function(string, data, resourceId) {

        var codeExpression = /¬([^¬]|_%)+¬/g;

        //Create a replacer for this metric
        var metricReplacer = this.replacer.bind(null, (resourceId || "???"), data);

        //Generate the label by replacing the variables
        return string.replace(codeExpression,metricReplacer);
    };

    // ---------------------------
    // ------PRIVATE METHODS------
    // ---------------------------

    var setLoadingSize = function setLoadingSize() {
        var wsize = this._common.container.getBoundingClientRect();
        // center the spinner vertically because a responsive
        // widget can change it height dynamically
        this._common.loadingLayer.style.lineHeight = wsize.height + 'px';
        this._common.loadingContainer.style.height = wsize.height + 'px';
        this._common.loadingContainer.style.width = wsize.width + 'px';
        if(this._common.loadingContainer.getBoundingClientRect().top == 0) {
            this._common.loadingContainer.style.top = wsize.top + 'px';
        }
        this._common.loadingContainer.style.left = 'auto';
    };

    var resizeHandler = function resizeHandler(e) {
        setLoadingSize.call(this);
    };

    var isNumber = function isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    };

    var isMetric = function isMetric(resource) {
        var resourceData = resource.data;
        return resourceData != null && resourceData.values instanceof Array && isNumber(resourceData.values[0]);
    };

    var zeroFillMetric = function zeroFillMetric(resource) {

        var resourceInterval = resource['data']['interval'];
        var requestedInterval = {
            from: resource['info']['request']['params']['from'],
            to: resource['info']['request']['params']['to']
        };

        var step = resource['data']['step'];
        var values = resource['data']['values'];

        //We need step to be a number
        if(!isNumber(step)){
            return;
        }
        step = Number(step);

        // Check 'from'
        if(resourceInterval['from'] != null && requestedInterval['from'] != null) {

            //Make sure they are numbers
            resourceInterval['from'] = Number(resourceInterval['from']);
            requestedInterval['from'] = Number(requestedInterval['from']);

            // We need to add zeros
            if(requestedInterval['from'] < resourceInterval['from']) {
                var diff = resourceInterval['from'] - requestedInterval['from'];
                var nZeros = Math.floor(diff/step);

                //Add the calculated number of zeros
                for(var i = nZeros; i > 0; --i) {
                    values.unshift(0);
                }

                //Update the new from
                resourceInterval['from'] -= nZeros * step;
            }

        }

        // Check 'to'
        if(resourceInterval['to'] != null && requestedInterval['to'] != null) {

            //Make sure they are numbers
            resourceInterval['to'] = Number(resourceInterval['to']);
            requestedInterval['to'] = Number(requestedInterval['to']);

            // We need to add zeros
            if(requestedInterval['to'] > resourceInterval['to']) {
                var diff = requestedInterval['to'] - resourceInterval['to'];
                var nZeros = Math.floor(diff/step);

                //Add the calculated number of zeros
                for(var i = nZeros; i > 0; --i) {
                    values.push(0);
                }

                //Update the new from
                resourceInterval['from'] += nZeros * step;
            }

        }

    };

    window.framework.widgets.CommonWidget = CommonWidget;

    // AMD compliant
    if ( typeof define === "function" && define.amd) {
        define( ['css!sdh-framework/framework.widget.common'], function () { return CommonWidget; } );
    }

})();<|endoftext|>


<commit_before>
let _ = require('underscore')
let plugin = require('..').plugin

let consoleCommands = plugin.extensionPoints['console.command']
let lineParsers = plugin.extensionPoints['console.line']

class Repl {
  constructor () {
    this.repl = new GameRepl(
      plugin.extensionPoints['console.command'],
      plugin.extensionPoints['console.command.token']
    )
  }

  evaluateLine (line, println) {
    let parsers = lineParsers.array
    for (let i=parsers.length - 1; i >= 0; i--) {
      let parser = parsers[i]
      let result = parser(line, println)
      if (result !== undefined) {
        return Promise.resolve(result)
      }
    }
    return Promise.reject(new Error('SYNTAX_ERROR'))
  }

  evaluateLineWithTokens (line, println) {
    return Promise.resolve(this.repl.gameEvaluateLine(println, line))
  }
}

let stopWords = new Set(['a', 'the', 'with', 'to'])

class GameRepl {

  constructor (commandsEP, argsEP) {
    commandsEP.onAdd((cmd) => {
      cmd.tokens = this.tokenize(cmd.command)
      delete this._sortedCommands
    })
    this.commandsEP = commandsEP
    this.argsEP = argsEP
  }

  get commands () {
    let s = this._sortedCommands
    if (s) {
      return s
    }
    this._sortedCommands = s = this.commandsEP.array
    s.sort((a, b) => {
      let delta
      delta = b.tokens.length - a.tokens.length
      return delta
    })
    return s
  }

  tokenize (line) {
    return line.split(/\s+/).filter((t) => t && !stopWords.has(t))
  }

  gameEvaluateLine (output, line) {
    let ctx = {
      args: [],
      consumed: [],
    }

    let tokens = this.tokenize(line)
    let command = this._matchCommand(tokens, ctx)

    if (!_.isFunction(command)) {
      // TODO better error handling.
      return
    }

    let args = ctx.args

    // I wish ...rest operators worked.
    // command(output, ...args, tokens)
    args.unshift(output)
    args.push(tokens)

    // the command is contributed from someplace else, so
    // we should be careful handling it.
    try {
      command.apply(null, args)
      return true
    } catch (e) {
      console.error(e.stack)
    }

  }

  _undo (ctx, tokens) {
    ctx.consumed.reverse().forEach((t) => {
      tokens.unshift(t)
    })
  }

  _appendArray(array, extender) {
    for (let ex of extender) {
      array.push(ex)
    }
    return array
  }

  _matchCommand (tokens, ctx) {
    let commands = this.commands

    return commands.find((command) => {
      let stashedConsumed = ctx.consumed
      ctx.consumed = []
      ctx.args = []

      let commandTokens = command.tokens
      // console.log('testing ' + command.line)
      for (let commandToken of commandTokens) {
        let success = true
        // console.log('\ttokens', tokens)
        let arg = this._matchArg(commandToken, tokens, ctx)
        if (arg) {
          // console.log('\t', commandToken, 'matches', arg)
          ctx.args.push(arg)
        } else if (commandToken.indexOf(tokens[0]) === 0) {
          // console.log('\t' + commandToken + ' matches user token')
          ctx.consumed.push(tokens.shift())
        } else {
          // console.log('\t\'' + tokens[0] + '\' does not match \'' + commandToken + '\'')
          this._undo(ctx, tokens)
          ctx.consumed = stashedConsumed
          return
        }
      }
      ctx.consumed = this._appendArray(stashedConsumed, ctx.consumed)
      return command
    })
  }

  _matchArg (commandToken, tokens, ctx) {
    let matchers = this.argsEP.object
    let argMatcher = matchers[commandToken]
    if (!argMatcher) {
      return
    }

    // command.line are really token types
    // get ITEM
    // so we want to check the item store
    // but don't want to do all this string parsing
    // for each thing we want to reference in the language.
    // so allow upto three words as ids or shortnames.
    let tokenIndex,
        max = Math.max(10, tokens.length - 1)

    let consumed = []
    let arg

    for (tokenIndex = 0; tokenIndex < max && !arg; tokenIndex++) {
      consumed.push(tokens[tokenIndex])
      let id = consumed.join(' ')
      arg = argMatcher(id)
    }

    if (arg) {
      tokens.splice(0, tokenIndex)
      if (ctx) {
        this._appendArray(ctx.consumed, consumed)
      }
    } else {
      //console.log('\t' + consumed.join(' ') + ' did not match ' + commandToken)
    }
    return arg
  }
}

module.exports = {
  Repl,
  GameRepl,
}<commit_msg>Pass the context around. In compo-game this was the player. In compo-contrib-console this is the output object.<commit_after>
let _ = require('underscore')
let plugin = require('..').plugin

let consoleCommands = plugin.extensionPoints['console.command']
let lineParsers = plugin.extensionPoints['console.line']

class Repl {
  constructor () {
    this.repl = new GameRepl(
      plugin.extensionPoints['console.command'],
      plugin.extensionPoints['console.command.token']
    )
  }

  evaluateLine (line, println) {
    let parsers = lineParsers.array
    for (let i=parsers.length - 1; i >= 0; i--) {
      let parser = parsers[i]
      let result = parser(line, println)
      if (result !== undefined) {
        return Promise.resolve(result)
      }
    }
    return Promise.reject(new Error('SYNTAX_ERROR'))
  }

  evaluateLineWithTokens (line, println) {
    return Promise.resolve(this.repl.gameEvaluateLine(println, line))
  }
}

let stopWords = new Set(['a', 'the', 'with', 'to'])

class GameRepl {

  constructor (commandsEP, argsEP, stopWordsEP) {
    commandsEP.onAdd((cmd) => {
      cmd.tokens = this.tokenize(cmd.command)
      delete this._sortedCommands
    })
    commandsEP.onRemove((cmd) => {
      delete this._sortedCommands
    })
    this.commandsEP = commandsEP
    this.argsEP = argsEP
  }

  get commands () {
    let s = this._sortedCommands
    if (s) {
      return s
    }
    this._sortedCommands = s = this.commandsEP.array
    s.sort((a, b) => {
      let delta
      delta = b.tokens.length - a.tokens.length
      return delta
    })
    return s
  }

  tokenize (line) {
    return line.split(/\s+/).filter((t) => t && !stopWords.has(t))
  }

  gameEvaluateLine (output, line) {
    // output is not used at all in the parser,
    // so it's upto the client of the parser what to pass in.
    let ctx = {
      args: [],
      consumed: [],
      evalContext: output,
    }

    let tokens = this.tokenize(line)
    let command = this._matchCommand(tokens, ctx)

    if (!_.isFunction(command)) {
      // TODO better error handling.
      return
    }

    let args = ctx.args

    // I wish ...rest operators worked.
    // command(output, ...args, tokens)
    args.unshift(output)
    args.push(tokens)

    // the command is contributed from someplace else, so
    // we should be careful handling it.
    try {
      command.apply(null, args)
      return true
    } catch (e) {
      console.error(e.stack)
    }

  }

  _undo (ctx, tokens) {
    ctx.consumed.reverse().forEach((t) => {
      tokens.unshift(t)
    })
  }

  _appendArray(array, extender) {
    for (let ex of extender) {
      array.push(ex)
    }
    return array
  }

  _matchCommand (tokens, ctx) {
    let commands = this.commands

    return commands.find((command) => {
      let stashedConsumed = ctx.consumed
      ctx.consumed = []
      ctx.args = []

      let commandTokens = command.tokens
      // console.log('testing ' + command.line)
      for (let commandToken of commandTokens) {
        let success = true
        // console.log('\ttokens', tokens)
        let arg = this._matchArg(commandToken, tokens, ctx)
        if (arg) {
          // console.log('\t', commandToken, 'matches', arg)
          ctx.args.push(arg)
        } else if (commandToken.indexOf(tokens[0]) === 0) {
          // console.log('\t' + commandToken + ' matches user token')
          ctx.consumed.push(tokens.shift())
        } else {
          // console.log('\t\'' + tokens[0] + '\' does not match \'' + commandToken + '\'')
          this._undo(ctx, tokens)
          ctx.consumed = stashedConsumed
          return
        }
      }
      ctx.consumed = this._appendArray(stashedConsumed, ctx.consumed)
      return command
    })
  }

  _matchArg (commandToken, tokens, ctx) {
    let matchers = this.argsEP.object
    let argMatcher = matchers[commandToken]
    if (!argMatcher) {
      return
    }

    // command.line are really token types
    // get ITEM
    // so we want to check the item store
    // but don't want to do all this string parsing
    // for each thing we want to reference in the language.
    // so allow upto three words as ids or shortnames.
    let tokenIndex,
        max = Math.max(10, tokens.length - 1)

    let consumed = [],
        evalContext = ctx.evalContext
    let arg

    for (tokenIndex = 0; tokenIndex < max && !arg; tokenIndex++) {
      consumed.push(tokens[tokenIndex])
      let id = consumed.join(' ')
      arg = argMatcher(id, evalContext)
    }

    if (arg) {
      tokens.splice(0, tokenIndex)
      if (ctx) {
        this._appendArray(ctx.consumed, consumed)
      }
    } else {
      //console.log('\t' + consumed.join(' ') + ' did not match ' + commandToken)
    }
    return arg
  }
}

module.exports = {
  Repl,
  GameRepl,
}<|endoftext|>


<commit_before>//////////////////////////////////////////////////////
// Swipe Gestures for ChocolateChip.
// Includes mouse gestures for desktop compatibility.
//////////////////////////////////////////////////////

(function($){
   'use strict';

   var touch = {};
   var touchTimeout;
   var swipeTimeout;
   var tapTimeout;
   var longTapDelay = 750;
   var singleTapDelay = 150;
   var longTapTimeout;

   function parentIfText(node) {
      return 'tagName' in node ? node : node.parentNode;
   }

   function swipeDirection(x1, x2, y1, y2) {
      var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
      return xDelta >= yDelta ? (x1 - x2 > 0 ? 'left' : 'right') : (y1 - y2 > 0 ? 'up' : 'down');
   }

   function longTap() {
      longTapTimeout = null;
      if (touch.last) {
         try {
            touch.el.trigger('longtap');
            touch = {};
         } catch(err) { }
      }
   }

   function cancelLongTap() {
      if (longTapTimeout) clearTimeout(longTapTimeout);
      longTapTimeout = null;
   }

   function cancelAll() {
      if (touchTimeout) clearTimeout(touchTimeout);
      if (tapTimeout) clearTimeout(tapTimeout);
      if (swipeTimeout) clearTimeout(swipeTimeout);
      if (longTapTimeout) clearTimeout(longTapTimeout);
      touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null;
      touch = {};
   }

   $(document).ready(function(){
      var now;
      var delta;
      var body = $(document.body);
      var twoTouches = false;
      body.on($.eventStart, function(e) {
         now = Date.now();
         delta = now - (touch.last || now);
         if (e.originalEvent) e = e.originalEvent;

         // Handle MSPointer Events:
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
            e = e.originalEvent ? e.originalEvent : e;
            body.on('MSHoldVisual', function (e) {
               e.preventDefault();
            });
               touch.el = $(parentIfText(e.target));
               touchTimeout && clearTimeout(touchTimeout);
               touch.x1 = e.pageX;
               touch.y1 = e.pageY;
               twoTouches = false;
         } else {
            if ($.eventStart === 'mousedown') {
               touch.el = $(parentIfText(e.target));
               touchTimeout && clearTimeout(touchTimeout);
               touch.x1 = e.pageX;
               touch.y1 = e.pageY;
               twoTouches = false;
            } else {
               // User to detect two or more finger gestures:
               if (e.touches.length === 2) {
                  console.log('two fingers');
               // One finger touch:
               } else if (e.touches.length === 1) {
                  touch.el = $(parentIfText(e.touches[0].target));
                  touchTimeout && clearTimeout(touchTimeout);
                  touch.x1 = e.touches[0].pageX;
                  touch.y1 = e.touches[0].pageY;
                  if (e.targetTouches.length === 2) {
                     twoTouches = true;
                  } else {
                     twoTouches = false;
                  }
               }
            }
         }
         if (delta > 0 && delta <= 250) {
            touch.isDoubleTap = true;
         }
         touch.last = now;
         longTapTimeout = setTimeout(longTap, longTapDelay);
      });
      body.on($.eventMove, function(e) {
         if (e.originalEvent) e = e.originalEvent;
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
            e = e.originalEvent ? e.originalEvent : e;
            cancelLongTap();
            touch.x2 = e.pageX;
            touch.y2 = e.pageY;
         } else {
            cancelLongTap();
            if ($.eventMove === 'mousemove') {
               touch.x2 = e.pageX;
               touch.y2 = e.pageY;
            } else {
               // One finger gesture:
               if (e.touches.length === 1) {
                  touch.x2 = e.touches[0].pageX;
                  touch.y2 = e.touches[0].pageY;
               }
            }
         }
      });
      body.on($.eventEnd, function(e) {
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
         }
         cancelLongTap();
         if (!!touch.el) {
            // Swipe detection:
            if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > $.gestureLength) ||
            (touch.y2 && Math.abs(touch.y1 - touch.y2) > $.gestureLength))  {
               swipeTimeout = setTimeout(function() {
                  touch.el.trigger('swipe');
                  touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
                  touch = {};
               }, 0);

            // Normal tap:
            } else if ('last' in touch) {

               // Delay by one tick so we can cancel the 'tap' event if 'scroll' fires:
               tapTimeout = setTimeout(function() {

                  // Trigger universal 'tap' with the option to cancelTouch():
                  touch.el.trigger('tap');

                  // Trigger double tap immediately:
                  if (touch.isDoubleTap) {
                     touch.el.trigger('doubletap');
                     touch = {};
                  } else {
                     // Trigger single tap after singleTapDelay:
                     touchTimeout = setTimeout(function(){
                        touchTimeout = null;
                        touch.el.trigger('singletap');
                        touch = {};
                        return false;
                     }, singleTapDelay);
                  }

               }, 0);
            }
         } else { return; }
      });
      body.on('touchcancel', cancelAll);
   });

   ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown', 'doubletap', 'tap', 'singletap', 'longtap'].forEach(function(method){
      // Add gesture events to ChocolateChipJS:
      $.fn.extend({
         method : function(callback){
            return this.on(method, callback);
         }
      });
   });
})(this.$);<commit_msg>Update for Gestures module to prevent errors with null touch object. or touch.el.<commit_after>//////////////////////////////////////////////////////
// Swipe Gestures for ChocolateChip.
// Includes mouse gestures for desktop compatibility.
//////////////////////////////////////////////////////

(function($){
   'use strict';

   var touch = {};
   var touchTimeout;
   var swipeTimeout;
   var tapTimeout;
   var longTapDelay = 750;
   var singleTapDelay = 150;
   var longTapTimeout;

   function parentIfText(node) {
      return 'tagName' in node ? node : node.parentNode;
   }

   function swipeDirection(x1, x2, y1, y2) {
      var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
      return xDelta >= yDelta ? (x1 - x2 > 0 ? 'left' : 'right') : (y1 - y2 > 0 ? 'up' : 'down');
   }

   function longTap() {
      longTapTimeout = null;
      if (touch.last) {
         try {
            if (touch && touch.el) {
               touch.el.trigger('longtap');
               touch = {};
            }
         } catch(err) { }
      }
   }

   function cancelLongTap() {
      if (longTapTimeout) clearTimeout(longTapTimeout);
      longTapTimeout = null;
   }

   function cancelAll() {
      if (touchTimeout) clearTimeout(touchTimeout);
      if (tapTimeout) clearTimeout(tapTimeout);
      if (swipeTimeout) clearTimeout(swipeTimeout);
      if (longTapTimeout) clearTimeout(longTapTimeout);
      touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null;
      touch = {};
   }

   $(document).ready(function(){
      var now;
      var delta;
      var body = $(document.body);
      var twoTouches = false;
      body.on($.eventStart, function(e) {
         now = Date.now();
         delta = now - (touch.last || now);
         if (e.originalEvent) e = e.originalEvent;

         // Handle MSPointer Events:
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
            e = e.originalEvent ? e.originalEvent : e;
            body.on('MSHoldVisual', function (e) {
               e.preventDefault();
            });
            touch.el = $(parentIfText(e.target));
            touchTimeout && clearTimeout(touchTimeout);
            touch.x1 = e.pageX;
            touch.y1 = e.pageY;
            twoTouches = false;
         } else {
            if ($.eventStart === 'mousedown') {
               touch.el = $(parentIfText(e.target));
               touchTimeout && clearTimeout(touchTimeout);
               touch.x1 = e.pageX;
               touch.y1 = e.pageY;
               twoTouches = false;
            } else {
               // User to detect two or more finger gestures:
               if (e.touches.length === 2) {
                  console.log('two fingers');
               // One finger touch:
               } else if (e.touches.length === 1) {
                  touch.el = $(parentIfText(e.touches[0].target));
                  touchTimeout && clearTimeout(touchTimeout);
                  touch.x1 = e.touches[0].pageX;
                  touch.y1 = e.touches[0].pageY;
                  if (e.targetTouches.length === 2) {
                     twoTouches = true;
                  } else {
                     twoTouches = false;
                  }
               }
            }
         }
         if (delta > 0 && delta <= 250) {
            touch.isDoubleTap = true;
         }
         touch.last = now;
         longTapTimeout = setTimeout(longTap, longTapDelay);
      });
      body.on($.eventMove, function(e) {
         if (e.originalEvent) e = e.originalEvent;
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
            e = e.originalEvent ? e.originalEvent : e;
            cancelLongTap();
            touch.x2 = e.pageX;
            touch.y2 = e.pageY;
         } else {
            cancelLongTap();
            if ($.eventMove === 'mousemove') {
               touch.x2 = e.pageX;
               touch.y2 = e.pageY;
            } else {
               // One finger gesture:
               if (e.touches.length === 1) {
                  touch.x2 = e.touches[0].pageX;
                  touch.y2 = e.touches[0].pageY;
               }
            }
         }
      });
      body.on($.eventEnd, function(e) {
         if (window.navigator.msPointerEnabled) {
            if (window && window.jQuery && $ === window.jQuery) {
               if (e.originalEvent && !e.originalEvent.isPrimary) return;
            } else {
               if (!e.isPrimary) return;
            }
         }
         cancelLongTap();
         if (!!touch.el) {
            // Swipe detection:
            if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > $.gestureLength) ||
            (touch.y2 && Math.abs(touch.y1 - touch.y2) > $.gestureLength))  {
               swipeTimeout = setTimeout(function() {
                  if (touch && touch.el) {
                     touch.el.trigger('swipe');
                     touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
                     touch = {};
                  }
               }, 0);

            // Normal tap:
            } else if ('last' in touch) {

               // Delay by one tick so we can cancel the 'tap' event if 'scroll' fires:
               tapTimeout = setTimeout(function() {

                  // Trigger universal 'tap' with the option to cancelTouch():
                  if (touch && touch.el) {
                     touch.el.trigger('tap');
                  }

                  // Trigger double tap immediately:
                  if (touch && touch.isDoubleTap) {
                     if (touch && touch.el) {
                        touch.el.trigger('doubletap');
                        touch = {};
                     }
                  } else {
                     // Trigger single tap after singleTapDelay:
                     touchTimeout = setTimeout(function(){
                        touchTimeout = null;
                        if (touch && touch.el) {
                           touch.el.trigger('singletap');
                           touch = {};
                           return false;
                        }
                     }, singleTapDelay);
                  }

               }, 0);
            }
         } else { return; }
      });
      body.on('touchcancel', cancelAll);
   });

   ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown', 'doubletap', 'tap', 'singletap', 'longtap'].forEach(function(method){
      // Add gesture events to ChocolateChipJS:
      $.fn.extend({
         method : function(callback){
            return this.on(method, callback);
         }
      });
   });
})(this.$);<|endoftext|>


<commit_before>// Load modules

var Utils = require('./utils');
var Err = require('./error');
var Types = require('joi').Types;
var Fs = require('fs');
var Handlebars = require('handlebars');

// Declare internals

var internals = {};

exports.init = function(config) {

    Utils.assert(config.indexTemplate || config.indexTemplatePath, 'Config needs to specify either a indexTemplate or a indexTemplatePath');
    Utils.assert(config.routeTemplate || config.routeTemplatePath, 'Config needs to specify either a routeTemplate or a routeTemplatePath');

    var indexTemplateSource = config.indexTemplate || Fs.readFileSync(config.indexTemplatePath, 'utf8');
    var routeTemplateSource = config.routeTemplate || Fs.readFileSync(config.routeTemplatePath, 'utf8');

    internals.compiledIndexTemplate = Handlebars.compile(indexTemplateSource);
    internals.compiledRouteTemplate = Handlebars.compile(routeTemplateSource);

    internals.config = config;

    delete internals.config.indexTemplate;
    delete internals.config.routeTemplate;

    return {
        endpoint: internals.endpoint
    };
};

internals.generateIndexMarkup = function(routes) {

    Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array');

    var templateData = internals.getRoutesData(routes);

    return internals.compiledIndexTemplate(templateData);
};

internals.generateRoutesMarkup = function(routes, path) {

    Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array');

    var templateData = internals.getRoutesData(routes);
    templateData.path = path;

    return internals.compiledRouteTemplate(templateData);
};

internals.endpoint = function() {

    return {
        handler: internals.handler,
        query: { path: Types.String() }
    };
};

internals.handler = function(request) {

    var path = request.query.path;
    if (path) {
        request.reply(internals.getRoutesResponse(request.server, path));
    }
    else {
        request.reply(internals.getIndexResponse(request.server));
    }
};

internals.getIndexResponse = function(server) {

    var routes = [].concat(server._routes.post, server._routes.get);

    routes = routes.filter(function(route) {
        return route !== null && route.path !== internals.config.docsEndpoint;
    });

    routes.sort(function(route1, route2) {
        return route1.path > route2.path;
    });

    return internals.generateIndexMarkup(routes);
};

internals.getRoutesResponse = function(server, path) {

    var routes = internals.findRoutes(server, path);

    return routes ? internals.generateRoutesMarkup(routes, path) : Err.notFound('No such path found');
};

internals.findRoutes = function (server, path) {

    var routes = [];
    routes.push(server._match('get', path));
    routes.push(server._match('post', path));

    routes = routes.filter(function(route) {
        return route !== null;
    });

    return routes.length > 0 ? routes : null;
};

internals.getRoutesData = function(routes) {

    var routesData = [];
    var templateConfig = Utils.clone(internals.config);

    for (var i = 0, il = routes.length; i < il; ++i) {
        var routeData = internals.getRouteData(routes[i]);
        if (routeData) {
            routesData.push(routeData);
        }
    }

    templateConfig.routes = routesData;

    return templateConfig;
};

internals.getRouteData = function(route) {

    return route === null ? null : {
        path: route.path,
        method: route.method.toUpperCase(),
        description: route.description,
        notes: route.notes,
        tags: route.tags,
        queryParams: internals.getParamsData(route.config.query),
        payloadParams: internals.getParamsData(route.config.schema),
        responseParams: internals.getParamsData(route.config.response)
    };
};

internals.getParamsData = function(params) {

    var paramsData = [];
    if (params === null || params === undefined) {
        return paramsData;
    }

    if (typeof params !== 'object') {
        return paramsData;
    }

    var keys = Object.keys(params);

    if (keys) {
        for (var i = 0, il = keys.length; i < il; ++i) {
            var key = keys[i];
            var param = params[key];

            paramsData.push({
                name: key,
                description: typeof param.description === 'function' ? '' : param.description,
                notes: typeof param.notes === 'function' ? '' : param.notes,
                tags: typeof param.tags === 'function' ? '' : param.tags,
                type: param.type,
                required: param.__modifiers && param.__modifiers._values ? param.__modifiers._values.some(internals.isRequiredParam) : null,
                allowedValues: param.__valids ? internals.getExistsValues(param.__valids._exists) : null,
                disallowedValues: param.__invalids ? internals.getExistsValues(param.__invalids._exists) : null
            });
        }
    }

    return paramsData;
};

internals.getExistsValues = function(exists) {

    var values = [];
    if (exists === null || exists === undefined) {
        return values;
    }

    var keys = Object.keys(exists);
    keys.forEach(function(key) {
        key = key.substring(1, key.length - 1);
        if (key !== 'ndefine' && key !== 'ul' && key.length !== 0) {
            values.push(key);
        }
    });

    return values;
};

internals.isRequiredParam = function(element) {

    return element === 'required';
};<commit_msg>Fix bug where /docs crashes if no POST routes are defined<commit_after>// Load modules

var Utils = require('./utils');
var Err = require('./error');
var Types = require('joi').Types;
var Fs = require('fs');
var Handlebars = require('handlebars');

// Declare internals

var internals = {};

exports.init = function(config) {

    Utils.assert(config.indexTemplate || config.indexTemplatePath, 'Config needs to specify either a indexTemplate or a indexTemplatePath');
    Utils.assert(config.routeTemplate || config.routeTemplatePath, 'Config needs to specify either a routeTemplate or a routeTemplatePath');

    var indexTemplateSource = config.indexTemplate || Fs.readFileSync(config.indexTemplatePath, 'utf8');
    var routeTemplateSource = config.routeTemplate || Fs.readFileSync(config.routeTemplatePath, 'utf8');

    internals.compiledIndexTemplate = Handlebars.compile(indexTemplateSource);
    internals.compiledRouteTemplate = Handlebars.compile(routeTemplateSource);

    internals.config = config;

    delete internals.config.indexTemplate;
    delete internals.config.routeTemplate;

    return {
        endpoint: internals.endpoint
    };
};

internals.generateIndexMarkup = function(routes) {

    Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array');

    var templateData = internals.getRoutesData(routes);

    return internals.compiledIndexTemplate(templateData);
};

internals.generateRoutesMarkup = function(routes, path) {

    Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array');

    var templateData = internals.getRoutesData(routes);
    templateData.path = path;

    return internals.compiledRouteTemplate(templateData);
};

internals.endpoint = function() {

    return {
        handler: internals.handler,
        query: { path: Types.String() }
    };
};

internals.handler = function(request) {

    var path = request.query.path;
    if (path) {
        request.reply(internals.getRoutesResponse(request.server, path));
    }
    else {
        request.reply(internals.getIndexResponse(request.server));
    }
};

internals.getIndexResponse = function(server) {

    var routes = [].concat(server._routes.post, server._routes.get);

    routes = routes.filter(function(route) {
        return route !== null && route !== undefined && route.path !== internals.config.docsEndpoint;
    });

    routes.sort(function(route1, route2) {
        return route1.path > route2.path;
    });

    return internals.generateIndexMarkup(routes);
};

internals.getRoutesResponse = function(server, path) {

    var routes = internals.findRoutes(server, path);

    return routes ? internals.generateRoutesMarkup(routes, path) : Err.notFound('No such path found');
};

internals.findRoutes = function (server, path) {

    var routes = [];
    routes.push(server._match('get', path));
    routes.push(server._match('post', path));

    routes = routes.filter(function(route) {
        return route !== null;
    });

    return routes.length > 0 ? routes : null;
};

internals.getRoutesData = function(routes) {

    var routesData = [];
    var templateConfig = Utils.clone(internals.config);

    for (var i = 0, il = routes.length; i < il; ++i) {
        var routeData = internals.getRouteData(routes[i]);
        if (routeData) {
            routesData.push(routeData);
        }
    }

    templateConfig.routes = routesData;

    return templateConfig;
};

internals.getRouteData = function(route) {

    return route === null ? null : {
        path: route.path,
        method: route.method.toUpperCase(),
        description: route.description,
        notes: route.notes,
        tags: route.tags,
        queryParams: internals.getParamsData(route.config.query),
        payloadParams: internals.getParamsData(route.config.schema),
        responseParams: internals.getParamsData(route.config.response)
    };
};

internals.getParamsData = function(params) {

    var paramsData = [];
    if (params === null || params === undefined) {
        return paramsData;
    }

    if (typeof params !== 'object') {
        return paramsData;
    }

    var keys = Object.keys(params);

    if (keys) {
        for (var i = 0, il = keys.length; i < il; ++i) {
            var key = keys[i];
            var param = params[key];

            paramsData.push({
                name: key,
                description: typeof param.description === 'function' ? '' : param.description,
                notes: typeof param.notes === 'function' ? '' : param.notes,
                tags: typeof param.tags === 'function' ? '' : param.tags,
                type: param.type,
                required: param.__modifiers && param.__modifiers._values ? param.__modifiers._values.some(internals.isRequiredParam) : null,
                allowedValues: param.__valids ? internals.getExistsValues(param.__valids._exists) : null,
                disallowedValues: param.__invalids ? internals.getExistsValues(param.__invalids._exists) : null
            });
        }
    }

    return paramsData;
};

internals.getExistsValues = function(exists) {

    var values = [];
    if (exists === null || exists === undefined) {
        return values;
    }

    var keys = Object.keys(exists);
    keys.forEach(function(key) {
        key = key.substring(1, key.length - 1);
        if (key !== 'ndefine' && key !== 'ul' && key.length !== 0) {
            values.push(key);
        }
    });

    return values;
};

internals.isRequiredParam = function(element) {

    return element === 'required';
};
<|endoftext|>


<commit_before>"use strict";

// our schedule library
const Schedule = require("./schedule");
const Moment = require("moment-timezone");

const Location = require("./location");

// symbols
const s_rideID = Symbol();
const s_rideName = Symbol();
const s_currentWaitTime = Symbol();
const s_fastPassAvailable = Symbol();
const s_fastPassReturnTimeAvailable = Symbol();
const s_fastPassReturnTimeStart = Symbol();
const s_fastPassReturnTimeEnd = Symbol();
const s_lastTimeUpdate = Symbol();
const s_lastTimeFastPassUpdate = Symbol();
const s_scheduleData = Symbol();
const s_customProperties = Symbol();

/**
 * @typedef RideData
 * @type Object
 * @property {String} id Unique Ride ID
 * @property {String} name The ride's name
 * @property {Bool} active Is this ride currently operating?
 * @property {Number} wait_time Ride's current queue time
 * @property {Number} last_update Last time this Ride has a wait time change (in milliseconds)
 */

/**
 * Ride Class
 * Each ride object represents one ride at a theme park.
 * This object will hold the ride's current state.
 * @class
 */
class Ride extends Location {
    /**
     * Create a new Ride object
     * @param {Object} options
     * @param {String} options.rideId Ride's Unique ID
     */
    constructor(options = {}) {
        super(options);

        if (!options.rideId) throw new Error("No ride ID supplied to new ride object");
        this[s_rideID] = options.rideId;

        // by default, rides don't support fastpass
        this[s_fastPassAvailable] = false;

        // make our own schedule data object!
        this[s_scheduleData] = new Schedule();
    }

    /**
     * Serialize this object (automatically called by JSON.stringify etc.)
     * @returns {Object} Current ride state
     */
    toJSON() {
        // try to extract schedule data for this ride
        const openingHours = this[s_scheduleData].GetDate({
            date: Moment(),
        });

        const jsonData = {};

        // start with custom properties by adding them to a field "meta"
        if (this[s_customProperties] !== undefined) {
            jsonData.meta = this[s_customProperties];
        }

        // populate with basic data
        jsonData.id = this[s_rideID];
        jsonData.name = this.Name;
        jsonData.active = this.Active;
        jsonData.waitTime = this.WaitTime;
        jsonData.fastPass = this.FastPass;
        jsonData.lastUpdate = this.LastUpdate;
        jsonData.status = this.Status;

        // add fastPass return times (if available)
        if (this[s_fastPassReturnTimeAvailable]) {
            jsonData.fastPassReturnTime = {
                startTime: this.FastPassReturnTimeStart,
                endTime: this.FastPassReturnTimeEnd,
                lastUpdate: this[s_lastTimeFastPassUpdate]
            };
        }

        // add opening hours to ride data if we actually have any!
        if (openingHours) {
            jsonData.schedule = openingHours;
        }

        return jsonData;
    }

    /**
     * Parse a JSON object and update this ride object's state
     * @param {Object} input
     */
    fromJSON(input) {
        if (input.name) {
            this[s_rideName] = input.name;
        }
        if (typeof input.fastPass !== "undefined") {
            this[s_fastPassAvailable] = input.fastPass;
        }

        this.WaitTime = input.waitTime;
    }

    /**
     * Get this ride's name
     * Note: Will attempt to return in English, but will fallback to park's local locale if English isn't available
     * @type {String}
     */
    get Name() {
        return this[s_rideName];
    }

    /**
     * Set this ride's wait time
     * Set to -1 when ride is Closed
     * Set to -2 when ride is Down
     * @type {Number}
     */
    set WaitTime(value) {
        // check for updated (or brand new) wait time for this ride
        if (this[s_currentWaitTime] === undefined || this[s_currentWaitTime] != value) {
            // update our last updated time to now
            this[s_lastTimeUpdate] = Date.now();
            // update our wait time for this ride
            this[s_currentWaitTime] = value;
        }

        // value hasn't changed, don't do anything
    }

    /**
     * Get this ride's current wait time. Will always be >= 0.
     * Use .Active to determine ride's open status
     * @type {Number}
     */
    get WaitTime() {
        // always return positive ints for the wait time, even when inactive or not defined yet
        if (this[s_currentWaitTime] === undefined || this[s_currentWaitTime] < 0) return 0;

        return this[s_currentWaitTime];
    }

    /**
     * Set this ride's fast pass availability
     * @type {Boolean}
     */
    set FastPass(value) {
        if (this[s_fastPassAvailable] != value) {
            // update our last updated time to now
            this[s_lastTimeUpdate] = Date.now();
            // update fastpass status
            this[s_fastPassAvailable] = value;
        }
    }

    /**
     * Get this ride's fast pass availability
     * @type {Boolean}
     */
    get FastPass() {
        return this[s_fastPassAvailable];
    }

    /**
     * Set whether we have valid fast pass return times or not
     * Setting start/end times automatically sets this to true, only need to call this if fastPass availability has ran out
     * @type {Boolean}
     */
    set FastPassReturnTimeAvailable(value) {
        if (value != this[s_fastPassReturnTimeAvailable]) {
            // update availability
            this[s_fastPassReturnTimeAvailable] = value;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
        }
    }

    /**
     * Does this ride have a fastPass return time available?
     */
    get FastPassReturnTimeAvailable() {
        return this[s_fastPassReturnTimeAvailable];
    }

    /**
     * Set this ride's fastPass return time window start
     * @type {Moment}
     */
    set FastPassReturnTimeStart(value) {
        // check if this fastPass is
        if (!value.isSame(this[s_fastPassReturnTimeStart])) {
            // mark this as true so we know to add this to our JSON object
            this[s_fastPassReturnTimeAvailable] = true;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
            // use new fastPass return start time
            this[s_fastPassReturnTimeStart] = value;
        }
    }

    /**
     * Get this ride's fast pass return time start in format of "HH:mm"
     * @type {String}
     */
    get FastPassReturnTimeStart() {
        return this[s_fastPassReturnTimeStart].format("HH:mm");
    }

    /**
     * Set this ride's fastPass return time window end
     * @type {Moment}
     */
    set FastPassReturnTimeEnd(value) {
        // check if this fastPass is
        if (!value.isSame(this[s_fastPassReturnTimeEnd])) {
            // mark this as true so we know to add this to our JSON object
            this[s_fastPassReturnTimeAvailable] = true;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
            // use new fastPass return end time
            this[s_fastPassReturnTimeEnd] = value;
        }
    }

    /**
     * Get this ride's fast pass return time emd in format of "HH:mm"
     * @type {String}
     */
    get FastPassReturnTimeEnd() {
        return this[s_fastPassReturnTimeEnd].format("HH:mm");
    }

    /**
     * Is this ride currently running?
     * @type {Boolean}
     */
    get Active() {
        // if we have no data yet, assume ride is inactive
        if (this[s_currentWaitTime] === undefined) return false;

        return this[s_currentWaitTime] >= 0;
    }

    /**
     * String status for this ride
     * Can only ever be either "Operating", "Down", "Closed", or "Refurbishment"
     * @type {String}
     */
    get Status() {
        // first, check the schedule for non-operating types
        //  refurbishment/closed schedule overrules all other statuses, as this is planned maintenance
        //  i.e, rides don't usually schedule maintenance and then randomly open mid-day
        const todaysSchedule = this.Schedule.GetDate({
            date: Moment()
        });
        if (todaysSchedule && todaysSchedule.type != "Operating") {
            return todaysSchedule.type;
        }

        // wait time set to -2 when ride is Down
        if (this[s_currentWaitTime] == -2) return "Down";

        // otherwise, return a string matching current Active status
        return (this.Active ? "Operating" : "Closed");
    }

    /**
     * Get this ride's last wait time update time.
     * Note: Can be undefined
     * @type {Number}
     */
    get LastUpdate() {
        return this[s_lastTimeUpdate];
    }

    /**
     * Get this ride's schedule object
     * @type {Schedule}
     */
    get Schedule() {
        return this[s_scheduleData];
    }

    /**
     * Add a custom property to this ride (eg. a thrill rating, or a custom setting a specific park implements)
     * @param {String} name Name (field) of property to add to this ride
     * @param {object} value Value of property
     */
    AddCustomProperty(name, value) {
        if (this[s_customProperties] === undefined) this[s_customProperties] = {};
        this[s_customProperties][name] = value;
    }
}

// export the Ride class
module.exports = Ride;<commit_msg>[~] Clean-up ride cache restoring<commit_after>"use strict";

// our schedule library
const Schedule = require("./schedule");
const Moment = require("moment-timezone");

const Location = require("./location");

// symbols
const s_rideID = Symbol();
const s_rideName = Symbol();
const s_currentWaitTime = Symbol();
const s_fastPassAvailable = Symbol();
const s_fastPassReturnTimeAvailable = Symbol();
const s_fastPassReturnTimeStart = Symbol();
const s_fastPassReturnTimeEnd = Symbol();
const s_lastTimeUpdate = Symbol();
const s_lastTimeFastPassUpdate = Symbol();
const s_scheduleData = Symbol();
const s_customProperties = Symbol();

/**
 * @typedef RideData
 * @type Object
 * @property {String} id Unique Ride ID
 * @property {String} name The ride's name
 * @property {Bool} active Is this ride currently operating?
 * @property {Number} wait_time Ride's current queue time
 * @property {Number} last_update Last time this Ride has a wait time change (in milliseconds)
 */

/**
 * Ride Class
 * Each ride object represents one ride at a theme park.
 * This object will hold the ride's current state.
 * @class
 */
class Ride extends Location {
    /**
     * Create a new Ride object
     * @param {Object} options
     * @param {String} options.rideId Ride's Unique ID
     */
    constructor(options = {}) {
        super(options);

        if (!options.rideId) throw new Error("No ride ID supplied to new ride object");
        this[s_rideID] = options.rideId;

        // by default, rides don't support fastpass
        this[s_fastPassAvailable] = false;

        // make our own schedule data object!
        this[s_scheduleData] = new Schedule();
    }

    /**
     * Serialize this object (automatically called by JSON.stringify etc.)
     * @returns {Object} Current ride state
     */
    toJSON() {
        // try to extract schedule data for this ride
        const openingHours = this[s_scheduleData].GetDate({
            date: Moment(),
        });

        const jsonData = {};

        // start with custom properties by adding them to a field "meta"
        if (this[s_customProperties] !== undefined) {
            jsonData.meta = this[s_customProperties];
        }

        // populate with basic data
        jsonData.id = this[s_rideID];
        jsonData.name = this.Name;
        jsonData.active = this.Active;
        jsonData.waitTime = this.WaitTime;
        jsonData.fastPass = this.FastPass;
        jsonData.lastUpdate = this.LastUpdate;
        jsonData.status = this.Status;

        // add fastPass return times (if available)
        if (this[s_fastPassReturnTimeAvailable]) {
            jsonData.fastPassReturnTime = {
                startTime: this.FastPassReturnTimeStart,
                endTime: this.FastPassReturnTimeEnd,
                lastUpdate: this[s_lastTimeFastPassUpdate]
            };
        }

        // add opening hours to ride data if we actually have any!
        if (openingHours) {
            jsonData.schedule = openingHours;
        }

        return jsonData;
    }

    /**
     * Parse a JSON object and update this ride object's state
     * @param {Object} input
     */
    fromJSON(input) {
        // restore name
        if (input.name !== undefined) {
            this[s_rideName] = input.name;
        }

        // restore fastPass status
        if (input.fastPass !== undefined) {
            this[s_fastPassAvailable] = input.fastPass;
        }

        // re-add any custom properties
        if (input.meta !== undefined) {
            for (let key in input.meta) {
                this.AddCustomProperty(key, input.meta[key]);
            }
        }

        // restore last update time
        if (input.lastUpdate !== undefined) {
            this[s_lastTimeUpdate] = input.lastUpdate;
        }

        // restore wait time
        if (input.status !== undefined && input.status != "Operating") {
            // if status isn't "Operating", wait time must be a negative number. Restore correctly
            this[s_currentWaitTime] = input.status == "Closed" ? -1 : -2;
        } else if (input.waitTime !== undefined) {
            // otherwise, restore the actual wait time
            this[s_currentWaitTime] = input.waitTime;
        }

        // TODO - restore ride schedule correctly
        // TODO - restore ride fastpass return time correctly
    }

    /**
     * Get this ride's name
     * Note: Will attempt to return in English, but will fallback to park's local locale if English isn't available
     * @type {String}
     */
    get Name() {
        return this[s_rideName];
    }

    /**
     * Set this ride's wait time
     * Set to -1 when ride is Closed
     * Set to -2 when ride is Down
     * @type {Number}
     */
    set WaitTime(value) {
        // check for updated (or brand new) wait time for this ride
        if (this[s_currentWaitTime] === undefined || this[s_currentWaitTime] != value) {
            // update our last updated time to now
            this[s_lastTimeUpdate] = Date.now();
            // update our wait time for this ride
            this[s_currentWaitTime] = value;
        }

        // value hasn't changed, don't do anything
    }

    /**
     * Get this ride's current wait time. Will always be >= 0.
     * Use .Active to determine ride's open status
     * @type {Number}
     */
    get WaitTime() {
        // always return positive ints for the wait time, even when inactive or not defined yet
        if (this[s_currentWaitTime] === undefined || this[s_currentWaitTime] < 0) return 0;

        return this[s_currentWaitTime];
    }

    /**
     * Set this ride's fast pass availability
     * @type {Boolean}
     */
    set FastPass(value) {
        if (this[s_fastPassAvailable] != value) {
            // update our last updated time to now
            this[s_lastTimeUpdate] = Date.now();
            // update fastpass status
            this[s_fastPassAvailable] = value;
        }
    }

    /**
     * Get this ride's fast pass availability
     * @type {Boolean}
     */
    get FastPass() {
        return this[s_fastPassAvailable];
    }

    /**
     * Set whether we have valid fast pass return times or not
     * Setting start/end times automatically sets this to true, only need to call this if fastPass availability has ran out
     * @type {Boolean}
     */
    set FastPassReturnTimeAvailable(value) {
        if (value != this[s_fastPassReturnTimeAvailable]) {
            // update availability
            this[s_fastPassReturnTimeAvailable] = value;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
        }
    }

    /**
     * Does this ride have a fastPass return time available?
     */
    get FastPassReturnTimeAvailable() {
        return this[s_fastPassReturnTimeAvailable];
    }

    /**
     * Set this ride's fastPass return time window start
     * @type {Moment}
     */
    set FastPassReturnTimeStart(value) {
        // check if this fastPass is
        if (!value.isSame(this[s_fastPassReturnTimeStart])) {
            // mark this as true so we know to add this to our JSON object
            this[s_fastPassReturnTimeAvailable] = true;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
            // use new fastPass return start time
            this[s_fastPassReturnTimeStart] = value;
        }
    }

    /**
     * Get this ride's fast pass return time start in format of "HH:mm"
     * @type {String}
     */
    get FastPassReturnTimeStart() {
        return this[s_fastPassReturnTimeStart].format("HH:mm");
    }

    /**
     * Set this ride's fastPass return time window end
     * @type {Moment}
     */
    set FastPassReturnTimeEnd(value) {
        // check if this fastPass is
        if (!value.isSame(this[s_fastPassReturnTimeEnd])) {
            // mark this as true so we know to add this to our JSON object
            this[s_fastPassReturnTimeAvailable] = true;
            // update our last fastPass updated time to now
            this[s_lastTimeFastPassUpdate] = Date.now();
            // use new fastPass return end time
            this[s_fastPassReturnTimeEnd] = value;
        }
    }

    /**
     * Get this ride's fast pass return time emd in format of "HH:mm"
     * @type {String}
     */
    get FastPassReturnTimeEnd() {
        return this[s_fastPassReturnTimeEnd].format("HH:mm");
    }

    /**
     * Is this ride currently running?
     * @type {Boolean}
     */
    get Active() {
        // if we have no data yet, assume ride is inactive
        if (this[s_currentWaitTime] === undefined) return false;

        return this[s_currentWaitTime] >= 0;
    }

    /**
     * String status for this ride
     * Can only ever be either "Operating", "Down", or "Closed"
     * @type {String}
     */
    get Status() {
        // first, check the schedule for non-operating types
        //  refurbishment/closed schedule overrules all other statuses, as this is planned maintenance
        //  i.e, rides don't usually schedule maintenance and then randomly open mid-day
        const todaysSchedule = this.Schedule.GetDate({
            date: Moment()
        });
        if (todaysSchedule && todaysSchedule.type != "Operating") {
            return todaysSchedule.type;
        }

        // wait time set to -2 when ride is Down
        if (this[s_currentWaitTime] == -2) return "Down";

        // otherwise, return a string matching current Active status
        return (this.Active ? "Operating" : "Closed");
    }

    /**
     * Get this ride's last wait time update time.
     * Note: Can be undefined
     * @type {Number}
     */
    get LastUpdate() {
        return this[s_lastTimeUpdate];
    }

    /**
     * Get this ride's schedule object
     * @type {Schedule}
     */
    get Schedule() {
        return this[s_scheduleData];
    }

    /**
     * Add a custom property to this ride (eg. a thrill rating, or a custom setting a specific park implements)
     * @param {String} name Name (field) of property to add to this ride
     * @param {object} value Value of property
     */
    AddCustomProperty(name, value) {
        if (this[s_customProperties] === undefined) this[s_customProperties] = {};
        this[s_customProperties][name] = value;
    }
}

// export the Ride class
module.exports = Ride;<|endoftext|>


<commit_before>/**
	The MIT License (MIT)

	Copyright (c) 2014 MyChannel-Apps.de

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	@author		Adrian Preuß <Bizarrus>, Christoph Kühl <djchrisnet>
*/

var DB	= (new function() {
	this.getUser = function(user) {
		Logger.info("DB.getUser(user) is DEPRECATED");
		return user.getPersistence();
	};

	this.getChannel = function() {
		Logger.info("DB.getChannel() is DEPRECATED");
		return KnuddelsServer.getPersistence();
	};

	this.load = function(key, defaultValue, user) {
		if(key === undefined) {
			Logger.error("No key submitted");
			return false;
		}

		selectedDB = KnuddelsServer.getPersistence();
		if(user != undefined) {
			selectedDB = user.getPersistence();
		}

		switch(typeof defaultValue) {
			case 'string':
				return selectedDB.getString(key, defaultValue);
			break;

			case 'number':
				return selectedDB.getNumber(key, defaultValue);
			break;

			case 'object':
				return selectedDB.getObject(key, defaultValue);
			break;

			case 'undefined':
				return selectedDB.getObject(key, {});
			break;
		}

		return false;
	};

	this.save = function(key, data, user) {
		if(key === undefined) {
			Logger.error("No key submitted");
			return false;
		}

		if(data === undefined) {
			Logger.error("No Data submitted");
			return false;
		}

		selectedDB = KnuddelsServer.getPersistence();
		if(user != undefined) {
			selectedDB = user.getPersistence();
		}

		switch(typeof data) {
			case 'string':
				selectedDB.setString(key, data);
			break;

			case 'number':
				selectedDB.setNumber(key, data);
			break;

			case 'object':
				selectedDB.setObject(key, data);
			break;
		}
		return true;
	};
}());<commit_msg>Change " to '<commit_after>/**
	The MIT License (MIT)

	Copyright (c) 2014 MyChannel-Apps.de

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	@author		Adrian Preuß <Bizarrus>, Christoph Kühl <djchrisnet>
*/

var DB	= (new function() {
	this.getUser = function(user) {
		Logger.info('DB.getUser(user) is DEPRECATED');
		return user.getPersistence();
	};

	this.getChannel = function() {
		Logger.info('DB.getChannel() is DEPRECATED');
		return KnuddelsServer.getPersistence();
	};

	this.load = function(key, defaultValue, user) {
		if(key === undefined) {
			Logger.error('No key submitted');
			return false;
		}

		selectedDB = KnuddelsServer.getPersistence();
		if(user != undefined) {
			selectedDB = user.getPersistence();
		}

		switch(typeof defaultValue) {
			case 'string':
				return selectedDB.getString(key, defaultValue);
			break;

			case 'number':
				return selectedDB.getNumber(key, defaultValue);
			break;

			case 'object':
				return selectedDB.getObject(key, defaultValue);
			break;

			case 'undefined':
				return selectedDB.getObject(key, {});
			break;
		}

		return false;
	};

	this.save = function(key, data, user) {
		if(key === undefined) {
			Logger.error('No key submitted');
			return false;
		}

		if(data === undefined) {
			Logger.error('No Data submitted');
			return false;
		}

		selectedDB = KnuddelsServer.getPersistence();
		if(user != undefined) {
			selectedDB = user.getPersistence();
		}

		switch(typeof data) {
			case 'string':
				selectedDB.setString(key, data);
			break;

			case 'number':
				selectedDB.setNumber(key, data);
			break;

			case 'object':
				selectedDB.setObject(key, data);
			break;
		}
		return true;
	};
}());<|endoftext|>