<commit_before>module.exports = function(grunt) {

  var path = require('path');
  var fs = require('fs');
  var amberc = require('../../bin/amberc.js');

  /**
     Full config looks like this:
     amberc: {
       _config: {
         amber_dir: process.cwd(),     // REQUIRED
         closure_jar: '',              // optional
         verbose: true                 // optional
       },
       helloWorld: {
         src: ['projects/HelloWorld/st/HelloWorld.st'], // REQUIRED
         output_dir: 'projects/HelloWorld/js',  // optional
         libraries: 'Canvas',                   // optional
         jsGlobals: ['global1', 'global2'],     // optional
         main_class: 'HelloWorld',              // optional
         output_name: 'helloWorld',            // optional
         init: 'myInit',                       // optional
         main_file: 'myMain.js',               // optional
         deploy: true,                         // optional
         output_suffix: 'mySuffix',            // optional
         library_suffix: '-0.9'                // optional
       },
     },

   */
  grunt.registerMultiTask('amberc', 'Compile Smalltalk files with the amberc compiler', function() {
    // mark required properties
    this.requiresConfig('amberc.options.amber_dir');
    this.requiresConfig(['amberc', this.target, 'src']);

    var options = this.options({
      amber_dir: undefined,
      closure_jar: '',
      verbose: false
    });
    this.data.verbose = options.verbose;

    // mark task as async task
    var done = this.async();

    // create and initialize amberc
    var compiler = new amberc.Compiler(grunt.config('amberc.options.amber_dir'), grunt.config('amberc.options.closure_jar'));

    // generate the amberc configuration out of the given target properties
    var configuration = generateCompilerConfiguration(this.data, grunt.config('amberc.options.amber_dir'));

    // run the compiler
    // change back to the old working directory and call the async callback once finished
    var self = this;
    compiler.main(configuration, function(){
      // signal that task has finished
      done();
    });
  });

  function generateCompilerConfiguration(data, amber_dir) {
    var configuration = amberc.createDefaults(amber_dir);
    var parameters = [];

    var libraries = data.libraries;
    if (undefined !== libraries) {
      configuration.load = libraries;
    }
    var initFile = data.init;
    if (undefined !== initFile) {
      configuration.init = initFile;
    }
    var mainClass = data.main_class;
    if (undefined !== mainClass) {
      configuration.main = mainClass;
    }
    var mainFile = data.main_file;
    if (undefined !== mainFile) {
      configuration.mainfile = mainFile;
    }
    if (true === data.deploy) {
      configuration.deploy = true;
    }
    var outputSuffix = data.output_suffix;
    if (undefined !== outputSuffix) {
      configuration.suffix = outputSuffix;
      configuration.suffix_used = outputSuffix;
    }
    var librarySuffix = data.library_suffix;
    if (undefined !== librarySuffix) {
      configuration.loadsuffix = librarySuffix;
      configuration.suffix_used = librarySuffix;
    }
    var sourceFiles = data.src;
    if (undefined !== sourceFiles) {
      sourceFiles.forEach(function(currentItem){
        var fileSuffix = path.extname(currentItem);
        switch (fileSuffix) {
          case '.st':
            configuration.stFiles.push(currentItem);
          break;
          case '.js':
            configuration.jsFiles.push(currentItem);
          break;
        }
      });
    }
    var outputName = data.output_name;
    if (undefined !== outputName) {
      configuration.program = outputName;
    }
    if (undefined !== data.output_dir) {
      configuration.output_dir = data.output_dir;
    }
    if (undefined !== data.jsGlobals) {
      configuration.jsGlobals.push.apply(configuration.jsGlobals, data.jsGlobals);
    }
    configuration.verbose = data.verbose;
    return configuration;
  }
};
<commit_msg>grunt-amberc: fix comment<commit_after>module.exports = function(grunt) {

  var path = require('path');
  var fs = require('fs');
  var amberc = require('../../bin/amberc.js');

  /**
     Full config looks like this:
     amberc: {
       _config: {
         amber_dir: process.cwd(),     // REQUIRED
         closure_jar: '',              // optional
         verbose: true                 // optional
       },
       helloWorld: {
         src: ['projects/HelloWorld/st/HelloWorld.st'], // REQUIRED
         output_dir: 'projects/HelloWorld/js',  // optional
         libraries: 'Canvas',                   // optional
         jsGlobals: ['global1', 'global2'],     // optional
         main_class: 'HelloWorld',              // optional
         output_name: 'helloWorld',            // optional
         init: 'myInit',                       // optional
         main_file: 'myMain.js',               // optional
         deploy: true,                         // optional
         output_suffix: 'mySuffix',            // optional
         library_suffix: '-0.9'                // optional
       },
     },

   */
  grunt.registerMultiTask('amberc', 'Compile Smalltalk files with the amberc compiler', function() {
    // mark required properties
    this.requiresConfig('amberc.options.amber_dir');
    this.requiresConfig(['amberc', this.target, 'src']);

    var options = this.options({
      amber_dir: undefined,
      closure_jar: '',
      verbose: false
    });
    this.data.verbose = options.verbose;

    // mark task as async task
    var done = this.async();

    // create and initialize amberc
    var compiler = new amberc.Compiler(grunt.config('amberc.options.amber_dir'), grunt.config('amberc.options.closure_jar'));

    // generate the amberc configuration out of the given target properties
    var configuration = generateCompilerConfiguration(this.data, grunt.config('amberc.options.amber_dir'));

    // run the compiler and call the async callback once finished
    var self = this;
    compiler.main(configuration, function(){
      // signal that task has finished
      done();
    });
  });

  function generateCompilerConfiguration(data, amber_dir) {
    var configuration = amberc.createDefaults(amber_dir);
    var parameters = [];

    var libraries = data.libraries;
    if (undefined !== libraries) {
      configuration.load = libraries;
    }
    var initFile = data.init;
    if (undefined !== initFile) {
      configuration.init = initFile;
    }
    var mainClass = data.main_class;
    if (undefined !== mainClass) {
      configuration.main = mainClass;
    }
    var mainFile = data.main_file;
    if (undefined !== mainFile) {
      configuration.mainfile = mainFile;
    }
    if (true === data.deploy) {
      configuration.deploy = true;
    }
    var outputSuffix = data.output_suffix;
    if (undefined !== outputSuffix) {
      configuration.suffix = outputSuffix;
      configuration.suffix_used = outputSuffix;
    }
    var librarySuffix = data.library_suffix;
    if (undefined !== librarySuffix) {
      configuration.loadsuffix = librarySuffix;
      configuration.suffix_used = librarySuffix;
    }
    var sourceFiles = data.src;
    if (undefined !== sourceFiles) {
      sourceFiles.forEach(function(currentItem){
        var fileSuffix = path.extname(currentItem);
        switch (fileSuffix) {
          case '.st':
            configuration.stFiles.push(currentItem);
          break;
          case '.js':
            configuration.jsFiles.push(currentItem);
          break;
        }
      });
    }
    var outputName = data.output_name;
    if (undefined !== outputName) {
      configuration.program = outputName;
    }
    if (undefined !== data.output_dir) {
      configuration.output_dir = data.output_dir;
    }
    if (undefined !== data.jsGlobals) {
      configuration.jsGlobals.push.apply(configuration.jsGlobals, data.jsGlobals);
    }
    configuration.verbose = data.verbose;
    return configuration;
  }
};
<|endoftext|>


<commit_before>/*
   Schedule things to happen at certain times

   By Thomas Levine, standing on the substantial shoulders
   of his group at the HTML5 Hackathon at Google Kirkland

   ----

   Copyright 2011, Thomas Levine
   Distributed under the terms of the GNU Affero General Public License

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

at = {
   //The queue
  'atq':{}
, 'DEFAULTS':{
    'PRECISION':8
  }
, 'dateToString':function(date) {
    return date.getTime()+'';
  }

   //Low-level interface for saving the function in the queue
, '_set':function(func,time,signedprecision){
      time.setTime(time.getTime()+signedprecision);
      var d = this.dateToString(time);
      this.atq[d] = func;
    }

   //User interface for saving the function in the queue
, 'at':function (func, time, options) {
    //If a timestamp is given instead of a Date
    if (!time.getSeconds) {
      var tmp = new Date();
      tmp.setTime(time);
      time = tmp;
    }
    if (typeof(options.precision)==='undefined'){
      //How far away from the exact time is acceptable?
      //Use a number in milliseconds
      options.precision=this.DEFAULTS.PRECISION;
    }
    if (typeof(options.log)==='undefined'){
      options.log=false;
    }

    //Add to the queue
    /* Daemon
    var p=0-Math.abs(precision);
    var p_end=Math.abs(precision);
    for (p;p<=precision;p++){
      this._set(func,time,p);
    }
    */
    //No daemon
    setTimeout(function(){
      var wrongness=time.getTime()-new Date().getTime());
      if (options.log){
        if (wrongness>0) {
          console.log('Running '+wrongness+' milliseconds early');
        } else if (wrongness<0) {
          console.log('Running '+(-1*wrongness)+' milliseconds late');
        } else {
          console.log('Running perfectly on time');
        }
      }
      func();
    },time.getTime()-new Date().getTime());
  }

, 'atd': function(thisAT) { //Daemon
    //thisAT just sends this to atd when it's run with setTimeout
    if (typeof(thisAT)==='undefined'){
      var thisAT=this;
    }

    var date = new Date();
    var d = thisAT.dateToString(date);
    var alarm = thisAT.atq[d];

    if (alarm) {
      alarm();
    }

    // Run again
    setTimeout(function(){
      thisAT.atd(thisAT);
    },1);
  }
}
//at.atd();
<commit_msg>remove )<commit_after>/*
   Schedule things to happen at certain times

   By Thomas Levine, standing on the substantial shoulders
   of his group at the HTML5 Hackathon at Google Kirkland

   ----

   Copyright 2011, Thomas Levine
   Distributed under the terms of the GNU Affero General Public License

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

at = {
   //The queue
  'atq':{}
, 'DEFAULTS':{
    'PRECISION':8
  }
, 'dateToString':function(date) {
    return date.getTime()+'';
  }

   //Low-level interface for saving the function in the queue
, '_set':function(func,time,signedprecision){
      time.setTime(time.getTime()+signedprecision);
      var d = this.dateToString(time);
      this.atq[d] = func;
    }

   //User interface for saving the function in the queue
, 'at':function (func, time, options) {
    //If a timestamp is given instead of a Date
    if (!time.getSeconds) {
      var tmp = new Date();
      tmp.setTime(time);
      time = tmp;
    }
    if (typeof(options.precision)==='undefined'){
      //How far away from the exact time is acceptable?
      //Use a number in milliseconds
      options.precision=this.DEFAULTS.PRECISION;
    }
    if (typeof(options.log)==='undefined'){
      options.log=false;
    }

    //Add to the queue
    /* Daemon
    var p=0-Math.abs(precision);
    var p_end=Math.abs(precision);
    for (p;p<=precision;p++){
      this._set(func,time,p);
    }
    */
    //No daemon
    setTimeout(function(){
      var wrongness=time.getTime()-new Date().getTime();
      if (options.log){
        if (wrongness>0) {
          console.log('Running '+wrongness+' milliseconds early');
        } else if (wrongness<0) {
          console.log('Running '+(-1*wrongness)+' milliseconds late');
        } else {
          console.log('Running perfectly on time');
        }
      }
      func();
    },time.getTime()-new Date().getTime());
  }

, 'atd': function(thisAT) { //Daemon
    //thisAT just sends this to atd when it's run with setTimeout
    if (typeof(thisAT)==='undefined'){
      var thisAT=this;
    }

    var date = new Date();
    var d = thisAT.dateToString(date);
    var alarm = thisAT.atq[d];

    if (alarm) {
      alarm();
    }

    // Run again
    setTimeout(function(){
      thisAT.atd(thisAT);
    },1);
  }
}
//at.atd();
<|endoftext|>


<commit_before>/*
NAME: OrbitalGamepadNavigation
DESC: Smooth Orbital navigation behavior using Gamepad.
FEATURES:
  - Designed to be attached to a Camera Actor
  - Smooth navigation
  - Gamepad support
  - zoom speed proportionnal to target distance (compatible with large scale navigations like Globe)
  - Multiple camera targets, switchable at runtime using gamepad e1 button.
VERSION: Developped on Creative Experience v2017x
KNOWN limitations:
- Tested on XBox360 gamepad only.
PARAMS:
    target (3DActor: null)
    verticalSensibility (Double: 0.05)
    horizontalSensibility (Double: 0.05)
    zoomSensibility (Double: 100.0)
    smoothFactor (Double: 0.5)
    targets (Collection: null)
    minDistance (Double: 100.0)
    maxDistance (Double: 20000)
*/

beScript.onStart = function() {
    //Will be called on the experience start.

    // clamp var
    this.elevationClamp_min = -0.9;
    this.elevationClamp_max = 0.9;

    this.gamepadDeadZone = 0.4;

    this.apple = new this.SphericalCoordinates('Test');
    this.apple.radius = 2500;
    this.apple.polar = 0;
    this.apple.elevation = 0.0;

    this.currentTarget = 0;
    if (this.targets === null || this.targets === undefined) {
        console.log("No target collection defined, using default target parameter as camera target");
    } else {
        var item = this.targets.getObjectAt(0);
        if (item !== null && item !== undefined) {
            console.log("Targets collection defined, using item #0 as camera target: " + item.name);
            this.target = item;
        } else {
            console.log("Empty targets collection defined, using default target parameter as camera target");
        }
    }

    this.desiredPos = cgmMath.addVectorToVector(this.target.getPosition(), this.SphericalToCartesian(this.apple.radius, this.apple.polar, this.apple.elevation));
    this.actor.setPosition(this.desiredPos);
    var targetPos = this.target.getPosition();

    this.lookAtFactor = 1;

    this.currentTime = new Date().getTime();
    this.previousTime = this.currentTime;
};

beScript.onStop = function() {
    //Will be called on experience stop.
};

beScript.execute = function() {
    var gamepad = EP.Devices.getGamepad();
    //Insert your code here.
    this.currentTime = new Date().getTime();
    this.deltaTime = (this.currentTime - this.previousTime) / 1000;

    /////////////////////////////////////////////////////
    //                                                 //
    //          Camero management
    //                                                 //
    /////////////////////////////////////////////////////
    if (this.isActive === true) {
        // Getting right stick X and Y values
        var rightStick_Xvalue = gamepad.getAxisValue(EP.Gamepad.EAxis.eRSX);
        var rightStick_Yvalue = -gamepad.getAxisValue(EP.Gamepad.EAxis.eRSY);
        var rightTrigger = gamepad.getAxisValue(EP.Gamepad.EAxis.eRT);
        var leftTrigger = gamepad.getAxisValue(EP.Gamepad.EAxis.eLT);

        if (rightStick_Xvalue < -this.gamepadDeadZone || rightStick_Xvalue > this.gamepadDeadZone) {
            this.apple.polar = this.apple.polar - rightStick_Xvalue * this.horizontalSensibility;
        } else {
            rightStick_Xvalue = 0;
        }

        if (rightStick_Yvalue < -this.gamepadDeadZone || rightStick_Yvalue > this.gamepadDeadZone) {
            this.apple.elevation = this.clamp((this.apple.elevation + rightStick_Yvalue * this.verticalSensibility), this.elevationClamp_min, this.elevationClamp_max);
        } else {
            rightStick_Yvalue = 0;
        }

        var distToTarget = (this.target.getPosition().sub(this.getActor().getPosition())).norm();
        if (distToTarget < this.range01Threshold) {
            this.range01.visible = false;
            this.range02.visible = false;
            this.range03.visible = false;
        }
        if (distToTarget > this.range01Threshold && distToTarget < this.range02Threshold) {
            this.range01.visible = true;
            this.range02.visible = false;
            this.range03.visible = false;
        }
        if (distToTarget > this.range02Threshold && distToTarget < this.range03Threshold) {
            this.range01.visible = true;
            this.range02.visible = true;
            this.range03.visible = true;
        }

        if (rightTrigger < -this.gamepadDeadZone / 2 || rightTrigger > this.gamepadDeadZone / 2) {
            this.apple.radius = this.clamp((this.apple.radius + rightTrigger * this.zoomSensibility * distToTarget / 1000.0), this.minDistance, this.maxDistance);
        } else {
            rightTrigger = 0;
        }

        if (leftTrigger < -this.gamepadDeadZone / 2 || leftTrigger > this.gamepadDeadZone / 2) {
            this.apple.radius = this.clamp((this.apple.radius - leftTrigger * this.zoomSensibility * distToTarget / 1000.0), this.minDistance, this.maxDistance);
        } else {
            leftTrigger = 0;
        }

        // Camera movement
        var targetPos = this.target.getPosition();
        this.desiredPos = cgmMath.addVectorToVector(targetPos, this.SphericalToCartesian(this.apple.radius, this.apple.polar, this.apple.elevation));
        var camPos = this.actor.getPosition();
        var newDampPos = this.SimpleDamping(camPos, this.desiredPos, this.smoothFactor);
        this.actor.setPosition(newDampPos);
        this.lookAt(targetPos);

    }

    this.previousTime = this.currentTime;
};

// Change camera target dynamically
beScript.changeTarget = function(iTargetNb) {
    console.log("Changing orbital camera target");
    var item;
    if (iTargetNb === null || iTargetNb === undefined) {
        item = this.targets.getObjectAt((++this.currentTarget) % this.targets.getObjectCount());
        if (item !== null && item !== undefined) {
            this.target = item;
            console.log("Changing camera target to: " + this.target.name);
        } else {
            console.error("null item in cam orbital targets collection");
        }
    } else {
        item = this.targets.getObjectAt(iTargetNb);
        if (item !== null && item !== undefined) {
            this.target = item;
            console.log("Changing camera target to: " + this.target.name);
        } else {
            console.error("null item in cam orbital targets collection");
        }
    }
};

// Target change on gamepad e1 button pressed
beScript.onAllGamepadPress = function(iGamepadPressEvent) {
    //console.log("Gamepad pressed: " + iGamepadPressEvent.getButton());
    if (iGamepadPressEvent.getButton() === EP.Gamepad.EButton.e1) {
        //this.changeTarget();
    }
};

/////////////////////////////////////////////////////
//                                                 //
//          Utilities
//                                                 //
/////////////////////////////////////////////////////

beScript.SphericalToCartesian = function(radius, polar, elevation) {
    var a = radius * Math.cos(elevation);
    var outCart = new ThreeDS.Mathematics.Vector3D();
    outCart.x = a * Math.cos(polar);
    outCart.y = a * Math.sin(polar);
    outCart.z = radius * Math.sin(elevation);
    return outCart;
};

beScript.CartesianToSpherical = function(cartCoords) {
    if (cartCoords.x === 0) {
        cartCoords.x = 0.000000001;
    }
    var outRadius = Math.sqrt((cartCoords.x * cartCoords.x) + (cartCoords.y * cartCoords.y) + (cartCoords.z * cartCoords.z));
    var outPolar = Math.atan(cartCoords.z / cartCoords.x);
    if (cartCoords.x < 0)
        outPolar += Math.PI;
    var outElevation = Math.asin(cartCoords.y / outRadius);

    var thisSc = new this.SphericalCoordinates("lo");
    thisSc.radius = outRadius;
    thisSc.polar = outPolar;
    thisSc.elevation = outElevation;

    return thisSc;
};

beScript.lookAt = function(iTarget) {

    var targetPos;
    if (iTarget instanceof STU.Actor3D) {
        targetPos = iTarget.getPosition();
    } else if (iTarget instanceof ThreeDS.Mathematics.Vector3D) {
        targetPos = iTarget.clone();
    }

    var Z = new ThreeDS.Mathematics.Vector3D();
    Z.z = 1;

    // computing the new sight direction using current eye position and the position of the target
    var dir = this.getActor().getPosition();
    dir.subVectorFromVector(targetPos);
    dir.normalize();

    // projecting the new sight direction on the ground to get a right direction parallel to the ground
    var dirOnGround = ThreeDS.Mathematics.cross(Z, ThreeDS.Mathematics.cross(dir, Z));
    dirOnGround.normalize();

    // computing the right direction using the ground-projected sight direction
    var right = ThreeDS.Mathematics.cross(Z, dirOnGround);
    right.normalize();

    // computing the up direction as usual
    var up = ThreeDS.Mathematics.cross(dir, right);
    up.normalize();

    // updating the transform
    var transform = this.getActor().getTransform();
    var matrix = transform.matrix;

    var qCurrent = new ThreeDS.Mathematics.Quaternion();
    qCurrent.rotMatrixToQuaternion(matrix);

    var mTargetLook = new ThreeDS.Mathematics.Matrix3x3();
    mTargetLook.setCoef([dir.x, right.x, up.x,
        dir.y, right.y, up.y,
        dir.z, right.z, up.z
    ]);

    var qTargetLook = new ThreeDS.Mathematics.Quaternion();
    qTargetLook.rotMatrixToQuaternion(mTargetLook);

    transform.matrix = qCurrent.slerp(qTargetLook, this.lookAtFactor).quaternionToRotMatrix();

    this.getActor().setTransform(transform);

};

beScript.SimpleDamping = function(actualValue, destinationValue, dampingFatcor) {
    var lerpVec = new cgmMath.Vector2D();
    lerpVec = actualValue.lerp(destinationValue, (dampingFatcor * this.deltaTime));
    return lerpVec;

};

beScript.clamp = function(value, min, max) {
    return Math.min(Math.max(value, min), max);
};

beScript.distance = function(vecA, vecB) {
    return Math.sqrt(Math.pow((vecA.x - vecB.x), 2) + Math.pow((vecA.y - vecB.y), 2) + Math.pow((vecA.z - vecB.z), 2));
};

beScript.SphericalCoordinates = function(type) {
    this.type = type;
    this.radius = 0;
    this.polar = 0;
    this.elevation = 0;
};

beScript.enableCamera = function() {
    this.isActive = true;
};

beScript.disableCamera = function() {
    this.isActive = false;
};
<commit_msg>added param isActive<commit_after>/*
NAME: OrbitalGamepadNavigation
DESC: Smooth Orbital navigation behavior using Gamepad.
FEATURES:
  - Designed to be attached to a Camera Actor
  - Smooth navigation
  - Gamepad support
  - zoom speed proportionnal to target distance (compatible with large scale navigations like Globe)
  - Multiple camera targets, switchable at runtime using gamepad e1 button.
VERSION: Developped on Creative Experience v2017x
KNOWN limitations:
- Tested on XBox360 gamepad only.
PARAMS:
    isActive (boolean: true)
    target (3DActor: null)
    verticalSensibility (Double: 0.05)
    horizontalSensibility (Double: 0.05)
    zoomSensibility (Double: 100.0)
    smoothFactor (Double: 0.5)
    targets (Collection: null)
    minDistance (Double: 100.0)
    maxDistance (Double: 20000)
*/

beScript.onStart = function() {
    //Will be called on the experience start.

    // clamp var
    this.elevationClamp_min = -0.9;
    this.elevationClamp_max = 0.9;

    this.gamepadDeadZone = 0.4;

    this.apple = new this.SphericalCoordinates('Test');
    this.apple.radius = 2500;
    this.apple.polar = 0;
    this.apple.elevation = 0.0;

    this.currentTarget = 0;
    if (this.targets === null || this.targets === undefined) {
        console.log("No target collection defined, using default target parameter as camera target");
    } else {
        var item = this.targets.getObjectAt(0);
        if (item !== null && item !== undefined) {
            console.log("Targets collection defined, using item #0 as camera target: " + item.name);
            this.target = item;
        } else {
            console.log("Empty targets collection defined, using default target parameter as camera target");
        }
    }

    this.desiredPos = cgmMath.addVectorToVector(this.target.getPosition(), this.SphericalToCartesian(this.apple.radius, this.apple.polar, this.apple.elevation));
    this.actor.setPosition(this.desiredPos);
    var targetPos = this.target.getPosition();

    this.lookAtFactor = 1;

    this.currentTime = new Date().getTime();
    this.previousTime = this.currentTime;
};

beScript.onStop = function() {
    //Will be called on experience stop.
};

beScript.execute = function() {
    var gamepad = EP.Devices.getGamepad();
    //Insert your code here.
    this.currentTime = new Date().getTime();
    this.deltaTime = (this.currentTime - this.previousTime) / 1000;

    /////////////////////////////////////////////////////
    //                                                 //
    //          Camero management
    //                                                 //
    /////////////////////////////////////////////////////
    if (this.isActive === true) {
        // Getting right stick X and Y values
        var rightStick_Xvalue = gamepad.getAxisValue(EP.Gamepad.EAxis.eRSX);
        var rightStick_Yvalue = -gamepad.getAxisValue(EP.Gamepad.EAxis.eRSY);
        var rightTrigger = gamepad.getAxisValue(EP.Gamepad.EAxis.eRT);
        var leftTrigger = gamepad.getAxisValue(EP.Gamepad.EAxis.eLT);

        if (rightStick_Xvalue < -this.gamepadDeadZone || rightStick_Xvalue > this.gamepadDeadZone) {
            this.apple.polar = this.apple.polar - rightStick_Xvalue * this.horizontalSensibility;
        } else {
            rightStick_Xvalue = 0;
        }

        if (rightStick_Yvalue < -this.gamepadDeadZone || rightStick_Yvalue > this.gamepadDeadZone) {
            this.apple.elevation = this.clamp((this.apple.elevation + rightStick_Yvalue * this.verticalSensibility), this.elevationClamp_min, this.elevationClamp_max);
        } else {
            rightStick_Yvalue = 0;
        }

        var distToTarget = (this.target.getPosition().sub(this.getActor().getPosition())).norm();
        if (distToTarget < this.range01Threshold) {
            this.range01.visible = false;
            this.range02.visible = false;
            this.range03.visible = false;
        }
        if (distToTarget > this.range01Threshold && distToTarget < this.range02Threshold) {
            this.range01.visible = true;
            this.range02.visible = false;
            this.range03.visible = false;
        }
        if (distToTarget > this.range02Threshold && distToTarget < this.range03Threshold) {
            this.range01.visible = true;
            this.range02.visible = true;
            this.range03.visible = true;
        }

        if (rightTrigger < -this.gamepadDeadZone / 2 || rightTrigger > this.gamepadDeadZone / 2) {
            this.apple.radius = this.clamp((this.apple.radius + rightTrigger * this.zoomSensibility * distToTarget / 1000.0), this.minDistance, this.maxDistance);
        } else {
            rightTrigger = 0;
        }

        if (leftTrigger < -this.gamepadDeadZone / 2 || leftTrigger > this.gamepadDeadZone / 2) {
            this.apple.radius = this.clamp((this.apple.radius - leftTrigger * this.zoomSensibility * distToTarget / 1000.0), this.minDistance, this.maxDistance);
        } else {
            leftTrigger = 0;
        }

        // Camera movement
        var targetPos = this.target.getPosition();
        this.desiredPos = cgmMath.addVectorToVector(targetPos, this.SphericalToCartesian(this.apple.radius, this.apple.polar, this.apple.elevation));
        var camPos = this.actor.getPosition();
        var newDampPos = this.SimpleDamping(camPos, this.desiredPos, this.smoothFactor);
        this.actor.setPosition(newDampPos);
        this.lookAt(targetPos);

    }

    this.previousTime = this.currentTime;
};

// Change camera target dynamically
beScript.changeTarget = function(iTargetNb) {
    console.log("Changing orbital camera target");
    var item;
    if (iTargetNb === null || iTargetNb === undefined) {
        item = this.targets.getObjectAt((++this.currentTarget) % this.targets.getObjectCount());
        if (item !== null && item !== undefined) {
            this.target = item;
            console.log("Changing camera target to: " + this.target.name);
        } else {
            console.error("null item in cam orbital targets collection");
        }
    } else {
        item = this.targets.getObjectAt(iTargetNb);
        if (item !== null && item !== undefined) {
            this.target = item;
            console.log("Changing camera target to: " + this.target.name);
        } else {
            console.error("null item in cam orbital targets collection");
        }
    }
};

// Target change on gamepad e1 button pressed
beScript.onAllGamepadPress = function(iGamepadPressEvent) {
    //console.log("Gamepad pressed: " + iGamepadPressEvent.getButton());
    if (iGamepadPressEvent.getButton() === EP.Gamepad.EButton.e1) {
        //this.changeTarget();
    }
};

/////////////////////////////////////////////////////
//                                                 //
//          Utilities
//                                                 //
/////////////////////////////////////////////////////

beScript.SphericalToCartesian = function(radius, polar, elevation) {
    var a = radius * Math.cos(elevation);
    var outCart = new ThreeDS.Mathematics.Vector3D();
    outCart.x = a * Math.cos(polar);
    outCart.y = a * Math.sin(polar);
    outCart.z = radius * Math.sin(elevation);
    return outCart;
};

beScript.CartesianToSpherical = function(cartCoords) {
    if (cartCoords.x === 0) {
        cartCoords.x = 0.000000001;
    }
    var outRadius = Math.sqrt((cartCoords.x * cartCoords.x) + (cartCoords.y * cartCoords.y) + (cartCoords.z * cartCoords.z));
    var outPolar = Math.atan(cartCoords.z / cartCoords.x);
    if (cartCoords.x < 0)
        outPolar += Math.PI;
    var outElevation = Math.asin(cartCoords.y / outRadius);

    var thisSc = new this.SphericalCoordinates("lo");
    thisSc.radius = outRadius;
    thisSc.polar = outPolar;
    thisSc.elevation = outElevation;

    return thisSc;
};

beScript.lookAt = function(iTarget) {

    var targetPos;
    if (iTarget instanceof STU.Actor3D) {
        targetPos = iTarget.getPosition();
    } else if (iTarget instanceof ThreeDS.Mathematics.Vector3D) {
        targetPos = iTarget.clone();
    }

    var Z = new ThreeDS.Mathematics.Vector3D();
    Z.z = 1;

    // computing the new sight direction using current eye position and the position of the target
    var dir = this.getActor().getPosition();
    dir.subVectorFromVector(targetPos);
    dir.normalize();

    // projecting the new sight direction on the ground to get a right direction parallel to the ground
    var dirOnGround = ThreeDS.Mathematics.cross(Z, ThreeDS.Mathematics.cross(dir, Z));
    dirOnGround.normalize();

    // computing the right direction using the ground-projected sight direction
    var right = ThreeDS.Mathematics.cross(Z, dirOnGround);
    right.normalize();

    // computing the up direction as usual
    var up = ThreeDS.Mathematics.cross(dir, right);
    up.normalize();

    // updating the transform
    var transform = this.getActor().getTransform();
    var matrix = transform.matrix;

    var qCurrent = new ThreeDS.Mathematics.Quaternion();
    qCurrent.rotMatrixToQuaternion(matrix);

    var mTargetLook = new ThreeDS.Mathematics.Matrix3x3();
    mTargetLook.setCoef([dir.x, right.x, up.x,
        dir.y, right.y, up.y,
        dir.z, right.z, up.z
    ]);

    var qTargetLook = new ThreeDS.Mathematics.Quaternion();
    qTargetLook.rotMatrixToQuaternion(mTargetLook);

    transform.matrix = qCurrent.slerp(qTargetLook, this.lookAtFactor).quaternionToRotMatrix();

    this.getActor().setTransform(transform);

};

beScript.SimpleDamping = function(actualValue, destinationValue, dampingFatcor) {
    var lerpVec = new cgmMath.Vector2D();
    lerpVec = actualValue.lerp(destinationValue, (dampingFatcor * this.deltaTime));
    return lerpVec;

};

beScript.clamp = function(value, min, max) {
    return Math.min(Math.max(value, min), max);
};

beScript.distance = function(vecA, vecB) {
    return Math.sqrt(Math.pow((vecA.x - vecB.x), 2) + Math.pow((vecA.y - vecB.y), 2) + Math.pow((vecA.z - vecB.z), 2));
};

beScript.SphericalCoordinates = function(type) {
    this.type = type;
    this.radius = 0;
    this.polar = 0;
    this.elevation = 0;
};

beScript.enableCamera = function() {
    this.isActive = true;
};

beScript.disableCamera = function() {
    this.isActive = false;
};
<|endoftext|>


<commit_before>/**
 * grunt-wrap
 * https://github.com/chrissrogers/grunt-wrap
 *
 * Copyright (c) 2012 Christopher Rogers
 * Licensed under the MIT license.
 */

// wrap: {
//   modules: {
//     src: ['assets/*.js'],
//     dest: 'dist/',
//     wrapper: ['define(function (require, exports, module) {\n', '\n});']
//   }
// }

module.exports = function(grunt) {

  var wrap;

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('wrap', 'Wrap files.', function () {
    var files = grunt.file.expandFiles(this.file.src),
        path = require('path'),
        task = this,
        src;

    // Concat specified files.
    if (files) {
      files.map(function (filepath) {
        src = wrap(filepath, {wrapper: task.data.wrapper});
        grunt.file.write(path.join(task.file.dest, filepath), src);
      });
    }

    // Fail task if errors were logged.
    if (this.errorCount) return false;

    // Otherwise, print a success message.
    grunt.log.writeln('Wrapped files created in "' + this.file.dest + '".');
  });

  // ==========================================================================
  // HELPERS
  // ==========================================================================

  wrap = function (filepath, options) {
    options = grunt.util._.defaults(options || {}, {
      wrapper: ['', '']
    });
    return options.wrapper[0] + grunt.file.read(filepath) + options.wrapper[1];
  }

};
<commit_msg>Grunt v0.4.0rc6 complatibility<commit_after>/**
 * grunt-wrap
 * https://github.com/chrissrogers/grunt-wrap
 *
 * Copyright (c) 2012 Christopher Rogers
 * Licensed under the MIT license.
 */

// wrap: {
//   modules: {
//     src: ['assets/*.js'],
//     dest: 'dist/',
//     wrapper: ['define(function (require, exports, module) {\n', '\n});']
//   }
// }

module.exports = function(grunt) {

  var wrap;

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('wrap', 'Wrap files.', function () {
    var path = require('path'),
        src;

    // Concat specified files.

    this.files.forEach(function(file) {
      if (!file.src) return;

      file.src.map(function(filepath) {
        src = wrap(filepath, { wrapper: this.data.wrapper });
        grunt.file.write(path.join(this.data.dest, filepath), src);
      }, this);
    }, this);

    // Fail task if errors were logged.
    if (this.errorCount) return false;

    // Otherwise, print a success message.
    grunt.log.writeln('Wrapped files created in "' + this.data.dest + '".');
  });

  // ==========================================================================
  // HELPERS
  // ==========================================================================

  wrap = function (filepath, options) {
    options = grunt.util._.defaults(options || {}, {
      wrapper: ['', '']
    });
    return options.wrapper[0] + grunt.file.read(filepath) + options.wrapper[1];
  }

};
<|endoftext|>


<commit_before>'use strict'

const libob = require('libobject');
const Parser = require('./Parser');

module.exports = (scope, event_iri, args, callback) => {

    let event = scope.cache.get(event_iri);
    if (event) {
        event.ready ? process.nextTick(() => callback(null, event)) : event.queue.push(callback);
        return;
    }

    event = {};
    event.queue = [];
    event.load = 0;
    event.iri = event_iri;
    event.seq = {};
    event.set = (handler_iri, key, value) => Handler(scope, event, handler_iri, key, value);
    event.done = (error, finish) => Done(scope, event, error, finish);

    scope.cache.set(event_iri, event);
    event.queue.push(callback);

    process.nextTick(() => {
        scope.read(event_iri, args).pipe(Parser(scope, event, args))
        .on('error', error => event.done(error))
        .on('finish', () => event.done(null, true));
    });
};

function loaded (error, event) {
    event.queue.forEach(callback => callback(error, event));
    event.queue = [];
    event.load = 0;
}

function Done (scope, event, error, finish) {

    if (error) {
        scope.cache.del(event.iri);
    } else {
        finish ? (event._done = finish) : --event.load;
        event.ready = event.load === 0;
    }

    if (error || event.ready && event._done) {
        loaded(error, event);
    }
}

function Handler (scope, event, iri, key, value) {

    let handler = event.seq[iri] = event.seq[iri] || {};
    handler[key] = value;

    if (handler.I) {

        if (typeof handler.D === 'string') {
            getMethodRef(event, handler, 'D', handler.D);
        }

        if (typeof handler.S === 'string') {
            getMethodRef(event, handler, 'S', handler.S);
        }
    }
}

function getMethodRef (event, handler, type, method) {
    if (!(handler[type] = libob.path(method, handler.I))) {
        return event.done(new Error('Flow.parse: Method "' + method + '" on instance "' + handler.I._name + '" not found.'));
    }
}
<commit_msg>set "once" flag for once handlers<commit_after>'use strict'

const libob = require('libobject');
const Parser = require('./Parser');

module.exports = (scope, event_iri, args, callback) => {

    let event = scope.cache.get(event_iri);
    if (event) {
        event.ready ? process.nextTick(() => callback(null, event)) : event.queue.push(callback);
        return;
    }

    event = {};
    event.queue = [];
    event.load = 0;
    event.iri = event_iri;
    event.seq = {};
    event.set = (handler_iri, key, value) => Handler(scope, event, handler_iri, key, value);
    event.done = (error, finish) => Done(scope, event, error, finish);

    scope.cache.set(event_iri, event);
    event.queue.push(callback);

    process.nextTick(() => {
        scope.read(event_iri, args).pipe(Parser(scope, event, args))
        .on('error', error => event.done(error))
        .on('finish', () => event.done(null, true));
    });
};

function loaded (error, event) {
    event.queue.forEach(callback => callback(error, event));
    event.queue = [];
    event.load = 0;
}

function Done (scope, event, error, finish) {

    if (error) {
        scope.cache.del(event.iri);
    } else {
        finish ? (event._done = finish) : --event.load;
        event.ready = event.load === 0;
    }

    if (error || event.ready && event._done) {
        loaded(error, event);
    }
}

function Handler (scope, event, iri, key, value) {

    let handler = event.seq[iri] = event.seq[iri] || {};

    if (key === '1') {
        handler.once = true;
        key = 'D';
    }

    handler[key] = value;

    if (handler.I) {

        if (typeof handler.D === 'string') {
            getMethodRef(event, handler, 'D', handler.D);
        }

        if (typeof handler.S === 'string') {
            getMethodRef(event, handler, 'S', handler.S);
        }
    }
}

function getMethodRef (event, handler, type, method) {
    if (!(handler[type] = libob.path(method, handler.I))) {
        return event.done(new Error('Flow.parse: Method "' + method + '" on instance "' + handler.I._name + '" not found.'));
    }
}
<|endoftext|>


<commit_before>define([
	"qunit",
	"inputmask.dependencyLib",
	"inputmask",
	"../dist/inputmask/inputmask.date.extensions",
	"../dist/inputmask/inputmask.extensions",
	"../dist/inputmask/inputmask.numeric.extensions",
	"../dist/inputmask/inputmask.phone.extensions",
	"../dist/inputmask/inputmask.regex.extensions",
	"prototypeExtensions",
	"simulator"
], function (qunit, $, Inputmask) {

	qunit.module("inputEventOnly: true");
	qunit.test("inputmask(\"9-a{3}9{3}\" - simple dynamic mask", function (assert) {
		var $fixture = $("#qunit-fixture");
		$fixture.append('<input type="text" id="testmask" />');
		var testmask = document.getElementById("testmask");
		Inputmask({
			inputEventOnly: true,
			mask: "XXX-9999-9999-XXX-XXX",
			definitions: {
				"X": {
					validator: "[A-Ha-hJ-Nj-nPpR-Zr-z2-9]",
					cardinality: 1,
					casing: "upper"
				}
			}
		}).mask(testmask);

		testmask.focus();
		//simulate input
		$(testmask).input("abc12341234abcabc");

		assert.equal(testmask.value, "ABC-1234-1234-ABC-ABC", "Result " + testmask.value);
	});

});
<commit_msg>update description<commit_after>define([
	"qunit",
	"inputmask.dependencyLib",
	"inputmask",
	"../dist/inputmask/inputmask.date.extensions",
	"../dist/inputmask/inputmask.extensions",
	"../dist/inputmask/inputmask.numeric.extensions",
	"../dist/inputmask/inputmask.phone.extensions",
	"../dist/inputmask/inputmask.regex.extensions",
	"prototypeExtensions",
	"simulator"
], function (qunit, $, Inputmask) {
	qunit.module("inputEventOnly: true");

	qunit.test("XXX-9999-9999-XXX-XXX - gersteba", function (assert) {
		var $fixture = $("#qunit-fixture");
		$fixture.append('<input type="text" id="testmask" />');
		var testmask = document.getElementById("testmask");
		Inputmask({
			inputEventOnly: true,
			mask: "XXX-9999-9999-XXX-XXX",
			definitions: {
				"X": {
					validator: "[A-Ha-hJ-Nj-nPpR-Zr-z2-9]",
					cardinality: 1,
					casing: "upper"
				}
			}
		}).mask(testmask);

		testmask.focus();
		//simulate input
		$(testmask).input("abc12341234abcabc");

		assert.equal(testmask.value, "ABC-1234-1234-ABC-ABC", "Result " + testmask.value);
	});

});
<|endoftext|>


<commit_before>var source = new EventSource("/stream");

source.addEventListener('message', function(e) {
  var json = JSON.parse(e.data);

}, false);

source.addEventListener('open', function(e) {
  // Connection was opened.
}, false);

source.addEventListener('error', function(e) {
  if (e.readyState == EventSource.CLOSED) {
    // Connection was closed.
  }
}, false);

<commit_msg>dispatch handlers<commit_after>
var sensors = {
  "light": function (state) {
    console.log(state ? "light on" : "light off");
  },
  "humidity": function (value) {
    console.log("humidity = " + value);
  },
  "temperature": function (value) {
    console.log("temperature = " + value);
  }
};

////////// Connect and handle the Stream ///////

function connect () {
  var source = new EventSource("/stream");

  source.addEventListener('message', function(e) {
    var json = JSON.parse(e.data);
    for (var key in json)
      if (key in sensors)
        sensors[key](json[key]);
  }, false);

  source.addEventListener('open', function(e) {
    // Connection was opened.
  }, false);

  source.addEventListener('error', function(e) {
    if (e.readyState == EventSource.CLOSED) {
      // Connection was closed.
    }
  }, false);
}

connect();
<|endoftext|>


<commit_before>'use strict';
var assert = require('power-assert').customize({output: {maxDepth: 2}});
var sinon = require('sinon');

require('../../mocks/angular');
var AddLatentVariable = require('../../../../app/src/views/concretes/dialogs/add-latent-variable/add-latent-variable');
var ControllerStatic = AddLatentVariable.Controller;

var mockRootScope, stubRootScope, mockScope, stubScope;
var Controller = (() => {
  mockRootScope = {
    $broadcast: () => {}
  };
  stubRootScope = {
    $broadcast: sinon.stub(mockRootScope, '$broadcast')
  };

  mockScope = {
    dialog: {close: () => {}}
  };
  stubScope = {
    dialog: {
      close: sinon.stub(mockScope.dialog, 'close')
    }
  };
  return new ControllerStatic(mockRootScope, mockScope);
})();
var Definition = AddLatentVariable.Definition;

describe('DialogAddLatentVariable', () => {
  describe('Controller', () => {
    describe('#add()', () => {
      var dummy = 'dummy';
      before(() => {
        Controller.add(dummy);
      });

      it('should give the event name to arg[0] of $broadcast()', () => {
        assert(stubRootScope.$broadcast.getCall(0).args[0] === 'isem:addVariable');
      });

      it('should give the value to arg[1] of $broadcast()', () => {
        assert(stubRootScope.$broadcast.getCall(0).args[1] === dummy);
      });

      it('should do close()', () => {
        assert(stubScope.dialog.close.callCount === 1);
      });
    });
  });

  describe('Definition', () => {
    describe('.ddo()', () => {
      it('should return the field of require including ^cwModal', () => {
        assert(Definition.ddo().require === '^cwModal');
      });

      it('should return the field of controllerAs including AddLatentVariable', () => {
        assert(Definition.ddo().controllerAs === 'AddLatentVariable');
      });
    });
  });

});<commit_msg>refactor(add-latent-variable-spec): improve text abstractness<commit_after>'use strict';
var assert = require('power-assert').customize({output: {maxDepth: 2}});
var sinon = require('sinon');

require('../../mocks/angular');
var AddLatentVariable = require('../../../../app/src/views/concretes/dialogs/add-latent-variable/add-latent-variable');
var ControllerStatic = AddLatentVariable.Controller;

var mockRootScope, stubRootScope, mockScope, stubScope;
var Controller = (() => {
  mockRootScope = {
    $broadcast: () => {}
  };
  stubRootScope = {
    $broadcast: sinon.stub(mockRootScope, '$broadcast')
  };

  mockScope = {
    dialog: {close: () => {}}
  };
  stubScope = {
    dialog: {
      close: sinon.stub(mockScope.dialog, 'close')
    }
  };
  return new ControllerStatic(mockRootScope, mockScope);
})();
var Definition = AddLatentVariable.Definition;

describe('DialogAddLatentVariable', () => {
  describe('Controller', () => {
    describe('#add()', () => {
      var dummy = 'dummy';
      before(() => {
        Controller.add(dummy);
      });

      it('should give the event name to arg[0] of $broadcast()', () => {
        assert(stubRootScope.$broadcast.getCall(0).args[0] === 'isem:addVariable');
      });

      it('should give the value to arg[1] of $broadcast()', () => {
        assert(stubRootScope.$broadcast.getCall(0).args[1] === dummy);
      });

      it('should do close()', () => {
        assert(stubScope.dialog.close.callCount === 1);
      });
    });
  });

  describe('Definition', () => {
    describe('.ddo()', () => {
      it('should return the field of require including the other controller', () => {
        assert(Definition.ddo().require === '^cwModal');
      });

      it('should return the field of controllerAs including the correct name', () => {
        assert(Definition.ddo().controllerAs === 'AddLatentVariable');
      });
    });
  });

});<|endoftext|>


<commit_before>const { isUUID, isDecimal, isEmail } = require('validator');

class Field {
  constructor(config = {}) {
    const { name, model, type, validate, cast, path } = config;

    if (!name) {
      throw new Error('Field requires a name');
    }

    if (!model || !(model.prototype instanceof Model)) {
      throw new Error(`Field '${name}' requires a subclass of Model`);
    }

    // TODO: throw correct errors for nested (schema) fields

    if (!type) {
      throw new Error(`Field '${model.name}.${name}' has no type configured`);
    }

    if (!typesArray.includes(type)) {
      throw new Error(
        `Field '${model.name}.${name}' has an invalid type ('${type}')`
      );
    }

    if (validate && typeof validate !== 'function') {
      throw new Error(
        `Custom validator for field '${model.name}.${name}' should be a function`
      );
    }

    if (cast) {
      if (cast.forSave) {
        if (typeof cast.forSave !== 'function') {
          throw new Error(
            `Pre-save cast function for field '${model.name}.${name}' should be a function`
          );
        }
        this.castors = this.castors || {};
        this.castors.forSave = cast.forSave;
      }
      if (cast.forFetch) {
        if (typeof cast.forFetch !== 'function') {
          throw new Error(
            `Post-fetch cast function for field '${model.name}.${name}' should be a function`
          );
        }
        this.castors = this.castors || {};
        this.castors.forFetch = cast.forFetch;
      }
    }

    this.name = name;
    this.type = type;
    this.model = model;

    if (config.default !== undefined) {
      this.default = config.default;
    }

    if (path) {
      this.path = path;
    } else {
      this.path = name;
    }

    const validators = { type };
    const { required, minLength, maxLength, oneOf, equals, schema } = config;

    if (validate) {
      validators.validate = validate;
    }
    if (required) {
      validators.required = required;
    }
    if (minLength) {
      validators.minLength = minLength;
    }
    if (maxLength) {
      validators.maxLength = maxLength;
    }
    if (oneOf) {
      validators.oneOf = oneOf;
    }
    if (equals) {
      validators.equals = equals;
    }
    if (schema && (type === types.json || type === types.jsonb)) {
      validators.schema = this.getSchemaValidators(schema);
    }

    this.validators = validators;

    // TODO: refactor this to updateModel
    // TODO: remove dependency between setModel, getSchemaValidators and setReference
    this.setModel(model);

    const { references } = config;
    if (references) {
      this.setReference(references);
    }

    let { column } = config;
    if (!column) {
      column = this.getColumnName(name);
    }
    this.column = column;
  }

  getSchemaValidators(schema) {
    const schemaIsArray = Array.isArray(schema);

    if (schemaIsArray) {
      schema = schema[0];
      if (!schema) {
        return [];
      }
    }

    const validators = Object.keys(schema).reduce((validators, key) => {
      // TODO: ensure no `references`, `cast` or `default` options are added in nested schemas?
      const fieldConfig = Object.assign(
        {
          name: key,
          path: `${this.path}.${key}`,
          model: this.model
        },
        schema[key]
      );

      validators[key] = new Field(fieldConfig);
      return validators;
    }, {});

    if (schemaIsArray) {
      return [validators];
    } else {
      return validators;
    }
  }

  getColumnName(fieldName) {
    return fieldName;
  }

  clone() {
    const clone = new this.constructor({
      name: this.name,
      type: this.type,
      model: this.model,
      default: this.default,
      references: this.references,
      column: this.column,
      cast: this.castors
    });
    clone.validators = this.validators;
    return clone;
  }

  throwValidationError(value, validator) {
    throw new this.constructor.ValidationError({
      value,
      validator,
      field: this
    });
  }

  validateIsRequired(value) {
    if (!isSet(value)) {
      this.throwValidationError(value, { required: true });
    }
  }

  validateTypeWith(value, type, validate) {
    if (!isSet(value)) {
      return true;
    }
    if (!validate(value)) {
      this.throwValidationError(value, { type });
    }
  }

  validateIsString(value, type) {
    this.validateTypeWith(value, type, value => typeof value === 'string');
  }

  validateIsEmail(value, type) {
    this.validateIsString(value, 'string');
    this.validateTypeWith(value, type, value => isEmail(value));
  }

  validateIsBinary(value, type) {
    this.validateTypeWith(value, type, value => value instanceof Buffer);
  }

  validateIsInteger(value, type) {
    this.validateTypeWith(value, type, value => Number.isInteger(value));
  }

  validateIsBoolean(value, type) {
    this.validateTypeWith(value, type, value => typeof value === 'boolean');
  }

  validateIsDate(value, type) {
    this.validateTypeWith(value, type, value => value instanceof Date);
  }

  validateWithRegex(value, pattern) {
    // TODO: this is unused atm. add support for regex validators
    this.validateTypeWith(value, value => pattern.test(value));
  }

  validateIsUuid(value, type) {
    this.validateTypeWith(value, type, value => isUUID(value));
  }

  validateIsUuidV4(value, type) {
    this.validateTypeWith(value, type, value => isUUID(value, 4));
  }

  validateIsDecimal(value, type) {
    // validator.isDecimal requires the value to be a string
    this.validateTypeWith(value, type, value => isDecimal(String(value)));
  }

  validateIsJson(value, type) {
    // TODO: autocast json fields since `string` type is not enforced now
    if (Array.isArray(value)) {
      return value;
    }

    const valueType = typeof value;

    if (valueType === 'object') {
      return value;
    }

    if (valueType === 'string') {
      let parsedValue;
      const validate = value => {
        try {
          // TODO: JSON.parse is expensive
          parsedValue = JSON.parse(value);
        } catch (e) {
          return false;
        }
        return !!parsedValue;
      };

      this.validateTypeWith(value, type, validate);

      return parsedValue;
    }
  }

  validateTypeIs(value, type) {
    switch (type) {
      case types.text:
      case types.string:
        return this.validateIsString(value, type);

      case types.binary:
        return this.validateIsBinary(value, type);

      case types.email:
        return this.validateIsEmail(value, type);

      case types.integer:
        return this.validateIsInteger(value, type);

      case types.boolean:
        return this.validateIsBoolean(value, type);

      case types.date:
      case types.dateTime:
        return this.validateIsDate(value, type);

      case types.uuid:
        return this.validateIsUuid(value, type);

      case types.uuidV4:
        return this.validateIsUuidV4(value, type);

      case types.json:
      case types.jsonb:
        return this.validateIsJson(value, type);

      case types.decimal:
        return this.validateIsDecimal(value, type);

      default:
        throw new Error(`no validator has been added for '${type}' types`);
    }
  }

  validateMinLengthIs(value, minLength) {
    if (!isSet(value)) {
      return true;
    }

    if (value.length < minLength) {
      this.throwValidationError(value, { minLength });
    }
  }

  validateMaxLengthIs(value, maxLength) {
    if (!isSet(value)) {
      return true;
    }

    if (value.length > maxLength) {
      this.throwValidationError(value, { maxLength });
    }
  }

  validateIsOneOf(value, oneOf) {
    if (!isSet(value)) {
      return true;
    }

    if (!oneOf.includes(value)) {
      this.throwValidationError(value, { oneOf });
    }
  }

  validateEquals(value, equals) {
    if (!isSet(value)) {
      return true;
    }

    if (value !== equals) {
      this.throwValidationError(value, { equals });
    }
  }

  async validateWithCustom(value, validate, modelInstance) {
    if (!isSet(value)) {
      return true;
    }

    const returnValue = await validate.call(modelInstance, value);

    if (returnValue === false) {
      this.throwValidationError(value, { validate });
    }

    if (typeof returnValue === 'object') {
      return this.validateWithValidators(value, returnValue, modelInstance);
    }
  }

  // async validateJsonField(value, validators, path, modelInstance) {
  //   const validate = async (value, validators) =>
  //     this.validateWithValidators(value, validators, path, modelInstance);
  //
  //   if (Array.isArray(validators)) {
  //     validators = validators[0];
  //
  //     this.validateIsArray(value, path);
  //
  //     if (validators && isSet(value)) {
  //       await Promise.all(value.map(async item => validate(item, validators)));
  //     }
  //   } else {
  //     await validate(value, validators);
  //   }
  // }
  //
  // async validateWithSchema(value, schema, path = '', modelInstance) {
  //   const validate = async (value, schema, index) => {
  //     await Promise.all(
  //       Object.keys(schema).map(async key => {
  //         // TODO: the path is is not working correctly with indexes
  //         path = `${path}.${key}${index !== undefined ? `.${index}` : ''}`;
  //         return this.validateJsonField(
  //           value[key],
  //           schema[key],
  //           path,
  //           modelInstance
  //         );
  //       })
  //     );
  //   };
  //
  //   if (Array.isArray(schema)) {
  //     schema = schema[0];
  //
  //     this.validateIsArray(value, path);
  //
  //     if (schema) {
  //       await Promise.all(
  //         value.map(async (item, index) => validate(item, schema, index))
  //       );
  //     }
  //   } else {
  //     await validate(value, schema);
  //   }
  // }

  async validateSchemaMatches(value, schema, modelInstance) {
    if (Array.isArray(schema)) {
      if (!Array.isArray(value)) {
        this.throwValidationError(value, { schema });
      }

      schema = schema[0];

      if (schema) {
        await Promise.all(
          Object.values(schema).map(async field => {
            const originalPath = field.path;
            await Promise.all(
              value.map(async (item, index) => {
                field.path = originalPath;
                const pathAsArray = field.path.split('.');
                pathAsArray.splice(-1, 0, index);
                field.path = pathAsArray.join('.');
                await field.validate(item[field.name], modelInstance);
                field.path = originalPath;
              })
            );
          })
        );
      }
    } else {
      if (Array.isArray(value)) {
        this.throwValidationError(value, { schema });
      }

      return Promise.all(
        Object.values(schema).map(async field =>
          field.validate(value[field.name], modelInstance)
        )
      );
    }

    // if (type === types.jsonArray) {
    //   if (isSet(value)) {
    //     const originalPath = this.path;
    //     await Promise.all(
    //       value.map(async (item, index) => {
    //         this.path = `${originalPath}.${index}`;
    //         return this.validate(item, modelInstance);
    //       })
    //     );
    //     this.path = originalPath;
    //   }
    // } else {
    //   await Promise.all(
    //     Object.values(schema).map(async field =>
    //       field.validate(typeValidatedValue[field.name], modelInstance)
    //     )
    //   );
    // }
  }

  async validateWithValidators(value, validators, modelInstance) {
    const {
      required,
      type,
      minLength,
      maxLength,
      oneOf,
      equals,
      validate,
      schema
    } = validators;

    if (required) {
      this.validateIsRequired(value);
    }

    if (type) {
      // NOTE: only the validateIsJson validator returns a value for now
      // this is to avoid having to JSON.parse twice
      const typeValidatedValue = this.validateTypeIs(value, type);
      if (schema) {
        await this.validateSchemaMatches(
          typeValidatedValue,
          schema,
          modelInstance
        );
      }
    }

    if (minLength) {
      this.validateMinLengthIs(value, minLength);
    }

    if (maxLength) {
      this.validateMaxLengthIs(value, maxLength);
    }

    if (oneOf) {
      this.validateIsOneOf(value, oneOf);
    }

    if (equals) {
      this.validateEquals(value, equals);
    }

    if (validate) {
      await this.validateWithCustom(value, validate, modelInstance);
    }
  }

  async validate(value, modelInstance) {
    await this.validateWithValidators(value, this.validators, modelInstance);
  }

  cast(value, modelInstance, { forSave, forFetch }) {
    if (!this.castors) {
      return;
    }

    if (forSave) {
      if (!this.castors.forSave) {
        return;
      }
      return this.castors.forSave.call(modelInstance, value);
    }

    if (forFetch) {
      if (!this.castors.forFetch) {
        return;
      }
      return this.castors.forFetch.call(modelInstance, value);
    }
  }

  hasDefault() {
    return this.default !== undefined;
  }

  getDefault(modelInstance) {
    const defaultValue = this.default;

    if (typeof defaultValue === 'function') {
      return defaultValue.call(modelInstance);
    }

    return defaultValue;
  }

  setReference(reference) {
    if (!this.model.references[reference.model.name]) {
      this.model.references[reference.model.name] = {};
    }
    this.model.references[reference.model.name][this.name] = reference;

    if (!reference.model.referenced[this.model.name]) {
      reference.model.referenced[this.model.name] = {};
    }
    if (!reference.model.referenced[this.model.name][reference.name]) {
      reference.model.referenced[this.model.name][reference.name] = [];
    }
    reference.model.referenced[this.model.name][reference.name].push(this);

    this.references = reference;

    return this;
  }

  setModel(model) {
    if (this.model && this.references) {
      const reference = this.references;
      const reverseReference = reference.model.referenced[this.model.name];
      reference.model.referenced[model.name] = reverseReference;
      // TODO: is it necessary to delete though
      delete reference.model.referenced[this.model.name];
    }

    if (this.validators.schema) {
      const schema = Array.isArray(this.validators.schema)
        ? this.validators.schema[0]
        : this.validators.schema;

      if (schema) {
        Object.values(schema).forEach(field => field.setModel(model));
      }
    }

    this.model = model;

    return this;
  }
}

const isSet = value => value !== undefined && value !== null;

// TODO: add password field
// TODO: add all the types supported by knex
// TODO: allow adding types (figure out how to share validation logic with existing types)
// TODO: knorm: support JSON schema for validation at least e.g. { schema: { values: [{ type: 'string' }] }
const types = {
  text: 'text',
  json: 'json',
  jsonb: 'jsonb',
  uuid: 'uuid',
  binary: 'binary',
  uuidV4: 'uuidV4',
  decimal: 'decimal',
  string: 'string',
  boolean: 'boolean',
  integer: 'integer',
  dateTime: 'dateTime',
  date: 'date',
  // custom types
  email: 'email'
};

const typesArray = Object.values(types);
Field.types = types;

const ValidationError = require('./ValidationError');
Field.ValidationError = ValidationError;

module.exports = Field;

const Model = require('./Model'); // circular dep
<commit_msg>chore(field): clean up commented out code<commit_after>const { isUUID, isDecimal, isEmail } = require('validator');

class Field {
  constructor(config = {}) {
    const { name, model, type, validate, cast, path } = config;

    if (!name) {
      throw new Error('Field requires a name');
    }

    if (!model || !(model.prototype instanceof Model)) {
      throw new Error(`Field '${name}' requires a subclass of Model`);
    }

    // TODO: throw correct errors for nested (schema) fields

    if (!type) {
      throw new Error(`Field '${model.name}.${name}' has no type configured`);
    }

    if (!typesArray.includes(type)) {
      throw new Error(
        `Field '${model.name}.${name}' has an invalid type ('${type}')`
      );
    }

    if (validate && typeof validate !== 'function') {
      throw new Error(
        `Custom validator for field '${model.name}.${name}' should be a function`
      );
    }

    if (cast) {
      if (cast.forSave) {
        if (typeof cast.forSave !== 'function') {
          throw new Error(
            `Pre-save cast function for field '${model.name}.${name}' should be a function`
          );
        }
        this.castors = this.castors || {};
        this.castors.forSave = cast.forSave;
      }
      if (cast.forFetch) {
        if (typeof cast.forFetch !== 'function') {
          throw new Error(
            `Post-fetch cast function for field '${model.name}.${name}' should be a function`
          );
        }
        this.castors = this.castors || {};
        this.castors.forFetch = cast.forFetch;
      }
    }

    this.name = name;
    this.type = type;
    this.model = model;

    if (config.default !== undefined) {
      this.default = config.default;
    }

    if (path) {
      this.path = path;
    } else {
      this.path = name;
    }

    const validators = { type };
    const { required, minLength, maxLength, oneOf, equals, schema } = config;

    if (validate) {
      validators.validate = validate;
    }
    if (required) {
      validators.required = required;
    }
    if (minLength) {
      validators.minLength = minLength;
    }
    if (maxLength) {
      validators.maxLength = maxLength;
    }
    if (oneOf) {
      validators.oneOf = oneOf;
    }
    if (equals) {
      validators.equals = equals;
    }
    if (schema && (type === types.json || type === types.jsonb)) {
      validators.schema = this.getSchemaValidators(schema);
    }

    this.validators = validators;

    // TODO: refactor this to updateModel
    // TODO: remove dependency between setModel, getSchemaValidators and setReference
    this.setModel(model);

    const { references } = config;
    if (references) {
      this.setReference(references);
    }

    let { column } = config;
    if (!column) {
      column = this.getColumnName(name);
    }
    this.column = column;
  }

  getSchemaValidators(schema) {
    const schemaIsArray = Array.isArray(schema);

    if (schemaIsArray) {
      schema = schema[0];
      if (!schema) {
        return [];
      }
    }

    const validators = Object.keys(schema).reduce((validators, key) => {
      const fieldConfig = Object.assign(
        {
          name: key,
          path: `${this.path}.${key}`,
          model: this.model
        },
        schema[key]
      );

      validators[key] = new Field(fieldConfig);
      return validators;
    }, {});

    if (schemaIsArray) {
      return [validators];
    } else {
      return validators;
    }
  }

  getColumnName(fieldName) {
    return fieldName;
  }

  clone() {
    const clone = new this.constructor({
      name: this.name,
      type: this.type,
      model: this.model,
      default: this.default,
      references: this.references,
      column: this.column,
      cast: this.castors
    });
    clone.validators = this.validators;
    return clone;
  }

  throwValidationError(value, validator) {
    throw new this.constructor.ValidationError({
      value,
      validator,
      field: this
    });
  }

  validateIsRequired(value) {
    if (!isSet(value)) {
      this.throwValidationError(value, { required: true });
    }
  }

  validateTypeWith(value, type, validate) {
    if (!isSet(value)) {
      return true;
    }
    if (!validate(value)) {
      this.throwValidationError(value, { type });
    }
  }

  validateIsString(value, type) {
    this.validateTypeWith(value, type, value => typeof value === 'string');
  }

  validateIsEmail(value, type) {
    this.validateIsString(value, 'string');
    this.validateTypeWith(value, type, value => isEmail(value));
  }

  validateIsBinary(value, type) {
    this.validateTypeWith(value, type, value => value instanceof Buffer);
  }

  validateIsInteger(value, type) {
    this.validateTypeWith(value, type, value => Number.isInteger(value));
  }

  validateIsBoolean(value, type) {
    this.validateTypeWith(value, type, value => typeof value === 'boolean');
  }

  validateIsDate(value, type) {
    this.validateTypeWith(value, type, value => value instanceof Date);
  }

  validateWithRegex(value, pattern) {
    // TODO: this is unused atm. add support for regex validators
    this.validateTypeWith(value, value => pattern.test(value));
  }

  validateIsUuid(value, type) {
    this.validateTypeWith(value, type, value => isUUID(value));
  }

  validateIsUuidV4(value, type) {
    this.validateTypeWith(value, type, value => isUUID(value, 4));
  }

  validateIsDecimal(value, type) {
    // validator.isDecimal requires the value to be a string
    this.validateTypeWith(value, type, value => isDecimal(String(value)));
  }

  validateIsJson(value, type) {
    // TODO: autocast json fields since `string` type is not enforced now
    if (Array.isArray(value)) {
      return value;
    }

    const valueType = typeof value;

    if (valueType === 'object') {
      return value;
    }

    if (valueType === 'string') {
      let parsedValue;
      const validate = value => {
        try {
          // TODO: JSON.parse is expensive
          parsedValue = JSON.parse(value);
        } catch (e) {
          return false;
        }
        return !!parsedValue;
      };

      this.validateTypeWith(value, type, validate);

      return parsedValue;
    }
  }

  validateTypeIs(value, type) {
    switch (type) {
      case types.text:
      case types.string:
        return this.validateIsString(value, type);

      case types.binary:
        return this.validateIsBinary(value, type);

      case types.email:
        return this.validateIsEmail(value, type);

      case types.integer:
        return this.validateIsInteger(value, type);

      case types.boolean:
        return this.validateIsBoolean(value, type);

      case types.date:
      case types.dateTime:
        return this.validateIsDate(value, type);

      case types.uuid:
        return this.validateIsUuid(value, type);

      case types.uuidV4:
        return this.validateIsUuidV4(value, type);

      case types.json:
      case types.jsonb:
        return this.validateIsJson(value, type);

      case types.decimal:
        return this.validateIsDecimal(value, type);

      default:
        throw new Error(`no validator has been added for '${type}' types`);
    }
  }

  validateMinLengthIs(value, minLength) {
    if (!isSet(value)) {
      return true;
    }

    if (value.length < minLength) {
      this.throwValidationError(value, { minLength });
    }
  }

  validateMaxLengthIs(value, maxLength) {
    if (!isSet(value)) {
      return true;
    }

    if (value.length > maxLength) {
      this.throwValidationError(value, { maxLength });
    }
  }

  validateIsOneOf(value, oneOf) {
    if (!isSet(value)) {
      return true;
    }

    if (!oneOf.includes(value)) {
      this.throwValidationError(value, { oneOf });
    }
  }

  validateEquals(value, equals) {
    if (!isSet(value)) {
      return true;
    }

    if (value !== equals) {
      this.throwValidationError(value, { equals });
    }
  }

  async validateWithCustom(value, validate, modelInstance) {
    if (!isSet(value)) {
      return true;
    }

    const returnValue = await validate.call(modelInstance, value);

    if (returnValue === false) {
      this.throwValidationError(value, { validate });
    }

    if (typeof returnValue === 'object') {
      return this.validateWithValidators(value, returnValue, modelInstance);
    }
  }

  async validateSchemaMatches(value, schema, modelInstance) {
    if (Array.isArray(schema)) {
      if (!Array.isArray(value)) {
        this.throwValidationError(value, { schema });
      }

      schema = schema[0];

      if (schema) {
        await Promise.all(
          Object.values(schema).map(async field => {
            const originalPath = field.path;
            await Promise.all(
              value.map(async (item, index) => {
                field.path = originalPath;
                const pathAsArray = field.path.split('.');
                pathAsArray.splice(-1, 0, index);
                field.path = pathAsArray.join('.');
                await field.validate(item[field.name], modelInstance);
                field.path = originalPath;
              })
            );
          })
        );
      }
    } else {
      if (Array.isArray(value)) {
        this.throwValidationError(value, { schema });
      }

      return Promise.all(
        Object.values(schema).map(async field =>
          field.validate(value[field.name], modelInstance)
        )
      );
    }
  }

  async validateWithValidators(value, validators, modelInstance) {
    const {
      required,
      type,
      minLength,
      maxLength,
      oneOf,
      equals,
      validate,
      schema
    } = validators;

    if (required) {
      this.validateIsRequired(value);
    }

    if (type) {
      // NOTE: only the validateIsJson validator returns a value for now
      // this is to avoid having to JSON.parse twice
      const typeValidatedValue = this.validateTypeIs(value, type);
      if (schema) {
        await this.validateSchemaMatches(
          typeValidatedValue,
          schema,
          modelInstance
        );
      }
    }

    if (minLength) {
      this.validateMinLengthIs(value, minLength);
    }

    if (maxLength) {
      this.validateMaxLengthIs(value, maxLength);
    }

    if (oneOf) {
      this.validateIsOneOf(value, oneOf);
    }

    if (equals) {
      this.validateEquals(value, equals);
    }

    if (validate) {
      await this.validateWithCustom(value, validate, modelInstance);
    }
  }

  async validate(value, modelInstance) {
    await this.validateWithValidators(value, this.validators, modelInstance);
  }

  cast(value, modelInstance, { forSave, forFetch }) {
    if (!this.castors) {
      return;
    }

    if (forSave) {
      if (!this.castors.forSave) {
        return;
      }
      return this.castors.forSave.call(modelInstance, value);
    }

    if (forFetch) {
      if (!this.castors.forFetch) {
        return;
      }
      return this.castors.forFetch.call(modelInstance, value);
    }
  }

  hasDefault() {
    return this.default !== undefined;
  }

  getDefault(modelInstance) {
    const defaultValue = this.default;

    if (typeof defaultValue === 'function') {
      return defaultValue.call(modelInstance);
    }

    return defaultValue;
  }

  setReference(reference) {
    if (!this.model.references[reference.model.name]) {
      this.model.references[reference.model.name] = {};
    }
    this.model.references[reference.model.name][this.name] = reference;

    if (!reference.model.referenced[this.model.name]) {
      reference.model.referenced[this.model.name] = {};
    }
    if (!reference.model.referenced[this.model.name][reference.name]) {
      reference.model.referenced[this.model.name][reference.name] = [];
    }
    reference.model.referenced[this.model.name][reference.name].push(this);

    this.references = reference;

    return this;
  }

  setModel(model) {
    if (this.model && this.references) {
      const reference = this.references;
      const reverseReference = reference.model.referenced[this.model.name];
      reference.model.referenced[model.name] = reverseReference;
      // TODO: is it necessary to delete though
      delete reference.model.referenced[this.model.name];
    }

    if (this.validators.schema) {
      const schema = Array.isArray(this.validators.schema)
        ? this.validators.schema[0]
        : this.validators.schema;

      if (schema) {
        Object.values(schema).forEach(field => field.setModel(model));
      }
    }

    this.model = model;

    return this;
  }
}

const isSet = value => value !== undefined && value !== null;

// TODO: add password field
// TODO: add all the types supported by knex
// TODO: allow adding types (figure out how to share validation logic with existing types)
// TODO: knorm: support JSON schema for validation at least e.g. { schema: { values: [{ type: 'string' }] }
const types = {
  text: 'text',
  json: 'json',
  jsonb: 'jsonb',
  uuid: 'uuid',
  binary: 'binary',
  uuidV4: 'uuidV4',
  decimal: 'decimal',
  string: 'string',
  boolean: 'boolean',
  integer: 'integer',
  dateTime: 'dateTime',
  date: 'date',
  // custom types
  email: 'email'
};

const typesArray = Object.values(types);
Field.types = types;

const ValidationError = require('./ValidationError');
Field.ValidationError = ValidationError;

module.exports = Field;

const Model = require('./Model'); // circular dep
<|endoftext|>


<commit_before>goog.provide('ol.structs.LRUCache');

goog.require('goog.asserts');
goog.require('goog.object');

/**
 * @constructor
 * Implements a Least-Recently-Used cache where the keys do not conflict with
 * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring
 * items from the cache is the responsibility of the user.
 */
ol.structs.LRUCache = function() {

  /**
   * @private
   * @type {number}
   */
  this.count_ = 0;

  /**
   * @private
   * @type {Object.<string, ol.structs.LRUCacheEntry_>}
   */
  this.entries_ = {};

  /**
   * @private
   * @type {?ol.structs.LRUCacheEntry_}
   */
  this.oldest_ = null;

  /**
   * @private
   * @type {?ol.structs.LRUCacheEntry_}
   */
  this.newest_ = null;

};

/**
 * FIXME empty description for jsdoc
 */
ol.structs.LRUCache.prototype.assertValid = function() {
  if (this.count_ === 0) {
    goog.asserts.assert(goog.object.isEmpty(this.entries_));
    goog.asserts.assert(goog.isNull(this.oldest_));
    goog.asserts.assert(goog.isNull(this.newest_));
  } else {
    goog.asserts.assert(goog.object.getCount(this.entries_) == this.count_);
    goog.asserts.assert(!goog.isNull(this.oldest_));
    goog.asserts.assert(goog.isNull(this.oldest_.older));
    goog.asserts.assert(!goog.isNull(this.newest_));
    goog.asserts.assert(goog.isNull(this.newest_.newer));
    var i, entry;
    var older = null;
    i = 0;
    for (entry = this.oldest_; !goog.isNull(entry); entry = entry.newer) {
      goog.asserts.assert(entry.older === older);
      older = entry;
      ++i;
    }
    goog.asserts.assert(i == this.count_);
    var newer = null;
    i = 0;
    for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
      goog.asserts.assert(entry.newer === newer);
      newer = entry;
      ++i;
    }
    goog.asserts.assert(i == this.count_);
  }
};

/**
 * FIXME empty description for jsdoc
 */
ol.structs.LRUCache.prototype.clear = function() {
  this.count_ = 0;
  this.entries_ = {};
  this.oldest_ = null;
  this.newest_ = null;
};

/**
 * @param {string} key Key.
 * @return {boolean} Contains key.
 */
ol.structs.LRUCache.prototype.containsKey = function(key) {
  return key in this.entries_;
};

/**
 * @param {Function} f Function.
 * @param {Object=} opt_obj Object.
 */
ol.structs.LRUCache.prototype.forEach = function(f, opt_obj) {
  var entry = this.oldest_;
  while (!goog.isNull(entry)) {
    f.call(opt_obj, entry.value_, entry.key_, this);
    entry = entry.newer;
  }
};

/**
 * @param {string} key Key.
 * @return {*} Value.
 */
ol.structs.LRUCache.prototype.get = function(key) {
  var entry = this.entries_[key];
  goog.asserts.assert(goog.isDef(entry));
  if (entry === this.newest_) {
    return entry.value_;
  } else if (entry === this.oldest_) {
    this.oldest_ = this.oldest_.newer;
    this.oldest_.older = null;
  } else {
    entry.newer.older = entry.older;
    entry.older.newer = entry.newer;
  }
  entry.newer = null;
  entry.older = this.newest_;
  this.newest_.newer = entry;
  this.newest_ = entry;
  return entry.value_;
};

/**
 * @return {number} Count.
 */
ol.structs.LRUCache.prototype.getCount = function() {
  return this.count_;
};

/**
 * @return {Array.<string>} Keys.
 */
ol.structs.LRUCache.prototype.getKeys = function() {
  var keys = new Array(this.count_);
  var i = 0;
  var entry;
  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    keys[i++] = entry.key_;
  }
  goog.asserts.assert(i == this.count_);
  return keys;
};

/**
 * @return {string} Last key.
 */
ol.structs.LRUCache.prototype.getLastKey = function() {
  goog.asserts.assert(!goog.isNull(this.newest_));
  return this.newest_.key_;
};

/**
 * @return {Array} Values.
 */
ol.structs.LRUCache.prototype.getValues = function() {
  var values = new Array(this.count_);
  var i = 0;
  var entry;
  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    values[i++] = entry.value_;
  }
  goog.asserts.assert(i == this.count_);
  return values;
};

/**
 * @return {*} Last value.
 */
ol.structs.LRUCache.prototype.peekLast = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  return this.oldest_.value_;
};

/**
 * @return {string} Last key.
 */
ol.structs.LRUCache.prototype.peekLastKey = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  return this.oldest_.key_;
};

/**
 * @return {*} value Value.
 */
ol.structs.LRUCache.prototype.pop = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  goog.asserts.assert(!goog.isNull(this.newest_));
  var entry = this.oldest_;
  goog.asserts.assert(entry.key_ in this.entries_);
  delete this.entries_[entry.key_];
  if (!goog.isNull(entry.newer)) {
    entry.newer.older = null;
  }
  this.oldest_ = entry.newer;
  if (goog.isNull(this.oldest_)) {
    this.newest_ = null;
  }
  --this.count_;
  return entry.value_;
};

/**
 * @param {string} key Key.
 * @param {*} value Value.
 */
ol.structs.LRUCache.prototype.set = function(key, value) {
  goog.asserts.assert(!(key in {}));
  goog.asserts.assert(!(key in this.entries_));
  var entry = {
    key_: key,
    newer: null,
    older: this.newest_,
    value_: value
  };
  if (goog.isNull(this.newest_)) {
    this.oldest_ = entry;
  } else {
    this.newest_.newer = entry;
  }
  this.newest_ = entry;
  this.entries_[key] = entry;
  ++this.count_;
};

/**
 * @typedef {{key_: string,
 *            newer: ol.structs.LRUCacheEntry_,
 *            older: ol.structs.LRUCacheEntry_,
 *            value_: *}}
 */
ol.structs.LRUCacheEntry_;
<commit_msg>Remove unnecessary undercores, thanks @fredj<commit_after>goog.provide('ol.structs.LRUCache');

goog.require('goog.asserts');
goog.require('goog.object');

/**
 * @constructor
 * Implements a Least-Recently-Used cache where the keys do not conflict with
 * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring
 * items from the cache is the responsibility of the user.
 */
ol.structs.LRUCache = function() {

  /**
   * @private
   * @type {number}
   */
  this.count_ = 0;

  /**
   * @private
   * @type {Object.<string, ol.structs.LRUCacheEntry>}
   */
  this.entries_ = {};

  /**
   * @private
   * @type {?ol.structs.LRUCacheEntry}
   */
  this.oldest_ = null;

  /**
   * @private
   * @type {?ol.structs.LRUCacheEntry}
   */
  this.newest_ = null;

};

/**
 * FIXME empty description for jsdoc
 */
ol.structs.LRUCache.prototype.assertValid = function() {
  if (this.count_ === 0) {
    goog.asserts.assert(goog.object.isEmpty(this.entries_));
    goog.asserts.assert(goog.isNull(this.oldest_));
    goog.asserts.assert(goog.isNull(this.newest_));
  } else {
    goog.asserts.assert(goog.object.getCount(this.entries_) == this.count_);
    goog.asserts.assert(!goog.isNull(this.oldest_));
    goog.asserts.assert(goog.isNull(this.oldest_.older));
    goog.asserts.assert(!goog.isNull(this.newest_));
    goog.asserts.assert(goog.isNull(this.newest_.newer));
    var i, entry;
    var older = null;
    i = 0;
    for (entry = this.oldest_; !goog.isNull(entry); entry = entry.newer) {
      goog.asserts.assert(entry.older === older);
      older = entry;
      ++i;
    }
    goog.asserts.assert(i == this.count_);
    var newer = null;
    i = 0;
    for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
      goog.asserts.assert(entry.newer === newer);
      newer = entry;
      ++i;
    }
    goog.asserts.assert(i == this.count_);
  }
};

/**
 * FIXME empty description for jsdoc
 */
ol.structs.LRUCache.prototype.clear = function() {
  this.count_ = 0;
  this.entries_ = {};
  this.oldest_ = null;
  this.newest_ = null;
};

/**
 * @param {string} key Key.
 * @return {boolean} Contains key.
 */
ol.structs.LRUCache.prototype.containsKey = function(key) {
  return key in this.entries_;
};

/**
 * @param {Function} f Function.
 * @param {Object=} opt_obj Object.
 */
ol.structs.LRUCache.prototype.forEach = function(f, opt_obj) {
  var entry = this.oldest_;
  while (!goog.isNull(entry)) {
    f.call(opt_obj, entry.value_, entry.key_, this);
    entry = entry.newer;
  }
};

/**
 * @param {string} key Key.
 * @return {*} Value.
 */
ol.structs.LRUCache.prototype.get = function(key) {
  var entry = this.entries_[key];
  goog.asserts.assert(goog.isDef(entry));
  if (entry === this.newest_) {
    return entry.value_;
  } else if (entry === this.oldest_) {
    this.oldest_ = this.oldest_.newer;
    this.oldest_.older = null;
  } else {
    entry.newer.older = entry.older;
    entry.older.newer = entry.newer;
  }
  entry.newer = null;
  entry.older = this.newest_;
  this.newest_.newer = entry;
  this.newest_ = entry;
  return entry.value_;
};

/**
 * @return {number} Count.
 */
ol.structs.LRUCache.prototype.getCount = function() {
  return this.count_;
};

/**
 * @return {Array.<string>} Keys.
 */
ol.structs.LRUCache.prototype.getKeys = function() {
  var keys = new Array(this.count_);
  var i = 0;
  var entry;
  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    keys[i++] = entry.key_;
  }
  goog.asserts.assert(i == this.count_);
  return keys;
};

/**
 * @return {string} Last key.
 */
ol.structs.LRUCache.prototype.getLastKey = function() {
  goog.asserts.assert(!goog.isNull(this.newest_));
  return this.newest_.key_;
};

/**
 * @return {Array} Values.
 */
ol.structs.LRUCache.prototype.getValues = function() {
  var values = new Array(this.count_);
  var i = 0;
  var entry;
  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    values[i++] = entry.value_;
  }
  goog.asserts.assert(i == this.count_);
  return values;
};

/**
 * @return {*} Last value.
 */
ol.structs.LRUCache.prototype.peekLast = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  return this.oldest_.value_;
};

/**
 * @return {string} Last key.
 */
ol.structs.LRUCache.prototype.peekLastKey = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  return this.oldest_.key_;
};

/**
 * @return {*} value Value.
 */
ol.structs.LRUCache.prototype.pop = function() {
  goog.asserts.assert(!goog.isNull(this.oldest_));
  goog.asserts.assert(!goog.isNull(this.newest_));
  var entry = this.oldest_;
  goog.asserts.assert(entry.key_ in this.entries_);
  delete this.entries_[entry.key_];
  if (!goog.isNull(entry.newer)) {
    entry.newer.older = null;
  }
  this.oldest_ = entry.newer;
  if (goog.isNull(this.oldest_)) {
    this.newest_ = null;
  }
  --this.count_;
  return entry.value_;
};

/**
 * @param {string} key Key.
 * @param {*} value Value.
 */
ol.structs.LRUCache.prototype.set = function(key, value) {
  goog.asserts.assert(!(key in {}));
  goog.asserts.assert(!(key in this.entries_));
  var entry = {
    key_: key,
    newer: null,
    older: this.newest_,
    value_: value
  };
  if (goog.isNull(this.newest_)) {
    this.oldest_ = entry;
  } else {
    this.newest_.newer = entry;
  }
  this.newest_ = entry;
  this.entries_[key] = entry;
  ++this.count_;
};

/**
 * @typedef {{key_: string,
 *            newer: ol.structs.LRUCacheEntry,
 *            older: ol.structs.LRUCacheEntry,
 *            value_: *}}
 */
ol.structs.LRUCacheEntry;
<|endoftext|>