<commit_before>//  Tile Sum Game mail file
//  Author: github.com/ansal

// global variable for holding game data
var TileSum = TileSum || {};

(function(){
  'use strict';

  // number of tiles
  var NUM_TILE_X = 8;
  var NUM_TILE_Y = 7;

  // number of tiles to sum for random numbers
  var RAND_NUM_TILES = 3;

  // game constructor
  TileSum.Game = function(game){};

  // shortcut
  var G = TileSum.Game;

  // load assets
  G.prototype.preload = function() {

    // background
    this.game.load.image('bg', 'assets/img/bg.png');

    // clouds
    this.game.load.image('cloud1', 'assets/img/cloud1.png');
    this.game.load.image('cloud2', 'assets/img/cloud2.png');
    this.game.load.image('cloud3', 'assets/img/cloud3.png');

    // bush
    this.game.load.image('bush', 'assets/img/bush.png');

    // num tiles
    for(var i = 1; i <= 9; i += 1) {
      this.game.load.image('numTile' + i, 'assets/img/num' + i + '.png');
    }

  };

  // create objects on screen
  G.prototype.create = function() {

    // game state
    this.gameOver = false;

    // draw background
    var xReq = 1 + this.game.width / 256;
    var yReq = 1 + this.game.width / 256;
    for(var i = 1, x, y = 0; i <= yReq; i += 1) {
      x = 0;
      for (var j = 1; j <= xReq; j += 1) {
        this.game.add.sprite(x, y, 'bg');
        x += 256;
      }
      y += 256;
    }

    // draw clouds
    this.cloudPool = this.game.add.group();
    for(var i = 1; i <= 10; i += 1) {
      for(var j = 1; j <= 3; j += 1) {
        var random = this.stage.game.rnd.integerInRange(1, this.game.height);
        var cloud = this.game.add.sprite(0, random, 'cloud' + j);
        this.cloudPool.add(cloud);
        cloud.anchor.setTo(0.5, 0.5);
        this.game.physics.enable(cloud, Phaser.Physics.ARCADE);
        cloud.checkWorldBounds = true;
        cloud.outOfBoundsKill = true;
        cloud.kill();
      }
    }
    // invoke drawCloud to draw one cloud
    drawCloud.apply(this);
    // timer for drawing clouds
    this.game.time.events.loop(
      Phaser.Timer.SECOND * 5,
      drawCloud,
      this
    );

    // draw bush
    var xReq = 1 + this.game.width  / 50;
    var y = this.game.height - 70;
    for(var i = 1, x = 0; i <= xReq; i += 1, x += 50) {
      this.game.add.sprite(x, y, 'bush');
    }

    this.numTilesPool = this.game.add.group();
    // invoke utility function to draw tiles
    drawNumTiles.apply(this, [NUM_TILE_X, NUM_TILE_Y]);

    // initialize puzzle num
    // 0 for puzzle number means new number should be displayed
    this.puzzleNumText = this.game.add.text(
      this.game.width - 150,
      30,
      "*", {
      font: "100px Helvetica",
      fill: "green",
      align: "right"
    });
    newPuzzleNum.apply(this);

    this.game.add.text(
      this.game.width - 150,
      10,
      "tap tiles to sum", {
      font: "18px Helvetica",
      fill: "green",
      align: "right"
    });

    // time remaining info
    this.timeRemaining = 180;
    this.timeRemainingText = this.game.add.text(
      this.game.width - 130,
      300,
      this.timeRemaining, {
      font: "60px Helvetica",
      fill: "green",
      align: "right"
    });

    // timer to update game
    this.game.time.events.loop(
      Phaser.Timer.SECOND,
      updateGameTime,
      this
    );

  };

  // game engine update call
  G.prototype.update = function() {

  };

  // utility functions

  // reset puzzle number and selected tiles
  function newPuzzleNum() {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    // shortcut for random function
    var randInt = this.stage.game.rnd.integerInRange;
    window.x = this.numTilesPool;
    var aliveTiles = this.numTilesPool.countLiving();

    var puzzleNum = 0;
    for(var i = 1; i <= RAND_NUM_TILES && i <= aliveTiles; i += 1) {
      var randomTile = this.numTilesPool.getRandom();
      if(randomTile.alive) {
        puzzleNum += randomTile.tileNum;
      }
    }
    if(puzzleNum === 0) {
      puzzleNum = this.numTilesPool.getFirstAlive().tileNum;
    }

    this.puzzleNum = puzzleNum;
    this.puzzleNumText.setText(puzzleNum);
    this.tappedTiles = [];
  }

  // draw an 8 x 8 coloumn of random number tiles
  function drawNumTiles(numX, numY) {
    for( var i = 1, x, y = 45, random; i <= numY; i += 1) {
      x = 50;
      for(var j = 1; j <= numX; j += 1) {
        random = this.stage.game.rnd.integerInRange(1, 9);
        var tile = this.game.add.sprite(x, y, 'numTile' + random);
        tile.anchor.setTo(0.5, 0.5);
        this.game.physics.enable(tile, Phaser.Physics.ARCADE);
        tile.checkWorldBounds = true;
        tile.outOfBoundsKill = true;
        tile.tileNum = random;
        tile.inputEnabled = true;
        tile.events.onInputDown.add(userTouchedTile, this);
        this.numTilesPool.add(tile);
        x += 77;
      }
      y += 77;
    }
  }

  // event handler for user touching/clicking a tile
  function userTouchedTile (tile, event) {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    // check whether the tile is already rotated or not
    if(tile.rotation !== 0) {
      return;
    }
    tile.rotation += -90;
    this.tappedTiles.push(tile);
    for(var i = 0, sum = 0; i < this.tappedTiles.length; i += 1) {
      sum += this.tappedTiles[i].tileNum;
    }

    // if sum of user selected tiles are greater than puzzle num,
    // its game over
    if(sum > this.puzzleNum) {
      this.gameOver = true;
      return;
    }

    // if sum equals numbers in the tiles kill all the tiles
    // and draw new number
    // if all the tiles are finished, advance to next level
    if(sum === this.puzzleNum) {
      this.tappedTiles.forEach(function(x){
        x.kill();
      });

      if(this.numTilesPool.countLiving() === 0) {
        this.gameOver = true;
        window.alert('Finished');
        return;
      }

      newPuzzleNum.apply(this);
    }

  }

  // draws a cloud
  function drawCloud() {
    var cloud = this.cloudPool.getFirstDead();
    if(cloud === null || cloud === undefined) {
      return;
    }
    cloud.revive();
    var random = this.stage.game.rnd.integerInRange(1, this.game.height);
    cloud.reset(0, random);
    cloud.body.velocity.x = 30;
  }

  // updates game clock
  function updateGameTime() {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    this.timeRemaining -= 1;
    this.timeRemainingText.setText(this.timeRemaining);
  }

})();<commit_msg>score update<commit_after>//  Tile Sum Game mail file
//  Author: github.com/ansal

// global variable for holding game data
var TileSum = TileSum || {};

(function(){
  'use strict';

  // number of tiles
  var NUM_TILE_X = 8;
  var NUM_TILE_Y = 7;

  // number of tiles to sum for random numbers
  var RAND_NUM_TILES = 3;

  // counter start
  var COUNTER_START = 480;

  // game constructor
  TileSum.Game = function(game){};

  // shortcut
  var G = TileSum.Game;

  // load assets
  G.prototype.preload = function() {

    // background
    this.game.load.image('bg', 'assets/img/bg.png');

    // clouds
    this.game.load.image('cloud1', 'assets/img/cloud1.png');
    this.game.load.image('cloud2', 'assets/img/cloud2.png');
    this.game.load.image('cloud3', 'assets/img/cloud3.png');

    // bush
    this.game.load.image('bush', 'assets/img/bush.png');

    // num tiles
    for(var i = 1; i <= 9; i += 1) {
      this.game.load.image('numTile' + i, 'assets/img/num' + i + '.png');
    }

  };

  // create objects on screen
  G.prototype.create = function() {

    // game state
    this.gameOver = false;

    // draw background
    var xReq = 1 + this.game.width / 256;
    var yReq = 1 + this.game.width / 256;
    for(var i = 1, x, y = 0; i <= yReq; i += 1) {
      x = 0;
      for (var j = 1; j <= xReq; j += 1) {
        this.game.add.sprite(x, y, 'bg');
        x += 256;
      }
      y += 256;
    }

    // draw clouds
    this.cloudPool = this.game.add.group();
    for(var i = 1; i <= 10; i += 1) {
      for(var j = 1; j <= 3; j += 1) {
        var random = this.stage.game.rnd.integerInRange(1, this.game.height);
        var cloud = this.game.add.sprite(0, random, 'cloud' + j);
        this.cloudPool.add(cloud);
        cloud.anchor.setTo(0.5, 0.5);
        this.game.physics.enable(cloud, Phaser.Physics.ARCADE);
        cloud.checkWorldBounds = true;
        cloud.outOfBoundsKill = true;
        cloud.kill();
      }
    }
    // invoke drawCloud to draw one cloud
    drawCloud.apply(this);
    // timer for drawing clouds
    this.game.time.events.loop(
      Phaser.Timer.SECOND * 5,
      drawCloud,
      this
    );

    // draw bush
    var xReq = 1 + this.game.width  / 50;
    var y = this.game.height - 70;
    for(var i = 1, x = 0; i <= xReq; i += 1, x += 50) {
      this.game.add.sprite(x, y, 'bush');
    }

    this.numTilesPool = this.game.add.group();
    // invoke utility function to draw tiles
    drawNumTiles.apply(this, [NUM_TILE_X, NUM_TILE_Y]);

    // initialize puzzle num
    // 0 for puzzle number means new number should be displayed
    this.puzzleNumText = this.game.add.text(
      this.game.width - 150,
      30,
      "*", {
      font: "100px Helvetica",
      fill: "green",
      align: "right"
    });
    newPuzzleNum.apply(this);

    this.game.add.text(
      this.game.width - 150,
      10,
      "tap tiles to sum", {
      font: "18px Helvetica",
      fill: "green",
      align: "right"
    });

    // time remaining info
    this.timeRemaining = COUNTER_START;
    this.timeRemainingText = this.game.add.text(
      this.game.width - 130,
      300,
      this.timeRemaining, {
      font: "60px Helvetica",
      fill: "green",
      align: "right"
    });

    // timer to update game
    this.game.time.events.loop(
      Phaser.Timer.SECOND,
      updateGameTime,
      this
    );

  };

  // game engine update call
  G.prototype.update = function() {

  };

  // utility functions

  // reset puzzle number and selected tiles
  function newPuzzleNum() {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    // shortcut for random function
    var randInt = this.stage.game.rnd.integerInRange;
    window.x = this.numTilesPool;
    var aliveTiles = this.numTilesPool.countLiving();

    var puzzleNum = 0;
    for(var i = 1; i <= RAND_NUM_TILES && i <= aliveTiles; i += 1) {
      var randomTile = this.numTilesPool.getRandom();
      if(randomTile.alive) {
        puzzleNum += randomTile.tileNum;
      }
    }
    if(puzzleNum === 0) {
      puzzleNum = this.numTilesPool.getFirstAlive().tileNum;
    }

    this.puzzleNum = puzzleNum;
    this.puzzleNumText.setText(puzzleNum);
    this.tappedTiles = [];
  }

  // draw an 8 x 8 coloumn of random number tiles
  function drawNumTiles(numX, numY) {
    for( var i = 1, x, y = 45, random; i <= numY; i += 1) {
      x = 50;
      for(var j = 1; j <= numX; j += 1) {
        random = this.stage.game.rnd.integerInRange(1, 9);
        var tile = this.game.add.sprite(x, y, 'numTile' + random);
        tile.anchor.setTo(0.5, 0.5);
        this.game.physics.enable(tile, Phaser.Physics.ARCADE);
        tile.checkWorldBounds = true;
        tile.outOfBoundsKill = true;
        tile.tileNum = random;
        tile.inputEnabled = true;
        tile.events.onInputDown.add(userTouchedTile, this);
        this.numTilesPool.add(tile);
        x += 77;
      }
      y += 77;
    }
  }

  // event handler for user touching/clicking a tile
  function userTouchedTile (tile, event) {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    // check whether the tile is already rotated or not
    if(tile.rotation !== 0) {
      return;
    }
    tile.rotation += -90;
    this.tappedTiles.push(tile);
    for(var i = 0, sum = 0; i < this.tappedTiles.length; i += 1) {
      sum += this.tappedTiles[i].tileNum;
    }

    // if sum of user selected tiles are greater than puzzle num,
    // its game over
    if(sum > this.puzzleNum) {
      this.gameOver = true;
      return;
    }

    // if sum equals numbers in the tiles kill all the tiles
    // and draw new number
    // if all the tiles are finished, advance to next level
    if(sum === this.puzzleNum) {
      this.tappedTiles.forEach(function(x){
        x.kill();
      });

      if(this.numTilesPool.countLiving() === 0) {
        this.gameOver = true;
        window.alert('Finished');
        return;
      }

      newPuzzleNum.apply(this);
    }

  }

  // draws a cloud
  function drawCloud() {
    var cloud = this.cloudPool.getFirstDead();
    if(cloud === null || cloud === undefined) {
      return;
    }
    cloud.revive();
    var random = this.stage.game.rnd.integerInRange(1, this.game.height);
    cloud.reset(0, random);
    cloud.body.velocity.x = 30;
  }

  // updates game clock
  function updateGameTime() {

    // check for game over state
    if(this.gameOver) {
      return;
    }

    this.timeRemaining -= 1;
    this.timeRemainingText.setText(this.timeRemaining);
  }

})();<|endoftext|>


<commit_before>var HashSplit = new function () {

    var hashtabberfirstactive = 0;
    var hashtabberclass = 'hashtab'; // TODO configbl

    (hashtabber = function( htmlId ) {

        if (typeof htmlId == "undefined") {
            var hashtabs = document.getElementsByClassName( hashtabberclass ) ;

            for(var i = 0; i != hashtabs.length; ++i) {
                hashtabs[i].style.visibility = 'hidden';

                var siblings = hashtabs[i].parentNode.getElementsByClassName( hashtabberclass ) ;
                siblings[hashtabberfirstactive].style.visibility = 'visible';
            }
        } else { // todo not hashtab
            var siblings = htmlId.parentNode.getElementsByClassName( hashtabberclass ) ;

            for(var i = 0; i != siblings.length; ++i) {
                siblings[i].style.visibility = 'hidden';
            }
            htmlId.style.visibility = 'visible'; // TODO not hashtab
        }
    })();

    hashElement = function (hashKey, hashVal) { // TODO this

        var htmlId = document.getElementById( hashKey ) ;
        if (htmlId == null )  return  ;

        switch( htmlId.tagName ) {
            case 'P' :
            case 'DIV' :
                hashtabber( htmlId );
            break
            case 'INPUT' :
                if( htmlId.getAttribute('type') == 'checkbox' ) {
                    htmlId.checked = ( hashVal === 'true' || hashVal == undefined ) ;
                } else {
                htmlId.value = hashVal;
                }
            break
            case 'A' :
                htmlId.click();
            break
        }
    };

   (this.hashrouter = function () {

       var hashStr = window.location.hash, hashElements
       hashStr = hashStr.substring(1, hashStr.length);
       hashElements = hashStr.split(/[\/&]+/);

       for(var i = 0; i < hashElements.length; i++) {
           hashKeyVal = hashElements[i].split('=');
           hashElement( hashKeyVal[0] , hashKeyVal[1] );
       }

   })();

   window.onhashchange = this.hashrouter;
}
<commit_msg>func hashForm<commit_after>var HashSplit = new function () {

    var hashtabberfirstactive = 0;
    var hashtabberclass = 'hashtab'; // TODO configbl

    (hashtabber = function( htmlId ) {

        if (typeof htmlId == "undefined") {
            var hashtabs = document.getElementsByClassName( hashtabberclass ) ;

            for(var i = 0; i != hashtabs.length; ++i) {
                hashtabs[i].style.visibility = 'hidden';

                var siblings = hashtabs[i].parentNode.getElementsByClassName( hashtabberclass ) ;
                siblings[hashtabberfirstactive].style.visibility = 'visible';
            }
        } else { // todo not hashtab
            var siblings = htmlId.parentNode.getElementsByClassName( hashtabberclass ) ;

            for(var i = 0; i != siblings.length; ++i) {
                siblings[i].style.visibility = 'hidden';
            }
            htmlId.style.visibility = 'visible'; // TODO not hashtab
        }
    })();

    hashForm = function( htmlId, hashVal ) {
        if( htmlId.getAttribute('type') == 'checkbox' ) {
            htmlId.checked = ( hashVal === 'true' || hashVal == undefined ) ;
        } else {
        htmlId.value = hashVal;
        }
    };

    hashElement = function (hashKey, hashVal) { // TODO this

        var htmlId = document.getElementById( hashKey ) ;
        if (htmlId == null )  return  ;

        switch( htmlId.tagName ) {
            case 'P' :
            case 'DIV' :
                hashtabber( htmlId );
            break
            case 'INPUT' :
                hashForm( htmlId, hashVal );
            break
            case 'A' :
                htmlId.click();
            break
        }
    };

   (this.hashrouter = function () {

       var hashStr = window.location.hash, hashElements
       hashStr = hashStr.substring(1, hashStr.length);
       hashElements = hashStr.split(/[\/&]+/);

       for(var i = 0; i < hashElements.length; i++) {
           hashKeyVal = hashElements[i].split('=');
           hashElement( hashKeyVal[0] , hashKeyVal[1] );
       }

   })();

   window.onhashchange = this.hashrouter;
}
<|endoftext|>


<commit_before>/**
 * OpenEyes
 *
 * (C) OpenEyes Foundation, 2016
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2016, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

var OpenEyes = OpenEyes || {};

OpenEyes.UI = OpenEyes.UI || {};

(function (exports) {

    'use strict';

    function DiagnosesSearchController(options) {
        this.options = $.extend(true, {}, DiagnosesSearchController._defaultOptions, options);

        this.$inputField = this.options.inputField;
        this.fieldPrefix = this.options.fieldPrefix;
        this.$row = this.options.inputField.closest('tr');
        this.code = this.options.code;
        this.commonlyUsedDiagnosesUrl = this.options.commonlyUsedDiagnosesUrl;
        this.singleTemplate = this.options.singleTemplate;
        this.renderTemplate = this.options.renderTemplate;

        this.init();
        this.initialiseAutocomplete();
        this.initialiseTriggers();
    }

    DiagnosesSearchController._defaultOptions = {
        fieldPrefix: 'fieldPrefixDefault',
        code: 'systemic',
        commonlyUsedDiagnosesUrl: '/disorder/getcommonlyuseddiagnoses/type/',
        renderTemplate: true,
        singleTemplate :
            "" + " " +
            "" +
            "<select class='commonly-used-diagnosis'></select>" +
            "{{{input_field}}}" +
            "<input type='hidden' name='{{field_prefix}}[id][]' class='savedDiagnosisId' value=''>" +
            "<input type='hidden' name='{{field_prefix}}[disorder_id][]' class='savedDiagnosis' value=''>"
    };

    DiagnosesSearchController.prototype.init = function(){
        var controller = this;
        var $parent = controller.$inputField.parent();
        var url = controller.commonlyUsedDiagnosesUrl + controller.code;
        controller.$inputField.addClass('diagnoses-search-inputfield');
        var savedDiagnoses;

        if( controller.renderTemplate === true ){
            var html = Mustache.render(
                this.singleTemplate,
                {
                    'input_field': controller.$inputField.prop("outerHTML"),
                    'row_count': OpenEyes.Util.getNextDataKey( $('#' + controller.fieldPrefix + '_diagnoses_table').find('tbody tr'), 'key'),
                    'field_prefix' : controller.fieldPrefix
                }
            );

            $parent.html(html);
            controller.$inputField = controller.$row.find('.diagnoses-search-inputfield');

            $.getJSON(url, function(data){

                var $select = $parent.find('.commonly-used-diagnosis');

                $select.append( $('<option>',{'text': 'Select a commonly used diagnosis'}));
                $.each(data, function(i, item){
                    $select.append( $('<option>',{'value': item.id, 'text': item.label, 'data-item': JSON.stringify(item)}));
                });

                controller.$inputField.before($select);
            });
        }

        savedDiagnoses = controller.$inputField.data('saved-diagnoses');

        if(savedDiagnoses){
            controller.addDiagnosis(savedDiagnoses.id, savedDiagnoses.name, savedDiagnoses.disorder_id );
        }

    }

    /**
     * Diagnosis selected for the row
     *
     * @param id
     * @param item
     */
    DiagnosesSearchController.prototype.addDiagnosis = function(id, item){
        var controller = this;
        var $displayDiagnosis = controller.$row.find('.diagnosis-display');
        controller.$row.find('.diagnosis-name').text(item.label);

        $displayDiagnosis.show();

        //This is not the disorder's ID but the "row" or "entry" ID (SystemicDiagnoses_Diagnosis model)
        // if there is no ID this will be a brand new entry
        if(id){
            controller.$row.find('.savedDiagnosisId').val(id);
        }

        controller.updatePatientConditions(item);
        //This will be the actual disorder ID - SNOMED code
        controller.$row.find('.savedDiagnosis').val(item.id);

        controller.$row.find('.commonly-used-diagnosis').hide();
        controller.$row.find('.diagnoses-search-inputfield').hide();
        controller.$row.find('.medication-display').show();
    };

    /**
     * Check the given disorder item for any condition attributes that should be updated on the page
     */
    DiagnosesSearchController.prototype.updatePatientConditions = function(item)
    {
        // note that at the moment this follows the simple premise of inserting or removing additional
        // hidden fields that are universally searched for on the page, following the pattern established
        // by the original diagnoses element.
        if (item.is_diabetes) {
            this.$row.append('<input type="hidden" name="diabetic_diagnoses[]" value="1" /> ');
        } else {
            this.$row.find('input[name^="diabetic_diagnoses"]').remove();
        }

        // glaucoma is unlikely to come up given this is only being used for systemic disorders
        // but it is included for completeness.
        if (item.is_glaucoma) {
            this.$row.append('<input type="hidden" name="glaucoma_diagnoses[]" value="1" /> ').trigger('change');
        } else {
            this.$row.find('input[name^="glaucoma_diagnoses"]').remove();
        }

        // trigger event change for any controls looking for them
        $(":input[name^='diabetic_diagnoses']").trigger('change');
        $(":input[name^='glaucoma_diagnoses']").trigger('change');
    };

    DiagnosesSearchController.prototype.initialiseAutocomplete = function(){
        var controller = this;

        controller.$inputField.autocomplete({
            minLength: 2,
            delay: 700,
            source: function (request, response) {
                $.ajax({
                    'url': '/disorder/autocomplete',
                    'type':'GET',
                    'data':{'term': request.term, 'code': controller.code},
                    'beforeSend': function(){
                    },
                    'success':function(data) {
                        data = $.parseJSON(data);
                        response(data);
                    }
                });
            },
            search: function () {
                controller.$inputField.addClass('inset-loader');
            },
            select: function(event, ui){
                //no multiple option
                controller.addDiagnosis(null, ui.item);

                //clear input
                $(this).val("");
                return false;
            },
            response: function (event, ui) {
                controller.$inputField.removeClass('inset-loader');
            }
        });
    };

    DiagnosesSearchController.prototype.initialiseTriggers = function(){
        var controller = this;
        var $parent = controller.$inputField.parent();

        controller.$row.on('click', '.diagnosis-rename', function(){
            controller.$row.find('.commonly-used-diagnosis').show();
            controller.$row.find('.diagnoses-search-inputfield').show();
            $(this).closest('.medication-display').hide();
        });

        controller.$row.on('change', 'select.commonly-used-diagnosis', function(){
            controller.addDiagnosis(null, $(this).find('option:selected').data('item') );
            $(this).val('');
        });
    }

    exports.DiagnosesSearchController = DiagnosesSearchController;

}(OpenEyes.UI));<commit_msg>OE-6815 - fixing the addDiagnosis JS function parameter passing<commit_after>/**
 * OpenEyes
 *
 * (C) OpenEyes Foundation, 2016
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2016, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

var OpenEyes = OpenEyes || {};

OpenEyes.UI = OpenEyes.UI || {};

(function (exports) {

    'use strict';

    function DiagnosesSearchController(options) {
        this.options = $.extend(true, {}, DiagnosesSearchController._defaultOptions, options);

        this.$inputField = this.options.inputField;
        this.fieldPrefix = this.options.fieldPrefix;
        this.$row = this.options.inputField.closest('tr');
        this.code = this.options.code;
        this.commonlyUsedDiagnosesUrl = this.options.commonlyUsedDiagnosesUrl;
        this.singleTemplate = this.options.singleTemplate;
        this.renderTemplate = this.options.renderTemplate;

        this.init();
        this.initialiseAutocomplete();
        this.initialiseTriggers();
    }

    DiagnosesSearchController._defaultOptions = {
        fieldPrefix: 'fieldPrefixDefault',
        code: 'systemic',
        commonlyUsedDiagnosesUrl: '/disorder/getcommonlyuseddiagnoses/type/',
        renderTemplate: true,
        singleTemplate :
            "" + " " +
            "" +
            "<select class='commonly-used-diagnosis'></select>" +
            "{{{input_field}}}" +
            "<input type='hidden' name='{{field_prefix}}[id][]' class='savedDiagnosisId' value=''>" +
            "<input type='hidden' name='{{field_prefix}}[disorder_id][]' class='savedDiagnosis' value=''>"
    };

    DiagnosesSearchController.prototype.init = function(){
        var controller = this;
        var $parent = controller.$inputField.parent();
        var url = controller.commonlyUsedDiagnosesUrl + controller.code;
        controller.$inputField.addClass('diagnoses-search-inputfield');
        var savedDiagnoses;

        if( controller.renderTemplate === true ){
            var html = Mustache.render(
                this.singleTemplate,
                {
                    'input_field': controller.$inputField.prop("outerHTML"),
                    'row_count': OpenEyes.Util.getNextDataKey( $('#' + controller.fieldPrefix + '_diagnoses_table').find('tbody tr'), 'key'),
                    'field_prefix' : controller.fieldPrefix
                }
            );

            $parent.html(html);
            controller.$inputField = controller.$row.find('.diagnoses-search-inputfield');

            $.getJSON(url, function(data){

                var $select = $parent.find('.commonly-used-diagnosis');

                $select.append( $('<option>',{'text': 'Select a commonly used diagnosis'}));
                $.each(data, function(i, item){
                    $select.append( $('<option>',{'value': item.id, 'text': item.label, 'data-item': JSON.stringify(item)}));
                });

                controller.$inputField.before($select);
            });
        }

        savedDiagnoses = controller.$inputField.data('saved-diagnoses');

        if(savedDiagnoses){
            controller.addDiagnosis(savedDiagnoses.id, {label: savedDiagnoses.name, id: savedDiagnoses.disorder_id} );
        }
    }

    /**
     * Diagnosis selected for the row
     *
     * @param id
     * @param item
     */
    DiagnosesSearchController.prototype.addDiagnosis = function(id, item){
        var controller = this;
        var $displayDiagnosis = controller.$row.find('.diagnosis-display');
        controller.$row.find('.diagnosis-name').text(item.label);

        $displayDiagnosis.show();

        //This is not the disorder's ID but the "row" or "entry" ID (SystemicDiagnoses_Diagnosis model)
        // if there is no ID this will be a brand new entry
        if(id){
            controller.$row.find('.savedDiagnosisId').val(id);
        }

        controller.updatePatientConditions(item);
        //This will be the actual disorder ID - SNOMED code
        controller.$row.find('.savedDiagnosis').val(item.id);

        controller.$row.find('.commonly-used-diagnosis').hide();
        controller.$row.find('.diagnoses-search-inputfield').hide();
        controller.$row.find('.medication-display').show();
    };

    /**
     * Check the given disorder item for any condition attributes that should be updated on the page
     */
    DiagnosesSearchController.prototype.updatePatientConditions = function(item)
    {
        // note that at the moment this follows the simple premise of inserting or removing additional
        // hidden fields that are universally searched for on the page, following the pattern established
        // by the original diagnoses element.
        if (item.is_diabetes) {
            this.$row.append('<input type="hidden" name="diabetic_diagnoses[]" value="1" /> ');
        } else {
            this.$row.find('input[name^="diabetic_diagnoses"]').remove();
        }

        // glaucoma is unlikely to come up given this is only being used for systemic disorders
        // but it is included for completeness.
        if (item.is_glaucoma) {
            this.$row.append('<input type="hidden" name="glaucoma_diagnoses[]" value="1" /> ').trigger('change');
        } else {
            this.$row.find('input[name^="glaucoma_diagnoses"]').remove();
        }

        // trigger event change for any controls looking for them
        $(":input[name^='diabetic_diagnoses']").trigger('change');
        $(":input[name^='glaucoma_diagnoses']").trigger('change');
    };

    DiagnosesSearchController.prototype.initialiseAutocomplete = function(){
        var controller = this;

        controller.$inputField.autocomplete({
            minLength: 2,
            delay: 700,
            source: function (request, response) {
                $.ajax({
                    'url': '/disorder/autocomplete',
                    'type':'GET',
                    'data':{'term': request.term, 'code': controller.code},
                    'beforeSend': function(){
                    },
                    'success':function(data) {
                        data = $.parseJSON(data);
                        response(data);
                    }
                });
            },
            search: function () {
                controller.$inputField.addClass('inset-loader');
            },
            select: function(event, ui){
                //no multiple option
                controller.addDiagnosis(null, ui.item);

                //clear input
                $(this).val("");
                return false;
            },
            response: function (event, ui) {
                controller.$inputField.removeClass('inset-loader');
            }
        });
    };

    DiagnosesSearchController.prototype.initialiseTriggers = function(){
        var controller = this;
        var $parent = controller.$inputField.parent();

        controller.$row.on('click', '.diagnosis-rename', function(){
            controller.$row.find('.commonly-used-diagnosis').show();
            controller.$row.find('.diagnoses-search-inputfield').show();
            $(this).closest('.medication-display').hide();
        });

        controller.$row.on('change', 'select.commonly-used-diagnosis', function(){
            controller.addDiagnosis(null, $(this).find('option:selected').data('item') );
            $(this).val('');
        });
    }

    exports.DiagnosesSearchController = DiagnosesSearchController;

}(OpenEyes.UI));<|endoftext|>


<commit_before>import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

const range = (n, m) => [...Array(m - n).keys()].map(i => i + n);
const sum = vs => vs.reduce((v, w) => ops.plus(v, w));
const centroid = pos => ops.div(sum(pos), pos.length);
const normalized = v => ops.div(v, ops.norm(v));

const projection = (normal, origin=ops.times(0, normal)) => p => {
  const d = ops.minus(p, origin);
  return ops.plus(origin,
                  ops.minus(d, ops.times(ops.times(normal, d), normal)));
};

export const averageRadius = solids => {
  let sum = 0;
  let count = 0;

  for (const { pos, faces } of solids) {
    for (const vs of faces) {
      const corners = vs.map(v => pos[v]);
      const center = centroid(corners);

      for (const p of corners) {
        sum += ops.norm(ops.minus(center, p));
        count += 1;
      }
    }
  }

  return sum / count;
};

export const tightened = ({ faces, pos, isFixed, faceLabels }) => {
  const n = pos.length;

  const px = new Float32Array(n);
  const py = new Float32Array(n);
  const pz = new Float32Array(n);

  const gx = new Float32Array(n);
  const gy = new Float32Array(n);
  const gz = new Float32Array(n);

  for (let v = 0; v < n; ++v) {
    px[v] = pos[v][0];
    py[v] = pos[v][1];
    pz[v] = pos[v][2];
  }

  for (let s = 0; s < 100; ++s) {
    for (let v = 0; v < n; ++v)
      gx[v] = gy[v] = gz[v] = 0;

    for (const f of faces) {
      const m = f.length;
      for (let k = 0; k < m; ++k) {
        const u = f[k];
        const v = f[(k + 1) % m];
        const w = f[(k + 2) % m];

        const ax = px[u] - px[v];
        const ay = py[u] - py[v];
        const az = pz[u] - pz[v];

        const bx = px[w] - px[v];
        const by = py[w] - py[v];
        const bz = pz[w] - pz[v];

        const cx = px[w] - px[u];
        const cy = py[w] - py[u];
        const cz = pz[w] - pz[u];

        const nx = by * az - bz * ay;
        const ny = bz * ax - bx * az;
        const nz = bx * ay - by * ax;

        const nl = Math.max(1e-6, Math.sqrt(nx * nx + ny * ny + nz * nz));

        gx[v] += (ny * cz - nz * cy) / nl + 0.1 * (ax + bx);
        gy[v] += (nz * cx - nx * cz) / nl + 0.1 * (ay + by);
        gz[v] += (nx * cy - ny * cx) / nl + 0.1 * (az + bz);
      }
    }

    for (let v = 0; v < n; ++v) {
      if (!isFixed[v]) {
        px[v] += 0.05 * gx[v];
        py[v] += 0.05 * gy[v];
        pz[v] += 0.05 * gz[v];
      }
    }
  }

  pos = [];
  for (let v = 0; v < n; ++v)
    pos.push([px[v], py[v], pz[v]]);

  return { faces, pos, isFixed, faceLabels };
};

export const subD = ({ faces, pos, isFixed, faceLabels }) => {
  const nrVerts = pos.length;
  const nrFaces = faces.length;

  const posNew = [...pos].concat(faces.map(vs => centroid(vs.map(v => pos[v]))));
  const fixedNew = [...isFixed].concat(faces.map(_ => false));
  const edgeIndex = {};
  const significantEdgeNeighbors = [];

  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const k = vs.length;

    for (let i = 0; i < k; ++i) {
      const [v, w] = [vs[i], vs[(i + 1) % k]];

      let e = edgeIndex[[v, w]];
      if (e == null) {
        e = edgeIndex[[v, w]] = edgeIndex[[w, v]] = posNew.length;
        posNew.push(centroid([pos[v], pos[w]]));
        fixedNew.push(isFixed[v] && isFixed[w]);
        significantEdgeNeighbors[e] = [v, w];
      }

      if (!fixedNew[e])
        significantEdgeNeighbors[e].push(f + nrVerts);
    }
  }

  for (let e = nrVerts + nrFaces; e < posNew.length; ++e)
    posNew[e] = centroid(significantEdgeNeighbors[e].map(v => posNew[v]));

  const facesNew = [];
  const fLabelsNew = [];
  const newFacesAtVertex = pos.map(_ => []);

  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const k = vs.length;

    for (let i = 0; i < k; ++i) {
      const [u, v, w] = [vs[(i + k - 1) % k], vs[i], vs[(i + 1) % k]];

      newFacesAtVertex[v].push(facesNew.length);
      facesNew.push([edgeIndex[[v, w]], f + nrVerts, edgeIndex[[u, v]], v]);
      fLabelsNew.push(faceLabels ? faceLabels[f] : f);
    }
  }

  for (let v = 0; v < nrVerts; ++v) {
    if (!isFixed[v]) {
      let t = ops.times(0, pos[0]);
      for (const f of newFacesAtVertex[v]) {
        const p = facesNew[f].map(v => posNew[v]);
        t = ops.plus(t, ops.minus(ops.times(2, ops.plus(p[0], p[2])), p[1]));
      }
      const m = newFacesAtVertex[v].length;
      posNew[v] = ops.plus(ops.times(1/(m*m), t), ops.times((m-3)/m, pos[v]));
    }
  }

  return {
    faces: facesNew,
    pos: posNew,
    isFixed: fixedNew,
    faceLabels: fLabelsNew,
  };
}

export const withFlattenedCenterFaces = (
  { faces, pos, isFixed, faceLabels }
) => {
  const newFaces = [];
  const newFaceLabels = [];
  const newPos = pos.slice();
  const newIsFixed = isFixed.slice();

  for (let f = 0; f < faces.length; ++f) {
    const label = faceLabels ? faceLabels[f] : f;
    const vs = faces[f];
    const ps = vs.map(i => pos[i]);
    const center = centroid(ps);

    const normal = normalized(sum(
      ps.map((p, i) => ops.crossProduct(p, ps[(i + 1) % ps.length]))
    ));

    const qs = ps.map(projection(normal, center));

    const isFlat = ps.every((p, i) => ops.norm(ops.minus(p, qs[i])) <= 0.01);

    if (isFlat) {
      newFaces.push(vs);
      newFaceLabels.push(label);
    }
    else {
      const k = newPos.length;
      const m = qs.length;

      for (const v of qs) {
        newPos.push(centroid([center, v]));
        newIsFixed.push(false);
      }

      newFaces.push(range(k, k + m));
      newFaceLabels.push(label);
      for (let i = 0; i < m; ++i) {
        const j = (i + 1) % m;
        newFaces.push([vs[i], vs[j], j + k, i + k]);
        newFaceLabels.push(label);
      }
    }
  }

  return {
    faces: newFaces,
    pos: newPos,
    isFixed: newIsFixed,
    faceLabels: newFaceLabels
  };
};

export const insetAt = ({ faces, pos, isFixed, faceLabels }, wd, isCorner) => {
  const { newPos, shrunkFaces } = shrunkAt(
    { faces, pos, isFixed }, wd, isCorner
  );

  const facesNew = [...shrunkFaces];

  for (let i = 0; i < faces.length; ++i) {
    const vsOld = faces[i];
    const vsNew = shrunkFaces[i];

    for (let k = 0; k < vsOld.length; ++k) {
      const k1 = (k + 1) % vsOld.length;
      if (vsOld[k] != vsNew[k] && vsOld[k1] != vsNew[k1])
        facesNew.push([vsOld[k], vsOld[k1], vsNew[k1], vsNew[k]]);
    }
  }

  return {
    faces: facesNew,
    pos: pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faceLabels: faceLabels || [...faces.keys()]
  };
};

export const beveledAt = (
  { faces, pos, isFixed, faceLabels }, wd, isCorner
) => {
  const { newPos, shrunkFaces } = shrunkAt(
    { faces, pos, isFixed }, wd, isCorner
  );

  const facesNew = [...shrunkFaces];
  const opposite = {};
  const m = {};

  for (let i = 0; i < faces.length; ++i) {
    const vsOld = faces[i];
    const vsNew = shrunkFaces[i];

    for (let k = 0; k < vsOld.length; ++k) {
      const k1 = (k + 1) % vsOld.length;
      const [a, b, c, d] = [vsOld[k], vsOld[k1], vsNew[k1], vsNew[k]];

      if (a != d && b != c) {
        const [f, e] = opposite[[b, a]] || [];
        if (e == null)
          opposite[[a, b]] = [d, c];
        else {
          facesNew.push([c, d, e, f]);
          m[e] = d;
          m[c] = f;
        }
      }
    }
  }

  const seen = {};

  for (const k of Object.keys(m)) {
    if (!seen[k]) {
      let i = parseInt(k);
      const f = [];
      while (!seen[i]) {
        seen[i] = true;
        f.push(i);
        i = m[i];
      }
      facesNew.push(f);
    }
  }

  return {
    faces: facesNew,
    pos: pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faceLabels: faceLabels || [...faces.keys()]
  };
};

const shrunkAt = ({ faces, pos }, wd, isCorner) => {
  const endIndex = (f, i) => faces[f][(i + 1) % faces[f].length];

  const edgeLoc = {};
  for (let f = 0; f < faces.length; ++f) {
    const is = faces[f];
    for (let k = 0; k < is.length; ++k)
      edgeLoc[[is[k], is[(k + 1) % is.length]]] = [f, k];
  }

  const seen = {};
  const mods = {};
  const newPos = [];

  for (let f0 = 0; f0 < faces.length; ++f0) {
    const is = faces[f0];

    for (let k0 = 0; k0 < is.length; ++k0) {
      const v = is[k0];

      if (!seen[v] && isCorner[v]) {
        seen[v] = true;

        const stretches = [];

        let stretch = [];
        let [f, k] = [f0, k0];
        do {
          stretch.push([f, k]);
          if (isCorner[endIndex(f, k)]) {
            stretches.push(stretch);
            stretch = [[f, k]];
          }
          const is = faces[f];
          [f, k] = edgeLoc[[is[k], is[(k + is.length - 1) % is.length]]];
        } while (f != f0 || k != k0);

        stretches[0] = stretch.concat(stretches[0]);

        for (const stretch of stretches) {
          const ends = stretch.map(([f, i]) => pos[endIndex(f, i)]);
          const c = centroid(
            ends.length > 2 ?
              ends.slice(1, -1) :
              faces[stretch[0][0]].map(v => pos[v])
          );
          newPos.push(insetPoint(pos[v], wd, ends[0], ends[ends.length-1], c));

          for (let j = 0; j < stretch.length - 1; ++j)
            mods[stretch[j]] = pos.length + newPos.length - 1;
        }
      }
    }
  }

  return {
    newPos,
    shrunkFaces: faces.map((is, f) => is.map((v, i) => mods[[f, i]] || v))
  };
};

const insetPoint = (corner, wd, left, right, center) => {
  const lft = normalized(ops.minus(left, corner));
  const rgt = normalized(ops.minus(right, corner));
  const dia = ops.plus(lft, rgt);

  if (ops.norm(dia) < 0.01) {
    const s = normalized(ops.minus(center, corner));
    const t = projection(lft)(s);
    return ops.plus(corner, ops.times(wd / ops.norm(t), s));
  }
  else if (ops.norm(ops.crossProduct(lft, rgt)) < 0.01) {
    return ops.plus(corner, ops.times(wd, lft));
  }
  else {
    const len = wd * ops.norm(dia) / ops.norm(projection(lft)(dia));
    const s   = normalized(ops.crossProduct(dia, ops.crossProduct(lft, rgt)));
    const t   = projection(s)(ops.minus(center, corner));
    const f   = len / ops.norm(t);
    return ops.plus(corner, ops.times(f, t));
  }
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return 'List [ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const cube = {
    pos: [[0,0,0], [0,0,1], [0,1,0], [0,1,1],
          [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
    faces: [[0,1,3,2],[5,4,6,7],
            [1,0,4,5],[2,3,7,6],
            [0,2,6,4],[3,1,5,7]],
    isFixed: Array(8).fill(0).map((_, i) => i < 4)
  };

  const t = withFlattenedCenterFaces(cube);

  console.log(insetAt(t, 0.1, Array(8).fill(true)));
  console.log();
  console.log(beveledAt(cube, 0.1, Array(8).fill(true)));
  console.log();
  console.log(subD(cube));
}
<commit_msg>ui/surface: move all fancy geometry code to the bottom<commit_after>import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

const range = (n, m) => [...Array(m - n).keys()].map(i => i + n);
const sum = vs => vs.reduce((v, w) => ops.plus(v, w));
const centroid = pos => ops.div(sum(pos), pos.length);

export const averageRadius = solids => {
  let sum = 0;
  let count = 0;

  for (const { pos, faces } of solids) {
    for (const vs of faces) {
      const corners = vs.map(v => pos[v]);
      const center = centroid(corners);

      for (const p of corners) {
        sum += ops.norm(ops.minus(center, p));
        count += 1;
      }
    }
  }

  return sum / count;
};

export const tightened = ({ faces, pos, isFixed, faceLabels }) => {
  const n = pos.length;

  const px = new Float32Array(n);
  const py = new Float32Array(n);
  const pz = new Float32Array(n);

  const gx = new Float32Array(n);
  const gy = new Float32Array(n);
  const gz = new Float32Array(n);

  for (let v = 0; v < n; ++v) {
    px[v] = pos[v][0];
    py[v] = pos[v][1];
    pz[v] = pos[v][2];
  }

  for (let s = 0; s < 100; ++s) {
    for (let v = 0; v < n; ++v)
      gx[v] = gy[v] = gz[v] = 0;

    for (const f of faces) {
      const m = f.length;
      for (let k = 0; k < m; ++k) {
        const u = f[k];
        const v = f[(k + 1) % m];
        const w = f[(k + 2) % m];

        const ax = px[u] - px[v];
        const ay = py[u] - py[v];
        const az = pz[u] - pz[v];

        const bx = px[w] - px[v];
        const by = py[w] - py[v];
        const bz = pz[w] - pz[v];

        const cx = px[w] - px[u];
        const cy = py[w] - py[u];
        const cz = pz[w] - pz[u];

        const nx = by * az - bz * ay;
        const ny = bz * ax - bx * az;
        const nz = bx * ay - by * ax;

        const nl = Math.max(1e-6, Math.sqrt(nx * nx + ny * ny + nz * nz));

        gx[v] += (ny * cz - nz * cy) / nl + 0.1 * (ax + bx);
        gy[v] += (nz * cx - nx * cz) / nl + 0.1 * (ay + by);
        gz[v] += (nx * cy - ny * cx) / nl + 0.1 * (az + bz);
      }
    }

    for (let v = 0; v < n; ++v) {
      if (!isFixed[v]) {
        px[v] += 0.05 * gx[v];
        py[v] += 0.05 * gy[v];
        pz[v] += 0.05 * gz[v];
      }
    }
  }

  pos = [];
  for (let v = 0; v < n; ++v)
    pos.push([px[v], py[v], pz[v]]);

  return { faces, pos, isFixed, faceLabels };
};

export const subD = ({ faces, pos, isFixed, faceLabels }) => {
  const nrVerts = pos.length;
  const nrFaces = faces.length;

  const posNew = [...pos].concat(faces.map(vs => centroid(vs.map(v => pos[v]))));
  const fixedNew = [...isFixed].concat(faces.map(_ => false));
  const edgeIndex = {};
  const significantEdgeNeighbors = [];

  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const k = vs.length;

    for (let i = 0; i < k; ++i) {
      const [v, w] = [vs[i], vs[(i + 1) % k]];

      let e = edgeIndex[[v, w]];
      if (e == null) {
        e = edgeIndex[[v, w]] = edgeIndex[[w, v]] = posNew.length;
        posNew.push(centroid([pos[v], pos[w]]));
        fixedNew.push(isFixed[v] && isFixed[w]);
        significantEdgeNeighbors[e] = [v, w];
      }

      if (!fixedNew[e])
        significantEdgeNeighbors[e].push(f + nrVerts);
    }
  }

  for (let e = nrVerts + nrFaces; e < posNew.length; ++e)
    posNew[e] = centroid(significantEdgeNeighbors[e].map(v => posNew[v]));

  const facesNew = [];
  const fLabelsNew = [];
  const newFacesAtVertex = pos.map(_ => []);

  for (let f = 0; f < faces.length; ++f) {
    const vs = faces[f];
    const k = vs.length;

    for (let i = 0; i < k; ++i) {
      const [u, v, w] = [vs[(i + k - 1) % k], vs[i], vs[(i + 1) % k]];

      newFacesAtVertex[v].push(facesNew.length);
      facesNew.push([edgeIndex[[v, w]], f + nrVerts, edgeIndex[[u, v]], v]);
      fLabelsNew.push(faceLabels ? faceLabels[f] : f);
    }
  }

  for (let v = 0; v < nrVerts; ++v) {
    if (!isFixed[v]) {
      let t = ops.times(0, pos[0]);
      for (const f of newFacesAtVertex[v]) {
        const p = facesNew[f].map(v => posNew[v]);
        t = ops.plus(t, ops.minus(ops.times(2, ops.plus(p[0], p[2])), p[1]));
      }
      const m = newFacesAtVertex[v].length;
      posNew[v] = ops.plus(ops.times(1/(m*m), t), ops.times((m-3)/m, pos[v]));
    }
  }

  return {
    faces: facesNew,
    pos: posNew,
    isFixed: fixedNew,
    faceLabels: fLabelsNew,
  };
}

export const withFlattenedCenterFaces = (
  { faces, pos, isFixed, faceLabels }
) => {
  const newFaces = [];
  const newFaceLabels = [];
  const newPos = pos.slice();
  const newIsFixed = isFixed.slice();

  for (let f = 0; f < faces.length; ++f) {
    const label = faceLabels ? faceLabels[f] : f;
    const vs = faces[f];
    const ps = vs.map(i => pos[i]);
    const qs = flattened(ps);
    const isFlat = ps.every((p, i) => ops.norm(ops.minus(p, qs[i])) <= 0.01);

    if (isFlat) {
      newFaces.push(vs);
      newFaceLabels.push(label);
    }
    else {
      const center = centroid(ps);
      const k = newPos.length;
      const m = qs.length;

      for (const v of qs) {
        newPos.push(centroid([center, v]));
        newIsFixed.push(false);
      }

      newFaces.push(range(k, k + m));
      newFaceLabels.push(label);
      for (let i = 0; i < m; ++i) {
        const j = (i + 1) % m;
        newFaces.push([vs[i], vs[j], j + k, i + k]);
        newFaceLabels.push(label);
      }
    }
  }

  return {
    faces: newFaces,
    pos: newPos,
    isFixed: newIsFixed,
    faceLabels: newFaceLabels
  };
};

export const insetAt = ({ faces, pos, isFixed, faceLabels }, wd, isCorner) => {
  const { newPos, shrunkFaces } = shrunkAt(
    { faces, pos, isFixed }, wd, isCorner
  );

  const facesNew = [...shrunkFaces];

  for (let i = 0; i < faces.length; ++i) {
    const vsOld = faces[i];
    const vsNew = shrunkFaces[i];

    for (let k = 0; k < vsOld.length; ++k) {
      const k1 = (k + 1) % vsOld.length;
      if (vsOld[k] != vsNew[k] && vsOld[k1] != vsNew[k1])
        facesNew.push([vsOld[k], vsOld[k1], vsNew[k1], vsNew[k]]);
    }
  }

  return {
    faces: facesNew,
    pos: pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faceLabels: faceLabels || [...faces.keys()]
  };
};

export const beveledAt = (
  { faces, pos, isFixed, faceLabels }, wd, isCorner
) => {
  const { newPos, shrunkFaces } = shrunkAt(
    { faces, pos, isFixed }, wd, isCorner
  );

  const facesNew = [...shrunkFaces];
  const opposite = {};
  const m = {};

  for (let i = 0; i < faces.length; ++i) {
    const vsOld = faces[i];
    const vsNew = shrunkFaces[i];

    for (let k = 0; k < vsOld.length; ++k) {
      const k1 = (k + 1) % vsOld.length;
      const [a, b, c, d] = [vsOld[k], vsOld[k1], vsNew[k1], vsNew[k]];

      if (a != d && b != c) {
        const [f, e] = opposite[[b, a]] || [];
        if (e == null)
          opposite[[a, b]] = [d, c];
        else {
          facesNew.push([c, d, e, f]);
          m[e] = d;
          m[c] = f;
        }
      }
    }
  }

  const seen = {};

  for (const k of Object.keys(m)) {
    if (!seen[k]) {
      let i = parseInt(k);
      const f = [];
      while (!seen[i]) {
        seen[i] = true;
        f.push(i);
        i = m[i];
      }
      facesNew.push(f);
    }
  }

  return {
    faces: facesNew,
    pos: pos.concat(newPos),
    isFixed: isFixed.concat(newPos.map(i => true)),
    faceLabels: faceLabels || [...faces.keys()]
  };
};

const shrunkAt = ({ faces, pos }, wd, isCorner) => {
  const endIndex = (f, i) => faces[f][(i + 1) % faces[f].length];

  const edgeLoc = {};
  for (let f = 0; f < faces.length; ++f) {
    const is = faces[f];
    for (let k = 0; k < is.length; ++k)
      edgeLoc[[is[k], is[(k + 1) % is.length]]] = [f, k];
  }

  const seen = {};
  const mods = {};
  const newPos = [];

  for (let f0 = 0; f0 < faces.length; ++f0) {
    const is = faces[f0];

    for (let k0 = 0; k0 < is.length; ++k0) {
      const v = is[k0];

      if (!seen[v] && isCorner[v]) {
        seen[v] = true;

        const stretches = [];

        let stretch = [];
        let [f, k] = [f0, k0];
        do {
          stretch.push([f, k]);
          if (isCorner[endIndex(f, k)]) {
            stretches.push(stretch);
            stretch = [[f, k]];
          }
          const is = faces[f];
          [f, k] = edgeLoc[[is[k], is[(k + is.length - 1) % is.length]]];
        } while (f != f0 || k != k0);

        stretches[0] = stretch.concat(stretches[0]);

        for (const stretch of stretches) {
          const ends = stretch.map(([f, i]) => pos[endIndex(f, i)]);
          const c = centroid(
            ends.length > 2 ?
              ends.slice(1, -1) :
              faces[stretch[0][0]].map(v => pos[v])
          );
          newPos.push(insetPoint(pos[v], wd, ends[0], ends[ends.length-1], c));

          for (let j = 0; j < stretch.length - 1; ++j)
            mods[stretch[j]] = pos.length + newPos.length - 1;
        }
      }
    }
  }

  return {
    newPos,
    shrunkFaces: faces.map((is, f) => is.map((v, i) => mods[[f, i]] || v))
  };
};

const flattened = ps => {
  const center = centroid(ps);
  const normal = normalized(
    sum(ps.map((p, i) => ops.crossProduct(p, ps[(i + 1) % ps.length])))
  );

  return ps.map(projection(normal, center));
};

const insetPoint = (corner, wd, left, right, center) => {
  const lft = normalized(ops.minus(left, corner));
  const rgt = normalized(ops.minus(right, corner));
  const dia = ops.plus(lft, rgt);

  if (ops.norm(dia) < 0.01) {
    const s = normalized(ops.minus(center, corner));
    const t = projection(lft)(s);
    return ops.plus(corner, ops.times(wd / ops.norm(t), s));
  }
  else if (ops.norm(ops.crossProduct(lft, rgt)) < 0.01) {
    return ops.plus(corner, ops.times(wd, lft));
  }
  else {
    const len = wd * ops.norm(dia) / ops.norm(projection(lft)(dia));
    const s   = normalized(ops.crossProduct(dia, ops.crossProduct(lft, rgt)));
    const t   = projection(s)(ops.minus(center, corner));
    const f   = len / ops.norm(t);
    return ops.plus(corner, ops.times(f, t));
  }
};

const normalized = v => ops.div(v, ops.norm(v));

const projection = (normal, origin=ops.times(0, normal)) => p => {
  const d = ops.minus(p, origin);
  return ops.plus(
    origin,
    ops.minus(d, ops.times(ops.times(normal, d), normal))
  );
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return 'List [ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const cube = {
    pos: [[0,0,0], [0,0,1], [0,1,0], [0,1,1],
          [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
    faces: [[0,1,3,2],[5,4,6,7],
            [1,0,4,5],[2,3,7,6],
            [0,2,6,4],[3,1,5,7]],
    isFixed: Array(8).fill(0).map((_, i) => i < 4)
  };

  const t = withFlattenedCenterFaces(cube);

  console.log(insetAt(t, 0.1, Array(8).fill(true)));
  console.log();
  console.log(beveledAt(cube, 0.1, Array(8).fill(true)));
  console.log();
  console.log(subD(cube));
}
<|endoftext|>


<commit_before>// User: Basic data for business processes

'use strict';

var _  = require('mano').i18n.bind('View: User');

exports._parent = require('./user-base');

exports['sub-main'] = {
	class: { content: true, 'user-forms': true },
	content: function () {
		var manager = this.manager, revertedBusinessProcesses = this.user.revertedBusinessProcesses;

		div({ class: 'user-account-boxes' },
			section({ id: 'welcome-box', class: 'user-account-welcome' },
				_if(manager, function () {
					return manager._currentlyManagedUser.map(function (managedUser) {
						if (!managedUser) return;

						return [header(h3(_("View for client: ${ clientFullName }",
							{ clientFullName: managedUser._fullName }))), div({ class: 'free-form' },
								md(_("1. From here you can access all the requests of this client in draft, " +
									"in process, finished\n" +
									"2. Start a new service for this client\n" +
									"3. Create an account for this client if not done already")))];
					}.bind(this));
				}.bind(this), function () {
					return [
						header(
							h3(_("Welcome to your account. From here you can:"))
						),
						div({ class: 'free-form' },
							md(_("1. Access all your requests in draft, in process, finished\n" +
								"2. Access and edit your documents and data\n" +
								"3. Start a new service related to your company")))
					];
				}.bind(this))));

		exports._notificationsBox.call(this);

		insert(_if(gt(revertedBusinessProcesses._size, 0), function () {
			div({ class: 'section-warning' },
				ul(
					revertedBusinessProcesses.map(function (pendingProcess) {
						return li({ class: 'section-warning-action' }, div(
							span({ class: "section-warning-action-description" },
								_('${ businessName } is pending for corrections',
									{ businessName: pendingProcess._businessName })),
							span({ class: "section-warning-action-button" }, postButton({
								action: url('business-process', pendingProcess.__id__),
								value: _('Correct now')
							})))
						);
					})
				));
		}));

		section({ class: 'section-tab-nav' },
			a({ class: 'section-tab-nav-tab user-account-tab',
				id: 'user-account-requests',
				href: '/' },
				_if(manager, _("Requests"), _("My requests"))),
			a({ class: 'section-tab-nav-tab user-account-tab',
				id: 'user-account-data',
				href: '/requests/' },
				_if(manager, _("Documents and data"), _("My documents and data"))),
			div({ id: 'user-account-content', class: 'section-primary' }));

		insert(_if(!manager || eq(this.user._manager, manager), [
			exports._followUpsBoxes.call(this),
			h3({ class: 'user-account-section-title' }, _("Available services")),
			section({ class: 'section-primary' },
				ul({ class: 'user-account-service-boxes' },
					exports._servicesBoxList.call(this),
					function (item) {
						var disabled = item.disabledCondition
						  , renderAsForm = item.actionUrl != null ? and(item.actionUrl, not(disabled)) : false
						  , renderAsDiv = not(renderAsForm)
						  , boxClasses = [ 'user-account-service-box', _if(disabled, 'disabled') ];

						// Explanation of superfluous _if usage within configuration below:
						// As item.content and item.buttonContent can be used in two scenarios (form and aHref)
						// which may toggle during time of interface being displayed, we need to ensure that
						// very same DOM content is moved between two containers, and that can only be ensured
						// with extra `_if(cond, item.x)` calls (otherwise those `_if` have no real function)
						return _if(item.condition || true, li(_if(
							renderAsForm,
							form({ class: boxClasses, action: item.actionUrl, method: 'post' },
								button({ type: 'submit' }, _if(renderAsForm, item.buttonContent)),
								div(div({ class: 'free-form' }, _if(renderAsForm, item.content)),
									p(button({ type: 'submit' },
										i({ class: 'fa fa-angle-right' }), _('Click to start'))))),
							div({ class: boxClasses },
								a({ href: _if(disabled, null, item.hrefUrl) },
									_if(renderAsDiv, item.buttonContent)),
								div(div({ class: 'free-form' }, _if(renderAsDiv, item.content)),
									p(a({ href: _if(disabled, null, item.hrefUrl) },
										i({ class: 'fa fa-angle-right' }), _if(disabled,
											_('You have reached the draft limit for this service'),
											_('Click to start'))))))
						)));
					}))]));

	}
};

exports._notificationsBox = Function.prototype;
exports._servicesBoxList  = Function.prototype;
exports._followUpsBoxes   = Function.prototype;
<commit_msg>fixe lint error<commit_after>// User: Basic data for business processes

'use strict';

var _  = require('mano').i18n.bind('View: User');

exports._parent = require('./user-base');

exports['sub-main'] = {
	class: { content: true, 'user-forms': true },
	content: function () {
		var manager = this.manager, revertedBusinessProcesses = this.user.revertedBusinessProcesses;

		div({ class: 'user-account-boxes' },
			section({ id: 'welcome-box', class: 'user-account-welcome' },
				_if(manager, function () {
					return manager._currentlyManagedUser.map(function (managedUser) {
						if (!managedUser) return;

						return [header(h3(_("View for client: ${ clientFullName }",
							{ clientFullName: managedUser._fullName }))), div({ class: 'free-form' },
								md(_("1. From here you can access all the requests of this client in draft, " +
									"in process, finished\n" +
									"2. Start a new service for this client\n" +
									"3. Create an account for this client if not done already")))];
					}.bind(this));
				}.bind(this), function () {
					return [
						header(
							h3(_("Welcome to your account. From here you can:"))
						),
						div({ class: 'free-form' },
							md(_("1. Access all your requests in draft, in process, finished\n" +
								"2. Access and edit your documents and data\n" +
								"3. Start a new service related to your company")))
					];
				}.bind(this))));

		exports._notificationsBox.call(this);

		insert(_if(gt(revertedBusinessProcesses._size, 0), function () {
			div({ class: 'section-warning' },
				ul(
					revertedBusinessProcesses.map(function (pendingProcess) {
						return li({ class: 'section-warning-action' }, div(
							span({ class: "section-warning-action-description" },
								_('${ businessName } is pending for corrections',
									{ businessName: pendingProcess._businessName })),
							span({ class: "section-warning-action-button" }, postButton({
								action: url('business-process', pendingProcess.__id__),
								value: _('Correct now')
							}))
						));
					})
				));
		}));

		section({ class: 'section-tab-nav' },
			a({ class: 'section-tab-nav-tab user-account-tab',
				id: 'user-account-requests',
				href: '/' },
				_if(manager, _("Requests"), _("My requests"))),
			a({ class: 'section-tab-nav-tab user-account-tab',
				id: 'user-account-data',
				href: '/requests/' },
				_if(manager, _("Documents and data"), _("My documents and data"))),
			div({ id: 'user-account-content', class: 'section-primary' }));

		insert(_if(!manager || eq(this.user._manager, manager), [
			exports._followUpsBoxes.call(this),
			h3({ class: 'user-account-section-title' }, _("Available services")),
			section({ class: 'section-primary' },
				ul({ class: 'user-account-service-boxes' },
					exports._servicesBoxList.call(this),
					function (item) {
						var disabled = item.disabledCondition
						  , renderAsForm = item.actionUrl != null ? and(item.actionUrl, not(disabled)) : false
						  , renderAsDiv = not(renderAsForm)
						  , boxClasses = [ 'user-account-service-box', _if(disabled, 'disabled') ];

						// Explanation of superfluous _if usage within configuration below:
						// As item.content and item.buttonContent can be used in two scenarios (form and aHref)
						// which may toggle during time of interface being displayed, we need to ensure that
						// very same DOM content is moved between two containers, and that can only be ensured
						// with extra `_if(cond, item.x)` calls (otherwise those `_if` have no real function)
						return _if(item.condition || true, li(_if(
							renderAsForm,
							form({ class: boxClasses, action: item.actionUrl, method: 'post' },
								button({ type: 'submit' }, _if(renderAsForm, item.buttonContent)),
								div(div({ class: 'free-form' }, _if(renderAsForm, item.content)),
									p(button({ type: 'submit' },
										i({ class: 'fa fa-angle-right' }), _('Click to start'))))),
							div({ class: boxClasses },
								a({ href: _if(disabled, null, item.hrefUrl) },
									_if(renderAsDiv, item.buttonContent)),
								div(div({ class: 'free-form' }, _if(renderAsDiv, item.content)),
									p(a({ href: _if(disabled, null, item.hrefUrl) },
										i({ class: 'fa fa-angle-right' }), _if(disabled,
											_('You have reached the draft limit for this service'),
											_('Click to start'))))))
						)));
					}))]));

	}
};

exports._notificationsBox = Function.prototype;
exports._servicesBoxList  = Function.prototype;
exports._followUpsBoxes   = Function.prototype;
<|endoftext|>


<commit_before>/*!
 * na-hermite-interpolation
 * https://github.com/tfoxy/na-hermite-interpolation
 *
 * Copyright 2015 Tomás Fox
 * Released under the MIT license
 */

/**
 * Numbers must have the following methods for the divided differences:
 * cmp, minus, div
 * Additionally, to calculate the polynomial coefficients:
 * plus, times, neg
 */
module.exports = (function() {
  'use strict';

  var events = require('events');

  function DuplicateError(message) {
    this.name = 'DuplicateError';
    this.message = message || 'Duplicate x value';
  }
  DuplicateError.prototype = Object.create(Error.prototype);
  DuplicateError.prototype.constructor = DuplicateError;

  function HermiteInterpolation() {
    this.data = [];
    this._column = [];
    this._prevColumn = [];
    this._data = [];
    this._currentFactorialResult = 1;
    this._currentFactorialFactor = 1;
  }

  HermiteInterpolation._dataCompareFn = function(left, right) {
    return left.x.cmp(right.x);
  };

  HermiteInterpolation.DuplicateError = DuplicateError;

  HermiteInterpolation.prototype = Object.create(events.EventEmitter.prototype);
  HermiteInterpolation.prototype.constructor = HermiteInterpolation;

  HermiteInterpolation.prototype.calculateDividedDifferences = function() {
    this._prepareData();
    this._calculateDividedDifferences();
    this._cleanUp();
  };

  HermiteInterpolation.prototype.calculatePolynomialCoefficients = function() {
    this._prepareData();

    var preCoef = [this._data[0].y];

    var stepListener = function(stepData) {
      if (stepData.i === 0) {
        preCoef.push(stepData.result);
      }
    };

    this.on('step', stepListener);

    this._calculateDividedDifferences();

    this.removeListener('step', stepListener);

    this.emit('preCoefficients', preCoef);

    return this._calculatePolynomialCoefficients(preCoef);
  };

  HermiteInterpolation.prototype._calculatePolynomialCoefficients = function(preCoef) {
    var coef = preCoef.slice();

    var tempCoef = [];
    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    var x_i, fx_0i;

    var timesCoefFn = function(c) {
      return c.times(x_i);
    };

    var addAuxCoefFn = function(auxC, i) {
      tempCoef[i + 1] = tempCoef[i + 1].plus(auxC);
    };

    var addTempCoefFn = function(tempC, i) {
      coef[i] = fx_0i.times(tempC).plus(coef[i]);
    };

    for (var i = 1; i < coef.length; i++) {
      // f(x[0], ..., x[i]) * tempCoef * (x - x[i-1])

      //noinspection JSUnresolvedFunction
      x_i = this._data[i - 1].x.neg();
      fx_0i = preCoef[i];
      var auxCoef = tempCoef;

      tempCoef = auxCoef.map(timesCoefFn);

      tempCoef.push(x_i);

      auxCoef.forEach(addAuxCoefFn);

      tempCoef.forEach(addTempCoefFn);
    }

    this.emit('coefficients', coef);

    this._cleanUp();

    return coef;
  };

  HermiteInterpolation.prototype._prepareData = function() {
    this._checkDuplicateX();
    this._cloneData();
    this._multiplyPointsWithDifferential();
    this._orderDataByX();
    this._initPrevColumn();

    this.emit('dataInitialized', this._data);
  };

  HermiteInterpolation.prototype._checkDuplicateX = function() {
    var set = Object.create(null);

    this.data.forEach(function(point) {
      var x = JSON.stringify(point.x);
      if (x in set) {
        this.emit('error', new DuplicateError());
      }
      set[x] = true;
    }, this);
  };

  HermiteInterpolation.prototype._cloneData = function() {
    this._data = this.data.map(function(point) {
      var _point = {x: point.x, y: point.y};

      if (!point.d) {
        _point.d = [];
      } else if (!Array.isArray(point.d)) {
        _point.d = [point.d];
      } else {
        _point.d = point.d;
      }

      return _point;
    });
  };

  HermiteInterpolation.prototype._multiplyPointsWithDifferential = function() {
    var length = this._data.length;

    for (var i = 0; i < length; ++i) {
      var point = this._data[i];
      for (var j = 0; j < point.d.length; ++j) {
        this._data.push(point);
      }
    }
  };

  HermiteInterpolation.prototype._orderDataByX = function() {
    this._data.sort(HermiteInterpolation._dataCompareFn);
  };

  HermiteInterpolation.prototype._initPrevColumn = function() {
    this._data.forEach(function(point) {
      this._prevColumn.push(point.y);
    }, this);
  };

  HermiteInterpolation.prototype._calculateDividedDifferences = function() {
    for (var j = 1; j < this._data.length; j++) {
      for (var i = 0; i < this._data.length - j; i++) {
        this._calculateStepResult(i, i + j);
      }
      this._prevColumn = this._column;
      this._column = [];
    }

    this._prevColumn = [];
  };

  HermiteInterpolation.prototype._calculateStepResult = function(i, j) {
    var result;
    var xI = this._data[i].x, xJ = this._data[j].x;

    if (xI.cmp(xJ) === 0) {
      result = this._getDifferentialResult(this._data[i], j - i);
    } else {
      var divisor = xJ.minus(xI);
      var dividend = this._prevColumn[i + 1].minus(this._prevColumn[i]);
      result = dividend.div(divisor);
    }

    this._column.push(result);

    this.emit('step', {i: i, j: j, result: result});
  };

  HermiteInterpolation.prototype._getDifferentialResult = function(point, order) {
    if (order === 1) {
      return point.d[0];
    } else {
      return point.d[order - 1].div(this._factorial(order));
    }
  };

  HermiteInterpolation.prototype._factorial = function(differentialOrder) {
    if (differentialOrder !== this._currentFactorialFactor) {
      this._currentFactorialResult *= ++this._currentFactorialFactor;
    }

    return this._currentFactorialResult;
  };

  HermiteInterpolation.prototype._cleanUp = function() {
    this._data = [];
    this._currentFactorialResult = 1;
    this._currentFactorialFactor = 1;
  };

  return HermiteInterpolation;
})();
<commit_msg>Handle special case when there is no data<commit_after>/*!
 * na-hermite-interpolation
 * https://github.com/tfoxy/na-hermite-interpolation
 *
 * Copyright 2015 Tomás Fox
 * Released under the MIT license
 */

/**
 * Numbers must have the following methods for the divided differences:
 * cmp, minus, div
 * Additionally, to calculate the polynomial coefficients:
 * plus, times, neg
 */
module.exports = (function() {
  'use strict';

  var events = require('events');

  function DuplicateError(message) {
    this.name = 'DuplicateError';
    this.message = message || 'Duplicate x value';
  }
  DuplicateError.prototype = Object.create(Error.prototype);
  DuplicateError.prototype.constructor = DuplicateError;

  function HermiteInterpolation() {
    this.data = [];
    this._column = [];
    this._prevColumn = [];
    this._data = [];
    this._currentFactorialResult = 1;
    this._currentFactorialFactor = 1;
  }

  HermiteInterpolation._dataCompareFn = function(left, right) {
    return left.x.cmp(right.x);
  };

  HermiteInterpolation.DuplicateError = DuplicateError;

  HermiteInterpolation.prototype = Object.create(events.EventEmitter.prototype);
  HermiteInterpolation.prototype.constructor = HermiteInterpolation;

  HermiteInterpolation.prototype.calculateDividedDifferences = function() {
    if (this.data.length === 0) {
      return;
    }

    this._prepareData();
    this._calculateDividedDifferences();
    this._cleanUp();
  };

  HermiteInterpolation.prototype.calculatePolynomialCoefficients = function() {
    if (this.data.length === 0) {
      return [];
    }

    this._prepareData();

    var preCoef = [this._data[0].y];

    var stepListener = function(stepData) {
      if (stepData.i === 0) {
        preCoef.push(stepData.result);
      }
    };

    this.on('step', stepListener);

    this._calculateDividedDifferences();

    this.removeListener('step', stepListener);

    this.emit('preCoefficients', preCoef);

    return this._calculatePolynomialCoefficients(preCoef);
  };

  HermiteInterpolation.prototype._calculatePolynomialCoefficients = function(preCoef) {
    var coef = preCoef.slice();

    var tempCoef = [];
    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    var x_i, fx_0i;

    var timesCoefFn = function(c) {
      return c.times(x_i);
    };

    var addAuxCoefFn = function(auxC, i) {
      tempCoef[i + 1] = tempCoef[i + 1].plus(auxC);
    };

    var addTempCoefFn = function(tempC, i) {
      coef[i] = fx_0i.times(tempC).plus(coef[i]);
    };

    for (var i = 1; i < coef.length; i++) {
      // f(x[0], ..., x[i]) * tempCoef * (x - x[i-1])

      //noinspection JSUnresolvedFunction
      x_i = this._data[i - 1].x.neg();
      fx_0i = preCoef[i];
      var auxCoef = tempCoef;

      tempCoef = auxCoef.map(timesCoefFn);

      tempCoef.push(x_i);

      auxCoef.forEach(addAuxCoefFn);

      tempCoef.forEach(addTempCoefFn);
    }

    this.emit('coefficients', coef);

    this._cleanUp();

    return coef;
  };

  HermiteInterpolation.prototype._prepareData = function() {
    this._checkDuplicateX();
    this._cloneData();
    this._multiplyPointsWithDifferential();
    this._orderDataByX();
    this._initPrevColumn();

    this.emit('dataInitialized', this._data);
  };

  HermiteInterpolation.prototype._checkDuplicateX = function() {
    var set = Object.create(null);

    this.data.forEach(function(point) {
      var x = JSON.stringify(point.x);
      if (x in set) {
        this.emit('error', new DuplicateError());
      }
      set[x] = true;
    }, this);
  };

  HermiteInterpolation.prototype._cloneData = function() {
    this._data = this.data.map(function(point) {
      var _point = {x: point.x, y: point.y};

      if (!point.d) {
        _point.d = [];
      } else if (!Array.isArray(point.d)) {
        _point.d = [point.d];
      } else {
        _point.d = point.d;
      }

      return _point;
    });
  };

  HermiteInterpolation.prototype._multiplyPointsWithDifferential = function() {
    var length = this._data.length;

    for (var i = 0; i < length; ++i) {
      var point = this._data[i];
      for (var j = 0; j < point.d.length; ++j) {
        this._data.push(point);
      }
    }
  };

  HermiteInterpolation.prototype._orderDataByX = function() {
    this._data.sort(HermiteInterpolation._dataCompareFn);
  };

  HermiteInterpolation.prototype._initPrevColumn = function() {
    this._data.forEach(function(point) {
      this._prevColumn.push(point.y);
    }, this);
  };

  HermiteInterpolation.prototype._calculateDividedDifferences = function() {
    for (var j = 1; j < this._data.length; j++) {
      for (var i = 0; i < this._data.length - j; i++) {
        this._calculateStepResult(i, i + j);
      }
      this._prevColumn = this._column;
      this._column = [];
    }

    this._prevColumn = [];
  };

  HermiteInterpolation.prototype._calculateStepResult = function(i, j) {
    var result;
    var xI = this._data[i].x, xJ = this._data[j].x;

    if (xI.cmp(xJ) === 0) {
      result = this._getDifferentialResult(this._data[i], j - i);
    } else {
      var divisor = xJ.minus(xI);
      var dividend = this._prevColumn[i + 1].minus(this._prevColumn[i]);
      result = dividend.div(divisor);
    }

    this._column.push(result);

    this.emit('step', {i: i, j: j, result: result});
  };

  HermiteInterpolation.prototype._getDifferentialResult = function(point, order) {
    if (order === 1) {
      return point.d[0];
    } else {
      return point.d[order - 1].div(this._factorial(order));
    }
  };

  HermiteInterpolation.prototype._factorial = function(differentialOrder) {
    if (differentialOrder !== this._currentFactorialFactor) {
      this._currentFactorialResult *= ++this._currentFactorialFactor;
    }

    return this._currentFactorialResult;
  };

  HermiteInterpolation.prototype._cleanUp = function() {
    this._data = [];
    this._currentFactorialResult = 1;
    this._currentFactorialFactor = 1;
  };

  return HermiteInterpolation;
})();
<|endoftext|>


<commit_before>/* eslint-disable */
import { observe } from "mobx";
import ProjectStore from "../../../stores/domain/ProjectStore";
import Suite from "../../../models/Suite";
import Test from "../../../models/Test";

describe("Project Store", () => {
  it("should have a name", () => {
    const store = new ProjectStore("myStore");
    expect(store.name).toBe("myStore");
  });
  it("should observe the name change", () => {
    const store = new ProjectStore("myStore");
    const disposer = observe(store, "name", (change) => {
      expect(change.newValue).toBe("changed");
    });
    store.name = "changed";
    disposer();
  });
  it("should observe adding addition test to the store", () => {
    const store = new ProjectStore();
    const disposer = observe(store, "tests", (change) => {
      expect(change.newValue.length).toBe(1);
    });
    store.tests.push(new Test());
    disposer();
  });
  it("should add a new Test", () => {
    const store = new ProjectStore();
    expect(store.tests.length).toBe(0);
    store.addTest(new Test());
    expect(store.tests.length).toBe(1);
  });
  it("should throw if no Test was given", () => {
    const store = new ProjectStore();
    expect(() => store.addTest()).toThrowError("Expected to receive Test instead received undefined");
  });
  it("should throw if a different type was given", () => {
    const store = new ProjectStore();
    expect(() => store.addTest(1)).toThrowError("Expected to receive Test instead received Number");
  });
  it("should delete a test", () => {
    const store = new ProjectStore();
    const test = new Test();
    store.addTest(test);
    expect(store.tests.length).toBe(1);
    store.deleteTest(test);
    expect(store.tests.length).toBe(0);
  });
  it("should create a test", () => {
    const store = new ProjectStore();
    expect(store.tests.length).toBe(0);
    store.createTest();
    expect(store.tests.length).toBe(1);
  });
  it("should pass ctor args to test when created", () => {
    const store = new ProjectStore();
    const test = store.createTest("my test");
    expect(test.name).toBe("my test");
  });
  it("should create a suite", () => {
    const store = new ProjectStore();
    expect(store.suites.length).toBe(0);
    store.createSuite();
    expect(store.suites.length).toBe(1);
  });
  it("should pass ctor args to suite when created", () => {
    const store = new ProjectStore();
    const suite = store.createSuite("my suite");
    expect(suite.name).toBe("my suite");
  });
  it("should remove the deleted test from it's suites", () => {
    const store = new ProjectStore();
    const firstSuite = new Suite(store);
    const secondSuite = new Suite(store);
    const controlSuite = new Suite(store);
    const toBeDeleted = new Test();
    const control = new Test();
    store.addTest(toBeDeleted);
    store.addTest(control);
    firstSuite.addTest(toBeDeleted);
    secondSuite.addTest(toBeDeleted);
    secondSuite.addTest(control);
    controlSuite.addTest(control);
    expect(firstSuite.tests.length).toBe(1);
    expect(secondSuite.tests.length).toBe(2);
    expect(controlSuite.tests.length).toBe(1);
    store.deleteTest(toBeDeleted);
    expect(firstSuite.tests.length).toBe(0);
    expect(secondSuite.tests.length).toBe(1);
    expect(controlSuite.tests.length).toBe(1);
  });
});
<commit_msg>init models in the current store context<commit_after>/* eslint-disable */
import { observe } from "mobx";
import ProjectStore from "../../../stores/domain/ProjectStore";
import Suite from "../../../models/Suite";
import Test from "../../../models/Test";

describe("Project Store", () => {
  it("should have a name", () => {
    const store = new ProjectStore("myStore");
    expect(store.name).toBe("myStore");
  });
  it("should observe the name change", () => {
    const store = new ProjectStore("myStore");
    const disposer = observe(store, "name", (change) => {
      expect(change.newValue).toBe("changed");
    });
    store.name = "changed";
    disposer();
  });
  it("should observe adding addition test to the store", () => {
    const store = new ProjectStore();
    const disposer = observe(store, "tests", (change) => {
      expect(change.newValue.length).toBe(1);
    });
    store.tests.push(new Test());
    disposer();
  });
  it("should add a new Test", () => {
    const store = new ProjectStore();
    expect(store.tests.length).toBe(0);
    store.addTest(new Test());
    expect(store.tests.length).toBe(1);
  });
  it("should throw if no Test was given", () => {
    const store = new ProjectStore();
    expect(() => store.addTest()).toThrowError("Expected to receive Test instead received undefined");
  });
  it("should throw if a different type was given", () => {
    const store = new ProjectStore();
    expect(() => store.addTest(1)).toThrowError("Expected to receive Test instead received Number");
  });
  it("should delete a test", () => {
    const store = new ProjectStore();
    const test = new Test();
    store.addTest(test);
    expect(store.tests.length).toBe(1);
    store.deleteTest(test);
    expect(store.tests.length).toBe(0);
  });
  it("should create a test", () => {
    const store = new ProjectStore();
    expect(store.tests.length).toBe(0);
    store.createTest();
    expect(store.tests.length).toBe(1);
  });
  it("should pass ctor args to test when created", () => {
    const store = new ProjectStore();
    const test = store.createTest("my test");
    expect(test.name).toBe("my test");
  });
  it("should create a suite", () => {
    const store = new ProjectStore();
    expect(store.suites.length).toBe(0);
    store.createSuite();
    expect(store.suites.length).toBe(1);
  });
  it("should pass ctor args to suite when created", () => {
    const store = new ProjectStore();
    const suite = store.createSuite("my suite");
    expect(suite.name).toBe("my suite");
  });
  it("should remove the deleted test from it's suites", () => {
    const store = new ProjectStore();
    const firstSuite = store.createSuite();
    const secondSuite = store.createSuite();
    const controlSuite = store.createSuite();
    const toBeDeleted = store.createTest();
    const control = store.createTest();
    store.addTest(toBeDeleted);
    store.addTest(control);
    firstSuite.addTest(toBeDeleted);
    secondSuite.addTest(toBeDeleted);
    secondSuite.addTest(control);
    controlSuite.addTest(control);
    expect(firstSuite.tests.length).toBe(1);
    expect(secondSuite.tests.length).toBe(2);
    expect(controlSuite.tests.length).toBe(1);
    store.deleteTest(toBeDeleted);
    expect(firstSuite.tests.length).toBe(0);
    expect(secondSuite.tests.length).toBe(1);
    expect(controlSuite.tests.length).toBe(1);
  });
});
<|endoftext|>


<commit_before>import binarySearch from './binary-search.js';
import compareSequences from './compare-sequences.js';

// Sort siblings next to each other, but don’t separate a (TODO: effective) TLD from the component before.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1315558
const getDomainSort = domain => {
	const components = domain.split('.');
	const suffixCount =
		components.length === 1 ? 0 :
		1;

	for (let i = 0; i < suffixCount; i++) {
		const tail = components.pop();
		components[components.length - 1] += '.' + tail;
	}

	return components.reverse();
};

// the context menu in this area of the page isn’t useful and could mislead users into thinking “Remove” means “remove this domain entry” and not “remove the add-on”
document.addEventListener('contextmenu', e => {
	e.preventDefault();
});

const associateSelectionActions = (list, buttons) => {
	const updateDisabled = () => {
		const disabled = list.selectedOptions.length === 0;

		buttons.forEach(button => {
			button.disabled = disabled;
		});
	};

	list.addEventListener('change', updateDisabled);
	updateDisabled();
};

const allowList = document.getElementById('allow');
const allowRemove = document.getElementById('allow-remove');
const allowMove = document.getElementById('allow-move');

const redirectList = document.getElementById('redirect');
const redirectRemove = document.getElementById('redirect-remove');
const redirectMove = document.getElementById('redirect-move');

associateSelectionActions(allowList, [allowRemove, allowMove]);
associateSelectionActions(redirectList, [redirectRemove, redirectMove]);

allowRemove.addEventListener('click', () => {
	port.postMessage({
		type: 'block',
		hostnames: Array.from(allowList.selectedOptions, option => option.text),
	});
});

allowMove.addEventListener('click', () => {
	port.postMessage({
		type: 'redirect',
		hostnames: Array.from(allowList.selectedOptions, option => option.text),
	});
});

redirectRemove.addEventListener('click', () => {
	port.postMessage({
		type: 'block',
		hostnames: Array.from(redirectList.selectedOptions, option => option.text),
	});
});

redirectMove.addEventListener('click', () => {
	port.postMessage({
		type: 'allow',
		hostnames: Array.from(redirectList.selectedOptions, option => option.text),
	});
});

const optionSorts = new WeakMap();
const domainOptions = new Map();

const createDomainOption = domain => {
	const option = document.createElement('option');
	option.text = domain;
	optionSorts.set(option, getDomainSort(domain));
	domainOptions.set(domain, option);
	return option;
};

const compareOptions = (a, b) =>
	compareSequences(optionSorts.get(a), optionSorts.get(b));

const removeIfExists = hostname => {
	const option = domainOptions.get(hostname);

	if (option === undefined) {
		return null;
	}

	const parent = option.parentNode;
	option.remove();
	domainOptions.delete(hostname);
	return parent;
};

const port = browser.runtime.connect({
	name: 'state',
});

port.onMessage.addListener(message => {
	switch (message.type) {
	case 'state': {
		const { redirect, allow } = message;

		allow.map(createDomainOption)
			.sort(compareOptions)
			.forEach(allowList.appendChild, allowList);

		redirect.map(createDomainOption)
			.sort(compareOptions)
			.forEach(redirectList.appendChild, redirectList);

		break;
	}

	case 'allow':
	case 'redirect':
	case 'block': {
		const { hostnames } = message;
		let allowChanged = false;
		let redirectChanged = false;

		for (const hostname of hostnames) {
			switch (removeIfExists(hostname)) {
			case allowList:
				allowChanged = true;
				break;

			case redirectList:
				redirectChanged = true;
				break;
			}
		}

		const targetList =
			message.type === 'allow' ? (allowChanged = true, allowList) :
			message.type === 'redirect' ? (redirectChanged = true, redirectList) :
			null;

		if (targetList !== null) {
			for (const hostname of hostnames) {
				const option = createDomainOption(hostname);
				const insertIndex = binarySearch(targetList.options, reference => compareOptions(option, reference));

				if (insertIndex === targetList.options.length) {
					targetList.appendChild(option);
				} else {
					targetList.options[insertIndex].before(option);
				}
			}
		}

		if (allowChanged) {
			allowList.dispatchEvent(new Event('change'));
		}

		if (redirectChanged) {
			allowList.dispatchEvent(new Event('change'));
		}

		break;
	}

	default:
		throw new Error(`Unexpected message type ${message.type}`);
	}
});

port.postMessage({
	type: 'state',
});
<commit_msg>Remove context menu suppression on preference page<commit_after>import binarySearch from './binary-search.js';
import compareSequences from './compare-sequences.js';

// Sort siblings next to each other, but don’t separate a (TODO: effective) TLD from the component before.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1315558
const getDomainSort = domain => {
	const components = domain.split('.');
	const suffixCount =
		components.length === 1 ? 0 :
		1;

	for (let i = 0; i < suffixCount; i++) {
		const tail = components.pop();
		components[components.length - 1] += '.' + tail;
	}

	return components.reverse();
};

const associateSelectionActions = (list, buttons) => {
	const updateDisabled = () => {
		const disabled = list.selectedOptions.length === 0;

		buttons.forEach(button => {
			button.disabled = disabled;
		});
	};

	list.addEventListener('change', updateDisabled);
	updateDisabled();
};

const allowList = document.getElementById('allow');
const allowRemove = document.getElementById('allow-remove');
const allowMove = document.getElementById('allow-move');

const redirectList = document.getElementById('redirect');
const redirectRemove = document.getElementById('redirect-remove');
const redirectMove = document.getElementById('redirect-move');

associateSelectionActions(allowList, [allowRemove, allowMove]);
associateSelectionActions(redirectList, [redirectRemove, redirectMove]);

allowRemove.addEventListener('click', () => {
	port.postMessage({
		type: 'block',
		hostnames: Array.from(allowList.selectedOptions, option => option.text),
	});
});

allowMove.addEventListener('click', () => {
	port.postMessage({
		type: 'redirect',
		hostnames: Array.from(allowList.selectedOptions, option => option.text),
	});
});

redirectRemove.addEventListener('click', () => {
	port.postMessage({
		type: 'block',
		hostnames: Array.from(redirectList.selectedOptions, option => option.text),
	});
});

redirectMove.addEventListener('click', () => {
	port.postMessage({
		type: 'allow',
		hostnames: Array.from(redirectList.selectedOptions, option => option.text),
	});
});

const optionSorts = new WeakMap();
const domainOptions = new Map();

const createDomainOption = domain => {
	const option = document.createElement('option');
	option.text = domain;
	optionSorts.set(option, getDomainSort(domain));
	domainOptions.set(domain, option);
	return option;
};

const compareOptions = (a, b) =>
	compareSequences(optionSorts.get(a), optionSorts.get(b));

const removeIfExists = hostname => {
	const option = domainOptions.get(hostname);

	if (option === undefined) {
		return null;
	}

	const parent = option.parentNode;
	option.remove();
	domainOptions.delete(hostname);
	return parent;
};

const port = browser.runtime.connect({
	name: 'state',
});

port.onMessage.addListener(message => {
	switch (message.type) {
	case 'state': {
		const { redirect, allow } = message;

		allow.map(createDomainOption)
			.sort(compareOptions)
			.forEach(allowList.appendChild, allowList);

		redirect.map(createDomainOption)
			.sort(compareOptions)
			.forEach(redirectList.appendChild, redirectList);

		break;
	}

	case 'allow':
	case 'redirect':
	case 'block': {
		const { hostnames } = message;
		let allowChanged = false;
		let redirectChanged = false;

		for (const hostname of hostnames) {
			switch (removeIfExists(hostname)) {
			case allowList:
				allowChanged = true;
				break;

			case redirectList:
				redirectChanged = true;
				break;
			}
		}

		const targetList =
			message.type === 'allow' ? (allowChanged = true, allowList) :
			message.type === 'redirect' ? (redirectChanged = true, redirectList) :
			null;

		if (targetList !== null) {
			for (const hostname of hostnames) {
				const option = createDomainOption(hostname);
				const insertIndex = binarySearch(targetList.options, reference => compareOptions(option, reference));

				if (insertIndex === targetList.options.length) {
					targetList.appendChild(option);
				} else {
					targetList.options[insertIndex].before(option);
				}
			}
		}

		if (allowChanged) {
			allowList.dispatchEvent(new Event('change'));
		}

		if (redirectChanged) {
			allowList.dispatchEvent(new Event('change'));
		}

		break;
	}

	default:
		throw new Error(`Unexpected message type ${message.type}`);
	}
});

port.postMessage({
	type: 'state',
});
<|endoftext|>


<commit_before>(() => {
  let gitHubTranslator = require('../translators/githubTranslator'),
    gitLabTranslator = require('../translators/gitLabTranslator');

  let TranslatorFactory = class {

      create(request) {
        if (gitHubTranslator.canTranslate(request)) {
          return gitHubTranslator;
        } else if (gitLabTranslator.canTranslate(request)) {
          return gitLabTranslator;
        } else {
          return undefined;
        }
      }

      pickTranslator(request) {
        // pick the correct translator based on what is inspected out of the request.
      }
    } // close the class

  module.exports = new TranslatorFactory();

})();<commit_msg>S-53159: this function was speculative and hasn't been used yet, so goodby<commit_after>(() => {
  let gitHubTranslator = require('../translators/githubTranslator'),
    gitLabTranslator = require('../translators/gitLabTranslator');

  let TranslatorFactory = class {

      create(request) {
        if (gitHubTranslator.canTranslate(request)) {
          return gitHubTranslator;
        } else if (gitLabTranslator.canTranslate(request)) {
          return gitLabTranslator;
        } else {
          return undefined;
        }
      }
    } // close the class

  module.exports = new TranslatorFactory();

})();<|endoftext|>


<commit_before>/**
 * 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.
 */

'use strict';

const invariant = require('invariant');

/**
 * Creates an immutable collection of constants which are meant to be passed
 * around to your various files.
 *
 * @constructor
 * @param {string...} any amount of strings to transform into constants for this
 *	group. The strings should be unique.
 */
export default class ConstantCollection {
	constructor (...names) {

		this.id = Math.random().toString(36).substring(2);
		names.forEach((name) => {
			invariant(
				this[name] === undefined,
				'The constant `%s` already exists in this collection',
				name
			);

			this[name] = new Constant(name, this);
		});

		if (Object.freeze instanceof Function) {
			Object.freeze(this);
		}
	}
}

class Constant {
	constructor (name, collection) {
		this.name = name;
		this.collection = collection;
	}
	toString() {
		return `${this.name}_#${this.collection.id}`;
	}
}
<commit_msg>removed undeeded check<commit_after>/**
 * 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.
 */

'use strict';

const invariant = require('invariant');

/**
 * Creates an immutable collection of constants which are meant to be passed
 * around to your various files.
 *
 * @constructor
 * @param {string...} any amount of strings to transform into constants for this
 *	group. The strings should be unique.
 */
export default class ConstantCollection {
	constructor (...names) {

		this.id = Math.random().toString(36).substring(2);
		names.forEach((name) => {
			invariant(
				this[name] === undefined,
				'The constant `%s` already exists in this collection',
				name
			);

			this[name] = new Constant(name, this);
		});

		Object.freeze(this);
	}
}

class Constant {
	constructor (name, collection) {
		this.name = name;
		this.collection = collection;
	}
	toString() {
		return `${this.name}_#${this.collection.id}`;
	}
}
<|endoftext|>