<commit_before>/* globals __DEV__ */
import Phaser from 'phaser'
import Character from '../things/Character'
import Creature from '../things/Creature'
import Defines from '../Defines'
import Map from '../map/Map'
import io from 'socket.io-client'

export default class extends Phaser.State {
  init () {}
  preload () {}

  create () {
    this.map = new Map({ game: this.game })

    // @ TODO
    // Make sounds for actions

    // @ TODO
    // Make logs attack player, ai action should test if player is near

    // @ TODO
    // Create drops
    // when Character kill() called drop random (make it an action)

    // @ TODO
    // make a player inventory
    // pick up things automatically that are on current square

    // @ TODO
    // make map

    // @ TODO
    // make map blocking

    // @ TODO
    // make map mutable with drops

    // @ TODO
    // make so you can click on map to go to location (route finding)

    // @ TODO
    // make so you can select controlled units in mass and direct

    // create our main character
    // @ TODO
    // use some sort of position generator for this
    let x = this.map.getCenterX()
    let y = this.map.getCenterY()
    while (this.map.isBlocked(x, y)) {
      x += 1
      y += 1
    }
    this.character = new Character({
      map: this.map,
      name: 'character',
      x: x,
      y: y,
      height: 22,
      width: 16
    })
    this.map.addThing(this.character)

    // create a bunch of random moving tree npcs
    // @ TODO use some sort of npc generator for this
    this.logs = []
    for (let i = 0; i < 40; i++) {
      let x, y
      do {
        x = Creature.randomRange(2, this.map.getHeight() - 3)
        y = Creature.randomRange(2, this.map.getWidth() - 3)
      } while (this.map.isBlocked(x, y))
      let log = new Creature({
        map: this.map,
        name: 'log',
        x: x,
        y: y,
        height: 22,
        width: 16
      })
      this.map.addThing(log)
      this.logs.push(log)
    }

    let socket = io('http://localhost:4000')
    socket.on('connect', function () {
      console.log('got conected')
      socket.send({log: 'test message from client'})
    })
    socket.on('disconnect', function () {
      console.log('got disconnected')
    })
    socket.on('error', function () {
      console.log('got error')
    })
    socket.on('onlog', function (data) {
      console.log('got log: ' + data.log)
    })
    socket.on('onping', function () {
      console.log('got ping')
      socket.send({type: 'pong'})
    })
  }

  update () {
    let destX = 0
    let destY = 0
    let dir = this.character.getDirection()
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.S)) {
      destY++
      dir = 'down'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.W)) {
      destY--
      dir = 'up'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.A)) {
      destX--
      dir = 'left'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.D)) {
      destX++
      dir = 'right'
    }
    if (destX !== 0 || destY !== 0) {
      if (this.game.input.keyboard.isDown(Phaser.KeyCode.SHIFT)) {
        this.character.setRun(true)
      } else {
        this.character.setRun(false)
      }
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.SPACEBAR)) {
      this.character.setAttack(true)
    } else {
      this.character.setAttack(false)
    }
    this.character.setMovement(destX, destY)
    this.character.setDirection(dir)

    this.logs.forEach((log) => {
      // @TODO
      // refactor this to use some sort of AI agent that generates actions
      // similar to the keyboard actions above, then we can just loop through
      // all actors and use their 'agent' to generate actions
      // main loop can just call getNextAction() from each agent
      // we can bind a keyboard agent to the player character
      // and we can bind an AI agent to each npc
      // at that point we may not need the Creature type, since it's basically
      // the same as a Character, or maybe the AI is baked into the Character/Creature
      // type of classes
      log.aiAction()
    })

    this.map.update()
  }

  render () {
    if (__DEV__) {
      // this.game.debug.spriteInfo(this.character.spriters[this.character.name].sprite, 32, 32)
      let x = Math.floor((this.character.x / Defines.mapTileWidth))
      let y = Math.floor((this.character.y / Defines.mapTileHeight))
      this.game.debug.text('Character: tile x,y = ' +
        String(x) + ', ' + String(y) + ' ' +
        'Character: x,y = ' +
        String(this.character.x) + ', ' + String(this.character.y)
      , 10, 20)
    }
  }
}
<commit_msg>todo comments<commit_after>/* globals __DEV__ */
import Phaser from 'phaser'
import Character from '../things/Character'
import Creature from '../things/Creature'
import Defines from '../Defines'
import Map from '../map/Map'
import io from 'socket.io-client'

export default class extends Phaser.State {
  init () {}
  preload () {}

  create () {
    this.map = new Map({ game: this.game })

    // @ TODO
    // sync map, spawns, movement, actions, kills with server

    // @ TODO
    // Make sounds for actions

    // @ TODO
    // Make logs attack player, ai action should test if player is near

    // @ TODO
    // Create drops
    // when Character kill() called drop random (make it an action)

    // @ TODO
    // make a player inventory
    // pick up things automatically that are on current square

    // @ TODO
    // make map

    // @ TODO
    // make map blocking

    // @ TODO
    // make map mutable with drops

    // @ TODO
    // make so you can click on map to go to location (route finding)

    // @ TODO
    // make so you can select controlled units in mass and direct

    // create our main character
    // @ TODO
    // use some sort of position generator for this
    let x = this.map.getCenterX()
    let y = this.map.getCenterY()
    while (this.map.isBlocked(x, y)) {
      x += 1
      y += 1
    }
    this.character = new Character({
      map: this.map,
      name: 'character',
      x: x,
      y: y,
      height: 22,
      width: 16
    })
    this.map.addThing(this.character)

    // create a bunch of random moving tree npcs
    // @ TODO use some sort of npc generator for this
    this.logs = []
    for (let i = 0; i < 40; i++) {
      let x, y
      do {
        x = Creature.randomRange(2, this.map.getHeight() - 3)
        y = Creature.randomRange(2, this.map.getWidth() - 3)
      } while (this.map.isBlocked(x, y))
      let log = new Creature({
        map: this.map,
        name: 'log',
        x: x,
        y: y,
        height: 22,
        width: 16
      })
      this.map.addThing(log)
      this.logs.push(log)
    }

    let socket = io('http://localhost:4000')
    socket.on('connect', function () {
      console.log('got conected')
      socket.send({log: 'test message from client'})
    })
    socket.on('disconnect', function () {
      console.log('got disconnected')
    })
    socket.on('error', function () {
      console.log('got error')
    })
    socket.on('onlog', function (data) {
      console.log('got log: ' + data.log)
    })
    socket.on('onping', function () {
      console.log('got ping')
      socket.send({type: 'pong'})
    })
  }

  update () {
    let destX = 0
    let destY = 0
    let dir = this.character.getDirection()
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.S)) {
      destY++
      dir = 'down'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.W)) {
      destY--
      dir = 'up'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.A)) {
      destX--
      dir = 'left'
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.D)) {
      destX++
      dir = 'right'
    }
    if (destX !== 0 || destY !== 0) {
      if (this.game.input.keyboard.isDown(Phaser.KeyCode.SHIFT)) {
        this.character.setRun(true)
      } else {
        this.character.setRun(false)
      }
    }
    if (this.game.input.keyboard.isDown(Phaser.KeyCode.SPACEBAR)) {
      this.character.setAttack(true)
    } else {
      this.character.setAttack(false)
    }
    this.character.setMovement(destX, destY)
    this.character.setDirection(dir)

    this.logs.forEach((log) => {
      // @TODO
      // refactor this to use some sort of AI agent that generates actions
      // similar to the keyboard actions above, then we can just loop through
      // all actors and use their 'agent' to generate actions
      // main loop can just call getNextAction() from each agent
      // we can bind a keyboard agent to the player character
      // and we can bind an AI agent to each npc
      // at that point we may not need the Creature type, since it's basically
      // the same as a Character, or maybe the AI is baked into the Character/Creature
      // type of classes
      log.aiAction()
    })

    this.map.update()
  }

  render () {
    if (__DEV__) {
      // this.game.debug.spriteInfo(this.character.spriters[this.character.name].sprite, 32, 32)
      let x = Math.floor((this.character.x / Defines.mapTileWidth))
      let y = Math.floor((this.character.y / Defines.mapTileHeight))
      this.game.debug.text('Character: tile x,y = ' +
        String(x) + ', ' + String(y) + ' ' +
        'Character: x,y = ' +
        String(this.character.x) + ', ' + String(this.character.y)
      , 10, 20)
    }
  }
}
<|endoftext|>


<commit_before>function parseDatacolumn(DATACOLUMN) {
  var obj = {};

  for (var i = 0, ilen = DATACOLUMN.attributes.length; i < ilen; i++) {
    obj[DATACOLUMN.attributes[i].name] = DATACOLUMN.attributes[i].value || true;
  }

  obj.data = obj.value;
  delete obj.value;

  obj.readOnly = obj.readonly;
  delete obj.readonly;

  obj.checkedTemplate = obj.checkedtemplate;
  delete obj.checkedtemplate;

  obj.uncheckedTemplate = obj.uncheckedtemplate;
  delete obj.uncheckedtemplate;

  if (obj.type === 'autocomplete' && typeof obj.source === 'string') {
    obj.source = window[obj.source];
  }

  return obj;
}

var publicMethods = ['updateSettings', 'loadData', 'render', 'setDataAtCell', 'setDataAtRowProp', 'getDataAtCell', 'getDataAtRowProp', 'countRows', 'countCols', 'rowOffset', 'colOffset', 'countVisibleRows', 'countVisibleCols', 'clear', 'clearUndo', 'getData', 'alter', 'getCell', 'getCellMeta', 'selectCell', 'deselectCell', 'getSelected', 'destroyEditor', 'getRowHeader', 'getColHeader', 'destroy', 'isUndoAvailable', 'isRedoAvailable', 'undo', 'redo', 'countEmptyRows', 'countEmptyCols', 'isEmptyRow', 'isEmptyCol', 'parseSettingsFromDOM'];

var publish = {};
for (var i = 0, ilen = publicMethods.length; i < ilen; i++) {
  publish[publicMethods[i]] = (function (methodName) {
    return function () {
      return this.instance[methodName].apply(this.instance, arguments);
    }
  })(publicMethods[i]);
}

Polymer.register(this, {
  instance: null,
  ready: function () {
    var DATACOLUMNs = this.querySelectorAll('datacolumn');
    var columns = [];
    for (var i = 0, ilen = DATACOLUMNs.length; i < ilen; i++) {
      columns.push(parseDatacolumn(DATACOLUMNs[i]));
    }

    var options = {
      data: window[this.datarows],
      columns: columns,
      minRows: 5,
      minCols: 6,
      minSpareRows: 1,
      autoWrapRow: true,
      colHeaders: true,
      contextMenu: true
    };

    jQuery(this.$.htContainer).handsontable(options);

    this.instance = jQuery(this.$.htContainer).data('handsontable');
  },
  publish: publish
});<commit_msg>allow methods addHook, addHookOnce<commit_after>function parseDatacolumn(DATACOLUMN) {
  var obj = {};

  for (var i = 0, ilen = DATACOLUMN.attributes.length; i < ilen; i++) {
    obj[DATACOLUMN.attributes[i].name] = DATACOLUMN.attributes[i].value || true;
  }

  obj.data = obj.value;
  delete obj.value;

  obj.readOnly = obj.readonly;
  delete obj.readonly;

  obj.checkedTemplate = obj.checkedtemplate;
  delete obj.checkedtemplate;

  obj.uncheckedTemplate = obj.uncheckedtemplate;
  delete obj.uncheckedtemplate;

  if (obj.type === 'autocomplete' && typeof obj.source === 'string') {
    obj.source = window[obj.source];
  }

  return obj;
}

var publicMethods = ['updateSettings', 'loadData', 'render', 'setDataAtCell', 'setDataAtRowProp', 'getDataAtCell', 'getDataAtRowProp', 'countRows', 'countCols', 'rowOffset', 'colOffset', 'countVisibleRows', 'countVisibleCols', 'clear', 'clearUndo', 'getData', 'alter', 'getCell', 'getCellMeta', 'selectCell', 'deselectCell', 'getSelected', 'destroyEditor', 'getRowHeader', 'getColHeader', 'destroy', 'isUndoAvailable', 'isRedoAvailable', 'undo', 'redo', 'countEmptyRows', 'countEmptyCols', 'isEmptyRow', 'isEmptyCol', 'parseSettingsFromDOM', 'addHook', 'addHookOnce'];

var publish = {};
for (var i = 0, ilen = publicMethods.length; i < ilen; i++) {
  publish[publicMethods[i]] = (function (methodName) {
    return function () {
      return this.instance[methodName].apply(this.instance, arguments);
    }
  })(publicMethods[i]);
}

Polymer.register(this, {
  instance: null,
  ready: function () {
    var DATACOLUMNs = this.querySelectorAll('datacolumn');
    var columns = [];
    for (var i = 0, ilen = DATACOLUMNs.length; i < ilen; i++) {
      columns.push(parseDatacolumn(DATACOLUMNs[i]));
    }

    var options = {
      data: window[this.datarows],
      columns: columns,
      minRows: 5,
      minCols: 6,
      minSpareRows: 0,
      autoWrapRow: true,
      colHeaders: true,
      contextMenu: true
    };

    jQuery(this.$.htContainer).handsontable(options);

    this.instance = jQuery(this.$.htContainer).data('handsontable');
  },
  publish: publish
});<|endoftext|>


<commit_before>angular.module('user.character.manage.services', ['ngResource', 'services.redirection', 'services.csrfResource', 'services.troupes', 'services.characters']);

angular.module('user.character.manage.controllers', ['user.character.manage.services', 'sources.settings', 'sources.clans']).
controller('manageCharacter', ['$scope', '$rootScope', 'redirect', 'characterRepository', 'clanSource', 'bloodlineSource', function($scope, $rootScope, redirect, characterRepository, clanSource, bloodlineSource) {
	$scope.requests = [];
	$scope.clans = clanSource.get();
	$scope.clan = $scope.clans[$scope.character.clan];

	$scope.bloodlines = [];
	if($scope.clan) {
		$scope.bloodlines = $scope.clan.bloodlines
		$scope.bloodline = bloodlineSource.get()[$scope.character.bloodline];
	}

	$scope.disciplineOptions = [];
	$scope.inClanDiscipline = [];
	if($scope.bloodline) {
		$scope.disciplineOptions = $scope.bloodline.disciplines;
	}
	if(!$scope.disciplineOptions) {
		$scope.disciplineOptions = bloodlineSource.get()[0].disciplines;
	}

	$scope.clanChange = function() {
		if($scope.clan) {
			$scope.requests.push({"trait": 0, "value": $scope.clan.id});
			$scope.bloodlines = $scope.clan.bloodlines;
			if($scope.bloodlines.length == 1) {
				$scope.bloodline = $scope.bloodlines[0];
				$scope.bloodlineChange();
			}
		} else {
			$scope.bloodlines = [];
		}
	}
	$scope.bloodlineChange = function() {
		if($scope.bloodline) {
			$scope.requests.push({"trait": 1, "value": $scope.bloodline.id});
			$scope.disciplineOptions = $scope.bloodline.disciplines;
		}
	}
	$scope.disciplineChange = function(disciplineIndex) {
		console.log(disciplineIndex);
		console.log(this.characterDisciplines[disciplineIndex].id);
	}
	$scope.submit = function(csrfHeader, csrfToken) {
		characterRepository.addRequestsToCharacter($scope.character.id, $scope.requests, csrfHeader, csrfToken).
			success(function(data, status, headers, config) {redirect.toUrl('/user/page/characters')}).
			error(function(data, status, headers, config) {console.log("addRequestsToCharacter failed")});
	};

}]);

angular.module('user.character.manage.directives', ['user.character.manage.services']).
directive('manageCharacter', ['characterRepository', function(characterRepository) {
	return {
		restrict: 'E',
		scope: {
			character: '=',
			csrf: '='
		},
		templateUrl: '/js/user/character/manage.html'
	};
}]);

angular.module('user.character.manage.filters', ['filters.setting']);

angular.module('user.character.manage', ['user.character.manage.filters', 'user.character.manage.controllers', 'user.character.manage.directives', 'user.character.manage.services']);<commit_msg>Cleaned up when old bloodlines / disciplines were getting cleaned out so they wouldn't stick around inappropriately.<commit_after>angular.module('user.character.manage.services', ['ngResource', 'services.redirection', 'services.csrfResource', 'services.troupes', 'services.characters']);

angular.module('user.character.manage.controllers', ['user.character.manage.services', 'sources.settings', 'sources.clans']).
controller('manageCharacter', ['$scope', '$rootScope', 'redirect', 'characterRepository', 'clanSource', 'bloodlineSource', function($scope, $rootScope, redirect, characterRepository, clanSource, bloodlineSource) {
	$scope.requests = [];
	$scope.clans = clanSource.get();
	$scope.clan = $scope.clans[$scope.character.clan];

	$scope.bloodlines = [];
	$scope.disciplineOptions = [];
	if($scope.clan) {
		$scope.bloodlines = $scope.clan.bloodlines
		$scope.bloodline = bloodlineSource.get()[$scope.character.bloodline];
	}

	if($scope.bloodline) {
		$scope.disciplineOptions = $scope.bloodline.disciplines;
	}
	if(!$scope.disciplineOptions) {
		$scope.disciplineOptions = bloodlineSource.get()[0].disciplines;
	}

	$scope.clanChange = function() {
		$scope.bloodline = null;
		$scope.bloodlines = [];
		if($scope.clan) {
			$scope.requests.push({"trait": 0, "value": $scope.clan.id});
			$scope.bloodlines = $scope.clan.bloodlines;
			if($scope.bloodlines.length == 1) {
				$scope.bloodline = $scope.bloodlines[0];
			}
		}
		$scope.bloodlineChange();
	}
	$scope.bloodlineChange = function() {
		$scope.disciplineOptions = [];
		if($scope.bloodline) {
			$scope.requests.push({"trait": 1, "value": $scope.bloodline.id});
			$scope.disciplineOptions = $scope.bloodline.disciplines;
		}
	}
	$scope.disciplineChange = function(disciplineIndex) {
		console.log(disciplineIndex);
		console.log(this.characterDisciplines[disciplineIndex].id);
	}
	$scope.submit = function(csrfHeader, csrfToken) {
		characterRepository.addRequestsToCharacter($scope.character.id, $scope.requests, csrfHeader, csrfToken).
			success(function(data, status, headers, config) {redirect.toUrl('/user/page/characters')}).
			error(function(data, status, headers, config) {console.log("addRequestsToCharacter failed")});
	};

}]);

angular.module('user.character.manage.directives', ['user.character.manage.services']).
directive('manageCharacter', ['characterRepository', function(characterRepository) {
	return {
		restrict: 'E',
		scope: {
			character: '=',
			csrf: '='
		},
		templateUrl: '/js/user/character/manage.html'
	};
}]);

angular.module('user.character.manage.filters', ['filters.setting']);

angular.module('user.character.manage', ['user.character.manage.filters', 'user.character.manage.controllers', 'user.character.manage.directives', 'user.character.manage.services']);<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013 ESHA Research
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Allows use of the 'overwrite' param on set calls to give an enforced expiration date
 * without breaking existing 'overwrite' functionality.
 *
 * Status: BETA - useful, needs testing
 */
;(function(_) {
    var prefix = 'exp@',
        suffix = ';',
        parse = _.parse,
        _get = _.get,
        _set = _.set;
    _.parse = function(s) {
        if (s && s.indexOf(prefix) === 0) {
            s = s.substring(s.indexOf(suffix)+1);
        }
        return parse(s);
    };
    _.expires = function(s) {
        if (s && s.indexOf(prefix) === 0) {
            return parseInt(s.substring(prefix.length, s.indexOf(suffix)), 10);
        }
        return false;
    };
    _.when = function(min) {// if min, return min->date, else date->min
        var now = Math.floor((new Date().getTime())/60000);
        return min ? new Date((now+min)*60000) : now;
    };
    _.cache = function(area, key) {
        var s = _get(area, key),
            min = _.expires(s);
        if (min && _.when() >= min) {
            return area.removeItem(key);
        }
        return s;
    };
    _.get = function(area, key) {
        var s = _.cache(area, key);
        return s === undefined ? null : s;
    };
    _.set = function(area, key, string, min) {
        try {
            if (min) {
                string = prefix + (_.when()+min) + suffix + string;
            }
            _set(area, key, string);
        } catch (e) {
            if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
                var changed = false;
                for (var i=0,m=area.length; i<m; i++) {
                    if (_.cache(area, key) === undefined) {
                        changed = true;
                    }
                }
                if (changed) {
                    return _.set.apply(this, arguments);
                }
            }
            throw e;
        }
    };
})(window.store._, undefined);<commit_msg>Changed cache to use seconds instead of minutes.<commit_after>/**
 * Copyright (c) 2013 ESHA Research
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Allows use of the 'overwrite' param on set calls to give an enforced expiration date
 * without breaking existing 'overwrite' functionality.
 *
 * Status: BETA - useful, needs testing
 */
;(function(_) {
    var prefix = 'exp@',
        suffix = ';',
        parse = _.parse,
        _get = _.get,
        _set = _.set;
    _.parse = function(s) {
        if (s && s.indexOf(prefix) === 0) {
            s = s.substring(s.indexOf(suffix)+1);
        }
        return parse(s);
    };
    _.expires = function(s) {
        if (s && s.indexOf(prefix) === 0) {
            return parseInt(s.substring(prefix.length, s.indexOf(suffix)), 10);
        }
        return false;
    };
    _.when = function(min) {// if min, return min->date, else date->min
        var now = Math.floor((new Date().getTime())/1000);
        return min ? new Date((now+min)*1000) : now;
    };
    _.cache = function(area, key) {
        var s = _get(area, key),
            min = _.expires(s);
        if (min && _.when() >= min) {
            return area.removeItem(key);
        }
        return s;
    };
    _.get = function(area, key) {
        var s = _.cache(area, key);
        return s === undefined ? null : s;
    };
    _.set = function(area, key, string, min) {
        try {
            if (min) {
                string = prefix + (_.when()+min) + suffix + string;
            }
            _set(area, key, string);
        } catch (e) {
            if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
                var changed = false;
                for (var i=0,m=area.length; i<m; i++) {
                    if (_.cache(area, key) === undefined) {
                        changed = true;
                    }
                }
                if (changed) {
                    return _.set.apply(this, arguments);
                }
            }
            throw e;
        }
    };
})(window.store._, undefined);
<|endoftext|>


<commit_before>import Vue from 'vue'
import Vuex from 'vuex'
import * as api from './api'
import * as Const from './const'

Vue.use(Vuex)

const store = new Vuex.Store({

  // State
  state: {
    matches: [],
    worlds: [],
    glicko: {},
    predictedglicko: {},
    objectives: [],
    guilds: {},
    leaderboard: {},
    timezones: {},
    chartsLoaded: false,
    grapherQuery: {},
    selectedWorld: ''
  },

  // Actions
  actions: {
    UPDATE_SELECTEDWORLD: ({ commit }, { selectedWorld }) => {
      commit('SET_SELECTEDWORLD', { selectedWorld })
    },

    UPDATE_GRAPHERQUERY: ({ commit }, { grapherQuery }) => {
      commit('SET_GRAPHERQUERY', { grapherQuery })
    },

    FETCH_MATCHES: ({ commit }) => {
      return api.fetchMatches()
        .then(matches => commit('SET_MATCHES', { matches }))
    },

    FETCH_WORLDS: ({ commit }) => {
      return api.fetchWorlds()
        .then(worlds => commit('SET_WORLDS', { worlds }))
    },

    FETCH_GLICKO: ({ commit }) => {
      return api.fetchGlicko()
        .then(glicko => commit('SET_GLICKO', { glicko }))
    },

    FETCH_PREDICTEDGLICKO: ({ commit }) => {
      return api.fetchPredictedGlicko()
        .then(predictedglicko => commit('SET_PREDICTEDGLICKO', { predictedglicko }))
    },

    FETCH_OBJECTIVES: ({ commit }) => {
      return api.fetchObjectives()
        .then(objectives => commit('SET_OBJECTIVES', { objectives }))
    },

    FETCH_GUILD: ({ commit, state }, { id }) => {
      return state.guilds[id]
        ? Promise.resolve(state.guilds[id])
        : api.fetchGuild(id).then(guild => commit('SET_GUILD', { guild }))
    },

    FETCH_LEADERBOARD: ({ commit }) => {
      return api.fetchLeaderboard()
        .then(leaderboard => commit('SET_LEADERBOARD', { leaderboard }))
    },

    FETCH_ARCHIVEDATA: ({ }, { matchid, data, start_time, end_time}) => {
      return api.fetchArchiveData(matchid, data, start_time, end_time)
    },

    FETCH_TIMEZONE: ({ commit, state }, { timezone_name, start_time }) => {
      return state.timezones[timezone_name]
        ? Promise.resolve(state.timezones[timezone_name])
        : api.fetchTimezone(timezone_name, start_time)
            .then(timezone => commit('SET_TIMEZONE', { timezone, timezone_name }))
    }
  },

  // Mutations
  mutations: {
    SET_SELECTEDWORLD: (state, { selectedWorld }) => {
      state.selectedWorld = selectedWorld
    },

    SET_GRAPHERQUERY: (state, { grapherQuery }) => {
      state.grapherQuery = grapherQuery
    },

    SET_MATCHES: (state, { matches }) => {
      state.matches = matches
    },

    SET_WORLDS: (state, { worlds }) => {
      state.worlds = worlds
    },

    SET_GLICKO: (state, { glicko }) => {
      state.glicko = glicko
    },

    SET_PREDICTEDGLICKO: (state, { predictedglicko }) => {
      state.predictedglicko = predictedglicko
    },

    SET_OBJECTIVES: (state, { objectives }) => {
      state.objectives = objectives
    },

    SET_GUILD: (state, { guild }) => {
      Vue.set(state.guilds, guild.id, guild)
    },

    SET_LEADERBOARD: (state, { leaderboard }) => {
      state.leaderboard = leaderboard
    },

    SET_CHARTSLOADED: (state) => {
      state.chartsLoaded = true
    },

    SET_TIMEZONE: (state, { timezone, timezone_name }) => {
      Vue.set(state.timezones, timezone_name, timezone)
    }
  },

  getters: {
    objectiveIds () {
      return Const.objectiveIds
    }
  }
})

export default store
<commit_msg>Store states for new endpoints for match history & data ranges.<commit_after>import Vue from 'vue'
import Vuex from 'vuex'
import * as api from './api'
import * as Const from './const'

Vue.use(Vuex)

const store = new Vuex.Store({

  // State
  state: {
    // Predefined state variables
    itemsPerPage: 10,
    chartsLoaded: false,
    selectedWorld: '',
    activeServer: null,
    // To be defined variables
    matches: [],
    worlds: [],
    glicko: {},
    predictedglicko: {},
    objectives: [],
    guilds: {},
    leaderboard: {},
    timezones: {},
    matchHistoryIds: {/*all: [], ...*/},
    matchHistory: {},
    grapherQuery: {}
  },

  // Actions
  actions: {
    UPDATE_SELECTEDWORLD: ({ commit }, { selectedWorld }) => {
      commit('SET_SELECTEDWORLD', { selectedWorld })
    },

    UPDATE_GRAPHERQUERY: ({ commit }, { grapherQuery }) => {
      commit('SET_GRAPHERQUERY', { grapherQuery })
    },

    FETCH_MATCHES: ({ commit }) => {
      return api.fetchMatches()
        .then(matches => commit('SET_MATCHES', { matches }))
    },

    FETCH_WORLDS: ({ commit }) => {
      return api.fetchWorlds()
        .then(worlds => commit('SET_WORLDS', { worlds }))
    },

    FETCH_GLICKO: ({ commit }) => {
      return api.fetchGlicko()
        .then(glicko => commit('SET_GLICKO', { glicko }))
    },

    FETCH_PREDICTEDGLICKO: ({ commit }) => {
      return api.fetchPredictedGlicko()
        .then(predictedglicko => commit('SET_PREDICTEDGLICKO', { predictedglicko }))
    },

    FETCH_OBJECTIVES: ({ commit }) => {
      return api.fetchObjectives()
        .then(objectives => commit('SET_OBJECTIVES', { objectives }))
    },

    FETCH_GUILD: ({ commit, state }, { id }) => {
      return state.guilds[id]
        ? Promise.resolve(state.guilds[id])
        : api.fetchGuild(id).then(guild => commit('SET_GUILD', { guild }))
    },

    FETCH_LEADERBOARD: ({ commit }) => {
      return api.fetchLeaderboard()
        .then(leaderboard => commit('SET_LEADERBOARD', { leaderboard }))
    },

    FETCH_ARCHIVEDATA: ({ }, { matchid, data, start_time, end_time}) => {
      return api.fetchArchiveData(matchid, data, start_time, end_time)
    },

    FETCH_TIMEZONE: ({ commit, state }, { timezone_name, start_time }) => {
      return state.timezones[timezone_name]
        ? Promise.resolve(state.timezones[timezone_name])
        : api.fetchTimezone(timezone_name, start_time)
            .then(timezone => commit('SET_TIMEZONE', { timezone, timezone_name }))
    },

    FETCH_MATCHHISTORY_IDS: ({ commit, dispatch, state }, { server }) => {
      commit('SET_ACTIVE_SERVER', { server })
      return state.matchHistoryIds[server]
        ? Promise.resolve(state.matchHistoryIds[server])
          .then(() => dispatch('ENSURE_ACTIVE_MATCHES'))
        : api.fetchMatchHistoryIds(server)
          .then(matchHistoryIds => commit('SET_MATCHHISTORY_IDS', { server, matchHistoryIds }))
          .then(() => dispatch('ENSURE_ACTIVE_MATCHES'))
    },

    ENSURE_ACTIVE_MATCHES: ({ dispatch, getters }) => {
      return dispatch('FETCH_HISTORICAL_MATCHES', {
        ids: getters.activeIds
      })
    },

    FETCH_HISTORICAL_MATCHES: ({ commit, state }, { ids }) => {
      ids = ids.filter(id => {
        const item = state.matchHistory[id]
        return !item
      })

      if (ids.length) {
        return api.fetchMultipleMatches(ids).then((matchHistory) => commit('SET_MATCHHISTORY', { matchHistory }))
      } else {
        return Promise.resolve()
      }
    },

    FETCH_DATA_RANGE: ({ commit, state }, { start, end, match }) => {
      return api.fetchDataRange(start, end, match)
    }
  },

  // Mutations
  mutations: {
    SET_SELECTEDWORLD: (state, { selectedWorld }) => {
      state.selectedWorld = selectedWorld
    },

    SET_GRAPHERQUERY: (state, { grapherQuery }) => {
      state.grapherQuery = grapherQuery
    },

    SET_MATCHES: (state, { matches }) => {
      state.matches = matches
    },

    SET_WORLDS: (state, { worlds }) => {
      state.worlds = worlds
    },

    SET_GLICKO: (state, { glicko }) => {
      state.glicko = glicko
    },

    SET_PREDICTEDGLICKO: (state, { predictedglicko }) => {
      state.predictedglicko = predictedglicko
    },

    SET_OBJECTIVES: (state, { objectives }) => {
      state.objectives = objectives
    },

    SET_GUILD: (state, { guild }) => {
      Vue.set(state.guilds, guild.id, guild)
    },

    SET_LEADERBOARD: (state, { leaderboard }) => {
      state.leaderboard = leaderboard
    },

    SET_CHARTSLOADED: (state) => {
      state.chartsLoaded = true
    },

    SET_TIMEZONE: (state, { timezone, timezone_name }) => {
      Vue.set(state.timezones, timezone_name, timezone)
    },

    SET_MATCHHISTORY_IDS: (state, { server, matchHistoryIds }) => {
      Vue.set(state.matchHistoryIds, server, matchHistoryIds)
    },

    SET_MATCHHISTORY: (state, { matchHistory }) => {
      matchHistory.forEach(item => {
        if (item) {
          Vue.set(state.matchHistory, item.id, item.match)
        }
      })
    },

    SET_ACTIVE_SERVER: (state, { server }) => {
      state.activeServer = server
    }
  },

  getters: {
    objectiveIds () {
      return Const.objectiveIds
    },

    activePage (state) {
      return Number(state.route.params.page) || 1
    },

    activeIds (state, getters) {
      const { activeServer, itemsPerPage, matchHistoryIds } = state
      const page = getters.activePage
      if (activeServer) {
        const start = (page - 1) * itemsPerPage
        const end = page * itemsPerPage
        return matchHistoryIds[activeServer].slice(start, end)
      } else {
        return []
      }
    },

    activeItems (state, getters) {
      return getters.activeIds.map(id => state.matchHistory[id]).filter(_ => _)
    }
  }
})

export default store
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, combineReducers, compose } from 'redux'

import { resetKeaCache } from '../reset'
import { keaReducer, attachStore } from './reducer'
import { plugins, activatePlugin } from '../plugins'

const reduxDevToolsCompose = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose

const defaultOptions = {
  paths: ['kea', 'scenes'],
  reducers: {},
  preloadedState: undefined,
  middleware: [],
  compose: reduxDevToolsCompose,
  enhancers: [],
  plugins: []
}

export function getStore (opts = {}) {
  // reset everything, needed for SSR
  resetKeaCache()

  // clone options
  let options = Object.assign({}, defaultOptions, opts)

  // activate all the plugins
  options.plugins.forEach(plugin => {
    activatePlugin(plugin)
  })

  // clone reducers
  options.reducers = Object.assign({}, options.reducers)
  options.paths.forEach(path => {
    options.reducers[path] = keaReducer(path)
  })

  // run pre-hooks
  plugins.forEach(f => f.beforeReduxStore && f.beforeReduxStore(options))

  // combine middleware into the first enhancer
  if (options.middleware.length > 0) {
    options.enhancers = [applyMiddleware(...options.middleware)].concat(options.enhancers)
  }

  // use a special compose function?
  const composeEnchancer = options.compose || compose

  // create the store creator
  const finalCreateStore = composeEnchancer(...options.enhancers)(createStore)

  // combine reducers
  const combinedReducers = combineReducers(options.reducers)

  // create store
  const store = finalCreateStore(combinedReducers, Object.assign({}, options.preloadedState))

  // give kea direct access to this store
  // we need this to dispatch hydration actions when new kea logic stores are
  // injected together with react components
  attachStore(store)

  // run post-hooks
  plugins.forEach(f => f.afterReduxStore && f.afterReduxStore(options, store))

  return store
}
<commit_msg>don't reset the cache every time a store is created<commit_after>import { createStore, applyMiddleware, combineReducers, compose } from 'redux'

import { keaReducer, attachStore } from './reducer'
import { plugins, activatePlugin } from '../plugins'

const reduxDevToolsCompose = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose

const defaultOptions = {
  paths: ['kea', 'scenes'],
  reducers: {},
  preloadedState: undefined,
  middleware: [],
  compose: reduxDevToolsCompose,
  enhancers: [],
  plugins: []
}

export function getStore (opts = {}) {
  // clone options
  let options = Object.assign({}, defaultOptions, opts)

  // activate all the plugins
  options.plugins.forEach(plugin => {
    activatePlugin(plugin)
  })

  // clone reducers
  options.reducers = Object.assign({}, options.reducers)
  options.paths.forEach(path => {
    options.reducers[path] = keaReducer(path)
  })

  // run pre-hooks
  plugins.forEach(f => f.beforeReduxStore && f.beforeReduxStore(options))

  // combine middleware into the first enhancer
  if (options.middleware.length > 0) {
    options.enhancers = [applyMiddleware(...options.middleware)].concat(options.enhancers)
  }

  // use a special compose function?
  const composeEnchancer = options.compose || compose

  // create the store creator
  const finalCreateStore = composeEnchancer(...options.enhancers)(createStore)

  // combine reducers
  const combinedReducers = combineReducers(options.reducers)

  // create store
  const store = finalCreateStore(combinedReducers, Object.assign({}, options.preloadedState))

  // give kea direct access to this store
  // we need this to dispatch hydration actions when new kea logic stores are
  // injected together with react components
  attachStore(store)

  // run post-hooks
  plugins.forEach(f => f.afterReduxStore && f.afterReduxStore(options, store))

  return store
}
<|endoftext|>


<commit_before>import { createStore, compose, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import rootReducer from '../reducers/rootReducer';
/* eslint-disable */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(reduxThunk))
);
/* eslint-enable */
export default store;
<commit_msg>feat(store): subscribe store to local storage<commit_after>import { createStore, compose, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import rootReducer from '../reducers/rootReducer';

/* eslint-disable */
const persistedState = localStorage.getItem('applican_store') ? JSON.parse(localStorage.getItem('reduxState')) : {};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  rootReducer,
  persistedState,
  composeEnhancers(applyMiddleware(reduxThunk))
);
/* eslint-enable */

store.subscribe(() => {
  localStorage.setItem('applican_store', JSON.stringify(store.getState()));
});
export default store;
<|endoftext|>


<commit_before>import styled, { css, createGlobalStyle } from 'styled-components'

import {
  PopoverContainer,
  PopoverContent,
  PopoverTrigger
} from 'react-popopo';

export const GlobalStyle = createGlobalStyle`
  .popover[class*="menu"] .popover__content a {
    color: rgba(255, 255, 255, 0.56);
    padding: .5em 1em;
    margin: 0;
    text-decoration: none;
  }

  .popover[class*="menu"] .popover__content a:hover {
    background-color: #00bcd4 !important;
    color: #37474F;
  }

  .comPlainTextContentEditable {
    -webkit-user-modify: read-write-plaintext-only;
    cursor: text;
  }

  .comPlainTextContentEditable--has-placeholder::before {
    content: attr(placeholder);
    opacity: 0.5;
    color: inherit;
    cursor: text;
  }

  .react_trello_dragClass {
    transform: rotate(3deg);
  }

  .react_trello_dragLaneClass {
    transform: rotate(3deg);
  }

  .icon-overflow-menu-horizontal:before {
    content: "\\E91F";
  }
  .icon-lg, .icon-sm {
    color: #798d99;
  }
  .icon-lg {
    height: 32px;
    font-size: 16px;
    line-height: 32px;
    width: 32px;
  }
`

export const CustomPopoverContainer = styled(PopoverContainer)`
  position: absolute;
  right: 10px;
  flex-flow: column nowrap;
`

export const CustomPopoverContent = styled(PopoverContent)`
  visibility: hidden;
  margin-top: -5px;
  opacity: 0;
  position: absolute;
  z-index: 10;
  box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
  transition: all 0.3s ease 0ms;
  border-radius: 3px;
  min-width: 7em;
  flex-flow: column nowrap;
  background-color: #fff;
  color: #000;
  padding: 5px;
  left: 50%;
  transform: translateX(-50%);
  ${props => props.active && `
    visibility: visible;
    opacity: 1;
    transition-delay: 100ms;
  `}
  &::before {
    content: "";
    position: absolute;
    background: transparent none repeat scroll 0 0;
    border: 6px solid transparent;
    transition: all 0.3s ease 0ms;
    left: 50%;
  }
`

export const BoardWrapper = styled.div`
  background-color: #3179ba;
  overflow-y: hidden;
  padding: 5px;
  color: #393939;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  height: 100vh;
`

export const Header = styled.header`
  margin-bottom: 10px;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
`

export const Section = styled.section`
  background-color: #e3e3e3;
  border-radius: 3px;
  margin: 5px 5px;
  position: relative;
  padding: 10px;
  display: inline-flex;
  height: auto;
  max-height: 90%;
  flex-direction: column;
`

export const LaneHeader = styled(Header)`
  margin-bottom: 0px;
  ${props => props.editLaneTitle && css`
    padding: 0px;
    line-height: 30px;
    `
  }
  ${props => !props.editLaneTitle && css`
    padding: 0px 5px;
    `
  }
`

export const LaneFooter = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  position: relative;
  height: 10px;
`

export const ScrollableLane = styled.div`
  flex: 1;
  overflow-y: auto;
  min-width: 250px;
  overflow-x: hidden;
  align-self: center;
  max-height: 90vh;
  margin-top: 10px;
  flex-direction: column;
  justify-content: space-between;
`

export const Title = styled.span`
  font-weight: bold;
  font-size: 15px;
  line-height: 18px;
  cursor: grab;
  width: 70%;
`

export const RightContent = styled.span`
  width: 30%;
  text-align: right;
  padding-right: 10px;
  font-size: 13px;
`
export const CardWrapper = styled.article`
  border-radius: 3px;
  border-bottom: 1px solid #ccc;
  background-color: #fff;
  position: relative;
  padding: 10px;
  cursor: pointer;
  max-width: 250px;
  margin-bottom: 7px;
  min-width: 230px;
`

export const MovableCardWrapper = styled(CardWrapper)`
  &:hover {
    background-color: #f0f0f0;
    color: #000;
  }
`

export const CardHeader = styled(Header)`
  border-bottom: 1px solid #eee;
  padding-bottom: 6px;
  color: #000;
`

export const CardTitle = styled(Title)`
  font-size: 14px;
`

export const CardRightContent = styled(RightContent)`
  font-size: 10px;
`

export const Detail = styled.div`
  font-size: 12px;
  color: #4d4d4d;
  white-space: pre-wrap;
`

export const Footer = styled.div`
  border-top: 1px solid #eee;
  padding-top: 6px;
  text-align: right;
  display: flex;
  justify-content: flex-end;
  flex-direction: row;
  flex-wrap: wrap;
`

export const TagSpan = styled.span`
  padding: 2px 3px;
  border-radius: 3px;
  margin: 2px 5px;
  font-size: 70%;
`

export const AddCardLink = styled.a`
  border-radius: 0 0 3px 3px;
  color: #838c91;
  display: block;
  padding: 5px 2px;
  margin-top: 10px;
  position: relative;
  text-decoration: none;
  cursor: pointer;

  &:hover {
    //background-color: #cdd2d4;
    color: #4d4d4d;
    text-decoration: underline;
  }
`

export const LaneTitle = styled.div`
  font-size: 15px;
  width: 268px;
  height: auto;
`

export const LaneSection = styled.section`
  background-color: #2b6aa3;
  border-radius: 3px;
  margin: 5px;
  position: relative;
  padding: 5px;
  display: inline-flex;
  height: auto;
  flex-direction: column;
`

export const NewLaneSection = styled(LaneSection)`
  width: 200px;
`

export const NewLaneButtons = styled.div`
  margin-top: 10px;
`

export const CardForm = styled.div`
  background-color: #e3e3e3;
`

export const InlineInput = styled.textarea`
  overflow-x: hidden; /* for Firefox (issue #5) */
  word-wrap: break-word;
  min-height: 28px;
  max-height: 112px; /* optional, but recommended */
  resize: none;
  width: 100%;
  height: 28px;
  font-size: 15px;
  line-height: 20px;
  background-color: transparent;
  box-shadow: none;
  box-sizing: border-box;
  border-radius: 3px;
  border: 0;
  padding: 4px 8px;
  outline: 0;
  ${props => props.border && css`
    &:focus {
      box-shadow: inset 0 0 0 2px #0079bf;
    }
    `
  }
  &:focus {
    background-color: white;
  }
`
<commit_msg>Minor updates popover styles<commit_after>import styled, { css, createGlobalStyle } from 'styled-components'

import {
  PopoverContainer,
  PopoverContent,
  PopoverTrigger
} from 'react-popopo'

export const GlobalStyle = createGlobalStyle`
  .comPlainTextContentEditable {
    -webkit-user-modify: read-write-plaintext-only;
    cursor: text;
  }

  .comPlainTextContentEditable--has-placeholder::before {
    content: attr(placeholder);
    opacity: 0.5;
    color: inherit;
    cursor: text;
  }

  .react_trello_dragClass {
    transform: rotate(3deg);
  }

  .react_trello_dragLaneClass {
    transform: rotate(3deg);
  }

  .icon-overflow-menu-horizontal:before {
    content: "\\E91F";
  }
  .icon-lg, .icon-sm {
    color: #798d99;
  }
  .icon-lg {
    height: 32px;
    font-size: 16px;
    line-height: 32px;
    width: 32px;
  }
`

export const CustomPopoverContainer = styled(PopoverContainer)`
  position: absolute;
  right: 10px;
  flex-flow: column nowrap;
`

export const CustomPopoverContent = styled(PopoverContent)`
  visibility: hidden;
  margin-top: -5px;
  opacity: 0;
  position: absolute;
  z-index: 10;
  box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
  transition: all 0.3s ease 0ms;
  border-radius: 3px;
  min-width: 7em;
  flex-flow: column nowrap;
  background-color: #fff;
  color: #000;
  padding: 5px;
  left: 50%;
  transform: translateX(-50%);
  ${props => props.active && `
    visibility: visible;
    opacity: 1;
    transition-delay: 100ms;
  `}
  &::before {
    content: "";
    position: absolute;
    background: transparent none repeat scroll 0 0;
    border: 6px solid transparent;
    transition: all 0.3s ease 0ms;
    left: 50%;
  }
  a {
    color: rgba(255, 255, 255, 0.56);
    padding: .5em 1em;
    margin: 0;
    text-decoration: none;
    &:hover {
      background-color: #00bcd4 !important;
      color: #37474F;
    }
  }
`

export const BoardWrapper = styled.div`
  background-color: #3179ba;
  overflow-y: hidden;
  padding: 5px;
  color: #393939;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  height: 100vh;
`

export const Header = styled.header`
  margin-bottom: 10px;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
`

export const Section = styled.section`
  background-color: #e3e3e3;
  border-radius: 3px;
  margin: 5px 5px;
  position: relative;
  padding: 10px;
  display: inline-flex;
  height: auto;
  max-height: 90%;
  flex-direction: column;
`

export const LaneHeader = styled(Header)`
  margin-bottom: 0px;
  ${props => props.editLaneTitle && css`
    padding: 0px;
    line-height: 30px;
    `
  }
  ${props => !props.editLaneTitle && css`
    padding: 0px 5px;
    `
  }
`

export const LaneFooter = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  position: relative;
  height: 10px;
`

export const ScrollableLane = styled.div`
  flex: 1;
  overflow-y: auto;
  min-width: 250px;
  overflow-x: hidden;
  align-self: center;
  max-height: 90vh;
  margin-top: 10px;
  flex-direction: column;
  justify-content: space-between;
`

export const Title = styled.span`
  font-weight: bold;
  font-size: 15px;
  line-height: 18px;
  cursor: grab;
  width: 70%;
`

export const RightContent = styled.span`
  width: 30%;
  text-align: right;
  padding-right: 10px;
  font-size: 13px;
`
export const CardWrapper = styled.article`
  border-radius: 3px;
  border-bottom: 1px solid #ccc;
  background-color: #fff;
  position: relative;
  padding: 10px;
  cursor: pointer;
  max-width: 250px;
  margin-bottom: 7px;
  min-width: 230px;
`

export const MovableCardWrapper = styled(CardWrapper)`
  &:hover {
    background-color: #f0f0f0;
    color: #000;
  }
`

export const CardHeader = styled(Header)`
  border-bottom: 1px solid #eee;
  padding-bottom: 6px;
  color: #000;
`

export const CardTitle = styled(Title)`
  font-size: 14px;
`

export const CardRightContent = styled(RightContent)`
  font-size: 10px;
`

export const Detail = styled.div`
  font-size: 12px;
  color: #4d4d4d;
  white-space: pre-wrap;
`

export const Footer = styled.div`
  border-top: 1px solid #eee;
  padding-top: 6px;
  text-align: right;
  display: flex;
  justify-content: flex-end;
  flex-direction: row;
  flex-wrap: wrap;
`

export const TagSpan = styled.span`
  padding: 2px 3px;
  border-radius: 3px;
  margin: 2px 5px;
  font-size: 70%;
`

export const AddCardLink = styled.a`
  border-radius: 0 0 3px 3px;
  color: #838c91;
  display: block;
  padding: 5px 2px;
  margin-top: 10px;
  position: relative;
  text-decoration: none;
  cursor: pointer;

  &:hover {
    //background-color: #cdd2d4;
    color: #4d4d4d;
    text-decoration: underline;
  }
`

export const LaneTitle = styled.div`
  font-size: 15px;
  width: 268px;
  height: auto;
`

export const LaneSection = styled.section`
  background-color: #2b6aa3;
  border-radius: 3px;
  margin: 5px;
  position: relative;
  padding: 5px;
  display: inline-flex;
  height: auto;
  flex-direction: column;
`

export const NewLaneSection = styled(LaneSection)`
  width: 200px;
`

export const NewLaneButtons = styled.div`
  margin-top: 10px;
`

export const CardForm = styled.div`
  background-color: #e3e3e3;
`

export const InlineInput = styled.textarea`
  overflow-x: hidden; /* for Firefox (issue #5) */
  word-wrap: break-word;
  min-height: 28px;
  max-height: 112px; /* optional, but recommended */
  resize: none;
  width: 100%;
  height: 28px;
  font-size: 15px;
  line-height: 20px;
  background-color: transparent;
  box-shadow: none;
  box-sizing: border-box;
  border-radius: 3px;
  border: 0;
  padding: 4px 8px;
  outline: 0;
  ${props => props.border && css`
    &:focus {
      box-shadow: inset 0 0 0 2px #0079bf;
    }
    `
  }
  &:focus {
    background-color: white;
  }
`
<|endoftext|>


<commit_before>/*
 * module : 用户注册
 * author : jarryliu
 * date   : 2011/01/21
 */

var _vcode;//验证码

//对每个输入进行验证

//定义元素变量

j6.$('usr').onblur = function () {
    if (this.value == undefined)return;
    if (this.value == '')j6.validator.setTip(this, false, 0, '请输入用户名!');
    //else if(!/^(?=[A-Za-z])/.test(this.value))valid.setError(this,1);     //必须字符开头
    else if (!/^[A-Za-z0-9]+$/.test(this.value))j6.validator.setTip(this, false,'3','');
    else {
        var t = this;
        j6.validator.setTip(t, false, null, '验证中...');
        j6.xhr.jsonPost('/user/ValidUsr', {usr: escape(t.value)}, function (json) {
            if (json.result) {
                j6.validator.setTip(t, true, null, '用户名可用');
            }
            else {
                j6.validator.setTip(t, false,null,json.message);
            }
        });
    }
};

/*
 nick.onblur=function(){
 if(this.value=='')valid.setError(this,0);
 //else if(!/^(?![_\d]+)/.test(this.value))valid.setError(this,1);  //必须字符开头
 else if(!/^[a-zA-Z0-9\u4e00-\u9fa5]+$/.test(this.value))valid.setError(this,2);
 else{
 var leng=this.value.length;
 var match=this.value.match(/[^\\\\\\\\\\\\\\\\x00-\\\\\\\\\\\\\\\\x80]/ig) ;
 if(match!=null)leng+=match.length;
 if(leng>10||leng<4)valid.setError(this,3);
 else{
 //检测昵称
 var t=this;
 j.ajax.get('app.axd?task=register,validfield,nickname,'+escape(this.value),
 function(x){if(x=="")valid.displayRight(t);else valid.displayError(t,x);});
 }
 }
 }

 */
j6.$('pwd').onblur = function () {
    if (/^(?=_)/.test(this.value) || this.value.indexOf('_') == this.value.length - 1)
        j6.validator.setTip(this, false, '1');
    else if (!/^[A-Za-z0-9_]*$/.test(this.value)) {
        j6.validator.setTip(this, false, '3');
    }
    else if (this.value.length < 6 || this.value.length > 12) {
        j6.validator.setTip(this, false, '2');
    }
    else {
        j6.validator.setTip(this, true, null, '');
    }
};

j6.$('rePwd').onblur = function () {
    if (this.value != j6.$('pwd').value) {
        j6.validator.setTip(this, false, null, "两次密码输入不一致")
    } else {
        j6.validator.removeTip(this);
    }
};

var phone = j6.$('phone');
if (phone != null) {
    phone.onblur = function () {
        if (this.value == undefined)return;
        if (this.value != '' && !/^(13[0-9]|15[0|1|2|3|4|5|6|8|9]|18[0|1|2|3|5|6|7|8|9]|17[0|6])(\d{8})$/.test(this.value)) {
            j6.validator.setTip(this, false, '0');
        } else {
            if (this.value != '') {
                j6.validator.removeTip(this);
            } else {
                j6.validator.setTip(this, false, '2');
            }
            /*
             if (this.value != ''){
             var t=this;
             j.ajax.get("app.axd?task=register,validfield,accountname,"+escape(this.value),
             function(x){
             if(x=="")valid.displayRight(t);
             else valid.setError(t,1);
             });

             }*/
        }
    };
}

var inviCode = j6.$('invi_code');
if (inviCode != null) {
    inviCode.onblur = function () {
        var val = this.value;
        if(val.length == 0) {
            j6.validator.removeTip(this);
        }else{
            var t = this;
            j6.validator.setTip(this, false, null, '验证中...');
            j6.xhr.jsonPost('/user/valid_invitation', {invi_code: val}, function (json) {
                if (json.result) {
                    //valid.setTip(t, true, null, '邀请人为:'+json.data.Name);
                    j6.validator.removeTip(t);
                }
                else {
                    j6.validator.setTip(t, false, null, json.message);
                }
            });
        }
    };
}

//if(email!=null){
//	email.onblur=function(){
//		if(!/\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(this.value)){
//			valid.setTip(this,false,0);
//		}else{
//            valid.setTip(this,true);
//		}
//	}
//}

//if(tguser!=null){
//	tguser.onblur=function(){
//		//valid.displayRight(this);
//		//return true;
//		if(this.value==''&&location.href.indexOf('partnerid=101')!=-1){
//			valid.setError(this,1);
//		}else{
//
//            var t=this;
//            j.ajax.get('/valid_tguser?partnerid=&user='+escape(t.value),
//            function(x){
//            	if(x=="")valid.displayRight(t);
//            	else valid.setError(t,0);
//            });
//		}
//	};
//}

//var aCity={11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江",31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}
//function isCardID(sId){
// var iSum=0 ;
// var info="" ;
// if(!/^\d{17}(\d|x)$/i.test(sId)) return " ";
// sId=sId.replace(/x$/i,"a");
// if(aCity[parseInt(sId.substr(0,2))]==null) return " ";
// sBirthday=sId.substr(6,4)+"-"+Number(sId.substr(10,2))+"-"+Number(sId.substr(12,2));
// var d=new Date(sBirthday.replace(/-/g,"/")) ;
// if(sBirthday!=(d.getFullYear()+"-"+ (d.getMonth()+1) + "-" + d.getDate()))return " ";
// for(var i = 17;i>=0;i --) iSum += (Math.pow(2,i) % 11) * parseInt(sId.charAt(17 - i),11) ;
// if(iSum%11!=1) return " ";
// return true;//aCity[parseInt(sId.substr(0,2))]+","+sBirthday+","+ (sId.substr(16,1)%2?"男":"女")
//}

//if(cardnumber!=null){
//    cardnumber.onblur=function(){
//        /*var t=this;
//        j.ajax.get("app.axd?task=register,valididnumber,"+escape(this.value),
//        function(x){if(x=="True")valid.displayRight(t);else valid.setError(t,0);});
//        */
//        if(isCardID(this.value)==true){
//            valid.displayRight(this);
//        }else{
//            valid.setError(this,0);
//        }
//    };
//}

var btnRegister = j6.$('btn_register');
btnRegister.disabled='';
btnRegister.onclick = function () {
    var t = this;
    if (j6.validator.validate('reg_panel')) {
        var d = j6.json.toObject('reg_panel');
        if (d.remember != 'on') {
            alert('请同意注册条款')
        } else {
            var tip = j6.$('tip');
            t.disabled='disabled';
            j6.xhr.jsonPost('/user/postRegisterInfo', d, function (json) {
                if (json.result) {
                    var returnUrl = j6.request('return_url');
                    showRegTip('注册成功
请等待页面跳转');
                    setTimeout(function(){
                        if (returnUrl != '') {
                            location.replace(returnUrl);
                        } else {
                            location.replace('/user/login?return_url=');
                        }
                    },5000);

                } else {
                    tip.className='tip-panel';
                    tip.innerHTML = "注册失败："+json.message;
                    t.disabled='';
                }
            }, function () {
                tip.className='tip-panel';
                tip.innerHTML = '注册失败，请重试';
                t.disabled='';
            });
        }
    }
}

function showRegTip(msg){
    var win = window.parent || window;
    if (!win.xhrCt) {
        win.xhrCt = document.createElement("DIV");
        win.xhrCt.className = 'xhr-container hidden';
        win.xhrCt.innerHTML = j6.template('

', {
            h: j6.screen.height()
        });
        win.document.body.appendChild(win.xhrCt);
        win.xhrGate = win.document.getElementById('xhr_gate_layout');
        win.xhrMsg = win.document.getElementById('xhr_msg_layout');
        win.xhrMsg.style.cssText += "top:200px;border:solid 1px #FC0;background:#FFE;border-radius:5px;color:#F00;font-weight:bold;";
    }

    win.xhrMsg.innerHTML = msg;
    win.xhrGate.className = 'gate';

    win.xhrCt.className = 'xhr-container';
}
<commit_msg>commit<commit_after>/*
 * module : 用户注册
 * author : jarryliu
 * date   : 2011/01/21
 */

var _vcode;//验证码

//对每个输入进行验证

//定义元素变量

j6.$('usr').onblur = function () {
    if (this.value == undefined)return;
    if (this.value == '')j6.validator.setTip(this, false, 0, '请输入用户名!');
    //else if(!/^(?=[A-Za-z])/.test(this.value))valid.setError(this,1);     //必须字符开头
    else if (!/^[A-Za-z0-9]+$/.test(this.value))j6.validator.setTip(this, false,'3','');
    else {
        var t = this;
        j6.validator.setTip(t, false, null, '验证中...');
        j6.xhr.jsonPost('/user/ValidUsr', {usr: escape(t.value)}, function (json) {
            if (json.result) {
                j6.validator.setTip(t, true, null, '用户名可用');
            }
            else {
                j6.validator.setTip(t, false,null,json.message);
            }
        });
    }
};

/*
 nick.onblur=function(){
 if(this.value=='')valid.setError(this,0);
 //else if(!/^(?![_\d]+)/.test(this.value))valid.setError(this,1);  //必须字符开头
 else if(!/^[a-zA-Z0-9\u4e00-\u9fa5]+$/.test(this.value))valid.setError(this,2);
 else{
 var leng=this.value.length;
 var match=this.value.match(/[^\\\\\\\\\\\\\\\\x00-\\\\\\\\\\\\\\\\x80]/ig) ;
 if(match!=null)leng+=match.length;
 if(leng>10||leng<4)valid.setError(this,3);
 else{
 //检测昵称
 var t=this;
 j.ajax.get('app.axd?task=register,validfield,nickname,'+escape(this.value),
 function(x){if(x=="")valid.displayRight(t);else valid.displayError(t,x);});
 }
 }
 }

 */
j6.$('pwd').onblur = function () {
    if (/^(?=_)/.test(this.value) || this.value.indexOf('_') == this.value.length - 1)
        j6.validator.setTip(this, false, '1');
    else if (!/^[A-Za-z0-9_]*$/.test(this.value)) {
        j6.validator.setTip(this, false, '3');
    }
    else if (this.value.length < 6 || this.value.length > 12) {
        j6.validator.setTip(this, false, '2');
    }
    else {
        j6.validator.setTip(this, true, null, '');
    }
};

j6.$('rePwd').onblur = function () {
    if (this.value != j6.$('pwd').value) {
        j6.validator.setTip(this, false, null, "两次密码输入不一致")
    } else {
        j6.validator.removeTip(this);
    }
};

var phone = j6.$('phone');
if (phone != null) {
    phone.onblur = function () {
        if (this.value == undefined)return;
        if (this.value != '' && !/^(13[0-9]|15[0|1|2|3|4|5|6|8|9]|18[0|1|2|3|5|6|7|8|9]|17[0|6])(\d{8})$/.test(this.value)) {
            j6.validator.setTip(this, false, '0');
        } else {
            if (this.value != '') {
                j6.validator.removeTip(this);
            } else {
                j6.validator.setTip(this, false, '2');
            }
            /*
             if (this.value != ''){
             var t=this;
             j.ajax.get("app.axd?task=register,validfield,accountname,"+escape(this.value),
             function(x){
             if(x=="")valid.displayRight(t);
             else valid.setError(t,1);
             });

             }*/
        }
    };
}

var inviCode = j6.$('invi_code');
if (inviCode != null) {
    inviCode.onblur = function () {
        var val = this.value;
        if(val.length == 0) {
            j6.validator.removeTip(this);
        }else{
            var t = this;
            j6.validator.setTip(this, false, null, '验证中...');
            j6.xhr.jsonPost('/user/valid_invitation', {invi_code: val}, function (json) {
                if (json.result) {
                    //valid.setTip(t, true, null, '邀请人为:'+json.data.Name);
                    j6.validator.removeTip(t);
                }
                else {
                    j6.validator.setTip(t, false, null, json.message);
                }
            });
        }
    };
}

//if(email!=null){
//	email.onblur=function(){
//		if(!/\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(this.value)){
//			valid.setTip(this,false,0);
//		}else{
//            valid.setTip(this,true);
//		}
//	}
//}

//if(tguser!=null){
//	tguser.onblur=function(){
//		//valid.displayRight(this);
//		//return true;
//		if(this.value==''&&location.href.indexOf('partnerid=101')!=-1){
//			valid.setError(this,1);
//		}else{
//
//            var t=this;
//            j.ajax.get('/valid_tguser?partnerid=&user='+escape(t.value),
//            function(x){
//            	if(x=="")valid.displayRight(t);
//            	else valid.setError(t,0);
//            });
//		}
//	};
//}

//var aCity={11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江",31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}
//function isCardID(sId){
// var iSum=0 ;
// var info="" ;
// if(!/^\d{17}(\d|x)$/i.test(sId)) return " ";
// sId=sId.replace(/x$/i,"a");
// if(aCity[parseInt(sId.substr(0,2))]==null) return " ";
// sBirthday=sId.substr(6,4)+"-"+Number(sId.substr(10,2))+"-"+Number(sId.substr(12,2));
// var d=new Date(sBirthday.replace(/-/g,"/")) ;
// if(sBirthday!=(d.getFullYear()+"-"+ (d.getMonth()+1) + "-" + d.getDate()))return " ";
// for(var i = 17;i>=0;i --) iSum += (Math.pow(2,i) % 11) * parseInt(sId.charAt(17 - i),11) ;
// if(iSum%11!=1) return " ";
// return true;//aCity[parseInt(sId.substr(0,2))]+","+sBirthday+","+ (sId.substr(16,1)%2?"男":"女")
//}

//if(cardnumber!=null){
//    cardnumber.onblur=function(){
//        /*var t=this;
//        j.ajax.get("app.axd?task=register,valididnumber,"+escape(this.value),
//        function(x){if(x=="True")valid.displayRight(t);else valid.setError(t,0);});
//        */
//        if(isCardID(this.value)==true){
//            valid.displayRight(this);
//        }else{
//            valid.setError(this,0);
//        }
//    };
//}

var btnRegister = j6.$('btn_register');
btnRegister.disabled='';
btnRegister.onclick = function () {
    var t = this;
    if (j6.validator.validate('reg_panel')) {
        var d = j6.json.toObject('reg_panel');
        if (d.remember != 'on') {
            alert('请同意注册条款')
        } else {
            var tip = j6.$('tip');
            t.disabled='disabled';
            j6.xhr.jsonPost('/user/postRegisterInfo', d, function (json) {
                if (json.result) {
                    var returnUrl = j6.request('return_url');
                    tip.className='tip-panel';
                    tip.innerHTML = '注册成功，请等待页面跳转';

                    setTimeout(function(){
                        if (returnUrl != '') {
                            location.replace(returnUrl);
                        } else {
                            location.replace('/user/login?return_url=');
                        }
                    },3000);

                } else {
                    tip.className='tip-panel';
                    tip.innerHTML = "注册失败："+json.message;
                    t.disabled='';
                }
            }, function () {
                tip.className='tip-panel';
                tip.innerHTML = '注册失败，请重试';
                t.disabled='';
            });
        }
    }
}

function showRegTip(msg) {
    var win = window.parent || window;

    win.xhrCt1 = document.createElement("DIV");
    win.xhrCt1.className = 'xhr1-container hidden';
    win.xhrCt1.style.cssText = 'position:absolute;left:0;top:0;';
    win.xhrCt1.innerHTML = j6.template('

', {
        h: j6.screen.height()
    });
    win.document.body.appendChild(win.xhrCt1);
    win.xhrGate1 = win.document.getElementById('xhr1_gate_layout');
    win.xhrMsg1 = win.document.getElementById('xhr1_msg_layout');
    win.xhrMsg1.style.cssText += "position:fixed;top:200px;border:solid 1px #FC0;background:#FFE;border-radius:5px;color:#F00;font-weight:bold;z-index:200;width:40%;margin:0 auto;left:30%;text-align:center;padding:0.5em 0;line-height:1.5em;";

    win.xhrMsg1.innerHTL = msg;
}
<|endoftext|>


<commit_before>// @flow
import * as React from 'react';
import type { _Column } from "./Types";

const _document = (document: any);

let scrollBarWidth: ?number;

export function getScrollBarWidth(): ?number {
  if (scrollBarWidth !== undefined) return scrollBarWidth;
  const dom = _document.createElement('div');
  const body = _document.body || dom;

  dom.style.visibility = 'hidden';
  dom.style.width = '100px';
  dom.style.position = 'absolute';
  dom.style.top = '-9999px';
  dom.style.overflow = 'scroll';

  body.appendChild(dom);

  const totalWidth = dom.offsetWidth;
  const widthWithoutScroll = dom.clientWidth;

  body.removeChild(dom);

  return totalWidth - widthWithoutScroll;
}

export function getValueByPath(data: Object, path: ?string): any {
  if (typeof path !== 'string') return null;
  return path.split('.').reduce((pre: Object, cur) => (pre || {})[cur], data);
}

export function getRowIdentity(row: Object, rowKey: any): any {
  if (typeof rowKey === 'string') {
    return getValueByPath(row, rowKey);
  } else if (typeof rowKey === 'function') {
    return rowKey(row);
  }
}

export function getLeafColumns(columns: Array<_Column>): Array<_Column> {
  const result = [];
  columns.forEach((column) => {
    if (column.subColumns) {
      result.push(...getLeafColumns(column.subColumns));
    } else {
      result.push(column);
    }
  });
  return result;
}

function convertChildrenToColumns(children: Array<Object> | Object) {
  return React.Children.map(children, (child) => {
    if (child.type.typeName !== 'TableColumn') {
      console.warn(`Table component's children must be TableColumn, but received ${child.type}`);
      return {};
    }

    const column = Object.assign({}, child.props);
    if (column.children) {
      column.subColumns = convertChildrenToColumns(column.children);
      delete column.children;
    }
    return column;
  })
}

export function getColumns(props: Object) {
  return props.children ? convertChildrenToColumns(props.children) : props.columns || [];
}

export function convertToRows(columns: Array<_Column>): Array<Array<_Column>> {
  let maxLevel = 1;

  function traverse(column: _Column, parent: ?_Column) {
    if (parent) {
      column.level = parent.level + 1;
      if (maxLevel < column.level) {
        maxLevel = column.level;
      }
    } else {
      column.level = 1;
    }

    if (column.subColumns) {
      let colSpan = 0;
      column.subColumns.forEach((subColumn) => {
        traverse(subColumn, column);
        colSpan += subColumn.colSpan;
      });
      column.colSpan = colSpan;
    } else {
      column.colSpan = 1;
    }
  }

  columns.forEach((column) => {
    traverse(column);
  });

  const rows = [];
  for (let i = 0; i < maxLevel; i++) {
    rows.push([]);
  }

  const allColumns = [];
  const queue = columns.slice();
  for (let i = 0; queue[i]; i++) {
    allColumns.push(queue[i]);
    if (queue[i].subColumns) queue.push(...queue[i].subColumns);
  }

  allColumns.forEach((column) => {
    if (!column.subColumns) {
      column.rowSpan = maxLevel - column.level + 1;
    } else {
      column.rowSpan = 1;
    }
    rows[column.level - 1].push(column);
  });
  return rows;
}

const isObject = (obj: Object): boolean => {
  return Object.prototype.toString.call(obj) === '[object Object]'
}
const deepCompare = (obj1: any, obj2: any): boolean => {
  if (obj1 && obj2 && obj1.length !== obj2.length) {
    return true
  } else if (Array.isArray(obj1) && Array.isArray(obj2)) {
    const firstOBJ = obj1.length;
    const sectionOBJ = obj2.length;
    if (firstOBJ !== sectionOBJ) {
      return true
    } else if (firstOBJ === 0 && sectionOBJ === 0) {
      return false
    }
    return obj1.every((value, key) => (
      isEqual(value, obj2[key])
    ))
  } else if (isObject(obj1) && isObject(obj2)) {
    const firstOBJ = Object.keys(obj1).length;
    const sectionOBJ = Object.keys(obj2).length;
    if (firstOBJ !== sectionOBJ) {
      return true
    } else if (firstOBJ === 0 && sectionOBJ === 0) {
      return false
    }
    for (let key in obj1) {
      if (isEqual(obj1[key], obj2[key])) {
        return true
      }
    }
    return true
  }
  return obj1 !== obj2
}

export {
  deepCompare,
  isObject
}
<commit_msg>fix：Table utils deepCompare function<commit_after>// @flow
import * as React from 'react';
import type { _Column } from "./Types";

const _document = (document: any);

let scrollBarWidth: ?number;

export function getScrollBarWidth(): ?number {
  if (scrollBarWidth !== undefined) return scrollBarWidth;
  const dom = _document.createElement('div');
  const body = _document.body || dom;

  dom.style.visibility = 'hidden';
  dom.style.width = '100px';
  dom.style.position = 'absolute';
  dom.style.top = '-9999px';
  dom.style.overflow = 'scroll';

  body.appendChild(dom);

  const totalWidth = dom.offsetWidth;
  const widthWithoutScroll = dom.clientWidth;

  body.removeChild(dom);

  return totalWidth - widthWithoutScroll;
}

export function getValueByPath(data: Object, path: ?string): any {
  if (typeof path !== 'string') return null;
  return path.split('.').reduce((pre: Object, cur) => (pre || {})[cur], data);
}

export function getRowIdentity(row: Object, rowKey: any): any {
  if (typeof rowKey === 'string') {
    return getValueByPath(row, rowKey);
  } else if (typeof rowKey === 'function') {
    return rowKey(row);
  }
}

export function getLeafColumns(columns: Array<_Column>): Array<_Column> {
  const result = [];
  columns.forEach((column) => {
    if (column.subColumns) {
      result.push(...getLeafColumns(column.subColumns));
    } else {
      result.push(column);
    }
  });
  return result;
}

function convertChildrenToColumns(children: Array<Object> | Object) {
  return React.Children.map(children, (child) => {
    if (child.type.typeName !== 'TableColumn') {
      console.warn(`Table component's children must be TableColumn, but received ${child.type}`);
      return {};
    }

    const column = Object.assign({}, child.props);
    if (column.children) {
      column.subColumns = convertChildrenToColumns(column.children);
      delete column.children;
    }
    return column;
  })
}

export function getColumns(props: Object) {
  return props.children ? convertChildrenToColumns(props.children) : props.columns || [];
}

export function convertToRows(columns: Array<_Column>): Array<Array<_Column>> {
  let maxLevel = 1;

  function traverse(column: _Column, parent: ?_Column) {
    if (parent) {
      column.level = parent.level + 1;
      if (maxLevel < column.level) {
        maxLevel = column.level;
      }
    } else {
      column.level = 1;
    }

    if (column.subColumns) {
      let colSpan = 0;
      column.subColumns.forEach((subColumn) => {
        traverse(subColumn, column);
        colSpan += subColumn.colSpan;
      });
      column.colSpan = colSpan;
    } else {
      column.colSpan = 1;
    }
  }

  columns.forEach((column) => {
    traverse(column);
  });

  const rows = [];
  for (let i = 0; i < maxLevel; i++) {
    rows.push([]);
  }

  const allColumns = [];
  const queue = columns.slice();
  for (let i = 0; queue[i]; i++) {
    allColumns.push(queue[i]);
    if (queue[i].subColumns) queue.push(...queue[i].subColumns);
  }

  allColumns.forEach((column) => {
    if (!column.subColumns) {
      column.rowSpan = maxLevel - column.level + 1;
    } else {
      column.rowSpan = 1;
    }
    rows[column.level - 1].push(column);
  });
  return rows;
}

const isObject = (obj: Object): boolean => {
  return Object.prototype.toString.call(obj) === '[object Object]'
}
const deepCompare = (obj1: any, obj2: any): boolean => {
  if (obj1 && obj2 && obj1.length !== obj2.length) {
    return true
  } else if (Array.isArray(obj1) && Array.isArray(obj2)) {
    const firstOBJ = obj1.length;
    const sectionOBJ = obj2.length;
    if (firstOBJ !== sectionOBJ) {
      return true
    } else if (firstOBJ === 0 && sectionOBJ === 0) {
      return false
    }
    return obj1.every((value, key) => (
      deepCompare(value, obj2[key])
    ))
  } else if (isObject(obj1) && isObject(obj2)) {
    const firstOBJ = Object.keys(obj1).length;
    const sectionOBJ = Object.keys(obj2).length;
    if (firstOBJ !== sectionOBJ) {
      return true
    } else if (firstOBJ === 0 && sectionOBJ === 0) {
      return false
    }
    for (let key in obj1) {
      if (deepCompare(obj1[key], obj2[key])) {
        return true
      }
    }
    return true
  }
  return obj1 !== obj2
}

export {
  deepCompare,
  isObject
}
<|endoftext|>


<commit_before>// The `glyf` table describes the glyphs in TrueType outline format.
// http://www.microsoft.com/typography/otspec/glyf.htm

'use strict';

var check = require('../check');
var glyphset = require('../glyphset');
var parse = require('../parse');
var path = require('../path');

// Parse the coordinate data for a glyph.
function parseGlyphCoordinate(p, flag, previousValue, shortVectorBitMask, sameBitMask) {
    var v;
    if ((flag & shortVectorBitMask) > 0) {
        // The coordinate is 1 byte long.
        v = p.parseByte();
        // The `same` bit is re-used for short values to signify the sign of the value.
        if ((flag & sameBitMask) === 0) {
            v = -v;
        }

        v = previousValue + v;
    } else {
        //  The coordinate is 2 bytes long.
        // If the `same` bit is set, the coordinate is the same as the previous coordinate.
        if ((flag & sameBitMask) > 0) {
            v = previousValue;
        } else {
            // Parse the coordinate as a signed 16-bit delta value.
            v = previousValue + p.parseShort();
        }
    }

    return v;
}

// Parse a TrueType glyph.
function parseGlyph(glyph, data, start) {
    var p = new parse.Parser(data, start);
    glyph.numberOfContours = p.parseShort();
    glyph._xMin = p.parseShort();
    glyph._yMin = p.parseShort();
    glyph._xMax = p.parseShort();
    glyph._yMax = p.parseShort();
    var flags;
    var flag;
    var i;
    if (glyph.numberOfContours > 0) {
        // This glyph is not a composite.
        var endPointIndices = glyph.endPointIndices = [];
        for (i = 0; i < glyph.numberOfContours; i += 1) {
            endPointIndices.push(p.parseUShort());
        }

        glyph.instructionLength = p.parseUShort();
        glyph.instructions = [];
        for (i = 0; i < glyph.instructionLength; i += 1) {
            glyph.instructions.push(p.parseByte());
        }

        var numberOfCoordinates = endPointIndices[endPointIndices.length - 1] + 1;
        flags = [];
        for (i = 0; i < numberOfCoordinates; i += 1) {
            flag = p.parseByte();
            flags.push(flag);
            // If bit 3 is set, we repeat this flag n times, where n is the next byte.
            if ((flag & 8) > 0) {
                var repeatCount = p.parseByte();
                for (var j = 0; j < repeatCount; j += 1) {
                    flags.push(flag);
                    i += 1;
                }
            }
        }

        check.argument(flags.length === numberOfCoordinates, 'Bad flags.');

        if (endPointIndices.length > 0) {
            var points = [];
            var point;
            // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
            if (numberOfCoordinates > 0) {
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = {};
                    point.onCurve = !!(flag & 1);
                    point.lastPointOfContour = endPointIndices.indexOf(i) >= 0;
                    points.push(point);
                }

                var px = 0;
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = points[i];
                    point.x = parseGlyphCoordinate(p, flag, px, 2, 16);
                    px = point.x;
                }

                var py = 0;
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = points[i];
                    point.y = parseGlyphCoordinate(p, flag, py, 4, 32);
                    py = point.y;
                }
            }

            glyph.points = points;
        } else {
            glyph.points = [];
        }
    } else if (glyph.numberOfContours === 0) {
        glyph.points = [];
    } else {
        glyph.isComposite = true;
        glyph.points = [];
        glyph.components = [];
        var moreComponents = true;
        while (moreComponents) {
            flags = p.parseUShort();
            var component = {
                glyphIndex: p.parseUShort(),
                xScale: 1,
                scale01: 0,
                scale10: 0,
                yScale: 1,
                dx: 0,
                dy: 0
            };
            if ((flags & 1) > 0) {
                // The arguments are words
                if ((flags & 2) > 0) {
                    // values are offset
                    component.dx = p.parseShort();
                    component.dy = p.parseShort();
                } else {
                    // values are matched points
                    component.matchedPoints = [p.parseUShort(), p.parseUShort()];
                }

            } else {
                // The arguments are bytes
                if ((flags & 2) > 0) {
                    // values are offset
                    component.dx = p.parseChar();
                    component.dy = p.parseChar();
                } else {
                    // values are matched points
                    component.matchedPoints = [p.parseByte(), p.parseByte()];
                }
            }

            if ((flags & 8) > 0) {
                // We have a scale
                component.xScale = component.yScale = p.parseF2Dot14();
            } else if ((flags & 64) > 0) {
                // We have an X / Y scale
                component.xScale = p.parseF2Dot14();
                component.yScale = p.parseF2Dot14();
            } else if ((flags & 128) > 0) {
                // We have a 2x2 transformation
                component.xScale = p.parseF2Dot14();
                component.scale01 = p.parseF2Dot14();
                component.scale10 = p.parseF2Dot14();
                component.yScale = p.parseF2Dot14();
            }

            glyph.components.push(component);
            moreComponents = !!(flags & 32);
        }
        if (flags & 0x100) {
            // We have instructions
            glyph.instructionLength = p.parseUShort();
            glyph.instructions = [];
            for (i = 0; i < glyph.instructionLength; i += 1) {
                glyph.instructions.push(p.parseByte());
            }
        }
    }
}

// Transform an array of points and return a new array.
function transformPoints(points, transform) {
    var newPoints = [];
    for (var i = 0; i < points.length; i += 1) {
        var pt = points[i];
        var newPt = {
            x: transform.xScale * pt.x + transform.scale01 * pt.y + transform.dx,
            y: transform.scale10 * pt.x + transform.yScale * pt.y + transform.dy,
            onCurve: pt.onCurve,
            lastPointOfContour: pt.lastPointOfContour
        };
        newPoints.push(newPt);
    }

    return newPoints;
}

function getContours(points) {
    var contours = [];
    var currentContour = [];
    for (var i = 0; i < points.length; i += 1) {
        var pt = points[i];
        currentContour.push(pt);
        if (pt.lastPointOfContour) {
            contours.push(currentContour);
            currentContour = [];
        }
    }

    check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
    return contours;
}

// Convert the TrueType glyph outline to a Path.
function getPath(points) {
    var p = new path.Path();
    if (!points) {
        return p;
    }

    var contours = getContours(points);
    for (var i = 0; i < contours.length; i += 1) {
        var contour = contours[i];
        var firstPt = contour[0];
        var lastPt = contour[contour.length - 1];
        var curvePt;
        var realFirstPoint;
        if (firstPt.onCurve) {
            curvePt = null;
            // The first point will be consumed by the moveTo command,
            // so skip it in the loop.
            realFirstPoint = true;
        } else {
            if (lastPt.onCurve) {
                // If the first point is off-curve and the last point is on-curve,
                // start at the last point.
                firstPt = lastPt;
            } else {
                // If both first and last points are off-curve, start at their middle.
                firstPt = { x: (firstPt.x + lastPt.x) / 2, y: (firstPt.y + lastPt.y) / 2 };
            }

            curvePt = firstPt;
            // The first point is synthesized, so don't skip the real first point.
            realFirstPoint = false;
        }

        p.moveTo(firstPt.x, firstPt.y);

        for (var j = realFirstPoint ? 1 : 0; j < contour.length; j += 1) {
            var pt = contour[j];
            var prevPt = j === 0 ? firstPt : contour[j - 1];
            if (prevPt.onCurve && pt.onCurve) {
                // This is a straight line.
                p.lineTo(pt.x, pt.y);
            } else if (prevPt.onCurve && !pt.onCurve) {
                curvePt = pt;
            } else if (!prevPt.onCurve && !pt.onCurve) {
                var midPt = { x: (prevPt.x + pt.x) / 2, y: (prevPt.y + pt.y) / 2 };
                p.quadraticCurveTo(prevPt.x, prevPt.y, midPt.x, midPt.y);
                curvePt = pt;
            } else if (!prevPt.onCurve && pt.onCurve) {
                // Previous point off-curve, this point on-curve.
                p.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
                curvePt = null;
            } else {
                throw new Error('Invalid state.');
            }
        }

        if (firstPt !== lastPt) {
            // Connect the last and first points
            if (curvePt) {
                p.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y);
            } else {
                p.lineTo(firstPt.x, firstPt.y);
            }
        }
    }

    p.closePath();
    return p;
}

function buildPath(glyphs, glyph) {
    if (glyph.isComposite) {
        for (var j = 0; j < glyph.components.length; j += 1) {
            var component = glyph.components[j];
            var componentGlyph = glyphs.get(component.glyphIndex);
            // Force the ttfGlyphLoader to parse the glyph.
            componentGlyph.getPath();
            if (componentGlyph.points) {
                var transformedPoints;
                if (component.matchedPoints === undefined) {
                    // component positioned by offset
                    transformedPoints = transformPoints(componentGlyph.points, component);
                } else {
                    // component positioned by matched points
                    if ((component.matchedPoints[0] > glyph.points.length - 1) ||
                        (component.matchedPoints[1] > componentGlyph.points.length - 1)) {
                        throw Error('Matched points out of range in ' + glyph.name);
                    }
                    var firstPt = glyph.points[component.matchedPoints[0]];
                    var secondPt = componentGlyph.points[component.matchedPoints[1]];
                    var transform = {
                        xScale: component.xScale, scale01: component.scale01,
                        scale10: component.scale10, yScale: component.yScale,
                        dx: 0, dy: 0
                    };
                    secondPt = transformPoints([secondPt], transform)[0];
                    transform.dx = firstPt.x - secondPt.x;
                    transform.dy = firstPt.y - secondPt.y;
                    transformedPoints = transformPoints(componentGlyph.points, transform);
                }
                glyph.points = glyph.points.concat(transformedPoints);
            }
        }
    }

    return getPath(glyph.points);
}

// Parse all the glyphs according to the offsets from the `loca` table.
function parseGlyfTable(data, start, loca, font) {
    var glyphs = new glyphset.GlyphSet(font);
    var i;

    // The last element of the loca table is invalid.
    for (i = 0; i < loca.length - 1; i += 1) {
        var offset = loca[i];
        var nextOffset = loca[i + 1];
        if (offset !== nextOffset) {
            glyphs.push(i, glyphset.ttfGlyphLoader(font, i, parseGlyph, data, start + offset, buildPath));
        } else {
            glyphs.push(i, glyphset.glyphLoader(font, i));
        }
    }

    return glyphs;
}

exports.getPath = getPath;

exports.parse = parseGlyfTable;
<commit_msg>Fix deformed paths for glyf table #255 (#270)<commit_after>// The `glyf` table describes the glyphs in TrueType outline format.
// http://www.microsoft.com/typography/otspec/glyf.htm

'use strict';

var check = require('../check');
var glyphset = require('../glyphset');
var parse = require('../parse');
var path = require('../path');

// Parse the coordinate data for a glyph.
function parseGlyphCoordinate(p, flag, previousValue, shortVectorBitMask, sameBitMask) {
    var v;
    if ((flag & shortVectorBitMask) > 0) {
        // The coordinate is 1 byte long.
        v = p.parseByte();
        // The `same` bit is re-used for short values to signify the sign of the value.
        if ((flag & sameBitMask) === 0) {
            v = -v;
        }

        v = previousValue + v;
    } else {
        //  The coordinate is 2 bytes long.
        // If the `same` bit is set, the coordinate is the same as the previous coordinate.
        if ((flag & sameBitMask) > 0) {
            v = previousValue;
        } else {
            // Parse the coordinate as a signed 16-bit delta value.
            v = previousValue + p.parseShort();
        }
    }

    return v;
}

// Parse a TrueType glyph.
function parseGlyph(glyph, data, start) {
    var p = new parse.Parser(data, start);
    glyph.numberOfContours = p.parseShort();
    glyph._xMin = p.parseShort();
    glyph._yMin = p.parseShort();
    glyph._xMax = p.parseShort();
    glyph._yMax = p.parseShort();
    var flags;
    var flag;
    var i;

    if (glyph.numberOfContours > 0) {
        // This glyph is not a composite.
        var endPointIndices = glyph.endPointIndices = [];
        for (i = 0; i < glyph.numberOfContours; i += 1) {
            endPointIndices.push(p.parseUShort());
        }

        glyph.instructionLength = p.parseUShort();
        glyph.instructions = [];
        for (i = 0; i < glyph.instructionLength; i += 1) {
            glyph.instructions.push(p.parseByte());
        }

        var numberOfCoordinates = endPointIndices[endPointIndices.length - 1] + 1;
        flags = [];
        for (i = 0; i < numberOfCoordinates; i += 1) {
            flag = p.parseByte();
            flags.push(flag);
            // If bit 3 is set, we repeat this flag n times, where n is the next byte.
            if ((flag & 8) > 0) {
                var repeatCount = p.parseByte();
                for (var j = 0; j < repeatCount; j += 1) {
                    flags.push(flag);
                    i += 1;
                }
            }
        }

        check.argument(flags.length === numberOfCoordinates, 'Bad flags.');

        if (endPointIndices.length > 0) {
            var points = [];
            var point;
            // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
            if (numberOfCoordinates > 0) {
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = {};
                    point.onCurve = !!(flag & 1);
                    point.lastPointOfContour = endPointIndices.indexOf(i) >= 0;
                    points.push(point);
                }

                var px = 0;
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = points[i];
                    point.x = parseGlyphCoordinate(p, flag, px, 2, 16);
                    px = point.x;
                }

                var py = 0;
                for (i = 0; i < numberOfCoordinates; i += 1) {
                    flag = flags[i];
                    point = points[i];
                    point.y = parseGlyphCoordinate(p, flag, py, 4, 32);
                    py = point.y;
                }
            }

            glyph.points = points;
        } else {
            glyph.points = [];
        }
    } else if (glyph.numberOfContours === 0) {
        glyph.points = [];
    } else {
        glyph.isComposite = true;
        glyph.points = [];
        glyph.components = [];
        var moreComponents = true;
        while (moreComponents) {
            flags = p.parseUShort();
            var component = {
                glyphIndex: p.parseUShort(),
                xScale: 1,
                scale01: 0,
                scale10: 0,
                yScale: 1,
                dx: 0,
                dy: 0
            };
            if ((flags & 1) > 0) {
                // The arguments are words
                if ((flags & 2) > 0) {
                    // values are offset
                    component.dx = p.parseShort();
                    component.dy = p.parseShort();
                } else {
                    // values are matched points
                    component.matchedPoints = [p.parseUShort(), p.parseUShort()];
                }

            } else {
                // The arguments are bytes
                if ((flags & 2) > 0) {
                    // values are offset
                    component.dx = p.parseChar();
                    component.dy = p.parseChar();
                } else {
                    // values are matched points
                    component.matchedPoints = [p.parseByte(), p.parseByte()];
                }
            }

            if ((flags & 8) > 0) {
                // We have a scale
                component.xScale = component.yScale = p.parseF2Dot14();
            } else if ((flags & 64) > 0) {
                // We have an X / Y scale
                component.xScale = p.parseF2Dot14();
                component.yScale = p.parseF2Dot14();
            } else if ((flags & 128) > 0) {
                // We have a 2x2 transformation
                component.xScale = p.parseF2Dot14();
                component.scale01 = p.parseF2Dot14();
                component.scale10 = p.parseF2Dot14();
                component.yScale = p.parseF2Dot14();
            }

            glyph.components.push(component);
            moreComponents = !!(flags & 32);
        }
        if (flags & 0x100) {
            // We have instructions
            glyph.instructionLength = p.parseUShort();
            glyph.instructions = [];
            for (i = 0; i < glyph.instructionLength; i += 1) {
                glyph.instructions.push(p.parseByte());
            }
        }
    }
}

// Transform an array of points and return a new array.
function transformPoints(points, transform) {
    var newPoints = [];
    for (var i = 0; i < points.length; i += 1) {
        var pt = points[i];
        var newPt = {
            x: transform.xScale * pt.x + transform.scale01 * pt.y + transform.dx,
            y: transform.scale10 * pt.x + transform.yScale * pt.y + transform.dy,
            onCurve: pt.onCurve,
            lastPointOfContour: pt.lastPointOfContour
        };
        newPoints.push(newPt);
    }

    return newPoints;
}

function getContours(points) {
    var contours = [];
    var currentContour = [];
    for (var i = 0; i < points.length; i += 1) {
        var pt = points[i];
        currentContour.push(pt);
        if (pt.lastPointOfContour) {
            contours.push(currentContour);
            currentContour = [];
        }
    }

    check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
    return contours;
}

// Convert the TrueType glyph outline to a Path.
function getPath(points) {
    var p = new path.Path();
    if (!points) {
        return p;
    }

    var contours = getContours(points);

    for (var contourIndex = 0; contourIndex < contours.length; ++contourIndex) {
        var contour = contours[contourIndex];

        var prev = null;
        var curr = contour[contour.length - 1];
        var next = contour[0];

        if (curr.onCurve) {
            p.moveTo(curr.x, curr.y);
        } else {
            if (next.onCurve) {
                p.moveTo(next.x, next.y);
            } else {
                // If both first and last points are off-curve, start at their middle.
                var start = { x: (curr.x + next.x) * 0.5, y: (curr.y + next.y) * 0.5 };
                p.moveTo(start.x, start.y);
            }
        }

        for (var i = 0; i < contour.length; ++i) {
            prev = curr;
            curr = next;
            next = contour[(i + 1) % contour.length];

            if (curr.onCurve) {
                // This is a straight line.
                p.lineTo(curr.x, curr.y);
            } else {
                var prev2 = prev;
                var next2 = next;

                if (!prev.onCurve) {
                    prev2 = { x: (curr.x + prev.x) * 0.5, y: (curr.y + prev.y) * 0.5 };
                    p.lineTo(prev2.x, prev2.y);
                }

                if (!next.onCurve) {
                    next2 = { x: (curr.x + next.x) * 0.5, y: (curr.y + next.y) * 0.5 };
                }

                p.lineTo(prev2.x, prev2.y);
                p.quadraticCurveTo(curr.x, curr.y, next2.x, next2.y);
            }
        }
    }

    p.closePath();
    return p;
}

function buildPath(glyphs, glyph) {
    if (glyph.isComposite) {
        for (var j = 0; j < glyph.components.length; j += 1) {
            var component = glyph.components[j];
            var componentGlyph = glyphs.get(component.glyphIndex);
            // Force the ttfGlyphLoader to parse the glyph.
            componentGlyph.getPath();
            if (componentGlyph.points) {
                var transformedPoints;
                if (component.matchedPoints === undefined) {
                    // component positioned by offset
                    transformedPoints = transformPoints(componentGlyph.points, component);
                } else {
                    // component positioned by matched points
                    if ((component.matchedPoints[0] > glyph.points.length - 1) ||
                        (component.matchedPoints[1] > componentGlyph.points.length - 1)) {
                        throw Error('Matched points out of range in ' + glyph.name);
                    }
                    var firstPt = glyph.points[component.matchedPoints[0]];
                    var secondPt = componentGlyph.points[component.matchedPoints[1]];
                    var transform = {
                        xScale: component.xScale, scale01: component.scale01,
                        scale10: component.scale10, yScale: component.yScale,
                        dx: 0, dy: 0
                    };
                    secondPt = transformPoints([secondPt], transform)[0];
                    transform.dx = firstPt.x - secondPt.x;
                    transform.dy = firstPt.y - secondPt.y;
                    transformedPoints = transformPoints(componentGlyph.points, transform);
                }
                glyph.points = glyph.points.concat(transformedPoints);
            }
        }
    }

    return getPath(glyph.points);
}

// Parse all the glyphs according to the offsets from the `loca` table.
function parseGlyfTable(data, start, loca, font) {
    var glyphs = new glyphset.GlyphSet(font);
    var i;

    // The last element of the loca table is invalid.
    for (i = 0; i < loca.length - 1; i += 1) {
        var offset = loca[i];
        var nextOffset = loca[i + 1];
        if (offset !== nextOffset) {
            glyphs.push(i, glyphset.ttfGlyphLoader(font, i, parseGlyph, data, start + offset, buildPath));
        } else {
            glyphs.push(i, glyphset.glyphLoader(font, i));
        }
    }

    return glyphs;
}

exports.getPath = getPath;

exports.parse = parseGlyfTable;
<|endoftext|>