<commit_before>angular.module('room', ['ionic'])
  .config(function($stateProvider) {
    $stateProvider
      .state('room', {
        url: '/room/:roomId',
        templateUrl: "app/room/room.html",
        controller: 'RoomCtrl'
      });
  })
  .controller('RoomCtrl', function($scope, $stateParams, $http, chatSocket, $ionicFrostedDelegate, $ionicScrollDelegate, $rootScope) {
    console.log('start RoomCtrl');
    $scope.room = {};
    $scope.user = {};
    $scope.players = {};
    $scope.sketchbooks = {};

    chatSocket.connect();

    // 접속자 정보 조회
    $http.get('/users/me').then(function(response) {
      $scope.user = response.data;
      // 방 정보 조회 후 참가 이벤트 소켓으로 전달
      chatSocket.emit('room:join', {
        userId: $scope.user._id,
        roomId: $stateParams.roomId
      });
    });

    function updateRoomInfo() {
      // 놀이방 정보 조회
      $http.get('/api/rooms/' + $stateParams.roomId).then(function(response) {
        console.log(response.data);

        var isparticipant = false;
        angular.forEach(response.data.players, function(player) {
          if (player.userId === $scope.user._id)
            isparticipant = true;
        });

        // if (!isparticipant) {
        //   window.location.href = '#/lobby';
        //   return;
        // }

        $scope.room = response.data;
        $scope.room.status = '02';

        // 방 참가자 정보 조회
        var i = 1;
        angular.forEach($scope.room.players, function(player) {

          if (player.playStatus === '01') $scope.room.status = '01'; // 한 명이라도 ready 상태가 아니면 '01'
          $http.get('/users/' + player.userId).then(function(response) {
            $scope.players[response.data._id] = response.data;
            $scope.players[response.data._id].playStatus = player.playStatus;
            $scope.players[response.data._id].seq = i++;
          });
        });

        $http.put('/api/rooms/' + $stateParams.roomId, {
          status: $scope.room.status
        }).then(function(response) {
          console.log(response.data);
        });
      });
    }

    updateRoomInfo();

    $scope.data = {
      messages: [{
        userId: '',
        content: 'Welcome!'
      }],
      message: ''
    };

    // 서버로 메시지 전송
    $scope.sendMessage = function() {
      if ($scope.data.message === '') {
        return;
      }

      chatSocket.emit('room:message', {
        userId: $scope.user._id,
        roomId: $scope.room.id,
        content: $scope.data.message
      });
      $scope.data.message = '';
    };

    // 서버로 메시지 전송
    $scope.sendStatusMessage = function() {

      var sketchbookId = "";
      if ($scope.user._id === $scope.room.ownerId) {
        chatSocket.emit('room:sendStartMessage', {
          userId: '',
          roomId: $scope.room.id,
          content: '게임 시작합니다!!!'
        });

        // 인원 수만큼 스케치북 생성
        angular.forEach($scope.room.players, function(user) {
          console.log(user.username + "의 스케치북 생성!!!!!!!!!!!!!");
          $http.post('/api/sketchbooks', {
            roomId: $scope.room.id,
            userId: user.userId
          }).then(function(response) {
            sketchbookId = response.data;
            $scope.sketchbooks[$scope.players[user.userId].seq - 1] = sketchbookId;
            var url = '#/word/' + $scope.room.id + '/user/' + user.userId + '/seq/' + $scope.players[user.userId].seq + '/sketchbook/' + sketchbookId;
            chatSocket.emit('room:changeDisplay', {
              userId: user.userId,
              roomId: $scope.room.id,
              url: url
            });
          });
        });

      } else {
        var readyStatus = $scope.players[$scope.user._id].playStatus === '01' ? '02' : '01';
        $http.put('/api/rooms/' + $stateParams.roomId + '/users/' + $scope.user._id, {
          status: readyStatus
        }).then(function(response) {
          console.log(response.data);
          //$scope.room = response.data;
        });

        chatSocket.emit('room:sendReadyMessage', {
          userId: '',
          roomId: $scope.room.id,
          content: (readyStatus === '02' ? $scope.players[$scope.user._id].username + '님이 준비가 됐습니다.' : $scope.players[$scope.user._id].username + '님이 준비를 취소하였습니다.')
        });
        updateRoomInfo();
      }
    };

    // 방에서 나가기
    $scope.leaveRoom = function() {
      console.log('leave room!');
      window.location.href = '#/lobby';
    };

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:message', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:sendReadyMessage', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:sendStartMessage', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 새로운 참가자 이벤트
    chatSocket.on('room:joined', function(msg) {
      updateRoomInfo();

      // 새로운 참가자 정보 조회
      $http.get('/users/' + msg.userId).then(function(response) {
        $scope.players[response.data._id] = response.data;
        $scope.data.messages.push({
          userId: '',
          content: response.data.username + '님이 참가하셨습니다.'
        });
        $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
      });
    });

    // 사용자 퇴장 이벤트
    chatSocket.on('room:left', function(msg) {
      console.log(msg);
      console.log($scope.players);
      $scope.data.messages.push({
        userId: '',
        content: $scope.players[msg.userId].username + '님이 퇴장하셨습니다.'
      });
      updateRoomInfo();
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 사용자 연결 끊김 이벤트
    chatSocket.on('room:lost', function(msg) {
      console.log(msg);
      console.log($scope.players);
      $scope.data.messages.push({
        userId: '',
        content: $scope.players[msg.userId].username + '님의 연결이 끊겼습니다.'
      });
      updateRoomInfo();
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 게임시작 후, 사용자별 화면 전환
    chatSocket.on('room:changeDisplay', function(msg) {
      console.log(msg);
      if (msg.userId === $scope.user._id) {
        window.location.href = msg.url;
      }
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    $scope.$on('$destroy', function() {
      console.log('RoomCtrl destroy');

      $http.delete('/api/rooms/' + $scope.room.id + '/users/' + $scope.user._id)
        .then(function(response) {
          // DB 업데이트 완료 후 소켓 room 퇴장
          chatSocket.emit('room:leave', {
            userId: $scope.user._id,
            roomId: $scope.room.id
          });

          chatSocket.disconnect();
        });
    });

    // Update the scroll area and tell the frosted glass to redraw itself
    $ionicFrostedDelegate.update();
    $ionicScrollDelegate.scrollBottom(true);
  });
<commit_msg>Update Room Api - room status<commit_after>angular.module('room', ['ionic'])
  .config(function($stateProvider) {
    $stateProvider
      .state('room', {
        url: '/room/:roomId',
        templateUrl: "app/room/room.html",
        controller: 'RoomCtrl'
      });
  })
  .controller('RoomCtrl', function($scope, $stateParams, $http, chatSocket, $ionicFrostedDelegate, $ionicScrollDelegate, $rootScope) {
    console.log('start RoomCtrl');
    $scope.room = {};
    $scope.user = {};
    $scope.players = {};
    $scope.sketchbooks = {};

    chatSocket.connect();

    // 접속자 정보 조회
    $http.get('/users/me').then(function(response) {
      $scope.user = response.data;
      // 방 정보 조회 후 참가 이벤트 소켓으로 전달
      chatSocket.emit('room:join', {
        userId: $scope.user._id,
        roomId: $stateParams.roomId
      });
    });

    function updateRoomInfo() {
      // 놀이방 정보 조회
      $http.get('/api/rooms/' + $stateParams.roomId).then(function(response) {
        console.log(response.data);

        var isparticipant = false;
        angular.forEach(response.data.players, function(player) {
          if (player.userId === $scope.user._id)
            isparticipant = true;
        });

        // if (!isparticipant) {
        //   window.location.href = '#/lobby';
        //   return;
        // }

        $scope.room = response.data;
        $scope.room.status = '02';

        // 방 참가자 정보 조회
        var i = 1;
        angular.forEach($scope.room.players, function(player) {

          if (player.playStatus === '01') $scope.room.status = '01'; // 한 명이라도 ready 상태가 아니면 '01'
          $http.get('/users/' + player.userId).then(function(response) {
            $scope.players[response.data._id] = response.data;
            $scope.players[response.data._id].playStatus = player.playStatus;
            $scope.players[response.data._id].seq = i++;
          });
        });

        $http.put('/api/rooms/' + $stateParams.roomId, {
          status: $scope.room.status
        }).then(function(response) {
          console.log(response.data);
        });
      });
    }

    updateRoomInfo();

    $scope.data = {
      messages: [{
        userId: '',
        content: 'Welcome!'
      }],
      message: ''
    };

    // 서버로 메시지 전송
    $scope.sendMessage = function() {
      if ($scope.data.message === '') {
        return;
      }

      chatSocket.emit('room:message', {
        userId: $scope.user._id,
        roomId: $scope.room.id,
        content: $scope.data.message
      });
      $scope.data.message = '';
    };

    // 서버로 메시지 전송
    $scope.sendStatusMessage = function() {

      var sketchbookId = "";
      if ($scope.user._id === $scope.room.ownerId) {
        chatSocket.emit('room:sendStartMessage', {
          userId: '',
          roomId: $scope.room.id,
          content: '게임 시작합니다!!!'
        });

        // 인원 수만큼 스케치북 생성
        angular.forEach($scope.room.players, function(user) {
          console.log(user.username + "의 스케치북 생성!!!!!!!!!!!!!");
          $http.post('/api/sketchbooks', {
            roomId: $scope.room.id,
            userId: user.userId
          }).then(function(response) {
            sketchbookId = response.data;
            $scope.sketchbooks[$scope.players[user.userId].seq - 1] = sketchbookId;
            var url = '#/word/' + $scope.room.id + '/user/' + user.userId + '/seq/' + $scope.players[user.userId].seq + '/sketchbook/' + sketchbookId;
            chatSocket.emit('room:changeDisplay', {
              userId: user.userId,
              roomId: $scope.room.id,
              url: url
            });
          });
        });

      } else {
        var readyStatus = $scope.players[$scope.user._id].playStatus === '01' ? '02' : '01';
        $http.put('/api/rooms/' + $stateParams.roomId + '/users/' + $scope.user._id, {
          status: readyStatus
        }).then(function(response) {
          console.log(response.data);
          //$scope.room = response.data;
        });

        chatSocket.emit('room:sendReadyMessage', {
          userId: '',
          roomId: $scope.room.id,
          content: (readyStatus === '02' ? $scope.players[$scope.user._id].username + '님이 준비가 됐습니다.' : $scope.players[$scope.user._id].username + '님이 준비를 취소하였습니다.')
        });
        //updateRoomInfo();
      }
    };

    // 방에서 나가기
    $scope.leaveRoom = function() {
      console.log('leave room!');
      window.location.href = '#/lobby';
    };

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:message', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:sendReadyMessage', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
      updateRoomInfo();
    });

    // 서버로부터 받은 메시지를 추가
    chatSocket.on('room:sendStartMessage', function(msg) {
      console.log(msg);
      $scope.data.messages.push(msg);
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 새로운 참가자 이벤트
    chatSocket.on('room:joined', function(msg) {
      updateRoomInfo();

      // 새로운 참가자 정보 조회
      $http.get('/users/' + msg.userId).then(function(response) {
        $scope.players[response.data._id] = response.data;
        $scope.data.messages.push({
          userId: '',
          content: response.data.username + '님이 참가하셨습니다.'
        });
        $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
      });
    });

    // 사용자 퇴장 이벤트
    chatSocket.on('room:left', function(msg) {
      console.log(msg);
      console.log($scope.players);
      $scope.data.messages.push({
        userId: '',
        content: $scope.players[msg.userId].username + '님이 퇴장하셨습니다.'
      });
      updateRoomInfo();
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 사용자 연결 끊김 이벤트
    chatSocket.on('room:lost', function(msg) {
      console.log(msg);
      console.log($scope.players);
      $scope.data.messages.push({
        userId: '',
        content: $scope.players[msg.userId].username + '님의 연결이 끊겼습니다.'
      });
      updateRoomInfo();
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    // 게임시작 후, 사용자별 화면 전환
    chatSocket.on('room:changeDisplay', function(msg) {
      console.log(msg);
      if (msg.userId === $scope.user._id) {
        window.location.href = msg.url;
      }
      $ionicScrollDelegate.$getByHandle('messages-scroll').scrollBottom(true);
    });

    $scope.$on('$destroy', function() {
      console.log('RoomCtrl destroy');

      $http.delete('/api/rooms/' + $scope.room.id + '/users/' + $scope.user._id)
        .then(function(response) {
          // DB 업데이트 완료 후 소켓 room 퇴장
          chatSocket.emit('room:leave', {
            userId: $scope.user._id,
            roomId: $scope.room.id
          });

          chatSocket.disconnect();
        });
    });

    // Update the scroll area and tell the frosted glass to redraw itself
    $ionicFrostedDelegate.update();
    $ionicScrollDelegate.scrollBottom(true);
  });
<|endoftext|>


<commit_before>"use strict";

////////////////////////////////////////////////////////////////////////////////

function hash(str) {
  return require('crypto').createHash('md5').update(str).digest('hex');
}

////////////////////////////////////////////////////////////////////////////////

// FIXME: this works for single-process model only.
//        switch to shared cache in future.
var etag = hash('fontello-app-' + Date.now());

////////////////////////////////////////////////////////////////////////////////

module.exports = function (N, apiPath) {
  N.validate(apiPath, {});

  N.wire.on(apiPath, function app(env, callback) {
    if ('http' !== env.request.type) {
      callback('HTTP only');
      return;
    }

    // Page cache headers temporary disabled - seems to cause error
    // on assets change, 'must-revalidate' does not help.

    // set headers
    //env.headers['ETag']          = etag;
    //env.headers['Cache-Control'] = 'private, max-age=0, must-revalidate';

    env.response.layout          = 'fontello.layout';

    // done
    callback();
  });
};
<commit_msg>lint fix<commit_after>"use strict";

////////////////////////////////////////////////////////////////////////////////

function hash(str) {
  return require('crypto').createHash('md5').update(str).digest('hex');
}

////////////////////////////////////////////////////////////////////////////////

// FIXME: this works for single-process model only.
//        switch to shared cache in future.
//var etag = hash('fontello-app-' + Date.now());

////////////////////////////////////////////////////////////////////////////////

module.exports = function (N, apiPath) {
  N.validate(apiPath, {});

  N.wire.on(apiPath, function app(env, callback) {
    if ('http' !== env.request.type) {
      callback('HTTP only');
      return;
    }

    // Page cache headers temporary disabled - seems to cause error
    // on assets change, 'must-revalidate' does not help.

    // set headers
    //env.headers['ETag']          = etag;
    //env.headers['Cache-Control'] = 'private, max-age=0, must-revalidate';

    env.response.layout          = 'fontello.layout';

    // done
    callback();
  });
};
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var DegToRad = require('../../math/DegToRad');
var Matrix4 = require('../../math/Matrix4');
var Vector3 = require('../../math/Vector3');
var Vector4 = require('../../math/Vector4');

/**
 * @classdesc
 * The Layer3D Camera.
 *
 * @class Layer3DCamera
 * @memberof Phaser.GameObjects
 * @constructor
 * @since 3.50.0
 */
var Layer3DCamera = new Class({

    initialize:

    function Layer3DCamera (fov, x, y, z, near, far)
    {
        this.dirty = true;
        this.aspectRatio = 1;

        this._fov = fov;
        this._near = near;
        this._far = far;

        this.position = new Vector3();
        this.rotation = new Vector3();

        this.forward = new Vector4();
        this.up = new Vector4(); //  What the up direction is, invert to get bottom
        this.right = new Vector4();	//  What the right direction is, invert to get left

        this.matrix = new Matrix4(); // the transform matrix
        this.viewMatrix = new Matrix4(); // the inverse of the transform matrix
        this.projectionMatrix = new Matrix4(); // perspective projection matrix
        this.viewProjectionMatrix = new Matrix4(); // perspective projection matrix multiplied by the view matrix

        this.mode = Layer3DCamera.MODE_ORBIT;
    },

    panX: function (v)
    {
        this.updateViewMatrix();

        this.position.addScale(this.right, v);
    },

    panY: function (v)
    {
        this.updateViewMatrix();

        this.position.y += this.up.y * v;

        if (this.mode === Layer3DCamera.MODE_ORBIT)
        {
            //  Can only move up and down the y axix in orbit mode
            return;
        }

        this.position.x += this.up.x * v;
        this.position.z += this.up.z * v;
    },

    panZ: function (v)
    {
        this.updateViewMatrix();

        if (this.mode === Layer3DCamera.MODE_ORBIT)
        {
            //  orbit mode does translate after rotate, so only need to set Z, the rotate will handle the rest.
            this.position.z += v;
        }
        else
        {
            //  In freemode to move forward, we need to move based on our forward which is relative to our current rotation
            this.position.addScale(this.forward, v);
        }
    },

    //  To have different modes of movements, this function handles the view matrix update for the transform object.
    updateViewMatrix: function ()
    {
        var matView = this.matrix;

        if (this.mode === Layer3DCamera.MODE_FREE)
        {
            matView.fromRotationXYTranslation(this.rotation, this.position, true);
        }
        else
        {
            matView.fromRotationXYTranslation(this.rotation, this.position, false);
        }

        this.updateDirection();

        this.viewMatrix.copy(matView).invert();

        this.dirty = true;
    },

    update: function (width, height)
    {
        this.aspectRatio = width / height;

        //  TODO - Only needs changing when the fov/etc does
        this.projectionMatrix.perspective(DegToRad(this._fov), this.aspectRatio, this._near, this._far);

        //  TODO - Only needs calculating when this camera is dirty
        this.projectionMatrix.multiplyToMat4(this.viewMatrix, this.viewProjectionMatrix);
    },

    updateDirection: function ()
    {
        var matView = this.matrix;

        this.forward.set(0, 0, 1, 0).transformMat4(matView);
        this.up.set(0, 1, 0, 0).transformMat4(matView);
        this.right.set(1, 0, 0, 0).transformMat4(matView);
    },

    fov: {

        get: function ()
        {
            return this._fov;
        },

        set: function (value)
        {
            if (value > 0 && value < 180)
            {
                this._fov = value;
                this.dirty = true;
            }
        }

    },

    near: {

        get: function ()
        {
            return this._near;
        },

        set: function (value)
        {
            if (value > 0)
            {
                this._near = value;
                this.dirty = true;
            }
        }

    },

    far: {

        get: function ()
        {
            return this._far;
        },

        set: function (value)
        {
            if (value > 0)
            {
                this._far = value;
                this.dirty = true;
            }
        }

    },

    x: {

        get: function ()
        {
            return this.position.x;
        },

        set: function (value)
        {
            this.position.x = value;
            this.updateViewMatrix();
        }

    },

    y: {

        get: function ()
        {
            return this.position.y;
        },

        set: function (value)
        {
            this.position.y = value;
            this.updateViewMatrix();
        }

    },

    z: {

        get: function ()
        {
            return this.position.z;
        },

        set: function (value)
        {
            this.position.z = value;
            this.updateViewMatrix();
        }

    },

    rotationX: {

        get: function ()
        {
            return this.rotation.x;
        },

        set: function (value)
        {
            this.rotation.x = value;
            this.updateViewMatrix();
        }

    },

    rotationY: {

        get: function ()
        {
            return this.rotation.y;
        },

        set: function (value)
        {
            this.rotation.y = value;
            this.updateViewMatrix();
        }

    },

    rotationZ: {

        get: function ()
        {
            return this.rotation.z;
        },

        set: function (value)
        {
            this.rotation.z = value;
            this.updateViewMatrix();
        }

    },

    destroy: function ()
    {
        //  TODO - Needed?
    }

});

// Allows free movement of position and rotation
Layer3DCamera.MODE_FREE = 0;

// Movement is locked to rotate around the origin
Layer3DCamera.MODE_ORBIT = 1;

module.exports = Layer3DCamera;
<commit_msg>Added all documentation and dirty flag handling<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var DegToRad = require('../../math/DegToRad');
var Matrix4 = require('../../math/Matrix4');
var Vector3 = require('../../math/Vector3');
var Vector4 = require('../../math/Vector4');

/**
 * @classdesc
 * The Layer3D Camera.
 *
 * @class Layer3DCamera
 * @memberof Phaser.GameObjects
 * @constructor
 * @since 3.50.0
 */
var Layer3DCamera = new Class({

    initialize:

    function Layer3DCamera (fov, x, y, z, near, far)
    {
        /**
         * Internal 'dirty' flag that tells the parent Layer3D if the
         * view matrix of this camera needs recalculating at the next step.
         *
         * @name Phaser.GameObjects.Layer3DCamera#dirtyView
         * @type {boolean}
         * @since 3.50.0
         */
        this.dirtyView = true;

        /**
         * Internal 'dirty' flag that tells the parent Layer3D if the
         * projection matrix of this camera needs recalculating at the next step.
         *
         * @name Phaser.GameObjects.Layer3DCamera#dirtyProjection
         * @type {boolean}
         * @since 3.50.0
         */
        this.dirtyProjection = true;

        /**
         * Internal fov value.
         *
         * @name Phaser.GameObjects.Layer3DCamera#_fov
         * @type {number}
         * @private
         * @since 3.50.0
         */
        this._fov = fov;

        /**
         * Internal near value.
         *
         * @name Phaser.GameObjects.Layer3DCamera#_near
         * @type {number}
         * @private
         * @since 3.50.0
         */
        this._near = near;

        /**
         * Internal far value.
         *
         * @name Phaser.GameObjects.Layer3DCamera#_far
         * @type {number}
         * @private
         * @since 3.50.0
         */
        this._far = far;

        /**
         * The aspect ratio of the camera.
         *
         * @name Phaser.GameObjects.Layer3DCamera#aspectRatio
         * @type {number}
         * @since 3.50.0
         */
        this.aspectRatio = 1;

        /**
         * The position of the camera.
         *
         * @name Phaser.GameObjects.Layer3DCamera#position
         * @type {Phaser.Math.Vector3}
         * @since 3.50.0
         */
        this.position = new Vector3(x, y, z);

        /**
         * The rotation of the camera.
         *
         * @name Phaser.GameObjects.Layer3DCamera#rotation
         * @type {Phaser.Math.Vector3}
         * @since 3.50.0
         */
        this.rotation = new Vector3();

        /**
         * The forward facing vector of the camera.
         *
         * Calculated and updated automatically when the view matrix changes.
         *
         * @name Phaser.GameObjects.Layer3DCamera#forward
         * @type {Phaser.Math.Vector4}
         * @since 3.50.0
         */
        this.forward = new Vector4();

        /**
         * The upward facing vector of the camera.
         * Invert it to get the bottom vector.
         *
         * Calculated and updated automatically when the view matrix changes.
         *
         * @name Phaser.GameObjects.Layer3DCamera#up
         * @type {Phaser.Math.Vector4}
         * @since 3.50.0
         */
        this.up = new Vector4();

        /**
         * The right facing vector of the camera.
         * Invert it to get the left vector.
         *
         * Calculated and updated automatically when the view matrix changes.
         *
         * @name Phaser.GameObjects.Layer3DCamera#right
         * @type {Phaser.Math.Vector4}
         * @since 3.50.0
         */
        this.right = new Vector4();

        /**
         * Internal transform matrix.
         *
         * Calculated and updated automatically when the camera is dirty.
         *
         * @name Phaser.GameObjects.Layer3DCamera#matrix
         * @type {Phaser.Math.Matrix4}
         * @since 3.50.0
         */
        this.matrix = new Matrix4();

        /**
         * The inverse of the transform matrix.
         *
         * Calculated and updated automatically when the camera is dirty.
         *
         * @name Phaser.GameObjects.Layer3DCamera#viewMatrix
         * @type {Phaser.Math.Matrix4}
         * @since 3.50.0
         */
        this.viewMatrix = new Matrix4();

        /**
         * The perspective projection matrix.
         *
         * Calculated and updated automatically when the camera is dirty.
         *
         * @name Phaser.GameObjects.Layer3DCamera#projectionMatrix
         * @type {Phaser.Math.Matrix4}
         * @since 3.50.0
         */
        this.projectionMatrix = new Matrix4();

        /**
         * The perspective projection matrix, multiplied by the view matrix.
         *
         * Calculated and updated automatically when the camera is dirty.
         *
         * @name Phaser.GameObjects.Layer3DCamera#viewProjectionMatrix
         * @type {Phaser.Math.Matrix4}
         * @since 3.50.0
         */
        this.viewProjectionMatrix = new Matrix4();

        /**
         * The movement and rotation mode of this camera.
         * Either ORBIT, or FREE.
         *
         * @name Phaser.GameObjects.Layer3DCamera#mode
         * @type {number}
         * @since 3.50.0
         */
        this.mode = Layer3DCamera.MODE_ORBIT;
    },

    /**
     * Pans this camera on the x axis by the given amount.
     *
     * @method Phaser.GameObjects.Layer3DCamera#panX
     * @since 3.50.0
     *
     * @param {number} v - The amount to pan by.
     */
    panX: function (v)
    {
        this.updateViewMatrix();

        this.position.addScale(this.right, v);
    },

    /**
     * Pans this camera on the y axis by the given amount.
     *
     * @method Phaser.GameObjects.Layer3DCamera#panY
     * @since 3.50.0
     *
     * @param {number} v - The amount to pan by.
     */
    panY: function (v)
    {
        this.updateViewMatrix();

        this.y += this.up.y * v;

        if (this.mode === Layer3DCamera.MODE_ORBIT)
        {
            //  Can only move up and down the y axis in orbit mode
            return;
        }

        this.x += this.up.x * v;
        this.z += this.up.z * v;
    },

    /**
     * Pans this camera on the z axis by the given amount.
     *
     * @method Phaser.GameObjects.Layer3DCamera#panZ
     * @since 3.50.0
     *
     * @param {number} v - The amount to pan by.
     */
    panZ: function (v)
    {
        this.updateViewMatrix();

        if (this.mode === Layer3DCamera.MODE_ORBIT)
        {
            //  Orbit mode translates after rotatation, so only need to set Z. The rotation will handle the rest.
            this.z += v;
        }
        else
        {
            //  In freemode to move forward, we move based on our forward, which is relative to our current rotation.
            this.position.addScale(this.forward, v);
        }
    },

    /**
     * Internal method that handles the update of the view transform matrix, based on the rotation
     * and position of the camera. Called automatically when the camera is updated.
     *
     * @method Phaser.GameObjects.Layer3DCamera#updateViewMatrix
     * @since 3.50.0
     */
    updateViewMatrix: function ()
    {
        var matView = this.matrix;

        if (this.mode === Layer3DCamera.MODE_FREE)
        {
            matView.fromRotationXYTranslation(this.rotation, this.position, true);
        }
        else
        {
            matView.fromRotationXYTranslation(this.rotation, this.position, false);
        }

        this.updateDirection();

        this.viewMatrix.copy(matView).invert();
    },

    /**
     * Internal method that is called by the Layer3D instance that owns this camera
     * during its `preUpdate` step. If the view matrix is dirty, it is recalculated
     * and then applied to the view projection matrix, ready for rendering.
     *
     * @method Phaser.GameObjects.Layer3DCamera#update
     * @since 3.50.0
     */
    update: function ()
    {
        if (this.dirtyView)
        {
            this.updateViewMatrix();
        }

        if (this.dirtyView || this.dirtyProjection)
        {
            this.projectionMatrix.multiplyToMat4(this.viewMatrix, this.viewProjectionMatrix);
        }
    },

    /**
     * Internal method that is called by the Layer3D instance that owns this camera
     * during its `preUpdate` step. If the projection matrix is dirty, or the renderer
     * width or height has changed, then a new projection matrix is calculated.
     *
     * @method Phaser.GameObjects.Layer3DCamera#updateProjectionMatrix
     * @since 3.50.0
     *
     * @param {number} width - The width of the renderer.
     * @param {number} height - The height of the renderer.
     */
    updateProjectionMatrix: function (width, height)
    {
        this.aspectRatio = width / height;

        this.projectionMatrix.perspective(DegToRad(this._fov), this.aspectRatio, this._near, this._far);
    },

    /**
     * Internal method that sets the forward, up and right vectors from
     * the view matrix. This is called automatically as part of the
     * `updateViewMatrix` method.
     *
     * @method Phaser.GameObjects.Layer3DCamera#updateDirection
     * @since 3.50.0
     */
    updateDirection: function ()
    {
        var matView = this.matrix;

        this.forward.set(0, 0, 1, 0).transformMat4(matView);
        this.up.set(0, 1, 0, 0).transformMat4(matView);
        this.right.set(1, 0, 0, 0).transformMat4(matView);
    },

    /**
     * The field of view, in degrees, of this camera.
     *
     * Limited to the range of 0 to 180.
     *
     * @name Phaser.GameObjects.Layer3DCamera#fov
     * @type {number}
     * @since 3.50.0
     */
    fov: {

        get: function ()
        {
            return this._fov;
        },

        set: function (value)
        {
            if (value > 0 && value < 180)
            {
                this._fov = value;
                this.dirtyProjection = true;
            }
        }

    },

    /**
     * The minimum distance the camera can see from.
     *
     * It's important to consider that depth buffers are not infinite and the closer
     * a camera starts, the more you may encounter depth fighting issues.
     *
     * @name Phaser.GameObjects.Layer3DCamera#near
     * @type {number}
     * @since 3.50.0
     */
    near: {

        get: function ()
        {
            return this._near;
        },

        set: function (value)
        {
            if (value > 0)
            {
                this._near = value;
                this.dirtyProjection = true;
            }
        }

    },

    /**
     * The maximum distance the camera can see to.
     *
     * It's important to consider that depth buffers are not infinite and the further
     * a camera ends, the more you may encounter depth fighting issues.
     *
     * @name Phaser.GameObjects.Layer3DCamera#far
     * @type {number}
     * @since 3.50.0
     */
    far: {

        get: function ()
        {
            return this._far;
        },

        set: function (value)
        {
            if (value > 0)
            {
                this._far = value;
                this.dirtyProjection = true;
            }
        }

    },

    /**
     * The x position of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#x
     * @type {number}
     * @since 3.50.0
     */
    x: {

        get: function ()
        {
            return this.position.x;
        },

        set: function (value)
        {
            this.position.x = value;
            this.dirtyView = true;
        }

    },

    /**
     * The y position of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#y
     * @type {number}
     * @since 3.50.0
     */
    y: {

        get: function ()
        {
            return this.position.y;
        },

        set: function (value)
        {
            this.position.y = value;
            this.dirtyView = true;
        }

    },

    /**
     * The z position of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#z
     * @type {number}
     * @since 3.50.0
     */
    z: {

        get: function ()
        {
            return this.position.z;
        },

        set: function (value)
        {
            this.position.z = value;
            this.dirtyView = true;
        }

    },

    /**
     * The x axis rotation, in radians, of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#rotationX
     * @type {number}
     * @since 3.50.0
     */
    rotationX: {

        get: function ()
        {
            return this.rotation.x;
        },

        set: function (value)
        {
            this.rotation.x = value;
            this.dirtyView = true;
        }

    },

    /**
     * The y axis rotation, in radians, of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#rotationY
     * @type {number}
     * @since 3.50.0
     */
    rotationY: {

        get: function ()
        {
            return this.rotation.y;
        },

        set: function (value)
        {
            this.rotation.y = value;
            this.dirtyView = true;
        }

    },

    /**
     * The z axis rotation, in radians, of the camera.
     *
     * @name Phaser.GameObjects.Layer3DCamera#rotationZ
     * @type {number}
     * @since 3.50.0
     */
    rotationZ: {

        get: function ()
        {
            return this.rotation.z;
        },

        set: function (value)
        {
            this.rotation.z = value;
            this.dirtyView = true;
        }

    },

    /**
     * Destroy handler for this camera.
     *
     * @method Phaser.GameObjects.Layer3DCamera#destroy
     * @since 3.50.0
     */
    destroy: function ()
    {
        this.position = null;
        this.rotation = null;
        this.forward = null;
        this.up = null;
        this.right = null;
        this.matrix = null;
        this.viewMatrix = null;
        this.projectionMatrix = null;
        this.viewProjectionMatrix = null;
    }

});

// Allows free movement of position and rotation
Layer3DCamera.MODE_FREE = 0;

// Movement is locked to rotate around the origin
Layer3DCamera.MODE_ORBIT = 1;

module.exports = Layer3DCamera;
<|endoftext|>


<commit_before>import React from 'react';

import SpellLink from 'common/SpellLink';
import { formatPercentage } from 'common/format';

import Module from 'Parser/Core/Module';
import getCastEfficiency from 'Parser/Core/getCastEfficiency';

import Tab from 'Main/Tab';
import CastEfficiencyComponent from 'Main/CastEfficiency';

import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';

class CastEfficiency extends Module {
  static SPELL_CATEGORIES = [
    {
      ROTATIONAL: 'Rotational Spell',
    },
    {
      ROTATIONAL_AOE: 'Spell (AOE)',
    },
    {
      COOLDOWNS: 'Cooldown',
    },
    {
      OTHERS: 'Spell',
    },
  ];
  static CPM_ABILITIES = [
    //TODO: fix the SPELL_CATEGORIES shenanigans so baseline on-use items can work
    //Shared trinkets and legendaries
    {
      spell: SPELLS.CLEANSING_MATRIX,
      getCooldown: haste => 60,
      isActive: combatant => combatant.hasTrinket(ITEMS.ARCHIVE_OF_FAITH.id),
    },
    {
      spell: SPELLS.GUIDING_HAND,
      charges: 2,
      getCooldown: haste => 120,
      isActive: combatant => combatant.hasTrinket(ITEMS.DECEIVERS_GRAND_DESIGN.id),
    },
    {
      spell: SPELLS.GNAWED_THUMB_RING,
      getCooldown: haste => 180,
      isActive: combatant => combatant.hasFinger(ITEMS.GNAWED_THUMB_RING.id),
    },
    {
      spell: SPELLS.KILJAEDENS_BURNING_WISH_CAST,
      getCooldown: haste => 75,
      isActive: combatant => combatant.hasTrinket(ITEMS.KILJAEDENS_BURNING_WISH.id),
    },
    {
      spell: SPELLS.SPECTRAL_OWL,
      getCooldown: haste => 120,
      isActive: combatant => combatant.hasTrinket(ITEMS.TARNISHED_SENTINEL_MEDALLION.id),
    },
    {
      spell: SPELLS.TOME_OF_UNRAVELING_SANITY_DAMAGE,
      getCooldown: haste => 60,
      isActive: combatant => combatant.hasTrinket(ITEMS.TOME_OF_UNRAVELING_SANITY.id),
    },
    {
      spell: SPELLS.VELENS_FUTURE_SIGHT,
      getCooldown: haste => 75,
      isActive: combatant => combatant.hasTrinket(ITEMS.VELENS_FUTURE_SIGHT.id),
    },
    {
      spell: SPELLS.SUMMON_DREAD_REFLECTION,
      getCooldown: haste => 45,
      isActive: combatant => combatant.hasTrinket(ITEMS.SPECTER_OF_BETRAYAL.id),
    },
    {
      spell: SPELLS.CEASELESS_TOXIN,
      getCooldown: haste => 60,//TODO: add detection if target has died and reduced cooldown
      isActive: combatant => combatant.hasTrinket(ITEMS.VIAL_OF_CEASELESS_TOXINS.id),
    },
  ];

  suggestions(when) {
    const castEfficiency = getCastEfficiency(this.constructor.CPM_ABILITIES, this.owner);
    castEfficiency.forEach(cpm => {
      if (cpm.ability.noSuggestion || cpm.castEfficiency === null) {
        return;
      }
      when(cpm.castEfficiency).isLessThan(cpm.recommendedCastEfficiency)
        .addSuggestion((suggest, actual, recommended) => {
          return suggest(Try to cast <SpellLink id={cpm.ability.spell.id} /> more often. {cpm.ability.extraSuggestion || ''})
            .icon(cpm.ability.spell.icon)
            .actual(`${cpm.casts} out of ${cpm.maxCasts} possible casts; ${formatPercentage(actual)}% cast efficiency`)
            .recommended(`>${formatPercentage(recommended)}% is recommended`)
            .regular(recommended - 0.05).major(recommended - 0.15).staticImportance(cpm.ability.importance);
        });
    });
  }
  tab() {
    return {
      title: 'Cast efficiency',
      url: 'cast-efficiency',
      render: () => (
        <Tab title="Cast efficiency">
          <CastEfficiencyComponent
            categories={this.constructor.SPELL_CATEGORIES}
            abilities={getCastEfficiency(this.constructor.CPM_ABILITIES, this.owner)}
          />
        </Tab>
      ),
    };
  }
}

export default CastEfficiency;
<commit_msg>Delete the snarky comment, will try to rework<commit_after>import React from 'react';

import SpellLink from 'common/SpellLink';
import { formatPercentage } from 'common/format';

import Module from 'Parser/Core/Module';
import getCastEfficiency from 'Parser/Core/getCastEfficiency';

import Tab from 'Main/Tab';
import CastEfficiencyComponent from 'Main/CastEfficiency';

import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';

class CastEfficiency extends Module {
  static SPELL_CATEGORIES = [
    {
      ROTATIONAL: 'Rotational Spell',
    },
    {
      ROTATIONAL_AOE: 'Spell (AOE)',
    },
    {
      COOLDOWNS: 'Cooldown',
    },
    {
      OTHERS: 'Spell',
    },
  ];
  static CPM_ABILITIES = [
    //Shared trinkets and legendaries
    {
      spell: SPELLS.CLEANSING_MATRIX,
      getCooldown: haste => 60,
      isActive: combatant => combatant.hasTrinket(ITEMS.ARCHIVE_OF_FAITH.id),
    },
    {
      spell: SPELLS.GUIDING_HAND,
      charges: 2,
      getCooldown: haste => 120,
      isActive: combatant => combatant.hasTrinket(ITEMS.DECEIVERS_GRAND_DESIGN.id),
    },
    {
      spell: SPELLS.GNAWED_THUMB_RING,
      getCooldown: haste => 180,
      isActive: combatant => combatant.hasFinger(ITEMS.GNAWED_THUMB_RING.id),
    },
    {
      spell: SPELLS.KILJAEDENS_BURNING_WISH_CAST,
      getCooldown: haste => 75,
      isActive: combatant => combatant.hasTrinket(ITEMS.KILJAEDENS_BURNING_WISH.id),
    },
    {
      spell: SPELLS.SPECTRAL_OWL,
      getCooldown: haste => 120,
      isActive: combatant => combatant.hasTrinket(ITEMS.TARNISHED_SENTINEL_MEDALLION.id),
    },
    {
      spell: SPELLS.TOME_OF_UNRAVELING_SANITY_DAMAGE,
      getCooldown: haste => 60,
      isActive: combatant => combatant.hasTrinket(ITEMS.TOME_OF_UNRAVELING_SANITY.id),
    },
    {
      spell: SPELLS.VELENS_FUTURE_SIGHT,
      getCooldown: haste => 75,
      isActive: combatant => combatant.hasTrinket(ITEMS.VELENS_FUTURE_SIGHT.id),
    },
    {
      spell: SPELLS.SUMMON_DREAD_REFLECTION,
      getCooldown: haste => 45,
      isActive: combatant => combatant.hasTrinket(ITEMS.SPECTER_OF_BETRAYAL.id),
    },
    {
      spell: SPELLS.CEASELESS_TOXIN,
      getCooldown: haste => 60,//TODO: add detection if target has died and reduced cooldown
      isActive: combatant => combatant.hasTrinket(ITEMS.VIAL_OF_CEASELESS_TOXINS.id),
    },
  ];

  suggestions(when) {
    const castEfficiency = getCastEfficiency(this.constructor.CPM_ABILITIES, this.owner);
    castEfficiency.forEach(cpm => {
      if (cpm.ability.noSuggestion || cpm.castEfficiency === null) {
        return;
      }
      when(cpm.castEfficiency).isLessThan(cpm.recommendedCastEfficiency)
        .addSuggestion((suggest, actual, recommended) => {
          return suggest(Try to cast <SpellLink id={cpm.ability.spell.id} /> more often. {cpm.ability.extraSuggestion || ''})
            .icon(cpm.ability.spell.icon)
            .actual(`${cpm.casts} out of ${cpm.maxCasts} possible casts; ${formatPercentage(actual)}% cast efficiency`)
            .recommended(`>${formatPercentage(recommended)}% is recommended`)
            .regular(recommended - 0.05).major(recommended - 0.15).staticImportance(cpm.ability.importance);
        });
    });
  }
  tab() {
    return {
      title: 'Cast efficiency',
      url: 'cast-efficiency',
      render: () => (
        <Tab title="Cast efficiency">
          <CastEfficiencyComponent
            categories={this.constructor.SPELL_CATEGORIES}
            abilities={getCastEfficiency(this.constructor.CPM_ABILITIES, this.owner)}
          />
        </Tab>
      ),
    };
  }
}

export default CastEfficiency;
<|endoftext|>


<commit_before>/**
 * Подменяем и расширяем Backbone.View, добавляя в него милые методы для работы с селекторами, выбором элементов и
 * дата атрибутами
 * @class Backbone.ElementsView
 * @extends Backbone.View
 */
Backbone.ElementsView = Backbone.View.redefine(function (origin) {
    return /**@lends Backbone.View*/{

        setElement: function () {
            origin.setElement.apply(this, arguments);

            /**
             * Дата атрибуты {@link Backbone.View.$el} в camelCase
             * @type {Object.<string, string>}
             * @protected
             */
            this._data = this.$el.data() || {};
            this._resetCaches();
            return this;
        },

        /**
         * Сбрасывает кэши клаасов, селекторов и элементов
         * @private
         */
        _resetCaches: function () {

            /**
             * Здесь сохраняются закешированные классы, полученные из
             * {@link Backbone.ElementsView._classes}
             * @type {Object.<string, string>}
             * @private
             */
            this._cachedClasses = this._classes();

            /**
             * Здесь сохраняются закешированные селекторы, в том числе полученные из
             * {@link Backbone.ElementsView._classes}
             * @type {Object.<string, string>}
             * @private
             */
            this._cachedSelectors = this._selectors();

            /**
             * Кеш для {@link Backbone.ElementsView._elem}
             * @type {Object.<string, jQuery>}
             * @private
             */
            this._cachedElements = {};
        },

        /**
         * CSS классы используемые во вьюшке складываются сюда. Ключи - имена элементов, значение - названия классов.
         * Свойство можно переопределять в наследуемых классах
         * @returns {Object.<string, string>}
         * @protected
         * @example
         *     _classes: function(){
         *         return {
         *             table: 'js-table'
         *         };
         *     }
         *
         */
        _classes: function () {
            return {};
        },

        /**
         * Возвращает css класс элемента по его имени, классы берутся из {@link Backbone.ElementsView._classes}
         * @param {String} name название элемента
         * @returns {String} css класс
         * @throws {Error} если название елемента нигде не найдено
         * @protected
         */
        _class: function (name) {
            var cl = this._cachedClasses[name];
            if (cl == null) {
                throw new Error('Selector for ' + name + ' does not found');
            }

            return cl;
        },

        /**
         * Селекторы используемые во вьюшке описываются здесь. Метод надо переопределять в наследуемых классах
         * @returns {Object.<string, string>} Ключи - названия элементов, значения - селекторы
         * @protected
         * @example
         *      _selectors: function(){
         *          return {
         *              map: '.google-map:first'
         *          };
         *      }
         *
         */
        _selectors: function () {
            return {};
        },

        /**
         * Возвращает селектор элемента по его имени, селекторы берутся из {@link Backbone.ElementsView._selectors} или
         * {@link Backbone.ElementsView._classes}
         * @param {String} name название элемента
         * @returns {String} селектор
         * @protected
         * @example
         *      events: function(){
         *          var events = {};
         *          events['click ' + this._selector('map')] = this._clickOnMap;
         *          events['click ' + this._selector('table')] = this._clickOnTable;
         *          return events;
         *      }
         *
         */
        _selector: function (name) {
            var args = null,
                cacheKey = this._getCacheKey.apply(this, arguments),
                selector = this._cachedSelectors[cacheKey];

            if (selector == null) {
                if (!this._cachedSelectors[name] && this._cachedClasses[name]) {
                    selector = '.' + this._class(name);
                    this._cachedSelectors[name] = selector;
                }

                args = Array.prototype.slice.call(arguments, 1);
                if (args.length) {
                    args.unshift(this._cachedSelectors[name]);
                    selector = _.sprintf.apply(_, args);
                    this._cachedSelectors[cacheKey] = selector;
                }
            }
            return selector;
        },

        /**
         * @param {String} name
         * @returns {Boolean}
         * @protected
         */
        _hasSelector: function (name) {
            return !!(this._cachedClasses[name] || this._cachedSelectors[name]);
        },

        /**
         * Возвращает jQuery коллекцию по имени из {@link Backbone.ElementsView._selectors} или
         * {@link Backbone.ElementsView._classes}. Кэширует результаты, для сброса кеша см.
         * {@link Backbone.ElementsView._dropElemCache}
         * @param {String} name название элемента
         * @returns {jQuery}
         * @protected
         * @example
         *     this._elem('table').hide();
         *
         */
        _elem: function (name) {
            var cacheKey = this._getCacheKey.apply(this, arguments),
                $elem = this._cachedElements[cacheKey];

            if ($elem != null) {
                return $elem;
            }

            $elem = this._findElem.apply(this, arguments);
            this._cachedElements[cacheKey] = $elem;

            return $elem;
        },

        /**
         * @param name
         * @returns {string}
         * @private
         */
        _getCacheKey: function (name) {
            return Array.prototype.join.call(arguments, '|');
        },

        /**
         * Finds element without using cache
         * @param {string} name
         * @returns {jQuery}
         * @protected
         */
        _findElem: function (name) {
            return this.$(this._selector.apply(this, arguments));
        },

        /**
         * Replaces element with passed content
         * @param {string} name
         * @param {HTMLElement|jQuery|string} content
         * @protected
         */
        _replaceElem: function (name, content) {
            this._elem(name).replaceWith(content);
            this._dropElemCache(name);
        },

        /**
         * Трет кеш для {@link Backbone.ElementsView._elem}
         * @param {String} [name] имя элемента для которого надо убрать кеш, если не задано - сотрется весь кеш
         * @protected
         */
        _dropElemCache: function (name) {
            if (name != null) {
                delete this._cachedElements[this._getCacheKey.apply(this, arguments)];
            }
            else {
                this._cachedElements = {};
            }
            return this;
        },

        /**
         * Возвращает дата атрибуты для элемента по его имени и имени атрибута
         * @param {String} name имя элемента
         * @param {String} [attr] имя атрибута, если не задано, то вернется объект со всеми дата атрибутами для элемента
         * @returns {*|Object}
         * @protected
         */
        _getElemData: function (name, attr) {
            var data = this._elem(name).data();
            if (attr == null) {
                return data;
            }

            return data[attr];
        },

        /**
         * Совместимость с Backbone.Decorate
         */
        beforeDecorate: function () {
            this._resetCaches();

            if (typeof origin.beforeDecorate == "function") {
                origin.beforeDecorate.apply(this, arguments);
            }
        }
    }
});<commit_msg>PSDJS-7018 Backbone.View.Elements _selector method must throw an error if there is no  selector with specified name<commit_after>/**
 * Подменяем и расширяем Backbone.View, добавляя в него милые методы для работы с селекторами, выбором элементов и
 * дата атрибутами
 * @class Backbone.ElementsView
 * @extends Backbone.View
 */
Backbone.ElementsView = Backbone.View.redefine(function (origin) {
    return /**@lends Backbone.View*/{

        setElement: function () {
            origin.setElement.apply(this, arguments);

            /**
             * Дата атрибуты {@link Backbone.View.$el} в camelCase
             * @type {Object.<string, string>}
             * @protected
             */
            this._data = this.$el.data() || {};
            this._resetCaches();
            return this;
        },

        /**
         * Сбрасывает кэши клаасов, селекторов и элементов
         * @private
         */
        _resetCaches: function () {

            /**
             * Здесь сохраняются закешированные классы, полученные из
             * {@link Backbone.ElementsView._classes}
             * @type {Object.<string, string>}
             * @private
             */
            this._cachedClasses = this._classes();

            /**
             * Здесь сохраняются закешированные селекторы, в том числе полученные из
             * {@link Backbone.ElementsView._classes}
             * @type {Object.<string, string>}
             * @private
             */
            this._cachedSelectors = this._selectors();

            /**
             * Кеш для {@link Backbone.ElementsView._elem}
             * @type {Object.<string, jQuery>}
             * @private
             */
            this._cachedElements = {};
        },

        /**
         * CSS классы используемые во вьюшке складываются сюда. Ключи - имена элементов, значение - названия классов.
         * Свойство можно переопределять в наследуемых классах
         * @returns {Object.<string, string>}
         * @protected
         * @example
         *     _classes: function(){
         *         return {
         *             table: 'js-table'
         *         };
         *     }
         *
         */
        _classes: function () {
            return {};
        },

        /**
         * Возвращает css класс элемента по его имени, классы берутся из {@link Backbone.ElementsView._classes}
         * @param {String} name название элемента
         * @returns {String} css класс
         * @throws {Error} если название елемента нигде не найдено
         * @protected
         */
        _class: function (name) {
            var cl = this._cachedClasses[name];
            if (cl == null) {
                throw new Error('Selector for ' + name + ' does not found');
            }

            return cl;
        },

        /**
         * Селекторы используемые во вьюшке описываются здесь. Метод надо переопределять в наследуемых классах
         * @returns {Object.<string, string>} Ключи - названия элементов, значения - селекторы
         * @protected
         * @example
         *      _selectors: function(){
         *          return {
         *              map: '.google-map:first'
         *          };
         *      }
         *
         */
        _selectors: function () {
            return {};
        },

        /**
         * Возвращает селектор элемента по его имени, селекторы берутся из {@link Backbone.ElementsView._selectors} или
         * {@link Backbone.ElementsView._classes}
         * @param {String} name название элемента
         * @returns {String} селектор
         * @protected
         * @example
         *      events: function(){
         *          var events = {};
         *          events['click ' + this._selector('map')] = this._clickOnMap;
         *          events['click ' + this._selector('table')] = this._clickOnTable;
         *          return events;
         *      }
         *
         */
        _selector: function (name) {
            var args = null,
                cacheKey = this._getCacheKey.apply(this, arguments),
                selector = this._cachedSelectors[cacheKey];

            if (selector == null) {
                if (!this._cachedSelectors[name]) {
                    selector = '.' + this._class(name);
                    this._cachedSelectors[name] = selector;
                }

                args = Array.prototype.slice.call(arguments, 1);
                if (args.length) {
                    args.unshift(this._cachedSelectors[name]);
                    selector = _.sprintf.apply(_, args);
                    this._cachedSelectors[cacheKey] = selector;
                }
            }
            return selector;
        },

        /**
         * @param {String} name
         * @returns {Boolean}
         * @protected
         */
        _hasSelector: function (name) {
            return !!(this._cachedClasses[name] || this._cachedSelectors[name]);
        },

        /**
         * Возвращает jQuery коллекцию по имени из {@link Backbone.ElementsView._selectors} или
         * {@link Backbone.ElementsView._classes}. Кэширует результаты, для сброса кеша см.
         * {@link Backbone.ElementsView._dropElemCache}
         * @param {String} name название элемента
         * @returns {jQuery}
         * @protected
         * @example
         *     this._elem('table').hide();
         *
         */
        _elem: function (name) {
            var cacheKey = this._getCacheKey.apply(this, arguments),
                $elem = this._cachedElements[cacheKey];

            if ($elem != null) {
                return $elem;
            }

            $elem = this._findElem.apply(this, arguments);
            this._cachedElements[cacheKey] = $elem;

            return $elem;
        },

        /**
         * @param name
         * @returns {string}
         * @private
         */
        _getCacheKey: function (name) {
            return Array.prototype.join.call(arguments, '|');
        },

        /**
         * Finds element without using cache
         * @param {string} name
         * @returns {jQuery}
         * @protected
         */
        _findElem: function (name) {
            return this.$(this._selector.apply(this, arguments));
        },

        /**
         * Replaces element with passed content
         * @param {string} name
         * @param {HTMLElement|jQuery|string} content
         * @protected
         */
        _replaceElem: function (name, content) {
            this._elem(name).replaceWith(content);
            this._dropElemCache(name);
        },

        /**
         * Трет кеш для {@link Backbone.ElementsView._elem}
         * @param {String} [name] имя элемента для которого надо убрать кеш, если не задано - сотрется весь кеш
         * @protected
         */
        _dropElemCache: function (name) {
            if (name != null) {
                delete this._cachedElements[this._getCacheKey.apply(this, arguments)];
            }
            else {
                this._cachedElements = {};
            }
            return this;
        },

        /**
         * Возвращает дата атрибуты для элемента по его имени и имени атрибута
         * @param {String} name имя элемента
         * @param {String} [attr] имя атрибута, если не задано, то вернется объект со всеми дата атрибутами для элемента
         * @returns {*|Object}
         * @protected
         */
        _getElemData: function (name, attr) {
            var data = this._elem(name).data();
            if (attr == null) {
                return data;
            }

            return data[attr];
        },

        /**
         * Совместимость с Backbone.Decorate
         */
        beforeDecorate: function () {
            this._resetCaches();

            if (typeof origin.beforeDecorate == "function") {
                origin.beforeDecorate.apply(this, arguments);
            }
        }
    }
});<|endoftext|>


<commit_before>$.fn.getSelector=function(){var e=this[0];if(!e.tagName){return""}var t=$(e);var n=t.attr("id");if(n){return"#"+n}var r=t.attr("class");var i;if(r){i="."+$.trim(r).replace(/\s/gi,".")}var s;var o=t.parent();var u=o.children();var a=false;if(i&&u.filter(i).length==1){s=i}else if(u.filter(e.tagName).length==1){s=e.tagName}else{s=":nth("+$(this).index()+")";a=true}if(!a){for(ancestor$=o.parent();ancestor$.length==1&&ancestor$.find(s).length==1;o=ancestor$,ancestor$=ancestor$.parent());if(ancestor$.length==0){return s}}return o.getSelector()+" > "+s}(function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else if(typeof exports==="object"){e(require("jquery"))}else{e(jQuery)}}(function(e){function n(e){return u.raw?e:encodeURIComponent(e)}function r(e){return u.raw?e:decodeURIComponent(e)}function i(e){return n(u.json?JSON.stringify(e):String(e))}function s(e){if(e.indexOf('"')===0){e=e.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\")}try{e=decodeURIComponent(e.replace(t," "));return u.json?JSON.parse(e):e}catch(n){}}function o(t,n){var r=u.raw?t:s(t);return e.isFunction(n)?n(r):r}var t=/\+/g;var u=e.cookie=function(t,s,a){if(arguments.length>1&&!e.isFunction(s)){a=e.extend({},u.defaults,a);if(typeof a.expires==="number"){var f=a.expires,l=a.expires=new Date;l.setTime(+l+f*864e5)}return document.cookie=[n(t),"=",i(s),a.expires?"; expires="+a.expires.toUTCString():"",a.path?"; path="+a.path:"",a.domain?"; domain="+a.domain:"",a.secure?"; secure":""].join("")}var c=t?undefined:{};var h=document.cookie?document.cookie.split("; "):[];for(var p=0,d=h.length;p<d;p++){var v=h[p].split("=");var m=r(v.shift());var g=v.join("=");if(t&&t===m){c=o(g,s);break}if(!t&&(g=o(g))!==undefined){c[m]=g}}return c};u.defaults={};e.removeCookie=function(t,n){if(e.cookie(t)===undefined){return false}e.cookie(t,"",e.extend({},n,{expires:-1}));return!e.cookie(t)}}));var SnailTracker=Object.create({api_key:null,api_url:null,url:function(e){return[this.api_url,e].join("/")},initialize:function(){var e=this;this.session.initialize(this);this.actions.initialize(this);$("body").on("click focus blur",function(t){e.actions.create(t,$(t.target))});$("body").on("change",":input",function(t){e.actions.create(t,$(t.target))})},cookies:{session:"snailtracker-session"}});SnailTracker.actions=Object.create({snailtracker:null,initialize:function(e){this.snailtracker=e},create:function(e,t){var n=t.getSelector();var r=e.type;var i=t.text();var s=this;var o={session_api_key:s.snailtracker.session.id,url:window.location.origin,keycode:e.keyCode,action_type_name:r,target_text:i,target_value:$(e.target).secure_val(),selector:n,"char":null};if($.inArray(e.type,["keyup","keydown"])){o.char=String.fromCharCode(e.which)}$.post(this.snailtracker.url("actions"),$.param({api_key:s.snailtracker.api_key,app_action:o}))}});SnailTracker.session=Object.create({snailtracker:null,id:null,create:function(){var e=this;$.post(this.snailtracker.url("sessions"),$.param({api_key:e.snailtracker.api_key,session:{user_id:e.snailtracker.user_id,user_name:e.snailtracker.user_name,email:e.snailtracker.email}}),function(t){e.id=t["session"]["api_key"];e.set_cookie()})},set_cookie:function(){$.cookie(this.snailtracker.cookies.session,this.id,{expires:1})},initialize:function(e){this.snailtracker=e;if($.cookie(this.snailtracker.cookies.session)){this.id=$.cookie(this.snailtracker.cookies.session);this.set_cookie()}else{this.create()}}});$.fn.secure_val=function(){var e="XXXXXX";var t=this.val();var n=t.replace(/[^0-9]/g,"");if(n.length>=12&&n.length<=20){return e}if(this.attr("type")=="password"){return e}return t}
<commit_msg>Deprecated non-generated min js file.<commit_after><|endoftext|>


<commit_before>'use strict';

var profile = require('../../lib/profile');

module.exports = profile().on('error', function (err) {
    process.stdout.write('\u001b[41m\u001b[37mWARNING\u001b[0m ' + err.message + ':\n');
    process.stdout.write('  - fix the file manually: "cortex config -e"\n');
    process.stdout.write('  - reset the configuration file: "cortex config --unset-all".\n\n');

}).init();<commit_msg>profile: adjust output; related to #74<commit_after>'use strict';

var profile = require('../../lib/profile');

module.exports = profile().on('error', function (err) {
    process.stdout.write('\u001b[41m\u001b[37mWARNING\u001b[0m ' + err.message + ':\n');
    process.stdout.write('  - fix the file manually: "cortex config -e"\n');
    process.stdout.write('  - or reset it: "cortex config --unset-all".\n\n');

}).init();<|endoftext|>


<commit_before>// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/new-module-imports');
const RuleTester = require('eslint').RuleTester;

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const eslintTester = new RuleTester();
eslintTester.run('new-module-imports', rule, {
  valid: [
    { code: 'Ember.onerror = function() {};' },
    { code: 'Ember.MODEL_FACTORY_INJECTIONS = true;' },
    { code: 'console.log(Ember.VERSION);' },
    { code: 'if (Ember.testing) {}' },
  ],
  invalid: [
    {
      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
      code: 'export default Ember.Service;',
      errors: [{ message: 'Use  import Service from \'@ember/service\';  instead of using  Ember.Service' }],
    },
    {
      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
      code: 'export default Ember.Service.extend({});',
      errors: [{ message: 'Use  import Service from \'@ember/service\';  instead of using  Ember.Service' }],
    },
    {
      code: 'Ember.computed();',
      errors: [{ message: 'Use  import { computed } from \'@ember/object\';  instead of using  Ember.computed' }],
    },
    {
      code: 'Ember.computed.not();',
      errors: [{ message: 'Use  import { not } from \'@ember/object/computed\';  instead of using  Ember.computed.not' }],
    },
    {
      code: 'Ember.inject.service(\'foo\');',
      errors: [{ message: 'Use  import { inject } from \'@ember/service\';  instead of using  Ember.inject.service' }],
    },
    {
      code: 'var Router = Ember.Router.extend({});',
      errors: [{ message: 'Use  import Router from \'@ember/routing/router\';  instead of using  Ember.Router' }],
    },
    {
      code: 'Ember.$(\'.foo\')',
      errors: [{ message: 'Use  import $ from \'jquery\';  instead of using  Ember.$' }],
    },
    {
      code: 'new Ember.RSVP.Promise();',
      errors: [{ message: 'Use  import RSVP from \'rsvp\';  instead of using  Ember.RSVP' }],
    },
  ],
});
<commit_msg>Adjust "new-module-imports" rule tests<commit_after>// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/new-module-imports');
const RuleTester = require('eslint').RuleTester;

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const eslintTester = new RuleTester();
eslintTester.run('new-module-imports', rule, {
  valid: [
    { code: 'Ember.onerror = function() {};' },
    { code: 'Ember.MODEL_FACTORY_INJECTIONS = true;' },
    { code: 'console.log(Ember.VERSION);' },
    { code: 'if (Ember.testing) {}' },
  ],
  invalid: [
    {
      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
      code: 'export default Ember.Service;',
      errors: [{ message: 'Use  import Service from \'@ember/service\';  instead of using  Ember.Service' }],
    },
    {
      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
      code: 'export default Ember.Service.extend({});',
      errors: [{ message: 'Use  import Service from \'@ember/service\';  instead of using  Ember.Service' }],
    },
    {
      code: 'Ember.computed();',
      errors: [{ message: 'Use  import { computed } from \'@ember/object\';  instead of using  Ember.computed' }],
    },
    {
      code: 'Ember.computed.not();',
      errors: [{ message: 'Use  import { not } from \'@ember/object/computed\';  instead of using  Ember.computed.not' }],
    },
    {
      code: 'Ember.inject.service(\'foo\');',
      errors: [{ message: 'Use  import { inject } from \'@ember/service\';  instead of using  Ember.inject.service' }],
    },
    {
      code: 'var Router = Ember.Router.extend({});',
      errors: [{ message: 'Use  import EmberRouter from \'@ember/routing/router\';  instead of using  Ember.Router' }],
    },
    {
      code: 'Ember.$(\'.foo\')',
      errors: [{ message: 'Use  import $ from \'jquery\';  instead of using  Ember.$' }],
    },
    {
      code: 'new Ember.RSVP.Promise();',
      errors: [{ message: 'Use  import { Promise } from \'rsvp\';  instead of using  Ember.RSVP.Promise' }],
    },
  ],
});
<|endoftext|>


<commit_before>import React from "react";
import SelectLabel from "../framework/select-label";
import DatePicker from "react-datepicker";
import _ from "lodash";
import { select} from "../../globalStyles";
import { connect } from "react-redux";
import { CHANGE_ANIMATION_TIME } from "../../actions/types";//*
import { CHANGE_ANIMATION_START } from "../../actions/types"; //*
import { CHANGE_ANIMATION_CUMULATIVE } from "../../actions/types"; //*
import { changeColorBy } from "../../actions/colors";
import { modifyURLquery } from "../../util/urlHelpers";
import { analyticsControlsEvent } from "../../util/googleAnalytics";
import moment from 'moment';
import { controlsWidth } from "../../util/globals";
import { dataFont, darkGrey } from "../../globalStyles";
import Toggle from "./toggle";

moment.updateLocale("en", {
  longDateFormat: {
    L: "YYYY-MM-DD"
  }
});

@connect((state) => {
  return {
    dateMin: state.controls.dateMin,
    dateMax: state.controls.dateMax,
    absoluteDateMin: state.controls.absoluteDateMin,
    absoluteDateMax: state.controls.absoluteDateMax,
    dateScale: state.controls.dateScale,
    dateFormat: state.controls.dateFormat,
    // mapAnimationStartDate: state.controls.mapAnimationStartDate,
  };
})

class MapAnimationStartDatePicker extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      lastSliderUpdateTime: Date.now()
    }
  }

  numericToCalendar(numDate) {
    return(this.props.dateFormat(this.props.dateScale.invert(numDate)));
  }

  calendarToNumeric(calDate) {
    return(this.props.dateScale(this.props.dateFormat.parse(calDate)));
  };

  validateSelectedAnimationStartDate(input) {

    /* duplicated */
    let absoluteDateMinStr = this.props.absoluteDateMin;
    let absoluteDateMaxStr = this.props.absoluteDateMax;
    let absoluteDateMin = moment(absoluteDateMinStr, "YYYY-MM-DD");
    let absoluteDateMax = moment(absoluteDateMaxStr, "YYYY-MM-DD");
    // let numberDays = moment.duration(absoluteDateMax.diff(absoluteDateMin)).asDays();
    // let maxDate = absoluteDateMax.diff(numberDays * 0.1, "days") // Latest start date is end date - 10%

    let inputDate = moment(input, "YYYY-MM-DD")

    let startDate;
    if (inputDate <= absoluteDateMin){
      startDate = absoluteDateMinStr;
    } else if (inputDate >= absoluteDateMax){
      startDate = absoluteDateMaxStr;
    } else {
      startDate = moment(input).format("YYYY-MM-DD")
    }

    return startDate;

  }

  updateFromPicker(userSelectedDateAsMoment) {

      analyticsControlsEvent("change-animation-start-date");
      this.props.dispatch({
        type: CHANGE_ANIMATION_START,
        data: this.validateSelectedAnimationStartDate(userSelectedDateAsMoment)
      }); //might need to convert format
  }

  render() {
    // minDistance={(this.props.absoluteMaxVal - this.props.absoluteMinVal) / 10.0}
    // onChange={this.updateFromSlider.bind(this, true)}

    // const absoluteMin = this.props.absoluteDateMin;
    // const absoluteMax = this.props.absoluteDateMax;
    // const selectedMin = this.props.dateMin;
    // const selectedMax = this.props.dateMax;
    //
    // const absoluteMinNumDate = this.calendarToNumeric(absoluteMin);
    // const absoluteMaxNumDate = this.calendarToNumeric(absoluteMax);
    // const selectedMinNumDate = this.calendarToNumeric(selectedMin);
    // const selectedMaxNumDate = this.calendarToNumeric(selectedMax);

    return (

        <DatePicker
          dateFormat="YYYY/MM/DD"

          selected={moment(this.props.mapAnimationStartDate)}
          onChange={this.updateFromPicker.bind(this)}
        />

    );
  }
}

@connect((state) => {
  return {
    metadata: state.metadata.metadata,
    mapAnimationStartDate: state.controls.mapAnimationStartDate,
    mapAnimationDurationInMilliseconds: state.controls.mapAnimationDurationInMilliseconds,
    mapAnimationCumulative: state.controls.mapAnimationCumulative
  };
})
class MapAnimationControls extends React.Component {

  static contextTypes = {
    router: React.PropTypes.object.isRequired
  }

  // checkAndTransformAnimationDuration(input) {
  //
  //   const minimumMapAnimationTimeInSeconds = 5;
  //   const maximumMapAnimationTimeInSeconds = 120;
  //   let validatedInput = input;
  //
  //   if (input < minimumMapAnimationTimeInSeconds) {
  //     validatedInput = minimumMapAnimationTimeInSeconds;
  //   } else if (input > maximumMapAnimationTimeInSeconds) {
  //     validatedInput = maximumMapAnimationTimeInSeconds;
  //   }
  //
  //   return validatedInput * 1000 // ship milliseconds instead of seconds to reducer
  // }

  handleChangeAnimationTimeClicked(userSelectedDuration) {
    return () => {
      const loopRunning = window.NEXTSTRAIN && window.NEXTSTRAIN.mapAnimationLoop;
      if (!loopRunning) {
        analyticsControlsEvent("change-animation-time");
        let duration;

        if (userSelectedDuration === "slow") {
          duration = 60000;
        } else if (userSelectedDuration === "medium") {
          duration = 30000;
        } else if (userSelectedDuration === "fast") {
          duration = 15000;
        } else {
          console.warn("Odd... controls/map-animation.js tried to set an animation speed we don't offer...")
        }

        // if (window.NEXTSTRAIN && window.NEXTSTRAIN.mapAnimationLoop)

        /* cast string to num, the see if its an integer, ie., don't send the action if they type 'd' */
        this.props.dispatch({
          type: CHANGE_ANIMATION_TIME,
          data: duration /* this.checkAndTransformAnimationDuration(+e.target.value) */
        });
      }
    }
  }

  render() {

    /* duplicated */
    let absoluteDateMinStr = this.props.absoluteDateMin;
    let absoluteDateMaxStr = this.props.absoluteDateMax;
    let absoluteDateMin = moment(absoluteDateMinStr, 'YYYY-MM-DD');
    let absoluteDateMax = moment(absoluteDateMaxStr, 'YYYY-MM-DD');
    let numberDays = moment.duration(absoluteDateMax.diff(absoluteDateMin)).asDays();

    return (

        <SelectLabel text="Animation speed"/>
        <button
          style={{
            padding: "5px 10px",
            outline: "none",
            color: this.props.mapAnimationDurationInMilliseconds === 60000 ? "white" : "black",
            backgroundColor: this.props.mapAnimationDurationInMilliseconds === 60000 ? "rgb(80, 151, 186)" : "white",
            border: this.props.mapAnimationDurationInMilliseconds === 60000 ? "1px solid rgb(80, 151, 186)" : "1px solid lightgrey",
            marginRight: 10,
            borderRadius: 3,
            fontSize: 14,
          }}
          onClick={this.handleChangeAnimationTimeClicked("slow")}>
          Slow
        </button>
        <button
          style={{
            padding: "5px 10px",
            outline: "none",
            color: this.props.mapAnimationDurationInMilliseconds === 30000 ? "white" : "black",
            backgroundColor: this.props.mapAnimationDurationInMilliseconds === 30000 ? "rgb(80, 151, 186)" : "white",
            border: this.props.mapAnimationDurationInMilliseconds === 30000 ? "1px solid rgb(80, 151, 186)" : "1px solid lightgrey",
            marginRight: 10,
            borderRadius: 3,
            fontSize: 14,
          }}
          onClick={this.handleChangeAnimationTimeClicked("medium")}>
          Medium
        </button>
        <button
          style={{
            padding: "5px 10px",
            outline: "none",
            color: this.props.mapAnimationDurationInMilliseconds === 15000 ? "white" : "black",
            backgroundColor: this.props.mapAnimationDurationInMilliseconds === 15000 ? "rgb(80, 151, 186)" : "white",
            border: this.props.mapAnimationDurationInMilliseconds === 15000 ? "1px solid rgb(80, 151, 186)" : "1px solid lightgrey",
            marginRight: 10,
            borderRadius: 3,
            fontSize: 14,
          }}
          onClick={this.handleChangeAnimationTimeClicked("fast")}>
          Fast
        </button>

      {/*<SelectLabel text="Animation start date (click to change)"/>*/}
      {/*<MapAnimationStartDatePicker/>*/}

      <Toggle
        display={true}
        on={this.props.mapAnimationCumulative}
        callback={(e) => {
          analyticsControlsEvent("change-animation-cumulative");
          this.props.dispatch({ type: CHANGE_ANIMATION_CUMULATIVE, data: !this.props.mapAnimationCumulative });
          // modifyURLquery(this.context.router, {apt: e.target.value}, true);
        }}
        label="Animate cumulative history"
      />

    );
  }
}

export default MapAnimationControls

// max={absoluteDateMax.year().month().date()} //These probably need to be reformatted (or just kept as strings above)
// min={absoluteDateMin.year().month().date()}
<commit_msg>Restyle animation speed button and decruft animation controls<commit_after>import React from "react";
import SelectLabel from "../framework/select-label";
import { connect } from "react-redux";
import { CHANGE_ANIMATION_TIME } from "../../actions/types";//*
import { CHANGE_ANIMATION_START } from "../../actions/types"; //*
import { CHANGE_ANIMATION_CUMULATIVE } from "../../actions/types"; //*
import { modifyURLquery } from "../../util/urlHelpers";
import { analyticsControlsEvent } from "../../util/googleAnalytics";
import Toggle from "./toggle";
import {materialButton, materialButtonSelected} from "../../globalStyles";

@connect((state) => {
  return {
    metadata: state.metadata.metadata,
    mapAnimationStartDate: state.controls.mapAnimationStartDate,
    mapAnimationDurationInMilliseconds: state.controls.mapAnimationDurationInMilliseconds,
    mapAnimationCumulative: state.controls.mapAnimationCumulative
  };
})
class MapAnimationControls extends React.Component {

  static contextTypes = {
    router: React.PropTypes.object.isRequired
  }

  // checkAndTransformAnimationDuration(input) {
  //
  //   const minimumMapAnimationTimeInSeconds = 5;
  //   const maximumMapAnimationTimeInSeconds = 120;
  //   let validatedInput = input;
  //
  //   if (input < minimumMapAnimationTimeInSeconds) {
  //     validatedInput = minimumMapAnimationTimeInSeconds;
  //   } else if (input > maximumMapAnimationTimeInSeconds) {
  //     validatedInput = maximumMapAnimationTimeInSeconds;
  //   }
  //
  //   return validatedInput * 1000 // ship milliseconds instead of seconds to reducer
  // }

  handleChangeAnimationTimeClicked(userSelectedDuration) {
    return () => {
      const loopRunning = window.NEXTSTRAIN && window.NEXTSTRAIN.mapAnimationLoop;
      if (!loopRunning) {
        analyticsControlsEvent("change-animation-time");
        let duration;

        if (userSelectedDuration === "slow") {
          duration = 60000;
        } else if (userSelectedDuration === "medium") {
          duration = 30000;
        } else if (userSelectedDuration === "fast") {
          duration = 15000;
        } else {
          console.warn("Odd... controls/map-animation.js tried to set an animation speed we don't offer...")
        }

        // if (window.NEXTSTRAIN && window.NEXTSTRAIN.mapAnimationLoop)

        /* cast string to num, the see if its an integer, ie., don't send the action if they type 'd' */
        this.props.dispatch({
          type: CHANGE_ANIMATION_TIME,
          data: duration /* this.checkAndTransformAnimationDuration(+e.target.value) */
        });
      }
    }
  }

  render() {

    return (

        <SelectLabel text="Animation speed"/>
        <button
          style={this.props.mapAnimationDurationInMilliseconds === 60000 ? materialButtonSelected : materialButton}
          onClick={this.handleChangeAnimationTimeClicked("slow")}>
          Slow
        </button>
        <button
          style={this.props.mapAnimationDurationInMilliseconds === 30000 ? materialButtonSelected : materialButton}
          onClick={this.handleChangeAnimationTimeClicked("medium")}>
          Medium
        </button>
        <button
          style={this.props.mapAnimationDurationInMilliseconds === 15000 ? materialButtonSelected : materialButton}
          onClick={this.handleChangeAnimationTimeClicked("fast")}>
          Fast
        </button>

      <Toggle
        display={true}
        on={this.props.mapAnimationCumulative}
        callback={(e) => {
          analyticsControlsEvent("change-animation-cumulative");
          this.props.dispatch({ type: CHANGE_ANIMATION_CUMULATIVE, data: !this.props.mapAnimationCumulative });
          // modifyURLquery(this.context.router, {apt: e.target.value}, true);
        }}
        label="Animate cumulative history"
      />

    );
  }
}

export default MapAnimationControls
<|endoftext|>


<commit_before>/**
 * GET /threesixty
 * Threesixty site
 */
exports.index = function(req, res) {
	// get show frontpage graph URL
	res.render('graph/show', {
		layout: 'threesixty',
		graphSrc: '/threesixty/featured',
		graphMinUrl: '/threesixty/featured',
		autoplay: true,
		hideEditButton: true,
		hideShareButton: true,
		hidePlayButton: true,
		meta : {
			bodyclass : 'bThreesixty b360'
		}
	});
}

exports.featured = function(req,res) {
	var featuredGraph = '/data/graph/eesn/flamingofront.json'
	switch (process.env.SITE) {
		case '360.vizor.io':
		case 'rc.vizor.io':
		case 'vizor.io':
		case '360vr.io':
			featuredGraph = '/data/graph/v/hrpfbjje86s5.json'
			break;
	}
	res.redirect(featuredGraph)
}<commit_msg>SITE to FQDN<commit_after>/**
 * GET /threesixty
 * Threesixty site
 */
exports.index = function(req, res) {
	// get show frontpage graph URL
	res.render('graph/show', {
		layout: 'threesixty',
		graphSrc: '/threesixty/featured',
		graphMinUrl: '/threesixty/featured',
		autoplay: true,
		hideEditButton: true,
		hideShareButton: true,
		hidePlayButton: true,
		meta : {
			bodyclass : 'bThreesixty b360'
		}
	});
}

exports.featured = function(req,res) {
	var featuredGraph = '/data/graph/eesn/flamingofront.json'
	switch (process.env.FQDN) {
		case '360.vizor.io':
		case 'rc.vizor.io':
		case 'vizor.io':
		case '360vr.io':
			featuredGraph = '/data/graph/v/hrpfbjje86s5.json'
			break;
	}
	res.redirect(featuredGraph)
}<|endoftext|>


<commit_before>var conf = {};

// Run the Kiwi server under a different user/group
conf.user = "";
conf.group = "";

// Log file location
conf.log = "kiwi.log";

/*
 * Server listen blocks
 */

// Do not edit this line!
conf.servers = [];

// Example server block
conf.servers.push({
    port:   7778,
    address: "0.0.0.0"
});

// Example SSL server block
//conf.servers.push({
//    port:     7777,
//    address: "0.0.0.0",
//
//    ssl:   true,
//    ssl_key: "server.key",
//    ssl_cert: "cert.pem"
//});

// Network interface for outgoing connections
conf.outgoing_address = {
    IPv4: '0.0.0.0'
    //IPv6: '::'
};

// Do we want to enable the built in Identd server?
conf.identd = {
    enabled: false,
    port: 113,
    address: "0.0.0.0"
};

// Where the client files are
conf.public_http = "client/";

// Max connections per connection. 0 to disable
conf.max_client_conns = 5;

// Max connections per server. 0 to disable.
// Setting is ignored if:
//   - There is a WEBIRC password configured for the server,
//   - Kiwi is configured to send the client's ip as a username for the server, or
//   - Kiwi is running in restricted server mode.
conf.max_server_conns = 0;

/*
* Default encoding to be used by the server
* As specified and limited to iconv-lite library support.
*/
conf.default_encoding = 'utf8';

/*
* Default GECOS (real name) for IRC connections
* %n will be replaced with the users nick
* %h will be replaced with the users hostname
*/
//conf.default_gecos = 'Web IRC Client';

/*
 * Client side plugins
 * Array of URLs that will be loaded into the browser when the client first loads up
 * See http://github.com/prawnsalad/KiwiIRC/wiki/Client-plugins
 */
conf.client_plugins = [
    // "http://server.com/kiwi/plugins/myplugin.html"
];

// Directory to find the server modules
conf.module_dir = "../server_modules/";

// Which modules to load
conf.modules = [];

// WebIRC passwords enabled for this server
conf.webirc_pass = {
    //"irc.network.com":  "configured_webirc_password",
    //"127.0.0.1":        "foobar"
};

// Some IRCDs require the clients IP via the username/ident
conf.ip_as_username = [
    //"irc.network.com",
    //"127.0.0.1"
];

// Whether to verify IRC servers' SSL certificates against built-in well-known certificate authorities
conf.reject_unauthorised_certificates = false;

/*
 * Reverse proxy settings
 * Reverse proxies that have been reported to work can be found at:
 *     https://kiwiirc.com/docs/installing/proxies
 */

// Whitelisted HTTP proxies in CIDR format
conf.http_proxies = ["127.0.0.1/32"];

// Header that contains the real-ip from the HTTP proxy
conf.http_proxy_ip_header = "x-forwarded-for";

// Base HTTP path to the KIWI IRC client (eg. /kiwi)
conf.http_base_path = "/kiwi";

/*
 * SOCKS (version 5) proxy settings
 * This feature is only available on node 0.10.0 and above.
 * Do not enable it if you're running 0.8 or below or Bad Things will happen.
 */
conf.socks_proxy = {};

// Enable proxying outbound connections through a SOCKS proxy
conf.socks_proxy.enabled = false;

// Proxy *all* outbound connections through a SOCKS proxy
conf.socks_proxy.all = false;

// Use SOCKS proxy for these hosts only (if conf.sock_proxy.all === false)
conf.socks_proxy.proxy_hosts = [
    "irc.example.com"
];

// Host and port for the SOCKS proxy
conf.socks_proxy.address = '127.0.0.1';
conf.socks_proxy.port = 1080;

// Username and password for the SOCKS proxy
// Set user to null to disable password authentication
conf.socks_proxy.user = null;
conf.socks_proxy.pass = null;

// Default quit message
conf.quit_message = "http://www.kiwiirc.com/ - A hand-crafted IRC client";

// Default settings for the client. These may be changed in the browser
conf.client = {
    server: 'irc.kiwiirc.com',
    port:    6697,
    ssl:     true,
    channel: '#kiwiirc',
    channel_key: '',
    nick:    'kiwi_?',
    settings: {
        theme: 'relaxed',
        channel_list_style: 'tabs',
        scrollback: 250,
        show_joins_parts: true,
        show_timestamps: false,
        mute_sounds: false,
        show_emoticons: true,
        rich_nicklist: false,
        rich_nicklist_gender_regexes: {'F': ['F001', ' f ', '/f/', '^f ', ' f$', '^f$'],
                                        'M': ['M002', ' h ', '/H/', ' m ', '^h ', ' h$', '^m ', ' m$', '^h$', '^m$',],
                                        'U': ['U003', '/U/']},
        rich_nicklist_track_asl: true,
        rich_nicklist_track_away: true,
        rich_nicklist_track_ircop: true,
        count_all_activity: true
    },
    window_title: 'Kiwi IRC'
};

// List of themes available for the user to choose from
conf.client_themes = [
    'relaxed',
    'mini',
    'cli',
    'basic'
];

// If set, the client may only connect to this 1 IRC server
//conf.restrict_server = "irc.kiwiirc.com";
//conf.restrict_server_port = 6667;
//conf.restrict_server_ssl = false;
//conf.restrict_server_channel = "#kiwiirc";
//conf.restrict_server_channel_key = "";
//conf.restrict_server_password = "";
//conf.restrict_server_nick = "kiwi_";

/*
 * Do not amend the below lines unless you understand the changes!
 */
module.exports.production = conf;
<commit_msg>Move ASL & status tracking to a new branch<commit_after>var conf = {};

// Run the Kiwi server under a different user/group
conf.user = "";
conf.group = "";

// Log file location
conf.log = "kiwi.log";

/*
 * Server listen blocks
 */

// Do not edit this line!
conf.servers = [];

// Example server block
conf.servers.push({
    port:   7778,
    address: "0.0.0.0"
});

// Example SSL server block
//conf.servers.push({
//    port:     7777,
//    address: "0.0.0.0",
//
//    ssl:   true,
//    ssl_key: "server.key",
//    ssl_cert: "cert.pem"
//});

// Network interface for outgoing connections
conf.outgoing_address = {
    IPv4: '0.0.0.0'
    //IPv6: '::'
};

// Do we want to enable the built in Identd server?
conf.identd = {
    enabled: false,
    port: 113,
    address: "0.0.0.0"
};

// Where the client files are
conf.public_http = "client/";

// Max connections per connection. 0 to disable
conf.max_client_conns = 5;

// Max connections per server. 0 to disable.
// Setting is ignored if:
//   - There is a WEBIRC password configured for the server,
//   - Kiwi is configured to send the client's ip as a username for the server, or
//   - Kiwi is running in restricted server mode.
conf.max_server_conns = 0;

/*
* Default encoding to be used by the server
* As specified and limited to iconv-lite library support.
*/
conf.default_encoding = 'utf8';

/*
* Default GECOS (real name) for IRC connections
* %n will be replaced with the users nick
* %h will be replaced with the users hostname
*/
//conf.default_gecos = 'Web IRC Client';

/*
 * Client side plugins
 * Array of URLs that will be loaded into the browser when the client first loads up
 * See http://github.com/prawnsalad/KiwiIRC/wiki/Client-plugins
 */
conf.client_plugins = [
    // "http://server.com/kiwi/plugins/myplugin.html"
];

// Directory to find the server modules
conf.module_dir = "../server_modules/";

// Which modules to load
conf.modules = [];

// WebIRC passwords enabled for this server
conf.webirc_pass = {
    //"irc.network.com":  "configured_webirc_password",
    //"127.0.0.1":        "foobar"
};

// Some IRCDs require the clients IP via the username/ident
conf.ip_as_username = [
    //"irc.network.com",
    //"127.0.0.1"
];

// Whether to verify IRC servers' SSL certificates against built-in well-known certificate authorities
conf.reject_unauthorised_certificates = false;

/*
 * Reverse proxy settings
 * Reverse proxies that have been reported to work can be found at:
 *     https://kiwiirc.com/docs/installing/proxies
 */

// Whitelisted HTTP proxies in CIDR format
conf.http_proxies = ["127.0.0.1/32"];

// Header that contains the real-ip from the HTTP proxy
conf.http_proxy_ip_header = "x-forwarded-for";

// Base HTTP path to the KIWI IRC client (eg. /kiwi)
conf.http_base_path = "/kiwi";

/*
 * SOCKS (version 5) proxy settings
 * This feature is only available on node 0.10.0 and above.
 * Do not enable it if you're running 0.8 or below or Bad Things will happen.
 */
conf.socks_proxy = {};

// Enable proxying outbound connections through a SOCKS proxy
conf.socks_proxy.enabled = false;

// Proxy *all* outbound connections through a SOCKS proxy
conf.socks_proxy.all = false;

// Use SOCKS proxy for these hosts only (if conf.sock_proxy.all === false)
conf.socks_proxy.proxy_hosts = [
    "irc.example.com"
];

// Host and port for the SOCKS proxy
conf.socks_proxy.address = '127.0.0.1';
conf.socks_proxy.port = 1080;

// Username and password for the SOCKS proxy
// Set user to null to disable password authentication
conf.socks_proxy.user = null;
conf.socks_proxy.pass = null;

// Default quit message
conf.quit_message = "http://www.kiwiirc.com/ - A hand-crafted IRC client";

// Default settings for the client. These may be changed in the browser
conf.client = {
    server: 'irc.kiwiirc.com',
    port:    6697,
    ssl:     true,
    channel: '#kiwiirc',
    channel_key: '',
    nick:    'kiwi_?',
    settings: {
        theme: 'relaxed',
        channel_list_style: 'tabs',
        scrollback: 250,
        show_joins_parts: true,
        show_timestamps: false,
        mute_sounds: false,
        show_emoticons: true,
        rich_nicklist: false,
        rich_nicklist_gender_regexes: {'F': ['F001', ' f ', '/f/', '^f ', ' f$', '^f$'],
                                        'M': ['M002', ' h ', '/H/', ' m ', '^h ', ' h$', '^m ', ' m$', '^h$', '^m$',],
                                        'U': ['U003', '/U/']},
        rich_nicklist_track_asl: true,
        rich_nicklist_track_away: true,
        rich_nicklist_track_ircop: true,
        count_all_activity: true
    },
    window_title: 'Kiwi IRC'
};

// List of themes available for the user to choose from
conf.client_themes = [
    'relaxed',
    'mini',
    'cli',
    'basic'
];

// If set, the client may only connect to this 1 IRC server
//conf.restrict_server = "irc.kiwiirc.com";
//conf.restrict_server_port = 6667;
//conf.restrict_server_ssl = false;
//conf.restrict_server_channel = "#kiwiirc";
//conf.restrict_server_channel_key = "";
//conf.restrict_server_password = "";
//conf.restrict_server_nick = "kiwi_";
//

/*
 * Do not amend the below lines unless you understand the changes!
 */
module.exports.production = conf;
<|endoftext|>


<commit_before>"use strict";

class ModelView {

    constructor(data) {

        if(data) this.data = data;

    }

    set data(newData) {
        this._data = newData;
        this._markup = undefined;
    }

    get data() {
        return this._data;
    }

    get markup() {
        if(!this._markup)
            this.parse();

        return (this._markup || "").trim();
    }

    get minified() {
        return ModelView.minify(this.markup);
    }

    parse() {

        console.log("Subclasses should implement how views are drawn. Do not call super. It does nothing.");

        return undefined;

    }

    static minify(html) {
        return html.replace(/\r?\n?/gim, "").replace(/\s{2,10000}/gim,"");
    }

}

class Layout extends ModelView {

    constructor(locals) {

        super(locals||{});

        this._locals = locals;

        if(!this._locals.renderPartial)
            this.parse()

    }

    parse() {

        console.log("You should write your common layout logic in a subclass of Layout. When you're done, simply call super with your rendered interstetials.");

        /*
         * In your View subclass
            var header = "...";
            var main = "...";
            var footer ="...";
            super.parse(
                header,
                main,
                footer
            )
         */

        /*
         * In your layout subclass

            var html = [].slice.call(arguments).join("");
            this._markup = html;

        */

    }

}

function viewEngine(app) {

    app.engine('es6', function (filePath, options, callback) {

        var partial = options.renderPartial;
        var layout = require(filePath);
        delete options["_locals"]; //circular

        var current = new layout(options);
        var env = process.env.NODE_ENV;
        var isDev = (env == "dev" || env == "development");

        if(partial) {
            var markup = current[partial](options);
            callback(undefined, isDev ? markup : ModelView.minify(markup));
            return;
        }

        callback(undefined, isDev ? current.markup : current.minified);

    });

}

export {ModelView as ModelView}
export {Layout as Layout}
export {viewEngine as viewEngine}<commit_msg>dont minify<commit_after>"use strict";

class ModelView {

    constructor(data) {

        if(data) this.data = data;

    }

    set data(newData) {
        this._data = newData;
        this._markup = undefined;
    }

    get data() {
        return this._data;
    }

    get markup() {
        if(!this._markup)
            this.parse();

        return (this._markup || "").trim();
    }

    get minified() {
        return ModelView.minify(this.markup);
    }

    parse() {

        console.log("Subclasses should implement how views are drawn. Do not call super. It does nothing.");

        return undefined;

    }

    static minify(html) {
        return html.replace(/\r?\n?/gim, "").replace(/\s{2,10000}/gim,"");
    }

}

class Layout extends ModelView {

    constructor(locals) {

        super(locals||{});

        this._locals = locals;

        if(!this._locals.renderPartial)
            this.parse()

    }

    parse() {

        console.log("You should write your common layout logic in a subclass of Layout. When you're done, simply call super with your rendered interstetials.");

        /*
         * In your View subclass
            var header = "...";
            var main = "...";
            var footer ="...";
            super.parse(
                header,
                main,
                footer
            )
         */

        /*
         * In your layout subclass

            var html = [].slice.call(arguments).join("");
            this._markup = html;

        */

    }

}

function viewEngine(app) {

    app.engine('es6', function (filePath, options, callback) {

        var partial = options.renderPartial;
        var layout = require(filePath);
        delete options["_locals"]; //circular

        var current = new layout(options);
        var env = process.env.NODE_ENV;
        var isDev = (env == "dev" || env == "development");

        if(partial) {
            var markup = current[partial](options);
            callback(undefined, markup);
            return;
        }

        callback(undefined, current.markup);

    });

}

export {ModelView as ModelView}
export {Layout as Layout}
export {viewEngine as viewEngine}<|endoftext|>