<commit_before>angular.module('ot.view.admin.accounts', [
    'ui.router',
    'ot.service.core',
    'ot.service.graphql'
])
    .config(function ($stateProvider) {
        $stateProvider.state('admin-accounts', {
            url: '/admin-accounts',
            templateUrl: 'app/view/view.admin.accounts.tpl.html',
            controller: 'AdminAccountsCtrl'
        });
    })

    .controller('AdminAccountsCtrl', function ($scope, $http, ot, otFormService, otAlertService, otGraphqlService) {
        var view = ot.view();
        view.title = "Account management";

        $scope.activeTab = 'accounts';

        $scope.selectAccountsTab = () => {
            $scope.activeTab = 'accounts';
        };

        $scope.selectAccountGroupsTab = () => {
            $scope.activeTab = 'account-groups';
        };

        const query = `
            {
              accounts {
                id
                name
                fullName
                email
                role
                authenticationSource {
                  key
                  provider
                  name
                }
                groups {
                  name
                }
                token {
                  creation
                  valid
                  validUntil
                }
                links {
                  _update
                  _delete
                  _revokeToken
                }
              }
              accountGroups {
                id
                name
                description
                links {
                  _update
                  _delete
                }
              }
            }
        `;

        $scope.loading = false;

        // Loading the data
        const load = () => {
            $scope.loading = true;
            otGraphqlService.pageGraphQLCall(query).then((data) => {
                $scope.accounts = data.accounts;
                $scope.groups = data.accountGroups;
                // Commands
                view.commands = [
                    // Global permissions
                    {
                        id: 'admin-global-acl',
                        name: "Global permissions",
                        cls: 'ot-command-admin-global-acl',
                        link: '/admin-global-acl'
                    },
                    // Group mappings
                    {
                        id: 'admin-group-mappings',
                        name: "Group mappings",
                        cls: 'ot-command-admin-action',
                        link: '/admin-group-mappings'
                    },
                    ot.viewCloseCommand('/home')
                ];
            }).finally(() => {
                $scope.loading = false;
            });
        };

        // Initialisation
        load();

        $scope.sourceDisplayName = (source) => {
            if (source.key) {
                return `${source.name} (${source.provider})`;
            } else {
                return source.name;
            }
        };

        // Creating an account
        $scope.createAccount = function () {
            otFormService.create('/rest/accounts/create', "Account creation").then(load);
        };

        // Updating an account
        $scope.updateAccount = function (account) {
            otFormService.update(account.links._update, "Updating account").then(load);
        };

        // Deleting an account
        $scope.deleteAccount = function (account) {
            otAlertService.confirm({
                title: "Account deletion",
                message: "Do you really want to delete this account?"
            }).then(function () {
                ot.call($http.delete(account.links._delete)).then(load);
            });

        };

        // Creating a group
        $scope.createGroup = function () {
            otFormService.create('/rest/accounts/groups/create', "Account group creation").then(load);
        };

        // Updating a group
        $scope.updateGroup = function (group) {
            otFormService.update(group.links._update, "Updating group").then(load);
        };

        // Deleting a group
        $scope.deleteGroup = function (group) {
            otAlertService.confirm({
                title: "Account group deletion",
                message: "Do you really want to delete this group?"
            }).then(function () {
                ot.call($http.delete(group.links._delete)).then(load);
            });

        };

    })

;<commit_msg>#756 `AccountGroup.autoJoin` column<commit_after>angular.module('ot.view.admin.accounts', [
    'ui.router',
    'ot.service.core',
    'ot.service.graphql'
])
    .config(function ($stateProvider) {
        $stateProvider.state('admin-accounts', {
            url: '/admin-accounts',
            templateUrl: 'app/view/view.admin.accounts.tpl.html',
            controller: 'AdminAccountsCtrl'
        });
    })

    .controller('AdminAccountsCtrl', function ($scope, $http, ot, otFormService, otAlertService, otGraphqlService) {
        var view = ot.view();
        view.title = "Account management";

        $scope.activeTab = 'accounts';

        $scope.selectAccountsTab = () => {
            $scope.activeTab = 'accounts';
        };

        $scope.selectAccountGroupsTab = () => {
            $scope.activeTab = 'account-groups';
        };

        const query = `
            {
              accounts {
                id
                name
                fullName
                email
                role
                authenticationSource {
                  key
                  provider
                  name
                }
                groups {
                  name
                }
                token {
                  creation
                  valid
                  validUntil
                }
                links {
                  _update
                  _delete
                  _revokeToken
                }
              }
              accountGroups {
                id
                name
                description
                autoJoin
                links {
                  _update
                  _delete
                }
              }
            }
        `;

        $scope.loading = false;

        // Loading the data
        const load = () => {
            $scope.loading = true;
            otGraphqlService.pageGraphQLCall(query).then((data) => {
                $scope.accounts = data.accounts;
                $scope.groups = data.accountGroups;
                // Commands
                view.commands = [
                    // Global permissions
                    {
                        id: 'admin-global-acl',
                        name: "Global permissions",
                        cls: 'ot-command-admin-global-acl',
                        link: '/admin-global-acl'
                    },
                    // Group mappings
                    {
                        id: 'admin-group-mappings',
                        name: "Group mappings",
                        cls: 'ot-command-admin-action',
                        link: '/admin-group-mappings'
                    },
                    ot.viewCloseCommand('/home')
                ];
            }).finally(() => {
                $scope.loading = false;
            });
        };

        // Initialisation
        load();

        $scope.sourceDisplayName = (source) => {
            if (source.key) {
                return `${source.name} (${source.provider})`;
            } else {
                return source.name;
            }
        };

        // Creating an account
        $scope.createAccount = function () {
            otFormService.create('/rest/accounts/create', "Account creation").then(load);
        };

        // Updating an account
        $scope.updateAccount = function (account) {
            otFormService.update(account.links._update, "Updating account").then(load);
        };

        // Deleting an account
        $scope.deleteAccount = function (account) {
            otAlertService.confirm({
                title: "Account deletion",
                message: "Do you really want to delete this account?"
            }).then(function () {
                ot.call($http.delete(account.links._delete)).then(load);
            });

        };

        // Creating a group
        $scope.createGroup = function () {
            otFormService.create('/rest/accounts/groups/create', "Account group creation").then(load);
        };

        // Updating a group
        $scope.updateGroup = function (group) {
            otFormService.update(group.links._update, "Updating group").then(load);
        };

        // Deleting a group
        $scope.deleteGroup = function (group) {
            otAlertService.confirm({
                title: "Account group deletion",
                message: "Do you really want to delete this group?"
            }).then(function () {
                ot.call($http.delete(group.links._delete)).then(load);
            });

        };

    })

;<|endoftext|>


<commit_before>
/**
 * sketch.js
 *
 * p5.js: http://p5js.org/reference
 *
 * @author : ruoho ruotsi
 */

// -----------------------------------------------------------------
// setup
// -----------------------------------------------------------------
var multiplier = 1;
var sq_dim = 350;
var canvasSize_w = 960 * multiplier;  // 1080p at retina // 1920×1080
var canvasSize_l = 540 * multiplier;  // 1080p at retina // 1920×1080

var celln1 = 6;
var cells1 = [];

var celln2 = 6;
var cells2 = [];

function setup() {
  createCanvas(canvasSize_w, canvasSize_l);
  frameRate(0.5); // Attempt to refresh at starting FPS

  // setup cell grid1
  for (var i = 0; i < celln1; i++) {
      cells1[i] = [];
      for (var j = 0; j < celln1; j++) {
          cells1[i][j] = new Cell(i*(sq_dim/celln1), j*(sq_dim/celln1), sq_dim/celln1, 0);
          cells1[i][j].rand();
      }
  }

  // setup cell grid2
  for (var i = 0; i < celln2; i++) {
      cells2[i] = [];
      for (var j = 0; j < celln2; j++) {
          cells2[i][j] = new Cell(i*(sq_dim/celln2), j*(sq_dim/celln2), sq_dim/celln2, 0);
          cells2[i][j].rand();
      }
  }

  noFill();
  noLoop()
}

function draw() {

  // clear background on each frame
  background(220, 220, 240);

  // bg cell grid color
  stroke(99, 40, 37);

  // draw one here
  drawOne(cells1, celln1, 25, 125);   // coordinates are offsets for R
  drawOne(cells2, celln2, 500, 125);

  console.log(rect2_y_start + rect2_height , sq_dim - rect3_height)
}

function drawOne(cells, celln, translateX, translateY){

  push(); // Start a new drawing state

  translate(translateX, translateY);

  // for letter background set stroke style
  strokeWeight(7);
  stroke(99,40,37);

  // draw bg cell grid
  for (var i = 0; i < celln; i++)
    for (var j = 0; j < celln; j++){
      cells[i][j].rand();
      cells[i][j].display();
    }

  print("mouseX: " + mouseX, mouseY)

  // main shape stroke color & weight
  stroke(0);
  strokeWeight(24);

  // dimensions of circumscribing-square
  rect(0, 0, sq_dim, sq_dim); // better rect

  //////////////////////////////////////////////////////////////////////////////////
  // rect 3 (bottom rect2)
  rect3_top_bound_y = sq_dim - 150; // random(sq_dim - 200, sq_dim);
  rect3_height = random(20,100);
  rect3_width = random(60,300);
  fill(255);
  rect((sq_dim - rect3_width)/2, sq_dim - rect3_height, rect3_width, rect3_height);

  //////////////////////////////////////////////////////////////////////////////////
  // rect 1 (left top/rect2)
  // randomly select a bounding line for rect1
  rect1_lower_bound_y = random(80, 300)
  rect1_offset_from_top = random(0, rect1_lower_bound_y - 40);
  // line(0, rect1_lower_bound_y, sq_dim, rect1_lower_bound_y); // horizontal line
  line((sq_dim - rect3_width)/2, sq_dim, (sq_dim - rect3_width)/2, rect1_lower_bound_y);

  rect1_width = random((sq_dim - rect3_width)/2, sq_dim/2 + rect3_width);
  rect1_height = rect1_lower_bound_y - rect1_offset_from_top
  rect1_x_start = random(0,100);
  if (rect1_width + rect1_x_start > sq_dim){
      rect1_width = sq_dim - rect1_x_start;
  }
  rect(rect1_x_start, rect1_offset_from_top, rect1_width, rect1_height);

  //////////////////////////////////////////////////////////////////////////////////
  // rect 2 (right rect1/rect3) # 200 needs to be anchored to whatever rect1 is doing
  var gap = 40
  rect2_x_start = random((sq_dim - rect3_width)/2 + gap, sq_dim - gap);
  rect2_y_start = random(rect1_lower_bound_y + gap/2, sq_dim - rect3_height - gap/2);

  // fills out the rest of the x dimension (sq_dim), anchoring rect2 to right side
  rect2_width = sq_dim - rect2_x_start;
  rect2_height = random(gap, sq_dim - rect3_height - rect2_y_start - gap);

  if (rect2_y_start + rect2_height + 15 > sq_dim - rect3_height){
    console.log("headup")
    console.log(rect2_y_start + rect2_height, sq_dim - rect3_height);
    rect2_height = random(gap/2, sq_dim - rect3_height - rect2_y_start - gap);

  }

  rect(rect2_x_start, rect2_y_start, rect2_width, rect2_height);
  noFill();

  pop(); // Restore original state
}

function mousePressed() {
  loop();
}

function mouseReleased() {
  noLoop();
}

function keyPressed() {
  if (key == 's' || key == 'S')
    saveCanvas(Math.round(new Date().getTime() / 1000).toString(), 'png');
}

// -----------------------------------------------------------------
// Cell class for background patterning
// -----------------------------------------------------------------

class Cell {

  // float x, y, s, pos = 0, speed = 1.5;
  // int m = 2, type; // 0-empty
  // boolean moving = false;

  constructor(inx, iny, ins, intp) {
    this.x = inx;
    this.y = iny;
    this.s = ins;
    this.type = intp;
    this.moving = false;
    this.m = 2;
    this.pos = 0;
    this.speed = 1.5;
  }

  rand() {
    this.type = ceil(random(2));
  }

  display() {
    if (this.moving) this.pos += this.speed;

    if (this.pos > this.s) {
      this.pos = 0;
      this.moving = false;
    }

    switch(this.type) {
    case 0:
      break;

    case 1:
      line(this.x, this.y, this.x+this.s, this.y+this.s);
      line(this.x+this.s/2, this.y, this.x+this.s, this.y+this.s/2);
      line(this.x, this.y+this.s/2, this.x+this.s/2, this.y+this.s);
      //break;

    case 2:
      line(this.x+this.s, this.y, this.x, this.y+this.s);
      line(this.x+this.s/2, this.y, this.x, this.y+this.s/2);
      line(this.x+this.s/2, this.y+this.s, this.x+this.s, this.y+this.s/2);
      break;
    }
  }
}<commit_msg>[ADD] wip double r generative logo, refactor repeated code for stoke {color, weight}<commit_after>
/**
 * sketch.js
 *
 * p5.js: http://p5js.org/reference
 *
 * @author : ruoho ruotsi
 */

// -----------------------------------------------------------------
// setup
// -----------------------------------------------------------------
var multiplier = 1;
var sq_dim = 350;
var canvasSize_w = 960 * multiplier;  // 1080p at retina // 1920×1080
var canvasSize_l = 540 * multiplier;  // 1080p at retina // 1920×1080

var celln1 = 6;
var cells1 = [];

var celln2 = 6;
var cells2 = [];

function setup() {
  createCanvas(canvasSize_w, canvasSize_l);
  frameRate(0.5); // Attempt to refresh at starting FPS

  // setup cell grid1
  for (var i = 0; i < celln1; i++) {
      cells1[i] = [];
      for (var j = 0; j < celln1; j++) {
          cells1[i][j] = new Cell(i*(sq_dim/celln1), j*(sq_dim/celln1), sq_dim/celln1, 0);
          cells1[i][j].rand();
      }
  }

  // setup cell grid2
  for (var i = 0; i < celln2; i++) {
      cells2[i] = [];
      for (var j = 0; j < celln2; j++) {
          cells2[i][j] = new Cell(i*(sq_dim/celln2), j*(sq_dim/celln2), sq_dim/celln2, 0);
          cells2[i][j].rand();
      }
  }

  noFill();
  noLoop()
}

function draw() {

  // clear background on each frame
  background(220, 220, 240);

  // bg cell grid color
  stroke(99, 40, 37);

  // draw one here
  drawOne(cells1, celln1, 25, 125);   // coordinates are offsets for R
  drawOne(cells2, celln2, 500, 125);

  console.log(rect2_y_start + rect2_height , sq_dim - rect3_height)
}

function ligneAvec(strokeWeight_thickness, lineStrokeColor_r, lineStrokeColor_g, lineStrokeColor_b ) {

  strokeWeight(strokeWeight_thickness);
  stroke(lineStrokeColor_r, lineStrokeColor_g, lineStrokeColor_b);
}

function drawOne(cells, celln, translateX, translateY){

  push(); // Start a new drawing state

  translate(translateX, translateY);

  // for letter background set stroke style
  ligneAvec(7, 99, 40, 37);

  // draw bg cell grid
  for (var i = 0; i < celln; i++)
    for (var j = 0; j < celln; j++){
      cells[i][j].rand();
      cells[i][j].display();
    }

  print("mouseX: " + mouseX, mouseY)

  // main shape stroke color & weight
  ligneAvec(24, 0, 0, 0);

  // dimensions of circumscribing-square
  rect(0, 0, sq_dim, sq_dim); // better rect
  fill(255); // white

  //////////////////////////////////////////////////////////////////////////////////
  // rect 3 (bottom rect2)
  rect3_top_bound_y = sq_dim - 150; // random(sq_dim - 200, sq_dim);
  rect3_height = random(20,100);
  rect3_width = random(60,300);
  rect((sq_dim - rect3_width)/2, sq_dim - rect3_height, rect3_width, rect3_height);

  //////////////////////////////////////////////////////////////////////////////////
  // rect 1 (left top/rect2)
  // randomly select a bounding line for rect1
  rect1_lower_bound_y = random(80, 300)
  rect1_offset_from_top = random(0, rect1_lower_bound_y - 40);

  ligneAvec(14, 155, 0, 0);
  line(0, rect1_lower_bound_y, sq_dim, rect1_lower_bound_y); // horizontal line
  ligneAvec(24, 0, 0, 0);

  line((sq_dim - rect3_width)/2, sq_dim, (sq_dim - rect3_width)/2, rect1_lower_bound_y);

  rect1_width = random((sq_dim - rect3_width)/2, sq_dim/2 + rect3_width);
  rect1_height = rect1_lower_bound_y - rect1_offset_from_top
  rect1_x_start = random(0,100);
  if (rect1_width + rect1_x_start > sq_dim){
      rect1_width = sq_dim - rect1_x_start;
  }
  rect(rect1_x_start, rect1_offset_from_top, rect1_width, rect1_height);

  //////////////////////////////////////////////////////////////////////////////////
  // rect 2 (right rect1/rect3) # 200 needs to be anchored to whatever rect1 is doing
  var gap = 40
  rect2_x_start = random((sq_dim - rect3_width)/2 + gap, sq_dim - gap);
  rect2_y_start = random(rect1_lower_bound_y + gap/2, sq_dim - rect3_height - gap/2);

  // fills out the rest of the x dimension (sq_dim), anchoring rect2 to right side
  rect2_width = sq_dim - rect2_x_start;
  rect2_height = random(gap, sq_dim - rect3_height - rect2_y_start - gap);

  if (rect2_y_start + rect2_height + 15 > sq_dim - rect3_height){
    console.log("headup")
    console.log(rect2_y_start + rect2_height, sq_dim - rect3_height);
    rect2_height = random(gap/2, sq_dim - rect3_height - rect2_y_start - gap);

  }

  rect(rect2_x_start, rect2_y_start, rect2_width, rect2_height);
  noFill();

  pop(); // Restore original state
}

function mousePressed() {
  loop();
}

function mouseReleased() {
  noLoop();
}

function keyPressed() {
  if (key == 's' || key == 'S')
    saveCanvas(Math.round(new Date().getTime() / 1000).toString(), 'png');
}

// -----------------------------------------------------------------
// Cell class for background patterning
// -----------------------------------------------------------------

class Cell {

  // float x, y, s, pos = 0, speed = 1.5;
  // int m = 2, type; // 0-empty
  // boolean moving = false;

  constructor(inx, iny, ins, intp) {
    this.x = inx;
    this.y = iny;
    this.s = ins;
    this.type = intp;
    this.moving = false;
    this.m = 2;
    this.pos = 0;
    this.speed = 1.5;
  }

  rand() {
    this.type = ceil(random(2));
  }

  display() {
    if (this.moving) this.pos += this.speed;

    if (this.pos > this.s) {
      this.pos = 0;
      this.moving = false;
    }

    switch(this.type) {
    case 0:
      break;

    case 1:
      line(this.x, this.y, this.x+this.s, this.y+this.s);
      line(this.x+this.s/2, this.y, this.x+this.s, this.y+this.s/2);
      line(this.x, this.y+this.s/2, this.x+this.s/2, this.y+this.s);
      //break;

    case 2:
      line(this.x+this.s, this.y, this.x, this.y+this.s);
      line(this.x+this.s/2, this.y, this.x, this.y+this.s/2);
      line(this.x+this.s/2, this.y+this.s, this.x+this.s, this.y+this.s/2);
      break;
    }
  }
}<|endoftext|>


<commit_before>angular.module('opal.controllers').controller(
    'EpisodeListCtrl', function($scope, $q, $http, $cookieStore,
                                $location, $routeParams,
                                $modal, $rootScope,
                                growl,
                                Flow, Item,
                                Episode, schema, episodes, options,
                                profile, episodeVisibility){
        $scope.ready = false;

      var version = window.version;
      $rootScope.state = 'normal';
      $scope.url = $location.url();

      $scope.options = options;
      $scope.listView = true;

      $scope.num_episodes = _.keys(episodes).length;

	    $scope.rix = 0; // row index
	    $scope.cix = 0; // column index
	    $scope.iix = 0; // item index

	    $scope.mouseRix = -1; // index of row mouse is currently over
	    $scope.mouseCix = -1; // index of column mouse is currently over
        $scope._ =  _;

	    $scope.query = {hospital_number: '', name: '', ward: '', bed: ''};
        $scope.$location = $location;

        $scope.path_base = '/list/';

        if(!$routeParams.tag){
            var tag =  $cookieStore.get('opal.currentTag') || _.keys(options.tag_hierarchy)[0];
            $location.path($scope.path_base + tag);
            return;
        }
        $scope.currentTag = $routeParams.tag;

        if(!$routeParams.subtag){
            // We now force redirect to the first subtag if there is one
            if($scope.currentTag in options.tag_hierarchy &&
               options.tag_hierarchy[$scope.currentTag].length > 0){
                var subtag = options.tag_hierarchy[$scope.currentTag][0];
                var target = $scope.path_base + $scope.currentTag + '/' + subtag;
                $location.path(target);
                return;
            }else{
                $scope.currentSubTag = 'all';
            }
        }else{
            $scope.currentSubTag = $routeParams.subtag;
        }
        $scope.columns = schema.columns;

        $scope.profile = profile;
        $scope.tag_display = options.tag_display;

	    $scope.getVisibleEpisodes = function() {
		    var visibleEpisodes = [];
            var episode_list = [];

            visibleEpisodes = _.filter(episodes, function(episode){
                return episodeVisibility(episode, $scope)
            });
		    visibleEpisodes.sort(compareEpisodes);
            if($scope.rows && visibleEpisodes.length == 1){
                rix = getRowIxFromEpisodeId(visibleEpisodes[0].id);
                $scope.select_episode(visibleEpisodes[0], rix);
            }
		    return visibleEpisodes;
	    };

	    $scope.rows = $scope.getVisibleEpisodes();
        $scope.episode = $scope.rows[0];

        $scope.ready = true;

        $scope.isSelectedEpisode = function(episode){
            return episode === $scope.episode;
        }

	    function compareEpisodes(p1, p2) {
		    return p1.compare(p2);
	    };

        $scope.jumpToTag = function(tag){
            if(_.contains(_.keys(options.tag_hierarchy), tag)){
                $location.path($scope.path_base + tag)
            }else{

                for(var prop in options.tag_hierarchy){
                    if(options.tag_hierarchy.hasOwnProperty(prop)){
                        if(_.contains(_.values(options.tag_hierarchy[prop]), tag)){
                            $location.path($scope.path_base + prop + '/' + tag)
                        }
                    }
                }

            };
        }

	    $scope.$watch('currentTag', function() {
		    $cookieStore.put('opal.currentTag', $scope.currentTag);
            if($scope.currentTag != $routeParams.tag){
                $$rootScope.state = 'reloading'
            }
            var target = $scope.path_base +  $scope.currentTag;

            $location.path(target);
	    });

	    $scope.$watch('currentSubTag', function(){
		    $cookieStore.put('opal.currentSubTag', $scope.currentSubTag);
            if($scope.currentSubTag == 'all'){
                if($routeParams.subtag && $scope.currentSubTag != $routeParams.subtag){
                    $rootScope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag);
            }else{
                if($scope.currentSubTag != $routeParams.subtag){
                    $rootScope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag + '/' +  $scope.currentSubTag);
            }
	    });

        $scope.showSubtags = function(withsubtags){
            var show =  _.contains(withsubtags, $scope.currentTag);
            return show
        };

	    $scope.$watch('query.hospital_number', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.ward', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.bed', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.name', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

        $scope.getEpisodeLink = function(){
            return "/episode/" + $scope.episode.id;
        };

	    $scope.$on('keydown', function(event, e) {
		    if ($rootScope.state == 'normal') {
			    switch (e.keyCode) {
                   case 191: // question mark
                        if(e.shiftKey){
                            $scope.keyboard_shortcuts();
                        }
                        break;
                    case 13:
                        if(profile.can_see_pid()){
                            $location.url($scope.getEpisodeLink());
                        }
                        break;
    			    case 38: // up
    				    goUp();
    				    break;
    			    case 40: // down
    				    goDown();
    				    break;
                    case 78: // n
                        $scope.addEpisode();
    		    }
            }
	    });

        $scope.$on('change', function(event, episode) {
            episode = new Episode(episode);
            if(episodes[episode.id]){
                episodes[episode.id] = episode;
                var rix = getRowIxFromEpisodeId(episode.id);
                if(rix != -1){
                    $scope.rows[rix] = episode;
                }
            }
        });

	    function getColumnName(cix) {
		    return $scope.columns[cix].name;
	    };

	    function getRowIxFromEpisodeId(episodeId) {
		    for (var rix = 0; rix < $scope.rows.length; rix++) {
			    if ($scope.rows[rix].id == episodeId) {
				    return rix;
			    }
		    };
		    return -1;
	    };

	    function getEpisode(rix) {
		    return $scope.rows[rix];
	    };

	    $scope.print = function() {
		    window.print();
	    };

	    $scope.selectItem = function(rix, cix, iix) {
		    $scope.rix = rix;
		    $scope.cix = cix;
		    $scope.iix = iix;
        $scope.episode = $scope.rows[rix];
	    };

	    $scope.focusOnQuery = function() {
		    // $scope.selectItem(-1, -1, -1);
		    $rootScope.state = 'search';
	    };

	    $scope.blurOnQuery = function() {
		    if ($scope.rix == -1) {
			    $scope.selectItem(0, 0, 0);
		    };
		    $rootScope.state = 'normal';
	    };

	    $scope.addEpisode = function() {
            if(profile.readonly){ return null; };

            var enter = Flow(
                'enter', schema, options,
                {
                    current_tags: {
                        tag: $scope.currentTag,
                        subtag: $scope.currentSubTag
                    }
                }
            );

		    $rootScope.state = 'modal';

            enter.then(
                function(resolved) {
		            // We have either retrieved an existing episode or created a new one,
                    // rendered a new modal for which we are waiting,
		            // or has cancelled the process at some point.

                    var return_to_normal = function(episode){
                    	// This ensures that the relevant episode is added to the table and
		                // selected.
		                var rowIx;
		                $rootScope.state = 'normal';
		                if (episode && episode != 'cancel') {
			                episodes[episode.id] = episode;
			                $scope.rows = $scope.getVisibleEpisodes();
			                rowIx = getRowIxFromEpisodeId(episode.id);
			                $scope.selectItem(rowIx, 0, 0);
                            $scope.num_episodes += 1;
                            growl.success("Added a new episode for " + episode.demographics[0].name);
		                };
                    }

                    if(resolved.then){ // OMG - it's a promise!
                        resolved.then(
                            function(r){ return_to_normal(r) },
                            function(r){ return_to_normal(r) }
                        );
                    }else{
                        return_to_normal(resolved);
                    }
	            },
                function(reason){
                    // The modal has been dismissed. Practically speaking this means
                    // that the Angular UI called dismiss rather than our cancel()
                    // method on the OPAL controller. We just need to re-set in order
                    // to re-enable keybard listeners.
                    $rootScope.state = 'normal';
                });
	    };

        $scope._post_discharge = function(result){
			$rootScope.state = 'normal';
			if (result == 'discharged' | result == 'moved') {
				$scope.rows = $scope.getVisibleEpisodes();
				$scope.selectItem(0, 0, 0);
                $scope.num_episodes -= 1;
			};
        };

	    $scope.dischargeEpisode = function(episode) {
            if(profile.readonly){ return null; };

		    $rootScope.state = 'modal';

            var exit = Flow(
                'exit', schema, options,
                {
                    current_tags: {
                        tag   : $scope.currentTag,
                        subtag: $scope.currentSubTag
                    },
                    episode: episode
                }
            );

            exit.then(function(result) {
                //
                // Sometimes our Flow will open another modal - we wait for that
                // to close before firing the Post discharge hooks - this avoids the list
                // scope from trapping keystrokes etc
                //
                if(result && result.then){
                    result.then(function(r){ $scope._post_discharge(r); });
                }else{
                    $scope._post_discharge(result);
                }
		    });
	    };

        // TODO: Test This!
        $scope.removeFromMine = function(rix, event){
            if(profile.readonly){
                return null;
            };

            event.preventDefault();

            var modal;
            var episode = getEpisode(rix);
            var tagging = episode.tagging[0];
            editing = tagging.makeCopy();
            editing.mine = false;
            tagging.save(editing).then(function(result){
                $scope.rows = $scope.getVisibleEpisodes();
            })
        };

        //
        // Do the work of editing an item - open the modal and
        // resolve it.
        //
        _openEditItemModal = function(item, columnName, episode) {
            if(profile.readonly){
                return null;
            };
            $rootScope.state = 'modal';
            var template_url = '/templates/modals/' + columnName + '.html/';
            template_url += $scope.currentTag + '/' + $scope.currentSubTag;

            var modal_opts = {
                templateUrl: template_url,
                controller: 'EditItemCtrl',
                resolve: {
                    item: function() { return item; },
                    options: function() { return options; },
                    profile: function() { return profile; },
                    episode: function() { return episode; }
                }
            }

            if(item.size){
                modal_opts.size = item.size;
            }

            modal = $modal.open(modal_opts);

            //
            // The state resetting logic is fairly simple, but we may have to
            // wait for an intermediary modal to close...
            //
            // TODO: Figure out & document how to actually take advantage of this hook :/
            //
            var reset_state = function(result){
                $rootScope.state = 'normal';

                if (columnName == 'tagging') {

                    // User may have removed current tag
                    $scope.rows = $scope.getVisibleEpisodes();
                    $scope.selectItem(getRowIxFromEpisodeId(episode.id), $scope.cix, 0);
                }

                if (result == 'save-and-add-another') {
                    $scope.newNamedItem(episode, columnName);
                    return
                };
                if (item.sort){
                    episode.sortColumn(item.columnName, item.sort);
                };
            };

            modal.result.then(function(result) {
                if(result && result.then){
                    result.then(function(r){ reset_state(r) });
                }else{
                    reset_state(result);
                }
            }, function(){
                $rootScope.state = 'normal';
            });
        }

        $scope.newNamedItem = function(episode, name) {
            var item = episode.newItem(name, {column: $rootScope.fields[name]});
            if (!episode[name]) {
                episode[name] = [];
            }
            episode[name].push(item);
            return _openEditItemModal(item, name, episode);
        }

        $scope.is_tag_visible_in_list = function(tag){
            return _.contains(options.tag_visible_in_list, tag);
        };

        $scope.editNamedItem = function(episode, name, iix) {
            var item;
            if (episode[name][iix] && episode[name][iix].columnName) {
                item = episode[name][iix];
            } else {
                item = new Item(episode[name][iix], episode, $rootScope.fields[name]);
                episode[name][iix] = item;
            }

            return _openEditItemModal(item, name, episode);
        }

	    $scope.editItem = function(rix, cix, iix) {
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item;

            if(!profile.can_edit(columnName)){ return false };

            if(columnName == 'demographics' && !profile.can_see_pid()){
                return false;
            }

		    if (iix == episode.getNumberOfItems(columnName)) {
			    item = episode.newItem(columnName);
		    } else {
			    item = episode.getItem(columnName, iix);
		    };

		    $scope.selectItem(rix, cix, iix);
            return _openEditItemModal(item, columnName, episode);
	    };

	    $scope.mouseEnter = function(rix, cix) {
		    $scope.mouseRix = rix;
		    $scope.mouseCix = cix;
	    }

	    $scope.mouseLeave = function() {
		    $scope.mouseRix = -1;
		    $scope.mouseCix = -1;
	    }

	    function goUp() {
		    var episode;
		    var columnName = getColumnName($scope.cix);

		    if ($scope.iix > 0) {
			    $scope.iix--;
		    } else if ($scope.rix > 0) {
			    $scope.rix--;
				$scope.episode = getEpisode($scope.rix);
			    if (schema.isSingleton(columnName)) {
				    $scope.iix = 0;
			    } else {
				    $scope.iix = $scope.episode.getNumberOfItems(columnName);
			    };
		    };
	    };

	    function goDown() {
		    var episode = getEpisode($scope.rix);
		    var columnName = getColumnName($scope.cix);

		    if (!schema.isSingleton(columnName) &&
		        ($scope.iix < episode.getNumberOfItems(columnName))) {
			    $scope.iix++;
		    } else if ($scope.rix < $scope.rows.length - 1) {
			    $scope.rix++;
                $scope.episode = $scope.rows[$scope.rix];
			    $scope.iix = 0;
		    };
	    };

        $scope.select_episode = function(episode, rix){
            if(rix == $scope.rix){
                return;
            }else{
                $scope.episode = episode;
                $scope.rix = rix;
                $scope.iix = 0;
            }
        }

        $scope.controller_for_episode = function(controller, template, size, episode){
            $modal.open({
                controller : controller,
                templateUrl: template,
                size       : size,
                resolve    : {
                    episode: function(){ return episode }
                }
            });
        }

        $scope.keyboard_shortcuts = function(){
            $modal.open({
                controller: "KeyBoardShortcutsCtrl",
                templateUrl: 'list_keyboard_shortcuts.html'
            })
        }
    });
<commit_msg>changed to add [[ patient name ]] to [[ sub team name ]] list<commit_after>angular.module('opal.controllers').controller(
    'EpisodeListCtrl', function($scope, $q, $http, $cookieStore,
                                $location, $routeParams,
                                $modal, $rootScope,
                                growl,
                                Flow, Item,
                                Episode, schema, episodes, options,
                                profile, episodeVisibility){
        $scope.ready = false;

      var version = window.version;
      $rootScope.state = 'normal';
      $scope.url = $location.url();

      $scope.options = options;
      $scope.listView = true;

      $scope.num_episodes = _.keys(episodes).length;

	    $scope.rix = 0; // row index
	    $scope.cix = 0; // column index
	    $scope.iix = 0; // item index

	    $scope.mouseRix = -1; // index of row mouse is currently over
	    $scope.mouseCix = -1; // index of column mouse is currently over
        $scope._ =  _;

	    $scope.query = {hospital_number: '', name: '', ward: '', bed: ''};
        $scope.$location = $location;

        $scope.path_base = '/list/';

        if(!$routeParams.tag){
            var tag =  $cookieStore.get('opal.currentTag') || _.keys(options.tag_hierarchy)[0];
            $location.path($scope.path_base + tag);
            return;
        }
        $scope.currentTag = $routeParams.tag;

        if(!$routeParams.subtag){
            // We now force redirect to the first subtag if there is one
            if($scope.currentTag in options.tag_hierarchy &&
               options.tag_hierarchy[$scope.currentTag].length > 0){
                var subtag = options.tag_hierarchy[$scope.currentTag][0];
                var target = $scope.path_base + $scope.currentTag + '/' + subtag;
                $location.path(target);
                return;
            }else{
                $scope.currentSubTag = 'all';
            }
        }else{
            $scope.currentSubTag = $routeParams.subtag;
        }
        $scope.columns = schema.columns;

        $scope.profile = profile;
        $scope.tag_display = options.tag_display;

	    $scope.getVisibleEpisodes = function() {
		    var visibleEpisodes = [];
            var episode_list = [];

            visibleEpisodes = _.filter(episodes, function(episode){
                return episodeVisibility(episode, $scope)
            });
		    visibleEpisodes.sort(compareEpisodes);
            if($scope.rows && visibleEpisodes.length == 1){
                rix = getRowIxFromEpisodeId(visibleEpisodes[0].id);
                $scope.select_episode(visibleEpisodes[0], rix);
            }
		    return visibleEpisodes;
	    };

	    $scope.rows = $scope.getVisibleEpisodes();
        $scope.episode = $scope.rows[0];

        $scope.ready = true;

        $scope.isSelectedEpisode = function(episode){
            return episode === $scope.episode;
        }

	    function compareEpisodes(p1, p2) {
		    return p1.compare(p2);
	    };

        $scope.jumpToTag = function(tag){
            if(_.contains(_.keys(options.tag_hierarchy), tag)){
                $location.path($scope.path_base + tag)
            }else{

                for(var prop in options.tag_hierarchy){
                    if(options.tag_hierarchy.hasOwnProperty(prop)){
                        if(_.contains(_.values(options.tag_hierarchy[prop]), tag)){
                            $location.path($scope.path_base + prop + '/' + tag)
                        }
                    }
                }

            };
        }

	    $scope.$watch('currentTag', function() {
		    $cookieStore.put('opal.currentTag', $scope.currentTag);
            if($scope.currentTag != $routeParams.tag){
                $$rootScope.state = 'reloading'
            }
            var target = $scope.path_base +  $scope.currentTag;

            $location.path(target);
	    });

	    $scope.$watch('currentSubTag', function(){
		    $cookieStore.put('opal.currentSubTag', $scope.currentSubTag);
            if($scope.currentSubTag == 'all'){
                if($routeParams.subtag && $scope.currentSubTag != $routeParams.subtag){
                    $rootScope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag);
            }else{
                if($scope.currentSubTag != $routeParams.subtag){
                    $rootScope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag + '/' +  $scope.currentSubTag);
            }
	    });

        $scope.showSubtags = function(withsubtags){
            var show =  _.contains(withsubtags, $scope.currentTag);
            return show
        };

	    $scope.$watch('query.hospital_number', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.ward', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.bed', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

	    $scope.$watch('query.name', function() {
		    $scope.rows = $scope.getVisibleEpisodes();
	    });

        $scope.getEpisodeLink = function(){
            return "/episode/" + $scope.episode.id;
        };

	    $scope.$on('keydown', function(event, e) {
		    if ($rootScope.state == 'normal') {
			    switch (e.keyCode) {
                   case 191: // question mark
                        if(e.shiftKey){
                            $scope.keyboard_shortcuts();
                        }
                        break;
                    case 13:
                        if(profile.can_see_pid()){
                            $location.url($scope.getEpisodeLink());
                        }
                        break;
    			    case 38: // up
    				    goUp();
    				    break;
    			    case 40: // down
    				    goDown();
    				    break;
                    case 78: // n
                        $scope.addEpisode();
    		    }
            }
	    });

        $scope.$on('change', function(event, episode) {
            episode = new Episode(episode);
            if(episodes[episode.id]){
                episodes[episode.id] = episode;
                var rix = getRowIxFromEpisodeId(episode.id);
                if(rix != -1){
                    $scope.rows[rix] = episode;
                }
            }
        });

	    function getColumnName(cix) {
		    return $scope.columns[cix].name;
	    };

	    function getRowIxFromEpisodeId(episodeId) {
		    for (var rix = 0; rix < $scope.rows.length; rix++) {
			    if ($scope.rows[rix].id == episodeId) {
				    return rix;
			    }
		    };
		    return -1;
	    };

	    function getEpisode(rix) {
		    return $scope.rows[rix];
	    };

	    $scope.print = function() {
		    window.print();
	    };

	    $scope.selectItem = function(rix, cix, iix) {
		    $scope.rix = rix;
		    $scope.cix = cix;
		    $scope.iix = iix;
        $scope.episode = $scope.rows[rix];
	    };

	    $scope.focusOnQuery = function() {
		    // $scope.selectItem(-1, -1, -1);
		    $rootScope.state = 'search';
	    };

	    $scope.blurOnQuery = function() {
		    if ($scope.rix == -1) {
			    $scope.selectItem(0, 0, 0);
		    };
		    $rootScope.state = 'normal';
	    };

	    $scope.addEpisode = function() {
            if(profile.readonly){ return null; };

            var enter = Flow(
                'enter', schema, options,
                {
                    current_tags: {
                        tag: $scope.currentTag,
                        subtag: $scope.currentSubTag
                    }
                }
            );

		    $rootScope.state = 'modal';

            enter.then(
                function(resolved) {
		            // We have either retrieved an existing episode or created a new one,
                    // rendered a new modal for which we are waiting,
		            // or has cancelled the process at some point.

                    var return_to_normal = function(episode){
                    	// This ensures that the relevant episode is added to the table and
		                // selected.
		                var rowIx;
		                $rootScope.state = 'normal';
  		                if (episode && episode != 'cancel') {
  			                episodes[episode.id] = episode;
  			                $scope.rows = $scope.getVisibleEpisodes();
  			                rowIx = getRowIxFromEpisodeId(episode.id);
  			                $scope.selectItem(rowIx, 0, 0);
                              $scope.num_episodes += 1;
                              var readableName = $scope.tag_display[$scope.currentSubTag];
                              var msg = episode.demographics[0].name + " added to the " + readableName + " list";
                              growl.success(msg);
  		                }
                    };

                    if(resolved.then){ // OMG - it's a promise!
                        resolved.then(
                            function(r){ return_to_normal(r) },
                            function(r){ return_to_normal(r) }
                        );
                    }else{
                        return_to_normal(resolved);
                    }
	            },
                function(reason){
                    // The modal has been dismissed. Practically speaking this means
                    // that the Angular UI called dismiss rather than our cancel()
                    // method on the OPAL controller. We just need to re-set in order
                    // to re-enable keybard listeners.
                    $rootScope.state = 'normal';
                });
	    };

        $scope._post_discharge = function(result){
			$rootScope.state = 'normal';
			if (result == 'discharged' | result == 'moved') {
				$scope.rows = $scope.getVisibleEpisodes();
				$scope.selectItem(0, 0, 0);
                $scope.num_episodes -= 1;
			};
        };

	    $scope.dischargeEpisode = function(episode) {
            if(profile.readonly){ return null; };

		    $rootScope.state = 'modal';

            var exit = Flow(
                'exit', schema, options,
                {
                    current_tags: {
                        tag   : $scope.currentTag,
                        subtag: $scope.currentSubTag
                    },
                    episode: episode
                }
            );

            exit.then(function(result) {
                //
                // Sometimes our Flow will open another modal - we wait for that
                // to close before firing the Post discharge hooks - this avoids the list
                // scope from trapping keystrokes etc
                //
                if(result && result.then){
                    result.then(function(r){ $scope._post_discharge(r); });
                }else{
                    $scope._post_discharge(result);
                }
		    });
	    };

        // TODO: Test This!
        $scope.removeFromMine = function(rix, event){
            if(profile.readonly){
                return null;
            };

            event.preventDefault();

            var modal;
            var episode = getEpisode(rix);
            var tagging = episode.tagging[0];
            editing = tagging.makeCopy();
            editing.mine = false;
            tagging.save(editing).then(function(result){
                $scope.rows = $scope.getVisibleEpisodes();
            })
        };

        //
        // Do the work of editing an item - open the modal and
        // resolve it.
        //
        _openEditItemModal = function(item, columnName, episode) {
            if(profile.readonly){
                return null;
            };
            $rootScope.state = 'modal';
            var template_url = '/templates/modals/' + columnName + '.html/';
            template_url += $scope.currentTag + '/' + $scope.currentSubTag;

            var modal_opts = {
                templateUrl: template_url,
                controller: 'EditItemCtrl',
                resolve: {
                    item: function() { return item; },
                    options: function() { return options; },
                    profile: function() { return profile; },
                    episode: function() { return episode; }
                }
            }

            if(item.size){
                modal_opts.size = item.size;
            }

            modal = $modal.open(modal_opts);

            //
            // The state resetting logic is fairly simple, but we may have to
            // wait for an intermediary modal to close...
            //
            // TODO: Figure out & document how to actually take advantage of this hook :/
            //
            var reset_state = function(result){
                $rootScope.state = 'normal';

                if (columnName == 'tagging') {

                    // User may have removed current tag
                    $scope.rows = $scope.getVisibleEpisodes();
                    $scope.selectItem(getRowIxFromEpisodeId(episode.id), $scope.cix, 0);
                }

                if (result == 'save-and-add-another') {
                    $scope.newNamedItem(episode, columnName);
                    return
                };
                if (item.sort){
                    episode.sortColumn(item.columnName, item.sort);
                };
            };

            modal.result.then(function(result) {
                if(result && result.then){
                    result.then(function(r){ reset_state(r) });
                }else{
                    reset_state(result);
                }
            }, function(){
                $rootScope.state = 'normal';
            });
        }

        $scope.newNamedItem = function(episode, name) {
            var item = episode.newItem(name, {column: $rootScope.fields[name]});
            if (!episode[name]) {
                episode[name] = [];
            }
            episode[name].push(item);
            return _openEditItemModal(item, name, episode);
        }

        $scope.is_tag_visible_in_list = function(tag){
            return _.contains(options.tag_visible_in_list, tag);
        };

        $scope.editNamedItem = function(episode, name, iix) {
            var item;
            if (episode[name][iix] && episode[name][iix].columnName) {
                item = episode[name][iix];
            } else {
                item = new Item(episode[name][iix], episode, $rootScope.fields[name]);
                episode[name][iix] = item;
            }

            return _openEditItemModal(item, name, episode);
        }

	    $scope.editItem = function(rix, cix, iix) {
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item;

            if(!profile.can_edit(columnName)){ return false };

            if(columnName == 'demographics' && !profile.can_see_pid()){
                return false;
            }

		    if (iix == episode.getNumberOfItems(columnName)) {
			    item = episode.newItem(columnName);
		    } else {
			    item = episode.getItem(columnName, iix);
		    };

		    $scope.selectItem(rix, cix, iix);
            return _openEditItemModal(item, columnName, episode);
	    };

	    $scope.mouseEnter = function(rix, cix) {
		    $scope.mouseRix = rix;
		    $scope.mouseCix = cix;
	    }

	    $scope.mouseLeave = function() {
		    $scope.mouseRix = -1;
		    $scope.mouseCix = -1;
	    }

	    function goUp() {
		    var episode;
		    var columnName = getColumnName($scope.cix);

		    if ($scope.iix > 0) {
			    $scope.iix--;
		    } else if ($scope.rix > 0) {
			    $scope.rix--;
				$scope.episode = getEpisode($scope.rix);
			    if (schema.isSingleton(columnName)) {
				    $scope.iix = 0;
			    } else {
				    $scope.iix = $scope.episode.getNumberOfItems(columnName);
			    };
		    };
	    };

	    function goDown() {
		    var episode = getEpisode($scope.rix);
		    var columnName = getColumnName($scope.cix);

		    if (!schema.isSingleton(columnName) &&
		        ($scope.iix < episode.getNumberOfItems(columnName))) {
			    $scope.iix++;
		    } else if ($scope.rix < $scope.rows.length - 1) {
			    $scope.rix++;
                $scope.episode = $scope.rows[$scope.rix];
			    $scope.iix = 0;
		    };
	    };

        $scope.select_episode = function(episode, rix){
            if(rix == $scope.rix){
                return;
            }else{
                $scope.episode = episode;
                $scope.rix = rix;
                $scope.iix = 0;
            }
        }

        $scope.controller_for_episode = function(controller, template, size, episode){
            $modal.open({
                controller : controller,
                templateUrl: template,
                size       : size,
                resolve    : {
                    episode: function(){ return episode }
                }
            });
        }

        $scope.keyboard_shortcuts = function(){
            $modal.open({
                controller: "KeyBoardShortcutsCtrl",
                templateUrl: 'list_keyboard_shortcuts.html'
            })
        }
    });
<|endoftext|>


<commit_before>$(function() {

  // nav
  $(".nav a").on("click", function(){
   $(".nav").find(".active").removeClass("active");
   $(this).parent().addClass("active");
});
  // end nav

  // velocidad parallax
  var parallax = document.querySelectorAll(".parallaxSecundario"),
      speed = 0.5;

  window.onscroll = function(){
    [].slice.call(parallax).forEach(function(el,i){

      var windowYOffset = window.pageYOffset,
          elBackgrounPos = "50% " + (windowYOffset * speed) + "px";

      el.style.backgroundPosition = elBackgrounPos;

    });
  };
  // end velocidad parallax

  $('a[href*="#"]:not([href="#"])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });

  // Animación
  var $window           = $(window),
      win_height_padded = $window.height() * 1.1,
      isTouch           = Modernizr.touch;

  if (isTouch) { $('.revealOnScroll').addClass('animated'); }

  $window.on('scroll', revealOnScroll);

  function revealOnScroll() {
    var scrolled = $window.scrollTop(),
        win_height_padded = $window.height() * 1.1;

    // Showed...
    $(".revealOnScroll:not(.animated)").each(function () {
      var $this     = $(this),
          offsetTop = $this.offset().top;

      if (scrolled + win_height_padded > offsetTop) {
        if ($this.data('timeout')) {
          window.setTimeout(function(){
            $this.addClass('animated ' + $this.data('animation'));
          }, parseInt($this.data('timeout'),10));
        } else {
          $this.addClass('animated ' + $this.data('animation'));
        }
      }
    });
    // Hidden...
   $(".revealOnScroll.animated").each(function (index) {
      var $this     = $(this),
          offsetTop = $this.offset().top;
      if (scrolled + win_height_padded < offsetTop) {
        $(this).removeClass('animated fadeInUp flipInX lightSpeedIn')
      }
    });
  }

  revealOnScroll();
});
<commit_msg>Update script.js<commit_after>$(function() {

  // nav
  $(".nav a").on("click", function(){
   $(".nav").find(".active").removeClass("active");
   $(this).parent().addClass("active");
});
  // end nav

  // velocidad parallax
  var parallax = document.querySelectorAll(".parallaxSecundario"),
      speed = 0.5;

  window.onscroll = function(){
    [].slice.call(parallax).forEach(function(el,i){

      var windowYOffset = window.pageYOffset,
          elBackgrounPos = "80% " + (windowYOffset * speed) + "px";

      el.style.backgroundPosition = elBackgrounPos;

    });
  };
  // end velocidad parallax

  $('a[href*="#"]:not([href="#"])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });

  // Animación
  var $window           = $(window),
      win_height_padded = $window.height() * 1.1,
      isTouch           = Modernizr.touch;

  if (isTouch) { $('.revealOnScroll').addClass('animated'); }

  $window.on('scroll', revealOnScroll);

  function revealOnScroll() {
    var scrolled = $window.scrollTop(),
        win_height_padded = $window.height() * 1.1;

    // Showed...
    $(".revealOnScroll:not(.animated)").each(function () {
      var $this     = $(this),
          offsetTop = $this.offset().top;

      if (scrolled + win_height_padded > offsetTop) {
        if ($this.data('timeout')) {
          window.setTimeout(function(){
            $this.addClass('animated ' + $this.data('animation'));
          }, parseInt($this.data('timeout'),10));
        } else {
          $this.addClass('animated ' + $this.data('animation'));
        }
      }
    });
    // Hidden...
   $(".revealOnScroll.animated").each(function (index) {
      var $this     = $(this),
          offsetTop = $this.offset().top;
      if (scrolled + win_height_padded < offsetTop) {
        $(this).removeClass('animated fadeInUp flipInX lightSpeedIn')
      }
    });
  }

  revealOnScroll();
});
<|endoftext|>


<commit_before>import {
  Project,
  Line,
  Hole,
  Item,
  Area,
  Layer,
  Vertex
} from './export';
import { Map, List } from 'immutable';
import { Group as GroupModel } from '../models';
import { history, IDBroker, MathUtils } from '../utils/export';

class Group{

  static select( state, groupID ){
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    state = Project.setAlterate( state ).updatedState;

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      if( lines ) lines.forEach( lineID => { state = Line.select( state, groupLayerID, lineID ).updatedState; });
      if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; });
      if( items ) items.forEach( itemID => { state = Item.select( state, groupLayerID, itemID ).updatedState; });
      if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; });
    });

    state = Project.setAlterate( state ).updatedState;

    let groups = state.getIn(['scene', 'groups']).map( g  => g.set('selected', false) );

    state = state.setIn(['scene', 'groups'], groups).setIn([ 'scene', 'groups', groupID, 'selected' ], true);

    return { updatedState: state };
  }

  static unselect( state, groupID ){
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);
    let reduced = layerList.reduce( ( newState, layer, layerID ) => Layer.unselectAll( newState, layerID ).updatedState, state );
    state = reduced.setIn([ 'scene', 'groups', groupID, 'selected' ], false);

    return { updatedState: state };
  }

  static create( state ){
    let groupID = IDBroker.acquireID();

    state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) );

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState: state };
  }

  static createFromSelectedElements( state ){
    let groupID = IDBroker.acquireID();

    state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) );

    state.getIn(['scene', 'layers']).forEach((layer) => {

      let layerID = layer.get('id');
      let layerElements = {
        'lines': layer.get('lines').filter( el => el.get('selected') ),
        'items': layer.get('items').filter( el => el.get('selected') ),
        'holes': layer.get('holes').filter( el => el.get('selected') ),
        'areas': layer.get('areas').filter( el => el.get('selected') )
      };

      for( let elementPrototype in layerElements ) {
        layerElements[elementPrototype].forEach( el => state = this.addElement( state, groupID, layerID, elementPrototype, el.get('id') ).updatedState );
      }
    });

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return {updatedState: state};
  }

  static addElement( state, groupID, layerID, elementPrototype, elementID ){
    let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]) || new List();

    if( !actualList.contains(elementID) ) {
      state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.push(elementID));

      state = this.reloadBaricenter( state, groupID ).updatedState;

      state = state.merge({
        sceneHistory: history.historyPush( state.sceneHistory, state.scene )
      });
    }

    return { updatedState: state };
  }

  static setBarycenter( state, groupID, x, y ) {
    if (typeof x !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'x'], x);
    if (typeof y !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'y'], y);

    return { updatedState: state };
  }

  static reloadBaricenter( state, groupID ) {
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    let { a, b, c, d, e, f, SVGHeight } = state.get('viewer2D').toJS();

    let m1 = [
      [ a, b, c ],
      [ d, e, f ],
      [ 0, 0, 1 ]
    ];

    let paper = document.getElementById('svg-drawing-paper');
    let { x: pX, y: pY } = paper.getBoundingClientRect();

    let xBar = 0;
    let yBar = 0;
    let elementCount = 0;

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      let cb = elementID => {
        let elementDom = document.querySelector(`[data-id="${elementID}"]`);
        if( elementDom ) {
          let { x: elX, y: elY, width: elW, height: elH } = elementDom.getBoundingClientRect();

          let elCx = elX - pX + ( elW / 2 );
          let elCy = elY - pY + ( elH / 2 );

          let m2 = [
            [ elCx, SVGHeight - elCy, 0 ],
            [ 0   , 1   , 0 ],
            [ 0   , 0   , 1 ]
          ];

          let transformResult = MathUtils.multiplyMatrices( m1, m2 );

          xBar += transformResult[0][0];
          yBar += transformResult[0][1];
        }
        elementCount++;
      };

      if( lines ) lines.forEach( cb );
      if( holes ) holes.forEach( cb );
      if( items ) items.forEach( cb );
      if( areas ) areas.forEach( cb );
    });

    if( elementCount ) {
      state = this.setBarycenter( state, groupID, xBar / elementCount, yBar / elementCount ).updatedState;
    }

    return { updatedState: state };
  }

  static removeElement( state, groupID, layerID, elementPrototype, elementID ) {
    let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]);

    if( !actualList || !actualList.contains(elementID) )
    {
      return { updatedState: state };
    }

    state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.filterNot( el => el === elementID ));

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static setAttributes( state, groupID, attributes ){
    state = state.mergeIn(['scene', 'groups', groupID], attributes);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static setProperties( state, groupID, properties ){
    state = state.mergeIn(['scene', 'groups', groupID, 'properties'], properties);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static remove( state, groupID ) {
    state = state.removeIn(['scene', 'groups', groupID]);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static removeAndDeleteElements( state, groupID ) {
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      if( lines ) {
        lines.forEach( lineID => {
          state = Line.remove( state, groupLayerID, lineID ).updatedState;
          state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState;
        });
      }

      if( holes ) holes.forEach( holeID => { state = Hole.remove( state, groupLayerID, holeID ).updatedState; });
      if( items ) items.forEach( itemID => { state = Item.remove( state, groupLayerID, itemID ).updatedState; });
      //( actually ) no effect by area's destruction
      if( false && areas ) areas.forEach( areaID => { state = Area.remove( state, groupLayerID, areaID ).updatedState; });

      state = state.deleteIn([ 'scene', 'groups', groupID ]);
    });

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState: state };
  }

  static translate( state, groupID, x, y ) {
    let deltaX = x - state.getIn(['scene', 'groups', groupID, 'x']);
    let deltaY = y - state.getIn(['scene', 'groups', groupID, 'y']);

    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      //move vertices instead lines avoiding multiple vertex translation
      if( lines ) {
        let vertices = {};
        lines.forEach( lineID => {
          let line = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID]);
          if( !vertices[ line.vertices.get(0) ] ) vertices[ line.vertices.get(0) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(0)])
          if( !vertices[ line.vertices.get(1) ] ) vertices[ line.vertices.get(1) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(1)])
        });

        for( let vertexID in vertices ) {
          let { x: xV, y: yV } = vertices[ vertexID ];
          state = Vertex.setAttributes( state, groupLayerID, vertexID, new Map({ x: xV + deltaX, y: yV + deltaY }) ).updatedState;
        }
      }

      if( items ) state = items
        .map( itemID => state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]) )
        .reduce( ( newState, item ) => {
          let { x: xI, y: yI } = item;

          return Item.setAttributes( newState, groupLayerID, item.id, new Map({ x: xI + deltaX, y: yI + deltaY }) ).updatedState;
        }, state );

      //translation of holes and areas should not take any effect
      //if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; });
      //if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; });

      state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState;
    });

    state = this.setBarycenter( state, groupID, x, y ).updatedState;

    return { updatedState: state };
  }

  static rotate( state, groupID, alpha ) {
    console.log('rotate', state.toJS(), groupID, toJS );
    return { updatedState: state };
  }

}

export { Group as default };
<commit_msg>moved barycenter calculus from svg bounding box to state coordinates<commit_after>import {
  Project,
  Line,
  Hole,
  Item,
  Area,
  Layer,
  Vertex
} from './export';
import { Map, List } from 'immutable';
import { Group as GroupModel } from '../models';
import { history, IDBroker, MathUtils, GeometryUtils } from '../utils/export';

class Group{

  static select( state, groupID ){
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    state = Project.setAlterate( state ).updatedState;

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      if( lines ) lines.forEach( lineID => { state = Line.select( state, groupLayerID, lineID ).updatedState; });
      if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; });
      if( items ) items.forEach( itemID => { state = Item.select( state, groupLayerID, itemID ).updatedState; });
      if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; });
    });

    state = Project.setAlterate( state ).updatedState;

    let groups = state.getIn(['scene', 'groups']).map( g  => g.set('selected', false) );

    state = state.setIn(['scene', 'groups'], groups).setIn([ 'scene', 'groups', groupID, 'selected' ], true);

    return { updatedState: state };
  }

  static unselect( state, groupID ){
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);
    let reduced = layerList.reduce( ( newState, layer, layerID ) => Layer.unselectAll( newState, layerID ).updatedState, state );
    state = reduced.setIn([ 'scene', 'groups', groupID, 'selected' ], false);

    return { updatedState: state };
  }

  static create( state ){
    let groupID = IDBroker.acquireID();

    state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) );

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState: state };
  }

  static createFromSelectedElements( state ){
    let groupID = IDBroker.acquireID();

    state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) );

    state.getIn(['scene', 'layers']).forEach((layer) => {

      let layerID = layer.get('id');
      let layerElements = {
        'lines': layer.get('lines').filter( el => el.get('selected') ),
        'items': layer.get('items').filter( el => el.get('selected') ),
        'holes': layer.get('holes').filter( el => el.get('selected') ),
        'areas': layer.get('areas').filter( el => el.get('selected') )
      };

      for( let elementPrototype in layerElements ) {
        layerElements[elementPrototype].forEach( el => state = this.addElement( state, groupID, layerID, elementPrototype, el.get('id') ).updatedState );
      }
    });

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return {updatedState: state};
  }

  static addElement( state, groupID, layerID, elementPrototype, elementID ){
    let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]) || new List();

    if( !actualList.contains(elementID) ) {
      state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.push(elementID));

      state = this.reloadBaricenter( state, groupID ).updatedState;

      state = state.merge({
        sceneHistory: history.historyPush( state.sceneHistory, state.scene )
      });
    }

    return { updatedState: state };
  }

  static setBarycenter( state, groupID, x, y ) {
    if (typeof x !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'x'], x);
    if (typeof y !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'y'], y);

    return { updatedState: state };
  }

  static reloadBaricenter( state, groupID ) {
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    let { a, b, c, d, e, f, SVGHeight } = state.get('viewer2D').toJS();

    let m1 = [
      [ a, b, c ],
      [ d, e, f ],
      [ 0, 0, 1 ]
    ];

    let paper = document.getElementById('svg-drawing-paper');
    let { x: pX, y: pY } = paper.getBoundingClientRect();

    let xBar = 0;
    let yBar = 0;
    let elementCount = 0;

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      if( lines ) lines.forEach( ( lineID ) => {
        let vertices = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID, 'vertices'])
          .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) );

        let { x: x1, y: y1 } = vertices.get(0);
        let { x: x2, y: y2 } = vertices.get(1);
        let { x: xM, y: yM } = GeometryUtils.midPoint( x1, y1, x2, y2 );

        xBar += xM;
        yBar += yM;
        elementCount++;
      });

      if( holes ) holes.forEach( holeID => {
        let hole = state.getIn(['scene', 'layers', groupLayerID, 'holes', holeID]);
        let lineVertices = state.getIn(['scene', 'layers', groupLayerID, 'lines', hole.line, 'vertices'])
          .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) );
        let { x: x1, y: y1 } = lineVertices.get(0);
        let { x: x2, y: y2 } = lineVertices.get(1);
        let { x, y } = GeometryUtils.extendLine( x1, y1, x2, y2, hole.offset * GeometryUtils.pointsDistance( x1, y1, x2, y2 ) );

        xBar += x;
        yBar += y;
        elementCount++;
      });

      if( items ) items.forEach( itemID => {
        let { x, y } = state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]);

        xBar += x;
        yBar += y;
        elementCount++;
      });

      if( areas ) areas.forEach( areaID => {
        let areaVertices = state.getIn(['scene', 'layers', groupLayerID, 'areas', areaID, 'vertices'])
          .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) ).toJS();
        let { x, y } = GeometryUtils.verticesMidPoint( areaVertices );

        xBar += x;
        yBar += y;
        elementCount++;
      });
    });

    if( elementCount ) {
      state = this.setBarycenter( state, groupID, xBar / elementCount, yBar / elementCount ).updatedState;
    }

    return { updatedState: state };
  }

  static removeElement( state, groupID, layerID, elementPrototype, elementID ) {
    let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]);

    if( !actualList || !actualList.contains(elementID) )
    {
      return { updatedState: state };
    }

    state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.filterNot( el => el === elementID ));

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static setAttributes( state, groupID, attributes ){
    state = state.mergeIn(['scene', 'groups', groupID], attributes);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static setProperties( state, groupID, properties ){
    state = state.mergeIn(['scene', 'groups', groupID, 'properties'], properties);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static remove( state, groupID ) {
    state = state.removeIn(['scene', 'groups', groupID]);

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState : state };
  }

  static removeAndDeleteElements( state, groupID ) {
    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      state = Layer.unselectAll( state, groupLayerID ).updatedState;

      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      if( lines ) {
        lines.forEach( lineID => {
          state = Line.remove( state, groupLayerID, lineID ).updatedState;
          state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState;
        });
      }

      if( holes ) holes.forEach( holeID => { state = Hole.remove( state, groupLayerID, holeID ).updatedState; });
      if( items ) items.forEach( itemID => { state = Item.remove( state, groupLayerID, itemID ).updatedState; });
      //( actually ) no effect by area's destruction
      if( false && areas ) areas.forEach( areaID => { state = Area.remove( state, groupLayerID, areaID ).updatedState; });

      state = state.deleteIn([ 'scene', 'groups', groupID ]);
    });

    state = state.merge({
      sceneHistory: history.historyPush( state.sceneHistory, state.scene )
    });

    return { updatedState: state };
  }

  static translate( state, groupID, x, y ) {
    let deltaX = x - state.getIn(['scene', 'groups', groupID, 'x']);
    let deltaY = y - state.getIn(['scene', 'groups', groupID, 'y']);

    let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]);

    layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => {
      let lines = groupLayerElements.get('lines');
      let holes = groupLayerElements.get('holes');
      let items = groupLayerElements.get('items');
      let areas = groupLayerElements.get('areas');

      //move vertices instead lines avoiding multiple vertex translation
      if( lines ) {
        let vertices = {};
        lines.forEach( lineID => {
          let line = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID]);
          if( !vertices[ line.vertices.get(0) ] ) vertices[ line.vertices.get(0) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(0)])
          if( !vertices[ line.vertices.get(1) ] ) vertices[ line.vertices.get(1) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(1)])
        });

        for( let vertexID in vertices ) {
          let { x: xV, y: yV } = vertices[ vertexID ];
          state = Vertex.setAttributes( state, groupLayerID, vertexID, new Map({ x: xV + deltaX, y: yV + deltaY }) ).updatedState;
        }
      }

      if( items ) state = items
        .map( itemID => state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]) )
        .reduce( ( newState, item ) => {
          let { x: xI, y: yI } = item;

          return Item.setAttributes( newState, groupLayerID, item.id, new Map({ x: xI + deltaX, y: yI + deltaY }) ).updatedState;
        }, state );

      //translation of holes and areas should not take any effect
      //if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; });
      //if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; });

      state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState;
    });

    state = this.setBarycenter( state, groupID, x, y ).updatedState;

    return { updatedState: state };
  }

  static rotate( state, groupID, alpha ) {
    console.log('rotate', state.toJS(), groupID, toJS );
    return { updatedState: state };
  }

}

export { Group as default };
<|endoftext|>