<commit_before>'use strict';

var height = $(window).height();
var width = $(window).width();

var game = new Phaser.Game(width, height, Phaser.AUTO, 'aws-serverless-samfarm', {
    preload: preload,
    create: create,
    update: update
});

var sprites;
var DESIRED_SAM_COUNT = 1;
var CURRENT_SAM_COUNT = 1;
var MAX_SAM_CHANGE = 10;
var LAST_CHANGE_TIME = Date.now();
var GET_SAM_COUNT_URL =  'https://1yhfbnimr1.execute-api.us-east-1.amazonaws.com/Prod/sam';
var TIME_BETWEEN_COUNT_UPDATE_MS = 4000;

function preload() {
    game.load.spritesheet('spinner', 'squirrel.png', 64, 64);
    game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
}

function create() {
    game.stage.disableVisibilityChange = true;
    game.stage.backgroundColor = '#967da7';
    sprites = game.add.physicsGroup(Phaser.Physics.ARCADE);
    createSprites(DESIRED_SAM_COUNT);
}

function createSprites(numberOfSprites) {
    for (var i = 0; i < numberOfSprites; i++) {
        var initX = game.rnd.integerInRange(100, width - 100);
        var initY = game.rnd.integerInRange(32, height - 32);
        var velX = game.rnd.integerInRange(-200, 200);
        var velY = game.rnd.integerInRange(-200, 200);

        var s = sprites.create(initX, initY, 'spinner');
        s.body.velocity.set(velX, velY);
        s.scale.setTo(1.4, 1.4);
    }

    sprites.setAll('body.collideWorldBounds', true);
    sprites.setAll('body.bounce.x', 1);
    sprites.setAll('body.bounce.y', 1);
}

function update() {
    game.physics.arcade.collide(sprites);

    var countToChange = DESIRED_SAM_COUNT - CURRENT_SAM_COUNT;

    samCountUpdate(countToChange);
}

function samCountUpdate(samCountChange) {

    var currTime = Date.now();

    // Only update the number of SAMs on the screen every second
    if (currTime - LAST_CHANGE_TIME > 1000) {

        // If we are increasing the amount of SAMs on the screen, take the min of the requested change value and
        // the max number that can be updated at a time.
        if (samCountChange > 0) {
            samCountChange = Math.min(samCountChange, MAX_SAM_CHANGE);

            createSprites(samCountChange);
            CURRENT_SAM_COUNT += samCountChange;
            LAST_CHANGE_TIME = currTime;

        } else if (samCountChange < 0) {
            // Else if we are decreasing the amount of SAMs on the screen, take the max of the negative change value and
            // the maximum that can be changed at a time
            samCountChange = Math.abs(Math.max(samCountChange, -MAX_SAM_CHANGE));

            // For each of the SAMs that are being removed, kill it's sprite
            for (var i = 0; i < samCountChange; i++) {
                sprites.children[i].kill();
            }

            // Remove killed SAMs from the array
            sprites.children.splice(0, samCountChange);

            CURRENT_SAM_COUNT -= samCountChange;
            LAST_CHANGE_TIME = currTime;
        }
    }
}

// Every X seconds as defined in the variable TIME_BETWEEN_COUNT_UPDATE_MS, call our API endpoint to tell use how many
// SAMs should be on the screen.
setInterval(() => {
    // Make frontend call to lambda
    $.get({
        url: GET_SAM_COUNT_URL
    }).done(function(data) {
        DESIRED_SAM_COUNT = data;
    });

}, TIME_BETWEEN_COUNT_UPDATE_MS);
<commit_msg>smena na pi Pro/sam ap endpoint<commit_after>'use strict';

var height = $(window).height();
var width = $(window).width();

var game = new Phaser.Game(width, height, Phaser.AUTO, 'aws-serverless-samfarm', {
    preload: preload,
    create: create,
    update: update
});

var sprites;
var DESIRED_SAM_COUNT = 1;
var CURRENT_SAM_COUNT = 1;
var MAX_SAM_CHANGE = 10 ;
var LAST_CHANGE_TIME = Date.now();
var GET_SAM_COUNT_URL =  'https://1yhfbnimr1.execute-api.us-east-1.amazonaws.com/Stage/sam';
var TIME_BETWEEN_COUNT_UPDATE_MS = 4000;

function preload() {
    game.load.spritesheet('spinner', 'squirrel.png', 64, 64);
    game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
}

function create() {
    game.stage.disableVisibilityChange = true;
    game.stage.backgroundColor = '#967da7';
    sprites = game.add.physicsGroup(Phaser.Physics.ARCADE);
    createSprites(DESIRED_SAM_COUNT);
}

function createSprites(numberOfSprites) {
    for (var i = 0; i < numberOfSprites; i++) {
        var initX = game.rnd.integerInRange(100, width - 100);
        var initY = game.rnd.integerInRange(32, height - 32);
        var velX = game.rnd.integerInRange(-200, 200);
        var velY = game.rnd.integerInRange(-200, 200);

        var s = sprites.create(initX, initY, 'spinner');
        s.body.velocity.set(velX, velY);
        s.scale.setTo(1.4, 1.4);
    }

    sprites.setAll('body.collideWorldBounds', true);
    sprites.setAll('body.bounce.x', 1);
    sprites.setAll('body.bounce.y', 1);
}

function update() {
    game.physics.arcade.collide(sprites);

    var countToChange = DESIRED_SAM_COUNT - CURRENT_SAM_COUNT;

    samCountUpdate(countToChange);
}

function samCountUpdate(samCountChange) {

    var currTime = Date.now();

    // Only update the number of SAMs on the screen every second
    if (currTime - LAST_CHANGE_TIME > 1000) {

        // If we are increasing the amount of SAMs on the screen, take the min of the requested change value and
        // the max number that can be updated at a time.
        if (samCountChange > 0) {
            samCountChange = Math.min(samCountChange, MAX_SAM_CHANGE);

            createSprites(samCountChange);
            CURRENT_SAM_COUNT += samCountChange;
            LAST_CHANGE_TIME = currTime;

        } else if (samCountChange < 0) {
            // Else if we are decreasing the amount of SAMs on the screen, take the max of the negative change value and
            // the maximum that can be changed at a time
            samCountChange = Math.abs(Math.max(samCountChange, -MAX_SAM_CHANGE));

            // For each of the SAMs that are being removed, kill it's sprite
            for (var i = 0; i < samCountChange; i++) {
                sprites.children[i].kill();
            }

            // Remove killed SAMs from the array
            sprites.children.splice(0, samCountChange);

            CURRENT_SAM_COUNT -= samCountChange;
            LAST_CHANGE_TIME = currTime;
        }
    }
}

// Every X seconds as defined in the variable TIME_BETWEEN_COUNT_UPDATE_MS, call our API endpoint to tell use how many
// SAMs should be on the screen.
setInterval(() => {
    // Make frontend call to lambda
    $.get({
        url: GET_SAM_COUNT_URL
    }).done(function(data) {
        DESIRED_SAM_COUNT = data;
    });

}, TIME_BETWEEN_COUNT_UPDATE_MS);
<|endoftext|>


<commit_before>'use strict';

var request = require('request')
  , extend = require('node.extend')
  , clean
;

/**
 * Constructor.
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 * @param  {Object}   options You can see the options to set in "defaultOptions"
 */
function Snarl(options){
  var self = this
    , options = extend(true, {}, self.defaultOptions, options)
    , server = options.server
    , port = options.port
  ;
  self.options = options;
  self.stack = [];

  if(port === 443){
    self.root  = 'https://' + server + ':' + port + '/';
  }else{
    self.root  = 'http://' + server + ':' + port + '/';
  }
  return self;
}

/**
 * Extending the prototype.
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 */
extend(Snarl.prototype, {
  /**
   * Referencing to constructor.
   * @type {function}
   */
  constructor: Snarl
  /**
   * Function to destroy an instance of Snarl (TODO).
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @return {undefined}
   */
, destroy: function(){
  }
  /**
   * Default parameters.
   * @type {Object}
   */
, defaultOptions: {
    server: 'localhost'
  , port: '8080'
  , name: 'Snarl4'
  , timeout: 3
  , title: 'Title'
  , text: 'Please specify "text" option'
  , path: {
      icon: __dirname+'\\..\\icon'
    }
  }
  /**
   * Function to register messages on an app name.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name Name of the app
   * @return {undefined}
   */
, register: function(name){
    var self = this
      , root = self.root
      , appName = name || self.options.name
      , url = root + 'register?app-sig='+appName+'&app-title='+appName
    ;
    request(url);
  }
  /**
   * Function to unregister messages of an app name.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name Name of a registered app
   * @return {undefined}
   */
, unregister: function(name){
    var self = this
      , root = self.root
      , appName = name || self.options.name
      , url = root + 'unregister?app-sig='+appName
    ;
    request(url);
  }
  /**
   * Function to check if Snarl4 server is running or not.
   * NOTE: This code have a workaround because in some external plugins, like
   * grunt-notify, there are a sync implementation. So i had to do the same
   * thing until these plugins are updated.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-03-27
   * @return {Boolean}
   */
, isAvailable: function(){
    var self = this
      , timeout = self.options.timeout * 1000
      , available
    ;
    request({
      url: self.root
    , timeout: timeout
    }, function(e){
      if(e){
        available = false;
      }else{
        available = true;
      }
    });
    while(available === undefined) {
      require('deasync').runLoopOnce();
    }
    return available;
  }
  /**
   * Function to create a notification and send it over a http request to snarl
   * server.
   * notification.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {undefined}
   */
, notify: function(name, options){
    var self = this
      , root = self.root
      , id = parseInt(new Date().toISOString().replace(/[\W\D]/g, ''))
      , url = root
      , params
    ;
    name = name || options.name;
    options = extend(true, {}, self.options, options);
    options.message = clean(options.message);
    params = Object.keys(options);
    url += 'notify?id='+id+'&app-sig='+name;
    for(var x = 0, len = params.length; x < len; ++x){
      var param = params[x];
      url += '&'+param+'='+options[param];
    }
    request(url, function(e, res){
      if (!e && res.statusCode === 200) {
        self.stack.push(id);
        setTimeout(function(){
          self.stack.shift(0);
        }, options.timeout * 1000);
      }
    });
  }
  /**
   * Same as notify function but here is setted a default icon "done".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, done: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\done.png';
    self.notify(name, options);
  }
  /**
   * Same as notify but here is setted a default icon "error".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, error: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\error.png';
    self.notify(name, options);
  }
  /**
   * Same as notify but here is setted a default icon "log".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, log: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\log.png';
    self.notify(name, options);
  }
});

/**
 * Clean message before output
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 * @private
 * @param  {String}   message Output message.
 * @return {String}
 */
clean = function(message){
  return message.replace(/\=/g, '\==').replace(/\\/g,'/').replace(/\n/gi,'\\n');
}

module.exports = new Snarl();
<commit_msg>Able to set message with 'message' option<commit_after>'use strict';

var request = require('request')
  , extend = require('node.extend')
  , clean
;

/**
 * Constructor.
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 * @param  {Object}   options You can see the options to set in "defaultOptions"
 */
function Snarl(options){
  var self = this
    , options = extend(true, {}, self.defaultOptions, options)
    , server = options.server
    , port = options.port
  ;
  self.options = options;
  self.stack = [];

  if(port === 443){
    self.root  = 'https://' + server + ':' + port + '/';
  }else{
    self.root  = 'http://' + server + ':' + port + '/';
  }
  return self;
}

/**
 * Extending the prototype.
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 */
extend(Snarl.prototype, {
  /**
   * Referencing to constructor.
   * @type {function}
   */
  constructor: Snarl
  /**
   * Function to destroy an instance of Snarl (TODO).
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @return {undefined}
   */
, destroy: function(){
  }
  /**
   * Default parameters.
   * @type {Object}
   */
, defaultOptions: {
    server: 'localhost'
  , port: '8080'
  , name: 'Snarl4'
  , timeout: 3
  , title: 'Title'
  , text: 'Please specify "text" option'
  , path: {
      icon: __dirname+'\\..\\icon'
    }
  }
  /**
   * Function to register messages on an app name.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name Name of the app
   * @return {undefined}
   */
, register: function(name){
    var self = this
      , root = self.root
      , appName = name || self.options.name
      , url = root + 'register?app-sig='+appName+'&app-title='+appName
    ;
    request(url);
  }
  /**
   * Function to unregister messages of an app name.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name Name of a registered app
   * @return {undefined}
   */
, unregister: function(name){
    var self = this
      , root = self.root
      , appName = name || self.options.name
      , url = root + 'unregister?app-sig='+appName
    ;
    request(url);
  }
  /**
   * Function to check if Snarl4 server is running or not.
   * NOTE: This code have a workaround because in some external plugins, like
   * grunt-notify, there are a sync implementation. So i had to do the same
   * thing until these plugins are updated.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-03-27
   * @return {Boolean}
   */
, isAvailable: function(){
    var self = this
      , timeout = self.options.timeout * 1000
      , available
    ;
    request({
      url: self.root
    , timeout: timeout
    }, function(e){
      if(e){
        available = false;
      }else{
        available = true;
      }
    });
    while(available === undefined) {
      require('deasync').runLoopOnce();
    }
    return available;
  }
  /**
   * Function to create a notification and send it over a http request to snarl
   * server.
   * notification.
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-21
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {undefined}
   */
, notify: function(name, options){
    var self = this
      , root = self.root
      , id = parseInt(new Date().toISOString().replace(/[\W\D]/g, ''))
      , url = root
      , params
    ;
    name = name || options.name;
    options = extend(true, {}, self.options, options);
    options.text = clean(options.message || options.text);
    params = Object.keys(options);
    url += 'notify?id='+id+'&app-sig='+name;
    for(var x = 0, len = params.length; x < len; ++x){
      var param = params[x];
      url += '&'+param+'='+options[param];
    }
    request(url, function(e, res){
      if (!e && res.statusCode === 200) {
        self.stack.push(id);
        setTimeout(function(){
          self.stack.shift(0);
        }, options.timeout * 1000);
      }
    });
  }
  /**
   * Same as notify function but here is setted a default icon "done".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, done: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\done.png';
    self.notify(name, options);
  }
  /**
   * Same as notify but here is setted a default icon "error".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, error: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\error.png';
    self.notify(name, options);
  }
  /**
   * Same as notify but here is setted a default icon "log".
   * @author Tomás Hernández <tomas.hernandez03@gmail.com>
   * @since  2015-02-22
   * @param  {String}   name    Name of the app
   * @param  {Object}   options See available options in
   * {@link https://sites.google.com/site/snarlapp/developers/api-reference|API Reference}
   * @return {Function}
   */
, log: function(name, options){
    var self = this;
    options.icon = self.options.path.icon + '\\log.png';
    self.notify(name, options);
  }
});

/**
 * Clean message before output
 * @author Tomás Hernández <tomas.hernandez03@gmail.com>
 * @since  2015-02-21
 * @private
 * @param  {String}   message Output message.
 * @return {String}
 */
clean = function(message){
  return message.replace(/\=/g, '\==').replace(/\\/g,'/').replace(/\n/gi,'\\n');
}

module.exports = new Snarl();
<|endoftext|>


<commit_before>'use strict';

angular.module('agreementsModule', []);

angular.module('agreementsModule').controller('agreementsController', ['$scope', '$state', '$log', 'AgreementsLoader', function ($scope, $state, $log, AgreementsLoader)
{
    $scope.currentPage = 1;
    $scope.pageSize    = 6;

    $scope.agreements = [ ];

    this.reload = function ()
    {
    	AgreementsLoader.load().then(function (data)
        {
            $scope.agreements = data.summaries;
        });
    };

    this.reload();

    this.examine = function (agreement)
    {
        $state.go('agreement', { "detailsurl": agreement.detailsurl });
    };

    this.examine = function (agreement)
    {
        $state.go('agreementtemplates');
    };

    this.setCurrentPage = function (currentIndex)
    {
        $scope.currentPage = currentIndex;
    };

    this.decreaseCurrentPage = function()
    {
        $scope.currentPage--;
    };

    this.increaseCurrentPage = function()
    {
        $scope.currentPage--;
    };

    this.isCurrentPage = function (currentPage)
    {
        return $scope.currentPage == currentPage;
    };

    this.needsPagination = function ()
    {
        return $scope.agreements.length > $scope.pageSize;
    };

    this.havePreviousPage = function ()
    {
        return $scope.currentPage > 1;
    };

    this.haveNextPage = function ()
    {
        return $scope.currentPage < this.numberOfPages();
    };

    this.numberOfPages = function ()
    {
        return Math.ceil($scope.agreements.length / $scope.pageSize);
    };
}]);

angular.module('agreementsModule').filter('onpage', ['$scope', function ($scope)
{
    return function (input)
    {
        return input.slice($scope.currentPage * $scope.pageSize, ($scope.currentPage + 1) * $scope.pageSize);
    }
}]);

angular.module('agreementsModule').factory('AgreementsLoader', ['$http', '$q', '$log', function ($http, $q, $log)
{
    return {
        load: function ()
        {
            var deferred = $q.defer();

            $http.get('/ws/agreements').
                success(function (data, status, headers, config)
                {
                    deferred.resolve(data);
                }).
                error(function (data, status, headers, config)
                {
                    deferred.resolve([ ]);
                });

            return deferred.promise;
        }
    };
}]);
<commit_msg>Fixed buttom<commit_after>'use strict';

angular.module('agreementsModule', []);

angular.module('agreementsModule').controller('agreementsController', ['$scope', '$state', '$log', 'AgreementsLoader', function ($scope, $state, $log, AgreementsLoader)
{
    $scope.currentPage = 1;
    $scope.pageSize    = 6;

    $scope.agreements = [ ];

    this.create = function ()
    {
        $state.go('agreementtemplates');
    };

    this.reload = function ()
    {
    	AgreementsLoader.load().then(function (data)
        {
            $scope.agreements = data.summaries;
        });
    };

    this.examine = function (agreement)
    {
        $state.go('agreement', { "detailsurl": agreement.detailsurl });
    };

    this.terminate = function (agreement)
    {
        $state.go('agreementtemplates');
    };

    this.setCurrentPage = function (currentIndex)
    {
        $scope.currentPage = currentIndex;
    };

    this.decreaseCurrentPage = function()
    {
        $scope.currentPage--;
    };

    this.increaseCurrentPage = function()
    {
        $scope.currentPage--;
    };

    this.isCurrentPage = function (currentPage)
    {
        return $scope.currentPage == currentPage;
    };

    this.needsPagination = function ()
    {
        return $scope.agreements.length > $scope.pageSize;
    };

    this.havePreviousPage = function ()
    {
        return $scope.currentPage > 1;
    };

    this.haveNextPage = function ()
    {
        return $scope.currentPage < this.numberOfPages();
    };

    this.numberOfPages = function ()
    {
        return Math.ceil($scope.agreements.length / $scope.pageSize);
    };

    this.reload();
}]);

angular.module('agreementsModule').filter('onpage', ['$scope', function ($scope)
{
    return function (input)
    {
        return input.slice($scope.currentPage * $scope.pageSize, ($scope.currentPage + 1) * $scope.pageSize);
    }
}]);

angular.module('agreementsModule').factory('AgreementsLoader', ['$http', '$q', '$log', function ($http, $q, $log)
{
    return {
        load: function ()
        {
            var deferred = $q.defer();

            $http.get('/ws/agreements').
                success(function (data, status, headers, config)
                {
                    deferred.resolve(data);
                }).
                error(function (data, status, headers, config)
                {
                    deferred.resolve([ ]);
                });

            return deferred.promise;
        }
    };
}]);
<|endoftext|>


<commit_before>// Design Basic Game Solo Challenge

// This is a solo challenge

// Your mission description:
// Overall mission: This is a version of the card game 'war' The mission is to collect all 52 cards in a standard playing deck.
// Goals: win as many battles and wars as you can (of course it is all random so ....)
// Characters: Player 1 and Player 2
// Objects: Player 1's cards, Player 2's cards, Temporary war cards
// Functions: shuffle, deal, play, war,

// Pseudocode
// 1- create a 4 series of 2-14.
// 2- shuffle the four arrays
// 3- randomly give half to player 1 and half to player 2
// 4- each player plays one card at a time and the player with the higher card adds cards to his pile
//    -if the cards are equal, add (up to) three cards each (assuming a player has at least 4 cards) to the temporary war pile
//    -repeat step 4. player with higher card gets all played cards and all cards in temp war pile
// 5- repeat step 4 until one player has all 52 cards.
// 6- that player wins

// Initial Code
// simple version is an array with 4 each of the numbers from 2-14 => [2,2,2,2,3,3,3,3] etc. a much more complicated version has 4 arrays each as properties of names in an object
// var deck = {
// 	Spades:   [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Hearts:   [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Diamonds: [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Clubs:    [2,3,4,5,6,7,8,9,10,11,12,13,14],

// };

// function numberAtRandom(min, max) {
//   return Math.floor(Math.random() * (max - min)) + min;
// };

// var cards = [2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14]
// var battleCards = []
// var warPile =[]
// var shuffledDeck = []

// var player1 = {pile: []};
// var player2 = {pile: []};

// var shuffle = function(deck) {
// 	while (deck.length > 0){
// 		shuffledDeck.push(deck.splice(numberAtRandom(0, deck.length),1))
//   };
//   for (var i = 0; i < shuffledDeck.length ; i++ ) {
// shuffledDeck[i] = shuffledDeck[i][0];
// }
// }

//   shuffle(cards);
// var deal = function(){
// //console.log(shuffledDeck);
// player1.pile.push(shuffledDeck.splice(0,26));
// player2.pile.push(shuffledDeck.splice(0,26));
// };
// // var pick = function(){

//  deal();

// var play = function(){

// 	battleCards = battleCards.push( player1.pile[0].shift(), player2.pile[0].shift() );

//  	console.log(battleCards);

 // 	var card1 =
// 		var card2 =
// 			if (player1.pile[0].length === 0)
// 				return	console.log("player1 is defeated. player2 Wins");
// 			else if (player2.pile[0].length === 0)
// 				return	console.log("player2 is defeated. player1 Wins");
// 			else
//  		while ((player1.pile[0].length > 0) && (player2.pile[0].length > 0)){

// 			 if (battleCards[0][0] > battleCards[0][1]
// 				player1.pile[0][0].push(battleCards.splice(0,2));
// 				console.log("player1 wins this round");
// 			else if ((battleCards[0][0] < battleCards[0][1])
// 				player2.pile[0][0].push(battleCards.splice(0,2));
// 				console.log("player1 wins this round");
// // 			else if (card1 == card2)
// // 				console.log("Try Again");
// // // 					warPile.push(battleCards.splice(0,2))
// // // 					warPile.push(player1.pile[0].splice(0,3), player2.pile[0].splice(0,3));
//  			else
//  				console.log("What's going on here?");

//-
// function Player(name, pile){
// this.name = name
// this.pile = pile
// };

// var lee = new Player("Lee")
// var grant = new Player("Grant")

// play("Lee", "Grant"); This was when I thought I would use constructors to create a class of player and then actually create players.
// -

// Refactored Code (This is my MVP really, because it tooook me so long just to get it here and I changed around so many of the variabls and the structure of the functions and how they are called. THere is obviously much work to be done. not at all DRY. I literally pop 6 times in a row. because splice is too annoying because it nests the array, so then I would have to flatten it all over again.)
// As of right now the game isn't actually finished. as it runs now it is only 1 hand at a time and it doesn't remember previous hands.
function numberAtRandom(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}

var cards = [2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14]

//for (var i = 1; cards.length > 0;}
var battleCards = []
var warPile =[]
var shuffledDeck = []

var player1 = {pile: []};
var player2 = {pile: []};

var shuffle = function(deck) {
	while (deck.length > 0){
		shuffledDeck.push(deck.splice(numberAtRandom(0, deck.length),1));
  };
  		for (var i = 0; i < shuffledDeck.length ; i++ ) {
			shuffledDeck[i] = shuffledDeck[i][0];
};
}
// console.log(cards.length); //Test
// console.log(shuffledDeck.length); //Test

var deal = function(){

player1.pile.push(shuffledDeck.splice(0,26));
player2.pile.push(shuffledDeck.splice(0,26));
};

var battle = function(){

	battleCards.push(player1.pile[0].shift(), player2.pile[0].shift());
	console.log(battleCards);
  if (battleCards[0] > battleCards[1]) {
		player1.pile[0] = player1.pile[0].concat(battleCards).concat(warPile);
		battleCards = []
		warPile = []
		console.log("player1 wins this round");
		console.log("player1 has " + player1.pile[0].length + " in his pile");
  			 if (player2.pile[0].length === 0)
  				return	console.log("player2 is defeated. player1 Wins");

 }
else if (battleCards[0] < battleCards[1]) {
		player2.pile[0] = player2.pile[0].concat(battleCards).concat(warPile);
		battleCards = []
		warPile = []
		console.log("player2 wins this round");
		console.log("player2 has " + player2.pile[0].length + " in his pile");
			if (player1.pile[0].length === 0)
				return	console.log("player1 is defeated. player2 Wins");
    }

    else{
    		war();
    }
}
var war = function(){
 			console.log("WAAAAAAAAAAAAAAARRRRRR!!!!!!!!");
 			warPile.push(battleCards.pop(),battleCards.pop(), player1.pile[0].shift(), player1.pile[0].shift(), player1.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift());
 			console.log(warPile);
 			battle();

}

var play = function(){

shuffle(cards);
deal();
battle();
}

play();
//
//
// console.log(player1.pile[0].length); // Test
// console.log(player2.pile[0].length); // Test
// console.log(player1.pile[0]); // Test
// console.log(player2.pile[0]); // Test

// // // Reflection

// What was the most difficult part of this challenge?
//      - Finding built in methods. I just don't get how to read the docs/find stuff in the docs. I feel like I could have done this game in Ruby in like an hour because there are so many amazing built in methods that do work that I had to do myself here which, then added more work because of formatting. Like I could have just done shuffle, here I couldnt. and even after splicing , the unwanted extra level deep array disappears with flatten. no flatten...
//		- Syntax. I was constantly forgtting a closing ),}, or ; - and if I would change the code, or comment out a function or while or if..else, then there would be a hanging } way down in the code that I forgot to get rid of as well
//		- Here is a biggie - forgetting empty parens after functions without parameters. sometimes .shift,pop etc I would just write the word without the paren. took me a loooong time to find it. Then again If I was more familiar with the error raising in JS it would be obvious(if I was remembering to print every line as a test), because it printed "Function: Shift"  soooo hindsight ...

// What did you learn about creating objects and functions that interact with one another?
// 		-
// Did you learn about any new built-in methods you could use in your refactored solution? If so, what were they and how do they work?
//		-Well I saw some that I thought could work like forEach. but I did not figure out how it worked.
// How can you access and manipulate properties of objects?
//		-type the name of the array followed by a dot and the name of the property and from there you can do whatevery you would do the value in the same way you would normally act upon that ype of value. if it is a string, number, or array, you can treat it as you would normally treat those types of values. if it is an array you access it with brackets. if you want to reassign it you can just do object.property = ------
//       you can call whatever function you would normally call on that ype of value like object.property.toString() if ex. it is a number that you want to convert to a string, no different than Number.toString()

<commit_msg>lightly refactor game<commit_after>// Design Basic Game Solo Challenge

// This is a solo challenge

// Your mission description:
// Overall mission: This is a version of the card game 'war' The mission is to collect all 52 cards in a standard playing deck.
// Goals: win as many battles and wars as you can (of course it is all random so ....)
// Characters: Player 1 and Player 2
// Objects: Player 1's cards, Player 2's cards, Temporary war cards
// Functions: shuffle, deal, play, war,

// Pseudocode
// 1- create a 4 series of 2-14.
// 2- shuffle the four arrays
// 3- randomly give half to player 1 and half to player 2
// 4- each player plays one card at a time and the player with the higher card adds cards to his pile
//    -if the cards are equal, add (up to) three cards each (assuming a player has at least 4 cards) to the temporary war pile
//    -repeat step 4. player with higher card gets all played cards and all cards in temp war pile
// 5- repeat step 4 until one player has all 52 cards.
// 6- that player wins

// Initial Code
// simple version is an array with 4 each of the numbers from 2-14 => [2,2,2,2,3,3,3,3] etc. a much more complicated version has 4 arrays each as properties of names in an object
// var deck = {
// 	Spades:   [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Hearts:   [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Diamonds: [2,3,4,5,6,7,8,9,10,11,12,13,14],
// 	Clubs:    [2,3,4,5,6,7,8,9,10,11,12,13,14],

// };

// function numberAtRandom(min, max) {
//   return Math.floor(Math.random() * (max - min)) + min;
// };

// // var cards = [2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14]
// // var battleCards = []
// // var warPile =[]
// // var shuffledDeck = []

// // var player1 = {pile: []};
// // var player2 = {pile: []};

// // var shuffle = function(deck) {
// // 	while (deck.length > 0){
// // 		shuffledDeck.push(deck.splice(numberAtRandom(0, deck.length),1))
// //   };
// //   for (var i = 0; i < shuffledDeck.length ; i++ ) {
// // shuffledDeck[i] = shuffledDeck[i][0];
// // }
// // }

// //   shuffle(cards);
// // var deal = function(){
// // //console.log(shuffledDeck);
// // player1.pile.push(shuffledDeck.splice(0,26));
// // player2.pile.push(shuffledDeck.splice(0,26));
// // };
// // // var pick = function(){

// //  deal();

// // var play = function(){

// // 	battleCards = battleCards.push( player1.pile[0].shift(), player2.pile[0].shift() );

// //  	console.log(battleCards);

//  // 	var card1 =
// // 		var card2 =
// // 			if (player1.pile[0].length === 0)
// // 				return	console.log("player1 is defeated. player2 Wins");
// // 			else if (player2.pile[0].length === 0)
// // 				return	console.log("player2 is defeated. player1 Wins");
// // 			else
// //  		while ((player1.pile[0].length > 0) && (player2.pile[0].length > 0)){

// // 			 if (battleCards[0][0] > battleCards[0][1]
// // 				player1.pile[0][0].push(battleCards.splice(0,2));
// // 				console.log("player1 wins this round");
// // 			else if ((battleCards[0][0] < battleCards[0][1])
// // 				player2.pile[0][0].push(battleCards.splice(0,2));
// // 				console.log("player1 wins this round");
// // // 			else if (card1 == card2)
// // // 				console.log("Try Again");
// // // // 					warPile.push(battleCards.splice(0,2))
// // // // 					warPile.push(player1.pile[0].splice(0,3), player2.pile[0].splice(0,3));
// //  			else
// //  				console.log("What's going on here?");

// //-
// // function Player(name, pile){
// // this.name = name
// // this.pile = pile
// // };

// // var lee = new Player("Lee")
// // var grant = new Player("Grant")

// // play("Lee", "Grant"); This was when I thought I would use constructors to create a class of player and then actually create players.
// // -

// // Refactored Code (This is my MVP really, because it tooook me so long just to get it here and I changed around so many of the variabls and the structure of the functions and how they are called. THere is obviously much work to be done. not at all DRY. I literally pop 6 times in a row. because splice is too annoying because it nests the array, so then I would have to flatten it all over again.)
// // As of right now the game isn't actually finished. as it runs now it is only 1 hand at a time and it doesn't remember previous hands.
// function numberAtRandom(min, max) {
//   return Math.floor(Math.random() * (max - min)) + min;
// }

// var cards = [2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14]

// //for (var i = 1; cards.length > 0;}
// var battleCards = [];
// var warPile =[];
// var shuffledDeck = [];
// var player1BattleCount = 0;
// var player2BattleCount = 0;
// var battleCount = 0;
// var warCount = 0;

// var player1 = {pile: []};
// var player2 = {pile: []};
// var victory = ""

// var shuffle = function(deck) {
// 	while (deck.length > 0){
// 		shuffledDeck.push(deck.splice(numberAtRandom(0, deck.length),1));
//   };
//   		for (var i = 0; i < shuffledDeck.length ; i++ ) {
// 			shuffledDeck[i] = shuffledDeck[i][0];
// };
// }
// // console.log(cards.length); //Test
// // console.log(shuffledDeck.length); //Test

// var deal = function(){

// player1.pile.push(shuffledDeck.splice(0,26));
// player2.pile.push(shuffledDeck.splice(0,26));
// };

//  var battleReport = function(){
//  	if (player2.pile[0].length === 0)
//   				return	console.log("player2 is defeated. player1 Wins. \n There were " + battleCount + "battles and " + warCount + "wars. \n player1 won " + player1BattleCount + " battles. player2 won " + player2BattleCount + "battles.");
// 	if (player1.pile[0].length === 0)
// 				return	console.log("player1 is defeated. player2 Wins. \n There were " + battleCount + "battles and " + warCount + "wars. \n player1 won " + player1BattleCount + " battles. player2 won " + player2BattleCount + "battles.");
//    }

// var battle = function(){
// 	while ((player1.pile[0].length > 0) && (player2.pile[0].length > 0)){
// 			if (battleCount > 5000 && player1.pile[0].length === 26)
// 				break
// 	battleCards.push(player1.pile[0].shift(), player2.pile[0].shift());
// 	console.log(battleCards);
//   if (battleCards[0] > battleCards[1]) {
// 		player1.pile[0] = player1.pile[0].concat(battleCards).concat(warPile);
// 		battleCards = []
// 		warPile = []
// 		console.log("player1 wins this round");
// 		player1BattleCount += 1
// 		battleCount += 1
// 		console.log("player1 has " + player1.pile[0].length + " in his pile");
// 		battleReport();

//  }
// else if (battleCards[0] < battleCards[1]) {
// 		player2.pile[0] = player2.pile[0].concat(battleCards).concat(warPile);
// 		battleCards = []
// 		warPile = []
// 		player2BattleCount += 1
// 		battleCount += 1
// 		console.log("player2 wins this round");
// 		console.log("player2 has " + player2.pile[0].length + " in his pile");
// 		battleReport();
// 	}

//     else{
//     		war();
//     }
// }
// }
// var war = function(){
//  			console.log("WAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARRRRRR!!!!!!!!");
//  			warCount += 1
//  			warPile.push(battleCards.pop(),battleCards.pop(), player1.pile[0].shift(), player1.pile[0].shift(), player1.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift());
//  			console.log(warPile);
//  			battleReport();
//  			battle();

// }

// var play = function(){

// shuffle(cards);
// deal();
// battle();
// }

// play();
// //
//
// console.log(player1.pile[0].length); // Test
// console.log(player2.pile[0].length); // Test
// console.log(player1.pile[0]); // Test
// console.log(player2.pile[0]); // Test

//Refactored

function numberAtRandom(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}

var cards = [2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,12,13,14]

//for (var i = 1; cards.length > 0;}
var battleCards = [];
var warPile =[];
var shuffledDeck = [];
var player1BattleCount = 0;
var player2BattleCount = 0;
var battleCount = 0;
var warCount = 0;

var player1 = {pile: []};
var player2 = {pile: []};
//var victory = ""

var shuffle = function(deck) {
	while (deck.length > 0){
		shuffledDeck.push(deck.splice(numberAtRandom(0, deck.length),1));
  };
  		for (var i = 0; i < shuffledDeck.length ; i++ ) {
			shuffledDeck[i] = shuffledDeck[i][0];
};
}
// console.log(cards.length); //Test
// console.log(shuffledDeck.length); //Test

var deal = function(){

player1.pile.push(shuffledDeck.splice(0,26));
player2.pile.push(shuffledDeck.splice(0,26));
};

var battleReport = function(){
 	if (player2.pile[0].length === 0)
  				return	console.log("player2 is defeated. player1 Wins. \n There were " + battleCount + " battles and " + warCount + " wars. \n player1 won " + player1BattleCount + " battles. player2 won " + player2BattleCount + "battles.");
	if (player1.pile[0].length === 0)
				return	console.log("player1 is defeated. player2 Wins. \n There were " + battleCount + " battles and " + warCount + " wars. \n player1 won " + player1BattleCount + " battles. player2 won " + player2BattleCount + "battles.");
   }

var nextRound = function(){
		battleCards = [];
		warPile = [];
		battleCount += 1;
		battleReport();
}

var battle = function(){
	while ((player1.pile[0].length > 0) && (player2.pile[0].length > 0)){
			if (battleCount > 5000 && player1.pile[0].length === 26)
				break

	  battleCards.push(player1.pile[0].shift(), player2.pile[0].shift());
	  console.log(battleCards);

		  	if 		(battleCards[0] > battleCards[1]) {
					player1.pile[0] = player1.pile[0].concat(battleCards).concat(warPile);
					player1BattleCount += 1
					console.log("player1 wins this round");
					console.log("player1 has " + player1.pile[0].length + " in his pile");
  			 		nextRound();
 			}
			else if (battleCards[0] < battleCards[1]) {
					player2.pile[0] = player2.pile[0].concat(battleCards).concat(warPile);
					player2BattleCount += 1
					console.log("player2 wins this round");
					console.log("player2 has " + player2.pile[0].length + " in his pile");
					nextRound();
			}

    		else{
    				war();
    		}
}
}
var war = function(){
 			console.log("WAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARRRRRR!!!!!!!!");
 			warCount += 1
 			warPile.push(battleCards.pop(),battleCards.pop(), player1.pile[0].shift(), player1.pile[0].shift(), player1.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift(), player2.pile[0].shift());
 			console.log(warPile);
 			battleReport();
 			battle();

}

var play = function(){

shuffle(cards);
deal();
battle();
}

play();
//
//
// console.log(player1.pile[0].length); // Test
// console.log(player2.pile[0].length); // Test
// console.log(player1.pile[0]); // Test
// console.log(player2.pile[0]); // Test

// // // Reflection

// I tried to set time delays for each battle and war but couldnt get it yet. otherwise the whole program just runs in a second.
//Sometimes the cards get shuffled randomly so that the battles are traded back and forth forever. So I instituted a break for that.

// There is so much refactoring I could do. But I it took me so many hours just to get to this point. Got to be enough for now.
// As mentioned above with all the pushing and popping splicing and flattening. There must be a better way.
// Also creating a variable for the pile so I don't have to type out player.pile[0] and that.length so many times. lotttts to do.
//
//

// What was the most difficult part of this challenge?
//      - Finding built in methods. I just don't get how to read the docs/find stuff in the docs. I feel like I could have done this game in Ruby in like an hour because there are so many amazing built in methods that do work that I had to do myself here which, then added more work because of formatting. Like I could have just done shuffle, here I couldnt. and even after splicing , the unwanted extra level deep array disappears with flatten. no flatten...
//		- Syntax. I was constantly forgtting a closing ),}, or ; - and if I would change the code, or comment out a function or while or if..else, then there would be a hanging } way down in the code that I forgot to get rid of as well
//		- Here is a biggie - forgetting empty parens after functions that don't take arguments. sometimes .shift,pop etc I would just write the word without the paren. took me a loooong time to find it. Then again If I was more familiar with the error raising in JS it would be obvious(if I was remembering to print every line as a test), because it printed "Function: Shift"  soooo hindsight ...

// What did you learn about creating objects and functions that interact with one another?
// 		- hmm. a lot, but hard to say. I was having issues with loops. So I thought of maybe defining variables inside functions instead of having that huge list of variables declared at the top. Which I think would have necessitated passing in each function to the next function. Opted not to try it.
//	      due to the long time it took me to get around the fact that splice creates a further level of nested arrays I had a lot of issues with accessing the level I wanted of the palyers cards. Probably I was tired and not focusing on the many different parts of the code. But I could have sworn that I would use a single bracket, but then get an error. so change it to a double bracket. but then get an error. just had to cut paste, repaste, and eventually somehow it clicked and I had it right. Helped that I finally figured out how to flatten the array. Although my solution only works with subarrays of 1 item each. if any of the subarrays were longer I wouldnt get the linear flat array I need.
// Did you learn about any new built-in methods you could use in your refactored solution? If so, what were they and how do they work?
//		-Well I saw some that I thought could work like forEach. but I did not figure out how it worked.
// How can you access and manipulate properties of objects?
//		-type the name of the array followed by a dot and the name of the property and from there you can do whatevery you would do the value in the same way you would normally act upon that ype of value. if it is a string, number, or array, you can treat it as you would normally treat those types of values. if it is an array you access it with brackets. if you want to reassign it you can just do object.property = ------
//       you can call whatever function you would normally call on that ype of value like object.property.toString() if ex. it is a number that you want to convert to a string, no different than Number.toString()

<|endoftext|>


<commit_before>//arrays helpers
function shuffle(array) {
    var m = array.length, t, i;
    while (m) {
        i = Math.floor(Math.random() * m--);
        t = array[m];
        array[m] = array[i];
        array[i] = t;
    }
    return array;
}

function random(min, max){
    return Math.floor(Math.random() * (max - min)) + min;
}

function randomElement(arr){
    return arr[random(0, arr.length)];
}

//params parser
function parseParams(){
    var params = {};
    if (location.search) {
        var parts = location.search.substring(1).split('&');

        for (var i = 0; i < parts.length; i++) {
            var nv = parts[i].split('=');
            if (!nv[0]) continue;
            params[nv[0]] = nv[1] || true;
        }
    }
    return params;
}

//moment.js helpers
function toUTC(date){
    return date ? date.utc() : null;
}

function format(date){
    return date ? date.format("MM/DD/YYYY hh:mm A") : null;
}

function multiply(time, n){
    return time.add(time.millisecond() * n, 'milliseconds')
}

//elements helpers
function collapsible(name, action){
    $(name).collapse(action);
}

function removeTags(name){
    $(name).tagsinput('removeAll');
}

function addTags(name, tags){
    $(name).tagsinput('add', tags);
}
function setTags(name, tags){
    removeTags(name);
    addTags(name, tags);
}

//Knockout
var randogramViewModel = new function RandogramViewModel() {
    var self = this;
    /////////////////////////////////////////////////////accordion//////////////////////////////////////////////////////
    self.basicCollapsibleHandler = function(){
        collapsible('#basicCollapsible', 'show');
        collapsible('#advancedCollapsible', 'hide');
    };
    self.advancedCollapsibleHandler = function(){
        collapsible('#basicCollapsible', 'hide');
        collapsible('#advancedCollapsible', 'show');
    };
    /////////////////////////////////////////////////////tags///////////////////////////////////////////////////////////
    //tags
    self.tags = ko.observable("griddynamics");

    //tags helpers
    self.areTagsInvalid = ko.computed (function () {
        return self.tags().length == 0;
    });

    //tagsinput fields handler (so bad!)
    self.tagsFix = ko.observable(true);
    self.checkedFix = ko.observable(0);
    self.tagsHandler = ko.pureComputed({
        read: function () {
            return self.tags();
        },
        write: function (value) {
            self.tags(value);

            //мне очень стыдно за этот костыль
            //защита от срабатывания при загрузке страницы
            if(self.checkedFix() < 2){
                self.checkedFix(self.checkedFix()+1);
                return;
            }
            //защита от зацикливания
            if(self.tagsFix()){
                self.tagsFix(false);
                setTags('#tags1', self.tags());
                setTags('#tags2', self.tags());
                setTimeout(function(){self.tagsFix(true)}, 100);
            }
        },
        owner: this
    });

    /////////////////////////////////////////////////////dates//////////////////////////////////////////////////////////
    //date filter type
    self.dateFilterSelected = ko.observable("Today");
    self.dateFilterPresetsBasic = [
        { statusName: "Today" },
        { statusName: "Specify day" },
        { statusName: "All time" }
    ];
    self.dateFilterPresetsAdvanced = [
        { statusName: "Today" },
        { statusName: "Last week" },
        { statusName: "Last month" },
        { statusName: "All time" },
        { statusName: "Specify day" },
        { statusName: "Specify interval" }
    ];
    self.dateFilterClickHandler = function(){
        self.dateFilterSelected(this.statusName);
        switch(self.dateFilterSelected()){
            case "Specify day":
                collapsible('#basicDateFrom-collapsible', 'show');
                collapsible('#advancedDateFrom-collapsible', 'show');
                collapsible('#advancedDateTo-collapsible', 'hide');
                break;
            case "Specify interval":
                collapsible('#advancedDateFrom-collapsible', 'show');
                collapsible('#advancedDateTo-collapsible', 'show');
                break;
            default:
                collapsible('#basicDateFrom-collapsible', 'hide');
                collapsible('#advancedDateFrom-collapsible', 'hide');
                collapsible('#advancedDateTo-collapsible', 'hide');
        }
    };

    //date fields
    self.dateFrom = ko.observable(moment().format("MM/DD/YYYY"));
    self.dateTo = ko.observable(moment().format("MM/DD/YYYY"));

    self.calculateDateFrom = function(){
        switch(self.dateFilterSelected()){
            case "Today":
                return moment().startOf('day');
            case "Last week":
                return moment().startOf('week');
            case "Last month":
                return moment().startOf('month');
            case "Specify day":
            case "Specify interval":
                return moment(self.dateFrom() ? self.dateFrom() : moment()).startOf('day');
            default:
                return null;
        }
    };
    self.calculateDateTo = function(){
        switch(self.dateFilterSelected()){
            case "Specify day":
                return moment(self.dateFrom() ? self.dateFrom() : moment()).endOf('day');
            case "Specify interval":
                return moment(self.dateTo() ? self.dateTo() : moment()).endOf('day');
            case "All time":
                return null;
            default:
                return moment();
        }
    };

    //unixTimestamp -> "... ago"
    self.getMoment = function(unixTime){
        return moment(unixTime, "X").fromNow();
    };

    /////////////////////////////////////////////////////observables////////////////////////////////////////////////////
    //arrays
    self.usersAndPosts = ko.observableArray([]);
    self.images = ko.observableArray([]);
    self.tempImages = ko.observableArray([]);
    self.preview = ko.observableArray();

    //statements
    self.isSearchDone = ko.observable(false);
    self.isLoading = ko.observable(false);
    self.isFollowing = ko.observable(false);

    self.isLuckyButtonEnabled = ko.computed (function () {
        return !self.isLoading() && self.images().length>0;
    });

    self.isSearchButtonEnabled = ko.computed (function () {
        return !self.areTagsInvalid() && !self.isLoading();
    });

    /////////////////////////////////////////////////////arrays helpers/////////////////////////////////////////////////
    self.setPreview = function() {
        if (self.isLoading()) {
            return;
        }
        self.preview([this]);
    };

    var setUsersAndPosts = function(){
        var users = new Set(self.images().map(function(image){return image.user.username}));
        var usersArray = Array.from(users);

        var usersPairs = usersArray.map(function(x){return {username: x, posts: []}});
        usersPairs.forEach(function(pair){
            pair.posts = self.images().filter(function(image){return image.user.username == pair.username;});
        });

        self.usersAndPosts(usersPairs);
    };

    /////////////////////////////////////////////////////winners////////////////////////////////////////////////////////

    self.winners = ko.observableArray([]);
    self.winnersSelectionType = ko.observable("posts");
    self.winnersAmount = ko.observable(1);
    self.winnersAmountMax = function(){
        return self.winnersSelectionType() === "users" ? self.usersAndPosts().length : self.images().length;
    };

    self.winnersAmountHandler = ko.pureComputed({
        read: function () {
            return self.winnersAmount();
        },
        write: function (value) {
            if(value > self.winnersAmountMax()){
                self.winnersAmount(self.winnersAmountMax());
            }else{
                self.winnersAmount(value);
            }
        },
        owner: this
    });
    //winnersAmount +/- buttons
    self.winnersAmountPlus = function () {
        if(self.winnersAmount() < self.winnersAmountMax()){
            self.winnersAmount(self.winnersAmount()+1);
        }else if(self.winnersAmountMax() != 0){
            self.winnersAmount(self.winnersAmountMax());
        }
    };
    self.winnersAmountMinus = function () {
        if(self.winnersAmount() > 1){
            self.winnersAmount(self.winnersAmount()-1);
        }else{
            self.winnersAmount(1);
        }
    };
    self.searchInfo = ko.computed (function () {
        if(self.images().length == 0){
            return "Sorry, we didn't find anything. Check your query."
        }
        return "We found " + self.images().length + " posts from " + self.usersAndPosts().length + " users!";
    });

    /////////////////////////////////////////////////////actions////////////////////////////////////////////////////////
    //progressbar time
    self.startTime = ko.observable(moment());
    self.endTime = ko.observable(moment());
    self.progress = function(){
        if(!self.isLoading()){return 1}
        var timeBetweenStartAndEnd = (self.endTime().toDate() - self.startTime().toDate());
        var timeBetweenStartAndToday = (moment().toDate() - self.startTime().toDate());
        var p = timeBetweenStartAndToday / timeBetweenStartAndEnd;
        return p;
    };
    //search
    self.searchAction = function () {
        self.images([]);
        self.tempImages([]);
        self.isLoading(true);

        collapsible('#basicCollapsible', 'hide');
        collapsible('#advancedCollapsible', 'hide');
        collapsible('#chooseLucky-collapsible', 'hide');
        collapsible('#search-info-collapsible', 'hide');
        collapsible('#winners-content-collapsible', 'hide');
        collapsible('#content-collapsible', 'hide');

        //get tag info
        var willBeLoaded = 100;
        $.ajax({
            url: "getTagAmount",
            data: {
                tags: self.tags()
            },
            dataType: "json",
            success: function (data) {
                willBeLoaded = data;
                if(data > 1000){
                    alert("Only first 1000 of "+data+" photos will be loaded");
                    willBeLoaded = 1000;
                }
                //get first 40 photos
                $.ajax({
                    url: "getFirstImages",
                    data: {
                        accesToken: self.accesToken(),
                        tags: self.tags(),
                        amount: 2
                    },
                    dataType: "json",
                    beforeSend: function(){
                        self.startTime(moment());
                    },
                    success: function (data) {
                        var m = moment();
                        var interval = m.diff(self.startTime(), 'milliseconds');
                        //magic numbers!
                        self.endTime(m.add(((interval/70.0)*(willBeLoaded-data.length))^1.2), 'milliseconds');

                        //slideshow
                        collapsible('#loading-content-collapsible', 'show');
                        var i = 0;
                        var timerId = setInterval(function() {
                            self.tempImages([data[i++]]);
                            if(i >= data.length){
                                i = 0;
                            }
                            if(!self.isLoading()){
                                clearInterval(timerId);
                            }
                        }, 4000);
                        collapsible('#loading-content-collapsible', 'hide');
                    },
                    error: function () {
                        alert("getFirstImages error");
                        self.isLoading(false);
                    }
                });
            },
            error: function () {
                alert("getTagAmount error");
            }
        });

        //get images
        $.ajax({
            url: "getImages",
            data: {
                accesToken: self.accesToken(),
                tags: self.tags(),
                following: self.isFollowing(),
                dateTimeFrom: format(toUTC(self.calculateDateFrom())),
                dateTimeTo: format(toUTC(self.calculateDateTo()))
            },
            dataType: "json",
            success: function (data) {
                self.images(data);
                setUsersAndPosts();

                self.isLoading(false);
                self.isSearchDone(true);
                self.winnersAmount(1);

                collapsible('#loading-content-collapsible', 'hide');
                if(self.images().length > 0){
                    collapsible('#chooseLucky-collapsible', 'show');
                    collapsible('#content-collapsible', 'show');
                }
                collapsible('#search-info-collapsible', 'show');
            },
            error: function () {
                alert("getImages error");
                self.isLoading(false);
            },
            timeout: 200000
        });
    };

    //select winners action
    self.selectAction = function () {
        if(self.winnersSelectionType() === "users"){
            var luckyUsers = shuffle(self.usersAndPosts()).slice(0, self.winnersAmount());
            var winnerPosts = luckyUsers.map(function(x){return randomElement(x.posts)});
            self.winners(winnerPosts);
        }else{
            var luckyPosts = shuffle(self.images()).slice(0, self.winnersAmount());
            self.winners(luckyPosts);
        }
        collapsible('#winners-content-collapsible', 'show');
    };

    //acces token
    self.accesToken = ko.observable(null);
    self.handleToken = ko.computed (function(){
        var params = parseParams();

        if(!params.accesToken){
            window.location = "/";
        }else{
            self.accesToken(params.accesToken);
        }
    });
};

ko.applyBindings(randogramViewModel);

//Activate datetimepickers and make them linked to each other
//$('#dateTimeFrom').datetimepicker();
$('#basicDateFrom').datetimepicker();
$('#advancedDateFrom').datetimepicker();
$('#advancedDateTo').datetimepicker();

Ladda.bind( '#srch-btn', {
    callback: function( instance ) {
        var progress = 0;
        var interval = setInterval( function() {
            progress = randogramViewModel.progress();
            instance.setProgress( progress );
            if( progress === 1 ) {
                instance.stop();
                clearInterval( interval );
            }
        }, 200 );
    }
} );<commit_msg>fix loading slideshow<commit_after>//arrays helpers
function shuffle(array) {
    var m = array.length, t, i;
    while (m) {
        i = Math.floor(Math.random() * m--);
        t = array[m];
        array[m] = array[i];
        array[i] = t;
    }
    return array;
}

function random(min, max){
    return Math.floor(Math.random() * (max - min)) + min;
}

function randomElement(arr){
    return arr[random(0, arr.length)];
}

//params parser
function parseParams(){
    var params = {};
    if (location.search) {
        var parts = location.search.substring(1).split('&');

        for (var i = 0; i < parts.length; i++) {
            var nv = parts[i].split('=');
            if (!nv[0]) continue;
            params[nv[0]] = nv[1] || true;
        }
    }
    return params;
}

//moment.js helpers
function toUTC(date){
    return date ? date.utc() : null;
}

function format(date){
    return date ? date.format("MM/DD/YYYY hh:mm A") : null;
}

function multiply(time, n){
    return time.add(time.millisecond() * n, 'milliseconds')
}

//elements helpers
function collapsible(name, action){
    $(name).collapse(action);
}

function removeTags(name){
    $(name).tagsinput('removeAll');
}

function addTags(name, tags){
    $(name).tagsinput('add', tags);
}
function setTags(name, tags){
    removeTags(name);
    addTags(name, tags);
}

//Knockout
var randogramViewModel = new function RandogramViewModel() {
    var self = this;
    /////////////////////////////////////////////////////accordion//////////////////////////////////////////////////////
    self.basicCollapsibleHandler = function(){
        collapsible('#basicCollapsible', 'show');
        collapsible('#advancedCollapsible', 'hide');
    };
    self.advancedCollapsibleHandler = function(){
        collapsible('#basicCollapsible', 'hide');
        collapsible('#advancedCollapsible', 'show');
    };
    /////////////////////////////////////////////////////tags///////////////////////////////////////////////////////////
    //tags
    self.tags = ko.observable("griddynamics");

    //tags helpers
    self.areTagsInvalid = ko.computed (function () {
        return self.tags().length == 0;
    });

    //tagsinput fields handler (so bad!)
    self.tagsFix = ko.observable(true);
    self.checkedFix = ko.observable(0);
    self.tagsHandler = ko.pureComputed({
        read: function () {
            return self.tags();
        },
        write: function (value) {
            self.tags(value);

            //мне очень стыдно за этот костыль
            //защита от срабатывания при загрузке страницы
            if(self.checkedFix() < 2){
                self.checkedFix(self.checkedFix()+1);
                return;
            }
            //защита от зацикливания
            if(self.tagsFix()){
                self.tagsFix(false);
                setTags('#tags1', self.tags());
                setTags('#tags2', self.tags());
                setTimeout(function(){self.tagsFix(true)}, 100);
            }
        },
        owner: this
    });

    /////////////////////////////////////////////////////dates//////////////////////////////////////////////////////////
    //date filter type
    self.dateFilterSelected = ko.observable("Today");
    self.dateFilterPresetsBasic = [
        { statusName: "Today" },
        { statusName: "Specify day" },
        { statusName: "All time" }
    ];
    self.dateFilterPresetsAdvanced = [
        { statusName: "Today" },
        { statusName: "Last week" },
        { statusName: "Last month" },
        { statusName: "All time" },
        { statusName: "Specify day" },
        { statusName: "Specify interval" }
    ];
    self.dateFilterClickHandler = function(){
        self.dateFilterSelected(this.statusName);
        switch(self.dateFilterSelected()){
            case "Specify day":
                collapsible('#basicDateFrom-collapsible', 'show');
                collapsible('#advancedDateFrom-collapsible', 'show');
                collapsible('#advancedDateTo-collapsible', 'hide');
                break;
            case "Specify interval":
                collapsible('#advancedDateFrom-collapsible', 'show');
                collapsible('#advancedDateTo-collapsible', 'show');
                break;
            default:
                collapsible('#basicDateFrom-collapsible', 'hide');
                collapsible('#advancedDateFrom-collapsible', 'hide');
                collapsible('#advancedDateTo-collapsible', 'hide');
        }
    };

    //date fields
    self.dateFrom = ko.observable(moment().format("MM/DD/YYYY"));
    self.dateTo = ko.observable(moment().format("MM/DD/YYYY"));

    self.calculateDateFrom = function(){
        switch(self.dateFilterSelected()){
            case "Today":
                return moment().startOf('day');
            case "Last week":
                return moment().startOf('week');
            case "Last month":
                return moment().startOf('month');
            case "Specify day":
            case "Specify interval":
                return moment(self.dateFrom() ? self.dateFrom() : moment()).startOf('day');
            default:
                return null;
        }
    };
    self.calculateDateTo = function(){
        switch(self.dateFilterSelected()){
            case "Specify day":
                return moment(self.dateFrom() ? self.dateFrom() : moment()).endOf('day');
            case "Specify interval":
                return moment(self.dateTo() ? self.dateTo() : moment()).endOf('day');
            case "All time":
                return null;
            default:
                return moment();
        }
    };

    //unixTimestamp -> "... ago"
    self.getMoment = function(unixTime){
        return moment(unixTime, "X").fromNow();
    };

    /////////////////////////////////////////////////////observables////////////////////////////////////////////////////
    //arrays
    self.usersAndPosts = ko.observableArray([]);
    self.images = ko.observableArray([]);
    self.tempImages = ko.observableArray([]);
    self.preview = ko.observableArray();

    //statements
    self.isSearchDone = ko.observable(false);
    self.isLoading = ko.observable(false);
    self.isFollowing = ko.observable(false);

    self.isLuckyButtonEnabled = ko.computed (function () {
        return !self.isLoading() && self.images().length>0;
    });

    self.isSearchButtonEnabled = ko.computed (function () {
        return !self.areTagsInvalid() && !self.isLoading();
    });

    /////////////////////////////////////////////////////arrays helpers/////////////////////////////////////////////////
    self.setPreview = function() {
        if (self.isLoading()) {
            return;
        }
        self.preview([this]);
    };

    var setUsersAndPosts = function(){
        var users = new Set(self.images().map(function(image){return image.user.username}));
        var usersArray = Array.from(users);

        var usersPairs = usersArray.map(function(x){return {username: x, posts: []}});
        usersPairs.forEach(function(pair){
            pair.posts = self.images().filter(function(image){return image.user.username == pair.username;});
        });

        self.usersAndPosts(usersPairs);
    };

    /////////////////////////////////////////////////////winners////////////////////////////////////////////////////////

    self.winners = ko.observableArray([]);
    self.winnersSelectionType = ko.observable("posts");
    self.winnersAmount = ko.observable(1);
    self.winnersAmountMax = function(){
        return self.winnersSelectionType() === "users" ? self.usersAndPosts().length : self.images().length;
    };

    self.winnersAmountHandler = ko.pureComputed({
        read: function () {
            return self.winnersAmount();
        },
        write: function (value) {
            if(value > self.winnersAmountMax()){
                self.winnersAmount(self.winnersAmountMax());
            }else{
                self.winnersAmount(value);
            }
        },
        owner: this
    });
    //winnersAmount +/- buttons
    self.winnersAmountPlus = function () {
        if(self.winnersAmount() < self.winnersAmountMax()){
            self.winnersAmount(self.winnersAmount()+1);
        }else if(self.winnersAmountMax() != 0){
            self.winnersAmount(self.winnersAmountMax());
        }
    };
    self.winnersAmountMinus = function () {
        if(self.winnersAmount() > 1){
            self.winnersAmount(self.winnersAmount()-1);
        }else{
            self.winnersAmount(1);
        }
    };
    self.searchInfo = ko.computed (function () {
        if(self.images().length == 0){
            return "Sorry, we didn't find anything. Check your query."
        }
        return "We found " + self.images().length + " posts from " + self.usersAndPosts().length + " users!";
    });

    /////////////////////////////////////////////////////actions////////////////////////////////////////////////////////
    //progressbar time
    self.startTime = ko.observable(moment());
    self.endTime = ko.observable(moment());
    self.progress = function(){
        if(!self.isLoading()){return 1}
        var timeBetweenStartAndEnd = (self.endTime().toDate() - self.startTime().toDate());
        var timeBetweenStartAndToday = (moment().toDate() - self.startTime().toDate());
        var p = timeBetweenStartAndToday / timeBetweenStartAndEnd;
        return p;
    };
    //search
    self.searchAction = function () {
        self.images([]);
        self.tempImages([]);
        self.isLoading(true);

        collapsible('#basicCollapsible', 'hide');
        collapsible('#advancedCollapsible', 'hide');
        collapsible('#chooseLucky-collapsible', 'hide');
        collapsible('#search-info-collapsible', 'hide');
        collapsible('#winners-content-collapsible', 'hide');
        collapsible('#content-collapsible', 'hide');

        //get tag info
        var willBeLoaded = 100;
        $.ajax({
            url: "getTagAmount",
            data: {
                tags: self.tags()
            },
            dataType: "json",
            success: function (data) {
                willBeLoaded = data;
                if(data > 1000){
                    alert("Only first 1000 of "+data+" photos will be loaded");
                    willBeLoaded = 1000;
                }
                //get first 40 photos
                $.ajax({
                    url: "getFirstImages",
                    data: {
                        accesToken: self.accesToken(),
                        tags: self.tags(),
                        amount: 2
                    },
                    dataType: "json",
                    beforeSend: function(){
                        self.startTime(moment());
                    },
                    success: function (data) {
                        var m = moment();
                        var interval = m.diff(self.startTime(), 'milliseconds');
                        //magic numbers!
                        self.endTime(m.add(((interval/70.0)*(willBeLoaded-data.length))^1.2), 'milliseconds');

                        //slideshow
                        if(self.isLoading()) {
                            collapsible('#loading-content-collapsible', 'show');
                        }
                        var i = 0;
                        var timerId = setInterval(function() {
                            self.tempImages([data[i++]]);
                            if(i >= data.length){
                                i = 0;
                            }
                            if(!self.isLoading()){
                                clearInterval(timerId);
                                collapsible('#loading-content-collapsible', 'hide');
                            }
                        }, 4000);
                    },
                    error: function () {
                        alert("getFirstImages error");
                        self.isLoading(false);
                    }
                });
            },
            error: function () {
                alert("getTagAmount error");
                collapsible('#loading-content-collapsible', 'hide');
            }
        });

        //get images
        $.ajax({
            url: "getImages",
            data: {
                accesToken: self.accesToken(),
                tags: self.tags(),
                following: self.isFollowing(),
                dateTimeFrom: format(toUTC(self.calculateDateFrom())),
                dateTimeTo: format(toUTC(self.calculateDateTo()))
            },
            dataType: "json",
            success: function (data) {
                self.images(data);
                setUsersAndPosts();

                self.isLoading(false);
                self.isSearchDone(true);
                self.winnersAmount(1);

                collapsible('#loading-content-collapsible', 'hide');
                if(self.images().length > 0){
                    collapsible('#chooseLucky-collapsible', 'show');
                    collapsible('#content-collapsible', 'show');
                }
                collapsible('#search-info-collapsible', 'show');
            },
            error: function () {
                alert("getImages error");
                self.isLoading(false);
                collapsible('#loading-content-collapsible', 'hide');
            },
            timeout: 200000
        });
    };

    //select winners action
    self.selectAction = function () {
        if(self.winnersSelectionType() === "users"){
            var luckyUsers = shuffle(self.usersAndPosts()).slice(0, self.winnersAmount());
            var winnerPosts = luckyUsers.map(function(x){return randomElement(x.posts)});
            self.winners(winnerPosts);
        }else{
            var luckyPosts = shuffle(self.images()).slice(0, self.winnersAmount());
            self.winners(luckyPosts);
        }
        collapsible('#winners-content-collapsible', 'show');
    };

    //acces token
    self.accesToken = ko.observable(null);
    self.handleToken = ko.computed (function(){
        var params = parseParams();

        if(!params.accesToken){
            window.location = "/";
        }else{
            self.accesToken(params.accesToken);
        }
    });
};

ko.applyBindings(randogramViewModel);

//Activate datetimepickers and make them linked to each other
//$('#dateTimeFrom').datetimepicker();
$('#basicDateFrom').datetimepicker();
$('#advancedDateFrom').datetimepicker();
$('#advancedDateTo').datetimepicker();

Ladda.bind( '#srch-btn', {
    callback: function( instance ) {
        var progress = 0;
        var interval = setInterval( function() {
            progress = randogramViewModel.progress();
            instance.setProgress( progress );
            if( progress === 1 ) {
                instance.stop();
                clearInterval( interval );
            }
        }, 200 );
    }
} );<|endoftext|>


<commit_before>/*
 *  Copyright 2020 Google LLC
 *
 *  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
 *
 *      https:www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

function initialise() {
    updateTimezoneOffset();
    populateRows();
}

function populateRows() {
    //TODO fetch existing timeslots for this volunteer from backend
    getExistingTimeSlots().then(rows => rows.forEach(row => addRow(row)));

    // add an empty row at bottom of form
    addEmptyRow();

    //TODO add delete button to each row

    //TODO validate fields so they're not empty, and start time is before end time
}

function parseForm() {
    const form = document.getElementById("availability-form");
    const divs = form.getElementsByTagName("div");
    for (let div of divs) {
        const children = div.children;
        children["start-time"].value = children["start-time"].valueAsDate.toISOString();
        children["end-time"].value = children["end-time"].valueAsDate.toISOString();
    }
    console.log("Form contents", divs);
    // divs[1].children["date"].value
    return false; // prevent form submission
}

async function getExistingTimeSlots() {
    const slots = await (await fetch('/volunteer-availability')).json();
    return slots.map(slot => slotToRow(slot));
}

function slotToRow(slot) {
    //parse start.seconds and end.seconds, getting the date and the day in the local timezone
    const startTime = new Date(slot.start.seconds * 1000);
    const endTime = new Date(slot.end.seconds * 1000);
    //const date = startTime.getFullYear() + "-" + startTime.getMonth() + "-" + startTime.getDate();
    const date = startTime.toISOString().replace(/[A-Za-z]+.*/, "");
    const start = startTime.getHours() + ":" + startTime.getMinutes();
    const end = endTime.getHours() + ":" + endTime.getMinutes();
    return constructNewRow(date, start, end);
}

function updateTimezoneOffset() {
    document.getElementById('timezoneOffset').value =
        new Date().toTimeString().split(' ')[1].replace('GMT', '');
}

function constructNewRow(dateValue, startTimeValue, endTimeValue) {
    const newRow = document.createElement("div");

    const startTime = document.createElement("input");
    startTime.type = "datetime-local";
    startTime.name = "start-time";
    startTime.value = startTimeValue;

    const ISOStartTime = document.createElement("input");
    ISOStartTime.name = "ISO-start-time";
    //ISOStartTime.hidden = true;

    startTime.addEventListener("change", (e) => {
        const inputElement = e.target;
        inputElement.parentNode.children["ISO-start-time"].value = new Date(inputElement.value).toISOString();
    });

    const endTime = document.createElement("input");
    endTime.type = "time";
    endTime.name = "end-time";
    endTime.value = endTimeValue;

    const ISOEndTime = document.createElement("input");
    ISOEndTime.name = "ISO-end-time";
    //ISOEndTime.hidden = true;

    endTime.addEventListener("change", (e) => {
        const inputElement = e.target;
        const children = inputElement.parentNode.children;
        const endTimeDate = inputElement.valueAsDate;
        const startTimeDate = new Date(children["ISO-start-time"].value);

        startTimeDate.setHours(endTimeDate.getHours())
        startTimeDate.setMinutes(endTimeDate.getMinutes())
        children["ISO-end-time"].value = startTimeDate.toISOString();
    });

    newRow.appendChild(startTime);
    newRow.appendChild(endTime);
    newRow.appendChild(ISOStartTime);
    newRow.appendChild(ISOEndTime);

    return newRow;
}

function addRow(row) {
    document.getElementById("availability-form").appendChild(row);
}

function addEmptyRow() {
    addRow(constructNewRow());
}<commit_msg>Fix loading existing volunteer timeslots<commit_after>/*
 *  Copyright 2020 Google LLC
 *
 *  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
 *
 *      https:www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

function initialise() {
    populateRows();
}

function populateRows() {
    getExistingTimeSlots().then(rows => rows.forEach(row => addRow(row)));

    //TODO add delete button to each row

    //TODO validate fields so they're not empty, and start time is before end time
}

async function getExistingTimeSlots() {
    const slots = await (await fetch('/volunteer-availability')).json();
    return slots.map(slot => slotToRow(slot));
}

function slotToRow(slot) {
    //parse start.seconds and end.seconds, getting the date and the day in the local timezone
    const startTime = new Date(slot.start.seconds * 1000);
    const endTime = new Date(slot.end.seconds * 1000);

    const end = endTime.getHours() + ":" + endTime.getMinutes();

    return constructNewRow(startTime.getTime(), end);
}

function fillISOStartTime(inputElement) {
    if (inputElement.value == null || inputElement.value === "") return;
    inputElement.parentNode.children["ISO-start-time"].value = new Date(inputElement.value).toISOString();
}

function fillISOEndTime(inputElement) {
    const children = inputElement.parentNode.children;
    const endTimeDate = inputElement.valueAsDate;
    const startTimeDate = new Date(children["ISO-start-time"].value);

    if (endTimeDate == null || startTimeDate == null) return;

    startTimeDate.setHours(endTimeDate.getHours())
    startTimeDate.setMinutes(endTimeDate.getMinutes())
    children["ISO-end-time"].value = startTimeDate.toISOString();
}

function constructNewRow(startTimeValue, endTimeValue) {
    const newRow = document.createElement("div");

    const startTime = document.createElement("input");
    startTime.type = "datetime-local";
    startTime.name = "start-time";
    startTime.valueAsNumber = startTimeValue;

    const ISOStartTime = document.createElement("input");
    ISOStartTime.name = "ISO-start-time";
    //ISOStartTime.hidden = true;

    startTime.addEventListener("change", (e) => fillISOStartTime(e.target));

    const endTime = document.createElement("input");
    endTime.type = "time";
    endTime.name = "end-time";
    endTime.value = endTimeValue;

    const ISOEndTime = document.createElement("input");
    ISOEndTime.name = "ISO-end-time";
    //ISOEndTime.hidden = true;

    endTime.addEventListener("change", (e) => fillISOEndTime(e.target));

    newRow.appendChild(startTime);
    newRow.appendChild(endTime);
    newRow.appendChild(ISOStartTime);
    newRow.appendChild(ISOEndTime);

    fillISOStartTime(startTime); // initialise with a value for submission
    fillISOEndTime(endTime); // initialise with a value for submission
    return newRow;
}

function addRow(row) {
    document.getElementById("availability-form").appendChild(row);
}

function addEmptyRow() {
    addRow(constructNewRow());
}<|endoftext|>


<commit_before><commit_msg>new value illustration ratio<commit_after><|endoftext|>


<commit_before>export const transmute = [
    {
        "item": "Sealed Sacred Oil",
        "createdBy": "Transmute",
        "orders": [
            {
                "cost": 150000,
                "output": 1,
                "duration": "1s",
                "ingredients": [
                    {"name": "Demon Spirit Stone", "quantity": 1},
                    {"name": "Evolved Stone", "quantity": 10},
                    {"name": "Silverfrost Transformation Stone", "quantity": 10},
                    {"name": "Soulstone", "quantity": 45},
                    {"name": "Moonstone", "quantity": 10},
                    {"name": "Sacred Orb", "quantity": 45},
                    {"name": "Elysian Orb", "quantity": 10}
                ]
            }
        ],
    },
    {
        "item": "Premium Transformation Stone",
        "createdBy": "Transmute",

        "orders": [
            {
                "cost": 150000,
                "output": 1,
                "duration": "1s",
                "ingredients": [
                    {"name": "Moonstone", "quantity": 8},
                    {"name": "Soulstone", "quantity": 30},
                    {"name": "Sacred Orb", "quantity": 30},
                    {"name": "Elysian Orb", "quantity": 8},
                    {"name": "Silverfrost Transformation Stone", "quantity": 4}
                ]
            }
        ]
    }
];
<commit_msg>Update transmuteCrafting.js<commit_after>export const transmute = [
    {
        "item": "Sealed Sacred Oil",
        "createdBy": "Transmute",
        "orders": [
            {
                "cost": 150000,
                "output": 1,
                "duration": "1s",
                "ingredients": [
                    {"name": "Demon Spirit Stone", "quantity": 1},
                    {"name": "Evolved Stone", "quantity": 10},
                    {"name": "Transformation Stone", "quantity": 10},
                    {"name": "Soulstone", "quantity": 45},
                    {"name": "Moonstone", "quantity": 10},
                    {"name": "Sacred Orb", "quantity": 45},
                    {"name": "Elysian Orb", "quantity": 10}
                ]
            }
        ],
    },
    {
        "item": "Premium Transformation Stone",
        "createdBy": "Transmute",

        "orders": [
            {
                "cost": 150000,
                "output": 1,
                "duration": "1s",
                "ingredients": [
                    {"name": "Moonstone", "quantity": 8},
                    {"name": "Soulstone", "quantity": 30},
                    {"name": "Sacred Orb", "quantity": 30},
                    {"name": "Elysian Orb", "quantity": 8},
                    {"name": "Transformation Stone", "quantity": 4}
                ]
            }
        ]
    }
];
<|endoftext|>


<commit_before>// @flow

import { timeoutPromise } from './timeoutPromise';

/**
 * Loads a script from a specific URL. React Native cannot load a JS
 * file/resource/URL via a <script> HTML element, so the implementation
 * fetches the specified {@code url} as plain text using {@link fetch()} and
 * then evaluates the fetched string as JavaScript code (using {@link eval()}).
 *
 * @param {string} url - The absolute URL from which the script is to be
 * (down)loaded.
 * @param {number} [timeout] - The timeout in millisecnods after which the
 * loading of the specified {@code url} is to be aborted/rejected (if not
 * settled yet).
 * @returns {void}
 */
export function loadScript(url: string, timeout: ?number): Promise<void> {
    return new Promise((resolve, reject) => {
        // XXX The implementation of fetch on Android will throw an Exception on
        // the Java side which will break the app if the URL is invalid (which
        // the implementation of fetch on Android calls 'unexpected url'). In
        // order to try to prevent the breakage of the app, try to fail on an
        // invalid URL as soon as possible.
        const { hostname, pathname, protocol } = new URL(url);

        // XXX The standard URL implementation should throw an Error if the
        // specified URL is relative. Unfortunately, the polyfill used on
        // react-native does not.
        if (!hostname || !pathname || !protocol) {
            reject(`unexpected url: ${url}`);

            return;
        }

        let fetch_ = fetch(url, { method: 'GET' });

        // The implementation of fetch provided by react-native is based on
        // XMLHttpRequest. Which defines timeout as an unsigned long with
        // default value 0, which means there is no timeout.
        if (timeout) {
            // FIXME I don't like the approach with timeoutPromise because:
            //
            // * It merely abandons the underlying XHR and, consequently, opens
            //   us to potential issues with NetworkActivityIndicator which
            //   tracks XHRs.
            //
            // * @paweldomas also reported that timeouts seem to be respected by
            //   the XHR implementation on iOS. Given that we have
            //   implementation of loadScript based on fetch and XHR (in an
            //   earlier revision), I don't see why we're not using an XHR
            //   directly on iOS.
            //
            // * The approach of timeoutPromise I found on the Internet is to
            //   directly use XHR instead of fetch and abort the XHR on timeout.
            //   Which may deal with the NetworkActivityIndicator at least.
            fetch_ = timeoutPromise(fetch_, timeout);
        }

        fetch_
            .then(response => {
                switch (response.status) {
                case 200:
                    return response.responseText || response.text();

                default:
                    throw response.statusText;
                }
            })
            .then(responseText => {
                eval.call(window, responseText); // eslint-disable-line no-eval
            })
            .then(resolve, reject);
    });
}
<commit_msg>rn: refactor loadScript<commit_after>// @flow

/**
 * Default timeout for loading scripts.
 */
const DEFAULT_TIMEOUT = 5000;

/**
 * Loads a script from a specific URL. React Native cannot load a JS
 * file/resource/URL via a <script> HTML element, so the implementation
 * fetches the specified {@code url} as plain text using {@link fetch()} and
 * then evaluates the fetched string as JavaScript code (using {@link eval()}).
 *
 * @param {string} url - The absolute URL from which the script is to be
 * (down)loaded.
 * @param {number} [timeout] - The timeout in millisecnods after which the
 * loading of the specified {@code url} is to be aborted/rejected (if not
 * settled yet).
 * @param {boolean} skipEval - Wether we want to skip evaluating the loaded content or not.
 * @returns {void}
 */
export async function loadScript(
        url: string, timeout: number = DEFAULT_TIMEOUT, skipEval: boolean = false): Promise<any> {
    // XXX The implementation of fetch on Android will throw an Exception on
    // the Java side which will break the app if the URL is invalid (which
    // the implementation of fetch on Android calls 'unexpected url'). In
    // order to try to prevent the breakage of the app, try to fail on an
    // invalid URL as soon as possible.
    const { hostname, pathname, protocol } = new URL(url);

    // XXX The standard URL implementation should throw an Error if the
    // specified URL is relative. Unfortunately, the polyfill used on
    // react-native does not.
    if (!hostname || !pathname || !protocol) {
        throw new Error(`unexpected url: ${url}`);
    }

    const controller = new AbortController();
    const signal = controller.signal;

    const timer = setTimeout(() => {
        controller.abort();
    }, timeout);

    const response = await fetch(url, { signal });

    // If the timeout hits the above will raise AbortError.

    clearTimeout(timer);

    switch (response.status) {
    case 200: {
        const txt = await response.text();

        if (skipEval) {
            return txt;
        }

        return eval.call(window, txt); // eslint-disable-line no-eval
    }
    default:
        throw new Error(`loadScript error: ${response.statusText}`);
    }
}
<|endoftext|>