<commit_before>export default {
	status: {
		isScrolling: false,  // app scroll
		currentPage: "home", // currentPage
		isHeaderVisible: false,  // if currentPage == "content"

		statusMessage: null,  // reveal status to user

		providers: ["4chan", "reddit"],
		provider: "4chan",
		boardID: "g",
		threadID: null,
	},

	boardList: {  // obj for each provider: {4chan: [], reddit: []}
		didInvalidate: false,
		favourites: []  // [{id:'4chan', board: 'g'}, ...]
	},

	board: {
		isFetching: false,
		didInvalidate: false,
		filterWord: null,
		history: {},
		posts: [],
		watch: [],
		limit: 30  // infinite scroll
	},

	thread: {
		isActive: false,
		isFetching: false,
		didInvalidate: false,
		history: {},
		posts: [],

	},

	post: {
		isAuthenticated: false,
		type: null,  // thread/comment
		references: [],
		content: null,  // user input
		upload: null
		// canReply
	},

	settings: {
		// TODO: all settings here in a flat structure
		userStoragePath: "./",
		customStyleSheet: null,
		filterKeywords: []  // filter board
		// boardPostMax: 30 // TODO: Add boardpost limit to state
	}
}
<commit_msg>Add receivedAt, request throttle to initial state<commit_after>export default {
	status: {
		isScrolling: false,  // app scroll
		currentPage: "home", // currentPage
		isHeaderVisible: false,  // if currentPage == "content"
		statusMessage: null,  // reveal status to user

		providers: ["4chan", "reddit"],
		provider: "4chan",
		boardID: "g",
		threadID: null,
	},

	boardList: {  // obj for each provider: {4chan: [], reddit: []}
		didInvalidate: false,
		favourites: []  // [{id:'4chan', board: 'g'}, ...]
	},

	board: {
		requestWhenOlderThan: 1200,  // in seconds
		receivedAt: 0,  // unix timestamp
		isFetching: false,
		didInvalidate: false,
		filterWord: null,
		history: {},
		posts: [],
		watch: [],
		limit: 30  // infinite scroll
	},

	thread: {
		requestWhenOlderThan: 15,  // in seconds
		receivedAt: 0,  // unix timestamp
		isActive: false,
		isFetching: false,
		didInvalidate: false,
		history: {},
		posts: [],

	},

	post: {
		isAuthenticated: false,
		type: null,  // thread/comment
		references: [],
		content: null,  // user input
		upload: null
		// canReply
	},

	settings: {
		// TODO: all settings here in a flat structure
		userStoragePath: "./",
		customStyleSheet: null,
		filterKeywords: []  // filter board
		// boardPostMax: 30 // TODO: Add boardpost limit to state
	}
}
<|endoftext|>


<commit_before><commit_msg>Separate horizontal and vertical thresholds<commit_after><|endoftext|>


<commit_before>
/* This file contains code to generate the configuration for different types of
 * levels. */

const RG = require('../src/rg');
RG.Names = require('./name-gen');
const WorldConf = require('../src/world.creator');

const LevelGen = {};

const getNumLevels = function(name) {
    switch (name) {
        case 'Cave': return 1;
        case 'Crypt': return 2;
        case 'Dungeon': return 3;
        case 'Labyrinth': return 1;
        default: return 3;
    }
};

/* Returns generation constraints based on the level name. */
const getConstraint = function(name) {
    switch (name) {
        case 'Cave': return {
            actor: actor => actor.type === 'animal' || actor.type === 'goblin'
        };
        case 'Crypt': return {
            actor: actor => actor.type === 'undead'
        };
        default: return null;
    }
};

const convertToImplemented = function(name) {
    switch (name) {
        case 'Grotto': return 'Cave';
        case 'Cavern': return 'Cave';
        case 'Catacombs': return 'Crypt';
        case 'Tombs': return 'Crypt';
        case 'Cells': return 'Dungeon';
        default: return name;
    }
};

//---------------------------------------------------------------------------
// DUNGEON GENERATION
//---------------------------------------------------------------------------

LevelGen.getDungeonConf = dungeonName => {
    const usedName = convertToImplemented(dungeonName);
    // const brName = RG.Names.getGenericPlaceName('branch');
    const nLevels = getNumLevels(usedName);
    const constraint = getConstraint(usedName);
    const obj = {
        name: dungeonName,
        dungeonType: usedName.toLowerCase(),
        nBranches: 1,
        branch: [
            {name: dungeonName, nLevels, entranceLevel: 0}
        ]
    };

    if (constraint) {
        obj.constraint = constraint;
    }

    return obj;
};

//---------------------------------------------------------------------------
// CITY GENERATION
//---------------------------------------------------------------------------

const getNumQuarters = (cityType) => {
    switch (cityType) {
        case 'Town': return 1;
        case 'Village': return 1;
        case 'Fort': return 2;
        case 'Stronhold': return RG.RAND.getUniformInt(2, 4);
        case 'Capital': return RG.RAND.getUniformInt(3, 5);
        default: return 1;
    }

};

const getRandomShopType = () => RG.RAND.arrayGetRand(RG.SHOP_TYPES);

/* Adds shop generation constraints for the quarter. */
const addShopConstraints = (qConf, conf) => {
    const maxValue = conf.maxValue || 100;
    const shopTypeConf = conf.shopType || 'random';
    const qName = conf.name;

    if (qName === 'Market' || qName === 'Bazaar') {
        const nShops = RG.RAND.getUniformInt(1, 3);
        qConf.nShops = nShops;
        qConf.shop = [];
        for (let i = 0; i < nShops; i++) {
            let shopType = getRandomShopType();

            // Optionally allow first shopType to be given
            if (shopTypeConf !== 'random' && i === 0) {
                shopType = shopTypeConf;
            }
            const shopFunc = (item) => (
                item.type === shopType && item.value <= maxValue
            );
            qConf.push(shopFunc);
        }
    }
};

/* Returns the configuration for city quarters. */
const getQuarterConf = (nQuarters, conf) => {
    const quarters = [];
    for (let i = 0; i < nQuarters; i++) {
        const qName = RG.Names.getGenericPlaceName('quarter');
        const qConf = {
            name: qName,
            nLevels: 1
        };
        if (i === 0) {
            qConf.entranceLevel = 0;
        }

        addShopConstraints(qConf, conf);
        // TODO add any other special features based on the type
        quarters.push(qConf);
    }
    return quarters;
};

LevelGen.getCityConf = (cityName, conf) => {
    let cityType = RG.Names.getGenericPlaceName('city');
    if (conf.type === 'fort') {
        cityType = 'Fort';
    }
    else if (conf.capital) {
        cityType = 'Capital';
    }
    else if (conf.type === 'stronghold') {
        cityType = 'Stronghold';
    }
    const nQuarters = getNumQuarters(cityType, conf);
    const quarters = getQuarterConf(nQuarters, conf);
    const connect = WorldConf.createQuarterConnections(quarters);
    const obj = {
        name: cityName,
        nQuarters,
        quarter: quarters
    };

    if (connect) {obj.connect = connect;}
    return obj;
};

module.exports = LevelGen;
<commit_msg>For village type, retrieves a random name.<commit_after>
/* This file contains code to generate the configuration for different types of
 * levels. */

const RG = require('../src/rg');
RG.Names = require('./name-gen');
const WorldConf = require('../src/world.creator');

const LevelGen = {};

const getNumLevels = function(name) {
    switch (name) {
        case 'Cave': return 1;
        case 'Crypt': return 2;
        case 'Dungeon': return 3;
        case 'Labyrinth': return 1;
        default: return 3;
    }
};

/* Returns generation constraints based on the level name. */
const getConstraint = function(name) {
    switch (name) {
        case 'Cave': return {
            actor: actor => actor.type === 'animal' || actor.type === 'goblin'
        };
        case 'Crypt': return {
            actor: actor => actor.type === 'undead'
        };
        default: return null;
    }
};

const convertToImplemented = function(name) {
    switch (name) {
        case 'Grotto': return 'Cave';
        case 'Cavern': return 'Cave';
        case 'Catacombs': return 'Crypt';
        case 'Tombs': return 'Crypt';
        case 'Cells': return 'Dungeon';
        default: return name;
    }
};

//---------------------------------------------------------------------------
// DUNGEON GENERATION
//---------------------------------------------------------------------------

LevelGen.getDungeonConf = dungeonName => {
    const usedName = convertToImplemented(dungeonName);
    // const brName = RG.Names.getGenericPlaceName('branch');
    const nLevels = getNumLevels(usedName);
    const constraint = getConstraint(usedName);
    const obj = {
        name: dungeonName,
        dungeonType: usedName.toLowerCase(),
        nBranches: 1,
        branch: [
            {name: dungeonName, nLevels, entranceLevel: 0}
        ]
    };

    if (constraint) {
        obj.constraint = constraint;
    }

    return obj;
};

//---------------------------------------------------------------------------
// CITY GENERATION
//---------------------------------------------------------------------------

const getNumQuarters = (cityType) => {
    switch (cityType) {
        case 'Town': return 1;
        case 'Village': return 1;
        case 'Fort': return 2;
        case 'Stronhold': return RG.RAND.getUniformInt(2, 4);
        case 'Capital': return RG.RAND.getUniformInt(3, 5);
        default: return 1;
    }

};

const getRandomShopType = () => RG.RAND.arrayGetRand(RG.SHOP_TYPES);

/* Adds shop generation constraints for the quarter. */
const addShopConstraints = (qConf, conf) => {
    const maxValue = conf.maxValue || 100;
    const shopTypeConf = conf.shopType || 'random';
    const qName = conf.name;

    if (qName === 'Market' || qName === 'Bazaar') {
        const nShops = RG.RAND.getUniformInt(1, 3);
        qConf.nShops = nShops;
        qConf.shop = [];
        for (let i = 0; i < nShops; i++) {
            let shopType = getRandomShopType();

            // Optionally allow first shopType to be given
            if (shopTypeConf !== 'random' && i === 0) {
                shopType = shopTypeConf;
            }
            const shopFunc = (item) => (
                item.type === shopType && item.value <= maxValue
            );
            qConf.push(shopFunc);
        }
    }
};

/* Returns the configuration for city quarters. */
const getQuarterConf = (nQuarters, conf) => {
    const quarters = [];
    for (let i = 0; i < nQuarters; i++) {
        const qName = RG.Names.getGenericPlaceName('quarter');
        const qConf = {
            name: qName,
            nLevels: 1
        };
        if (i === 0) {
            qConf.entranceLevel = 0;
        }

        addShopConstraints(qConf, conf);
        // TODO add any other special features based on the type
        quarters.push(qConf);
    }
    return quarters;
};

LevelGen.getCityConf = (cityName, conf) => {
    let cityType = RG.Names.getGenericPlaceName('city');
    if (conf.type === 'fort') {
        cityType = 'Fort';
    }
    else if (conf.capital) {
        cityType = 'Capital';
    }
    else if (conf.type === 'stronghold') {
        cityType = 'Stronghold';
    }
    else if (conf.type === 'village') {
        cityType = RG.Names.getVillageType();
    }
    const nQuarters = getNumQuarters(cityType, conf);
    const quarters = getQuarterConf(nQuarters, conf);
    const connect = WorldConf.createQuarterConnections(quarters);
    const obj = {
        name: cityName,
        nQuarters,
        quarter: quarters
    };

    if (connect) {obj.connect = connect;}
    return obj;
};

module.exports = LevelGen;
<|endoftext|>


<commit_before>import Random from 'random-seed'
import {ZERO_POINT, INFINITY_POINT, traverse, traversalDistance, min as minPoint, isZero as isZeroPoint, compare as comparePoints} from './point-helpers'
import {getExtent} from './text-helpers'
import Iterator from './iterator'

export default class Patch {
  constructor (params = {}) {
    this.combineChanges = (params.combineChanges != null) ? Boolean(params.combineChanges) : true
    this.batchMode = (params.batchMode != null) ? Boolean(params.batchMode) : false
    if (params.seed) {
      let randomGenerator = new Random(params.seed)
      this.generateRandom = randomGenerator.random.bind(randomGenerator)
    }

    this.root = null
    this.nodesCount = 0
    this.iterator = this.buildIterator()
  }

  buildIterator () {
    return new Iterator(this)
  }

  rebalance () {
    this.createBackbone()
    this.createPerfectTree()
  }

  createBackbone () {
    while (this.root != null) {
      if (this.root.left != null) {
        this.rotateNodeRight(pseudoRoot.left)
      } else {
        this.root = this.root.right
      }
    }
  }

  createPerfectTree () {
    for (var i = 0; i < Math.log(this.nodesCount, 2); i++) {
      this.rotateNodeLeft(this.root.right)
      for (var j = 0; j < this.nodesCount / 2 - 1; j++) {
        this.rotateNodeLeft(this.root.right)
      }
    }
  }

  spliceWithText (start, oldExtent, newText, options) {
    let metadata = options ? options.metadata : null
    this.splice(start, oldExtent, getExtent(newText), {text: newText, metadata})
  }

  splice (outputStart, oldExtent, newExtent, options) {
    if (isZeroPoint(oldExtent) && isZeroPoint(newExtent)) return

    let outputOldEnd = traverse(outputStart, oldExtent)
    let outputNewEnd = traverse(outputStart, newExtent)

    let startNode = this.iterator.insertSpliceBoundary(outputStart)
    startNode.isChangeStart = true
    if (this.batchMode) this.splayNode(startNode)

    let endNode = this.iterator.insertSpliceBoundary(outputOldEnd, startNode)
    endNode.isChangeEnd = true
    if (this.batchMode) this.splayNode(endNode)
    if (options && options.metadata) endNode.metadata = options.metadata

    if (this.batchMode) {
      if (startNode !== endNode && endNode.left !== startNode) this.rotateNodeRight(startNode)
    } else {
      startNode.priority = -1
      this.bubbleNodeUp(startNode)
      endNode.priority = -2
      this.bubbleNodeUp(endNode)
    }

    startNode.right = null
    startNode.inputExtent = startNode.inputLeftExtent
    startNode.outputExtent = startNode.outputLeftExtent

    endNode.outputExtent = traverse(outputNewEnd, traversalDistance(endNode.outputExtent, endNode.outputLeftExtent))
    endNode.outputLeftExtent = outputNewEnd
    endNode.newText = options && options.text

    if (endNode.isChangeStart && this.combineChanges) {
      endNode.priority = Infinity
      let rightAncestor = this.bubbleNodeDown(endNode)
      if (endNode.newText != null) {
        rightAncestor.newText = endNode.newText + rightAncestor.newText
      }
      this.deleteNode(endNode)
    } else if (comparePoints(endNode.outputLeftExtent, startNode.outputLeftExtent) === 0
        && comparePoints(endNode.inputLeftExtent, startNode.inputLeftExtent) === 0) {
      startNode.isChangeStart = endNode.isChangeStart
      this.deleteNode(endNode)
    } else if (!this.batchMode) {
      endNode.priority = this.generateRandom()
      this.bubbleNodeDown(endNode)
    }

    if (startNode.isChangeStart && startNode.isChangeEnd && this.combineChanges) {
      startNode.priority = Infinity
      let rightAncestor = this.bubbleNodeDown(startNode) || endNode
      if (startNode.newText != null) {
        rightAncestor.newText = startNode.newText + rightAncestor.newText
      }
      this.deleteNode(startNode)
    } else if (!this.batchMode) {
      startNode.priority = this.generateRandom()
      this.bubbleNodeDown(startNode)
    }
  }

  spliceInput (inputStart, oldExtent, newExtent) {
    let oldExtentIsZero = isZeroPoint(oldExtent)

    if (oldExtentIsZero && isZeroPoint(newExtent)) {
      let outputStart = this.translateInputPosition(inputStart)
      return {start: outputStart, oldExtent, newExtent}
    }

    let inputOldEnd = traverse(inputStart, oldExtent)
    let inputNewEnd = traverse(inputStart, newExtent)

    let startNode = this.iterator.insertSpliceInputBoundary(inputStart, true, oldExtentIsZero)
    if (this.batchMode) this.splayNode(startNode)
    let endNode = this.iterator.insertSpliceInputBoundary(inputOldEnd, false, oldExtentIsZero)
    if (this.batchMode) this.splayNode(endNode)

    if (this.batchMode) {
      if (startNode !== endNode && endNode.left !== startNode) this.rotateNodeRight(startNode)
    } else {
      startNode.priority = -1
      this.bubbleNodeUp(startNode)
      endNode.priority = -2
      this.bubbleNodeUp(endNode)
    }

    startNode.right = null
    startNode.inputExtent = startNode.inputLeftExtent
    startNode.outputExtent = startNode.outputLeftExtent
    startNode.isChangeStart = false

    let outputStart = startNode.outputLeftExtent
    let outputOldExtent = traversalDistance(endNode.outputLeftExtent, startNode.outputLeftExtent)

    let endNodeInputRightExtent = traversalDistance(endNode.inputExtent, endNode.inputLeftExtent)
    let endNodeOutputRightExtent = traversalDistance(endNode.outputExtent, endNode.outputLeftExtent)
    endNode.inputLeftExtent = inputNewEnd
    endNode.inputExtent = traverse(endNode.inputLeftExtent, endNodeInputRightExtent)
    endNode.outputLeftExtent = traverse(startNode.outputLeftExtent, newExtent)
    endNode.outputExtent = traverse(endNode.outputLeftExtent, endNodeOutputRightExtent)
    endNode.isChangeEnd = false
    endNode.newText = null

    let outputNewExtent = traversalDistance(endNode.outputLeftExtent, startNode.outputLeftExtent)

    if (startNode.isChangeEnd) {
      if (!this.batchMode) {
        startNode.priority = this.generateRandom()
        this.bubbleNodeDown(startNode)
      }
    } else {
      this.deleteNode(startNode)
    }

    if (endNode.isChangeStart) {
      if (!this.batchMode) {
        endNode.priority = this.generateRandom()
        this.bubbleNodeDown(endNode)
      }
    } else {
      this.deleteNode(endNode)
    }

    return {
      start: outputStart,
      oldExtent: outputOldExtent,
      newExtent: outputNewExtent
    }
  }

  isChangedAtInputPosition (inputPosition) {
    this.iterator.seekToInputPosition(inputPosition)
    return this.iterator.inChange()
  }

  isChangedAtOutputPosition (outputPosition) {
    this.iterator.seekToOutputPosition(outputPosition)
    return this.iterator.inChange()
  }

  translateInputPosition (inputPosition) {
    this.iterator.seekToInputPosition(inputPosition)
    let overshoot = traversalDistance(inputPosition, this.iterator.getInputStart())
    let outputPosition = minPoint(traverse(this.iterator.getOutputStart(), overshoot), this.iterator.getOutputEnd())
    this.splayNode(this.iterator.getCurrentNode())
    return outputPosition
  }

  translateOutputPosition (outputPosition) {
    this.iterator.seekToOutputPosition(outputPosition)
    let overshoot = traversalDistance(outputPosition, this.iterator.getOutputStart())
    let inputPosition = minPoint(traverse(this.iterator.getInputStart(), overshoot), this.iterator.getInputEnd())
    this.splayNode(this.iterator.getCurrentNode())
    return inputPosition
  }

  getChanges () {
    return this.iterator.getChanges()
  }

  deleteNode (node) {
    node.priority = Infinity
    this.bubbleNodeDown(node)
    if (node.parent) {
      if (node.parent.left === node) {
        node.parent.left = null
      } else {
        node.parent.right = null
        node.parent.inputExtent = node.parent.inputLeftExtent
        node.parent.outputExtent = node.parent.outputLeftExtent
        let ancestor = node.parent
        while (ancestor.parent && ancestor.parent.right === ancestor) {
          ancestor.parent.inputExtent = traverse(ancestor.parent.inputLeftExtent, ancestor.inputExtent)
          ancestor.parent.outputExtent = traverse(ancestor.parent.outputLeftExtent, ancestor.outputExtent)
          ancestor = ancestor.parent
        }
      }

      if (this.batchMode) this.splayNode(node.parent)
    } else {
      this.root = null
    }

    this.nodesCount--
  }

  bubbleNodeUp (node) {
    while (node.parent && node.priority < node.parent.priority) {
      if (node === node.parent.left) {
        this.rotateNodeRight(node)
      } else {
        this.rotateNodeLeft(node)
      }
    }
  }

  bubbleNodeDown (node) {
    let rightAncestor

    while (true) {
      let leftChildPriority = node.left ? node.left.priority : Infinity
      let rightChildPriority = node.right ? node.right.priority : Infinity

      if (leftChildPriority < rightChildPriority && leftChildPriority < node.priority) {
        this.rotateNodeRight(node.left)
      } else if (rightChildPriority < node.priority) {
        rightAncestor = node.right
        this.rotateNodeLeft(node.right)
      } else {
        break
      }
    }

    return rightAncestor
  }

  splayNode (node) {
    if (node == null) return

    while (true) {
      if (this.isNodeLeftChild(node.parent) && this.isNodeRightChild(node)) { // zig-zag
        this.rotateNodeLeft(node)
        this.rotateNodeRight(node)
      } else if (this.isNodeRightChild(node.parent) && this.isNodeLeftChild(node)) { // zig-zag
        this.rotateNodeRight(node)
        this.rotateNodeLeft(node)
      } else if (this.isNodeLeftChild(node.parent) && this.isNodeLeftChild(node)) { // zig-zig
        this.rotateNodeRight(node.parent)
        this.rotateNodeRight(node)
      } else if (this.isNodeRightChild(node.parent) && this.isNodeRightChild(node)) { // zig-zig
        this.rotateNodeLeft(node.parent)
        this.rotateNodeLeft(node)
      } else { // zig
        if (this.isNodeLeftChild(node)) {
          this.rotateNodeRight(node)
        } else if (this.isNodeRightChild(node)) {
          this.rotateNodeLeft(node)
        }

        return
      }
    }
  }

  isNodeLeftChild (node) {
    return node != null && node.parent != null && node.parent.left === node
  }

  isNodeRightChild (node) {
    return node != null && node.parent != null && node.parent.right === node
  }

  rotateNodeLeft (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root === root.parent.left) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.right = pivot.left
    if (root.right) {
      root.right.parent = root
    }

    pivot.left = root
    pivot.left.parent = pivot

    pivot.inputLeftExtent = traverse(root.inputLeftExtent, pivot.inputLeftExtent)
    pivot.inputExtent = traverse(pivot.inputLeftExtent, (pivot.right ? pivot.right.inputExtent : ZERO_POINT))
    root.inputExtent = traverse(root.inputLeftExtent, (root.right ? root.right.inputExtent : ZERO_POINT))

    pivot.outputLeftExtent = traverse(root.outputLeftExtent, pivot.outputLeftExtent)
    pivot.outputExtent = traverse(pivot.outputLeftExtent, (pivot.right ? pivot.right.outputExtent : ZERO_POINT))
    root.outputExtent = traverse(root.outputLeftExtent, (root.right ? root.right.outputExtent : ZERO_POINT))
  }

  rotateNodeRight (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root === root.parent.left) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.left = pivot.right
    if (root.left) {
      root.left.parent = root
    }

    pivot.right = root
    pivot.right.parent = pivot

    root.inputLeftExtent = traversalDistance(root.inputLeftExtent, pivot.inputLeftExtent)
    root.inputExtent = traversalDistance(root.inputExtent, pivot.inputLeftExtent)
    pivot.inputExtent = traverse(pivot.inputLeftExtent, root.inputExtent)

    root.outputLeftExtent = traversalDistance(root.outputLeftExtent, pivot.outputLeftExtent)
    root.outputExtent = traversalDistance(root.outputExtent, pivot.outputLeftExtent)
    pivot.outputExtent = traverse(pivot.outputLeftExtent, root.outputExtent)
  }

  generateRandom () {
    return Math.random()
  }
}
<commit_msg>Implement createBalancedTree()<commit_after>import Random from 'random-seed'
import {ZERO_POINT, INFINITY_POINT, traverse, traversalDistance, min as minPoint, isZero as isZeroPoint, compare as comparePoints} from './point-helpers'
import {getExtent} from './text-helpers'
import Iterator from './iterator'

export default class Patch {
  constructor (params = {}) {
    this.combineChanges = (params.combineChanges != null) ? Boolean(params.combineChanges) : true
    this.batchMode = (params.batchMode != null) ? Boolean(params.batchMode) : false
    if (params.seed) {
      let randomGenerator = new Random(params.seed)
      this.generateRandom = randomGenerator.random.bind(randomGenerator)
    }

    this.root = null
    this.nodesCount = 0
    this.iterator = this.buildIterator()
  }

  buildIterator () {
    return new Iterator(this)
  }

  rebalance () {
    this.createBackbone()
    this.createBalancedTree()
  }

  createBackbone () {
    let pseudoRoot = this.root
    while (pseudoRoot != null) {
      let leftChild = pseudoRoot.left
      let rightChild = pseudoRoot.right
      if (leftChild != null) {
        this.rotateNodeRight(leftChild)
        pseudoRoot = leftChild
      } else {
        pseudoRoot = rightChild
      }
    }
  }

  createBalancedTree() {
    let n = this.nodesCount
    let m = Math.pow(2, Math.floor(Math.log2(n + 1))) - 1
    this.performLeftRotations(n - m)
    while (m > 1) {
      m /= 2
      this.performLeftRotations(m - 1)
    }
  }

  performLeftRotations (count) {
    let root = this.root
    for (var i = 0; i < count; i++) {
      if (root == null) return
      let rightChild = root.right
      if (rightChild == null) return
      root = rightChild.right
      this.rotateNodeLeft(rightChild)
    }
  }

  spliceWithText (start, oldExtent, newText, options) {
    let metadata = options ? options.metadata : null
    this.splice(start, oldExtent, getExtent(newText), {text: newText, metadata})
  }

  splice (outputStart, oldExtent, newExtent, options) {
    if (isZeroPoint(oldExtent) && isZeroPoint(newExtent)) return

    let outputOldEnd = traverse(outputStart, oldExtent)
    let outputNewEnd = traverse(outputStart, newExtent)

    let startNode = this.iterator.insertSpliceBoundary(outputStart)
    startNode.isChangeStart = true
    if (this.batchMode) this.splayNode(startNode)

    let endNode = this.iterator.insertSpliceBoundary(outputOldEnd, startNode)
    endNode.isChangeEnd = true
    if (this.batchMode) this.splayNode(endNode)
    if (options && options.metadata) endNode.metadata = options.metadata

    if (this.batchMode) {
      if (startNode !== endNode && endNode.left !== startNode) this.rotateNodeRight(startNode)
    } else {
      startNode.priority = -1
      this.bubbleNodeUp(startNode)
      endNode.priority = -2
      this.bubbleNodeUp(endNode)
    }

    startNode.right = null
    startNode.inputExtent = startNode.inputLeftExtent
    startNode.outputExtent = startNode.outputLeftExtent

    endNode.outputExtent = traverse(outputNewEnd, traversalDistance(endNode.outputExtent, endNode.outputLeftExtent))
    endNode.outputLeftExtent = outputNewEnd
    endNode.newText = options && options.text

    if (endNode.isChangeStart && this.combineChanges) {
      endNode.priority = Infinity
      let rightAncestor = this.bubbleNodeDown(endNode)
      if (endNode.newText != null) {
        rightAncestor.newText = endNode.newText + rightAncestor.newText
      }
      this.deleteNode(endNode)
    } else if (comparePoints(endNode.outputLeftExtent, startNode.outputLeftExtent) === 0
        && comparePoints(endNode.inputLeftExtent, startNode.inputLeftExtent) === 0) {
      startNode.isChangeStart = endNode.isChangeStart
      this.deleteNode(endNode)
    } else if (!this.batchMode) {
      endNode.priority = this.generateRandom()
      this.bubbleNodeDown(endNode)
    }

    if (startNode.isChangeStart && startNode.isChangeEnd && this.combineChanges) {
      startNode.priority = Infinity
      let rightAncestor = this.bubbleNodeDown(startNode) || endNode
      if (startNode.newText != null) {
        rightAncestor.newText = startNode.newText + rightAncestor.newText
      }
      this.deleteNode(startNode)
    } else if (!this.batchMode) {
      startNode.priority = this.generateRandom()
      this.bubbleNodeDown(startNode)
    }
  }

  spliceInput (inputStart, oldExtent, newExtent) {
    let oldExtentIsZero = isZeroPoint(oldExtent)

    if (oldExtentIsZero && isZeroPoint(newExtent)) {
      let outputStart = this.translateInputPosition(inputStart)
      return {start: outputStart, oldExtent, newExtent}
    }

    let inputOldEnd = traverse(inputStart, oldExtent)
    let inputNewEnd = traverse(inputStart, newExtent)

    let startNode = this.iterator.insertSpliceInputBoundary(inputStart, true, oldExtentIsZero)
    if (this.batchMode) this.splayNode(startNode)
    let endNode = this.iterator.insertSpliceInputBoundary(inputOldEnd, false, oldExtentIsZero)
    if (this.batchMode) this.splayNode(endNode)

    if (this.batchMode) {
      if (startNode !== endNode && endNode.left !== startNode) this.rotateNodeRight(startNode)
    } else {
      startNode.priority = -1
      this.bubbleNodeUp(startNode)
      endNode.priority = -2
      this.bubbleNodeUp(endNode)
    }

    startNode.right = null
    startNode.inputExtent = startNode.inputLeftExtent
    startNode.outputExtent = startNode.outputLeftExtent
    startNode.isChangeStart = false

    let outputStart = startNode.outputLeftExtent
    let outputOldExtent = traversalDistance(endNode.outputLeftExtent, startNode.outputLeftExtent)

    let endNodeInputRightExtent = traversalDistance(endNode.inputExtent, endNode.inputLeftExtent)
    let endNodeOutputRightExtent = traversalDistance(endNode.outputExtent, endNode.outputLeftExtent)
    endNode.inputLeftExtent = inputNewEnd
    endNode.inputExtent = traverse(endNode.inputLeftExtent, endNodeInputRightExtent)
    endNode.outputLeftExtent = traverse(startNode.outputLeftExtent, newExtent)
    endNode.outputExtent = traverse(endNode.outputLeftExtent, endNodeOutputRightExtent)
    endNode.isChangeEnd = false
    endNode.newText = null

    let outputNewExtent = traversalDistance(endNode.outputLeftExtent, startNode.outputLeftExtent)

    if (startNode.isChangeEnd) {
      if (!this.batchMode) {
        startNode.priority = this.generateRandom()
        this.bubbleNodeDown(startNode)
      }
    } else {
      this.deleteNode(startNode)
    }

    if (endNode.isChangeStart) {
      if (!this.batchMode) {
        endNode.priority = this.generateRandom()
        this.bubbleNodeDown(endNode)
      }
    } else {
      this.deleteNode(endNode)
    }

    return {
      start: outputStart,
      oldExtent: outputOldExtent,
      newExtent: outputNewExtent
    }
  }

  isChangedAtInputPosition (inputPosition) {
    this.iterator.seekToInputPosition(inputPosition)
    return this.iterator.inChange()
  }

  isChangedAtOutputPosition (outputPosition) {
    this.iterator.seekToOutputPosition(outputPosition)
    return this.iterator.inChange()
  }

  translateInputPosition (inputPosition) {
    this.iterator.seekToInputPosition(inputPosition)
    let overshoot = traversalDistance(inputPosition, this.iterator.getInputStart())
    let outputPosition = minPoint(traverse(this.iterator.getOutputStart(), overshoot), this.iterator.getOutputEnd())
    this.splayNode(this.iterator.getCurrentNode())
    return outputPosition
  }

  translateOutputPosition (outputPosition) {
    this.iterator.seekToOutputPosition(outputPosition)
    let overshoot = traversalDistance(outputPosition, this.iterator.getOutputStart())
    let inputPosition = minPoint(traverse(this.iterator.getInputStart(), overshoot), this.iterator.getInputEnd())
    this.splayNode(this.iterator.getCurrentNode())
    return inputPosition
  }

  getChanges () {
    return this.iterator.getChanges()
  }

  deleteNode (node) {
    node.priority = Infinity
    this.bubbleNodeDown(node)
    if (node.parent) {
      if (node.parent.left === node) {
        node.parent.left = null
      } else {
        node.parent.right = null
        node.parent.inputExtent = node.parent.inputLeftExtent
        node.parent.outputExtent = node.parent.outputLeftExtent
        let ancestor = node.parent
        while (ancestor.parent && ancestor.parent.right === ancestor) {
          ancestor.parent.inputExtent = traverse(ancestor.parent.inputLeftExtent, ancestor.inputExtent)
          ancestor.parent.outputExtent = traverse(ancestor.parent.outputLeftExtent, ancestor.outputExtent)
          ancestor = ancestor.parent
        }
      }

      if (this.batchMode) this.splayNode(node.parent)
    } else {
      this.root = null
    }

    this.nodesCount--
  }

  bubbleNodeUp (node) {
    while (node.parent && node.priority < node.parent.priority) {
      if (node === node.parent.left) {
        this.rotateNodeRight(node)
      } else {
        this.rotateNodeLeft(node)
      }
    }
  }

  bubbleNodeDown (node) {
    let rightAncestor

    while (true) {
      let leftChildPriority = node.left ? node.left.priority : Infinity
      let rightChildPriority = node.right ? node.right.priority : Infinity

      if (leftChildPriority < rightChildPriority && leftChildPriority < node.priority) {
        this.rotateNodeRight(node.left)
      } else if (rightChildPriority < node.priority) {
        rightAncestor = node.right
        this.rotateNodeLeft(node.right)
      } else {
        break
      }
    }

    return rightAncestor
  }

  splayNode (node) {
    if (node == null) return

    while (true) {
      if (this.isNodeLeftChild(node.parent) && this.isNodeRightChild(node)) { // zig-zag
        this.rotateNodeLeft(node)
        this.rotateNodeRight(node)
      } else if (this.isNodeRightChild(node.parent) && this.isNodeLeftChild(node)) { // zig-zag
        this.rotateNodeRight(node)
        this.rotateNodeLeft(node)
      } else if (this.isNodeLeftChild(node.parent) && this.isNodeLeftChild(node)) { // zig-zig
        this.rotateNodeRight(node.parent)
        this.rotateNodeRight(node)
      } else if (this.isNodeRightChild(node.parent) && this.isNodeRightChild(node)) { // zig-zig
        this.rotateNodeLeft(node.parent)
        this.rotateNodeLeft(node)
      } else { // zig
        if (this.isNodeLeftChild(node)) {
          this.rotateNodeRight(node)
        } else if (this.isNodeRightChild(node)) {
          this.rotateNodeLeft(node)
        }

        return
      }
    }
  }

  isNodeLeftChild (node) {
    return node != null && node.parent != null && node.parent.left === node
  }

  isNodeRightChild (node) {
    return node != null && node.parent != null && node.parent.right === node
  }

  rotateNodeLeft (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root === root.parent.left) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.right = pivot.left
    if (root.right) {
      root.right.parent = root
    }

    pivot.left = root
    pivot.left.parent = pivot

    pivot.inputLeftExtent = traverse(root.inputLeftExtent, pivot.inputLeftExtent)
    pivot.inputExtent = traverse(pivot.inputLeftExtent, (pivot.right ? pivot.right.inputExtent : ZERO_POINT))
    root.inputExtent = traverse(root.inputLeftExtent, (root.right ? root.right.inputExtent : ZERO_POINT))

    pivot.outputLeftExtent = traverse(root.outputLeftExtent, pivot.outputLeftExtent)
    pivot.outputExtent = traverse(pivot.outputLeftExtent, (pivot.right ? pivot.right.outputExtent : ZERO_POINT))
    root.outputExtent = traverse(root.outputLeftExtent, (root.right ? root.right.outputExtent : ZERO_POINT))
  }

  rotateNodeRight (pivot) {
    let root = pivot.parent

    if (root.parent) {
      if (root === root.parent.left) {
        root.parent.left = pivot
      } else {
        root.parent.right = pivot
      }
    } else {
      this.root = pivot
    }
    pivot.parent = root.parent

    root.left = pivot.right
    if (root.left) {
      root.left.parent = root
    }

    pivot.right = root
    pivot.right.parent = pivot

    root.inputLeftExtent = traversalDistance(root.inputLeftExtent, pivot.inputLeftExtent)
    root.inputExtent = traversalDistance(root.inputExtent, pivot.inputLeftExtent)
    pivot.inputExtent = traverse(pivot.inputLeftExtent, root.inputExtent)

    root.outputLeftExtent = traversalDistance(root.outputLeftExtent, pivot.outputLeftExtent)
    root.outputExtent = traversalDistance(root.outputExtent, pivot.outputLeftExtent)
    pivot.outputExtent = traverse(pivot.outputLeftExtent, root.outputExtent)
  }

  generateRandom () {
    return Math.random()
  }
}
<|endoftext|>


<commit_before>
var packageDisplayGraphs = function () {

    if ($('#report-Version').length) {
        if (Modernizr.svg) {
            drawDownloadsByVersionBarChart();
        }
    }
    if ($('#report-ClientName').length) {
        if (Modernizr.svg) {
            drawDownloadsByClientNameBarChart();
        }
    }
}

var SemVer = function (s) {
    var n = s.split('-');
    var v = n[0].split('.');

    this.preRelease = n[1];

    if (v[0] !== undefined) {
        this.major = Number(v[0]);
    }
    if (v[1] !== undefined) {
        this.minor = Number(v[1]);
    }
    if (v[2] !== undefined) {
        this.patch = Number(v[2]);
    }

    this.toString = function () {
        var s = '';
        if (this.major !== undefined && !isNaN(this.major)) {
            s += this.major.toString();
        }
        if (this.minor !== undefined && !isNaN(this.minor)) {
            s += '.';
            s += this.minor.toString();
        }
        if (this.patch !== undefined && !isNaN(this.patch)) {
            s += '.';
            s += this.patch.toString();
        }
        if (this.preRelease !== undefined) {
            s += '-';
            s += this.preRelease;
        }
        return s;
    }
}

var semVerCompare = function (a, b) {
    if (a.major === b.major && a.minor === b.minor && a.patch === b.patch && a.preRelease === b.preRelease) {
        return 0;
    }
    if (a.major < b.major) {
        return -1;
    }
    if (a.major === b.major) {
        if (a.minor < b.minor) {
            return -1;
        }
        if (a.minor === b.minor) {
            if (a.patch < b.patch) {
                return -1;
            }
            if (a.patch === b.patch) {
                if (a.preRelease < b.preRelease) {
                    return -1;
                }
            }
        }
    }
    return 1;
}

var sortByVersion = function (a, b) {
    return semVerCompare(new SemVer(a.version), new SemVer(b.version));
}

var drawDownloadsByVersionBarChart = function () {

    //  scrape data

    var data = [];

    d3.selectAll('#report-Version .statistics-data tbody tr').each(function () {
        var item = {
            version: d3.select(this).select(':nth-child(1)').text().replace(/(^\s*)|(\s*$)/g, ''),
            downloads: +(d3.select(this).select(':nth-child(2)').text().replace(/[^0-9]+/g, ''))
        };
        data[data.length] = item;
    });

    data.sort(sortByVersion);

    //  draw graph

    var reportTableWidth = $('#report-Version').width();

    var reportGraphWidth = 960 - reportTableWidth;

    reportGraphWidth = Math.min(reportGraphWidth, 590);

    var margin = { top: 20, right: 30, bottom: 160, left: 80 },
        width = reportGraphWidth - margin.left - margin.right,
        height = 450 - margin.top - margin.bottom;

    var xScale = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

    var yScale = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');

    var svg = d3.select('#statistics-graph-id')
        .append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    xScale.domain(data.map(function (d) { return d.version; }));
    yScale.domain([0, d3.max(data, function (d) { return d.downloads; })]);

    //  the use of dx attribute on the text element is correct, however, the negative shift doesn't appear to work on Firefox
    //  the workaround employed here is to add a translation to the rotation transform

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        //.attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65),translate(-10,0)"
        });

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Downloads");

    svg.selectAll(".bar")
        .data(data)
        .enter()
        .append("rect")
            .attr("class", "bar")
            .attr("x", function (d) { return xScale(d.version); })
            .attr("width", xScale.rangeBand())
            .attr("y", function (d) { return yScale(d.downloads); })
            .attr("height", function (d) { return height - yScale(d.downloads); });
}

var drawDownloadsByClientNameBarChart = function () {

    //  scrape data

    var data = [];

    d3.selectAll('#report-ClientName .statistics-data tbody tr').each(function () {
        var item = {
            clientName: d3.select(this).select(':nth-child(1)').text().replace(/(^\s*)|(\s*$)/g, ''),
            downloads: +(d3.select(this).select(':nth-child(2)').text().replace(/[^0-9]+/g, ''))
        };
        data[data.length] = item;
    });

    data.reverse();

    //  draw graph

    var margin = { top: 20, right: 30, bottom: 220, left: 100 },
        width = 420 - margin.left - margin.right,
        height = 550 - margin.top - margin.bottom;

    var xScale = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

    var yScale = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');

    var svg = d3.select('#statistics-graph-id')
        .append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    xScale.domain(data.map(function (d) { return d.clientName; }));
    yScale.domain([0, d3.max(data, function (d) { return d.downloads; })]);

    //  the use of dx attribute on the text element is correct, however, the negative shift doesn't appear to work on Firefox
    //  the workaround employed here is to add a translation to the rotation transform

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        //.attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65),translate(-10,0)"
        });

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Downloads");

    svg.selectAll(".bar")
        .data(data)
        .enter()
        .append("rect")
            .attr("class", "bar")
            .attr("x", function (d) { return xScale(d.clientName); })
            .attr("width", xScale.rangeBand())
            .attr("y", function (d) { return yScale(d.downloads); })
            .attr("height", function (d) { return height - yScale(d.downloads); });
}
<commit_msg>code review feedback<commit_after>
var packageDisplayGraphs = function () {

    if ($('#report-Version').length) {
        if (Modernizr.svg) {
            drawDownloadsByVersionBarChart();
        }
    }
    if ($('#report-ClientName').length) {
        if (Modernizr.svg) {
            drawDownloadsByClientNameBarChart();
        }
    }
}

var SemVer = function (versionString) {
    var n = versionString.split('-');
    var v = n[0].split('.');

    this.preRelease = n[1];

    if (v[0] !== undefined) {
        this.major = Number(v[0]);
    }
    if (v[1] !== undefined) {
        this.minor = Number(v[1]);
    }
    if (v[2] !== undefined) {
        this.patch = Number(v[2]);
    }

    this.toString = function () {
        var s = '';
        if (this.major !== undefined && !isNaN(this.major)) {
            s += this.major.toString();
        }
        if (this.minor !== undefined && !isNaN(this.minor)) {
            s += '.';
            s += this.minor.toString();
        }
        if (this.patch !== undefined && !isNaN(this.patch)) {
            s += '.';
            s += this.patch.toString();
        }
        if (this.preRelease !== undefined) {
            s += '-';
            s += this.preRelease;
        }
        return s;
    }

    this.compareTo = function (other) {
        if (this.major === other.major && this.minor === other.minor && this.patch === other.patch && this.preRelease === other.preRelease) {
            return 0;
        }
        if (this.major < other.major) {
            return -1;
        }
        if (this.major === other.major) {
            if (this.minor < other.minor) {
                return -1;
            }
            if (this.minor === other.minor) {
                if (this.patch < other.patch) {
                    return -1;
                }
                if (this.patch === other.patch) {
                    if (this.preRelease < other.preRelease) {
                        return -1;
                    }
                }
            }
        }
        return 1;
    }
}

var sortByVersion = function (a, b) {
    return (new SemVer(a.version)).compareTo(new SemVer(b.version));
}

var drawDownloadsByVersionBarChart = function () {

    //  scrape data

    var data = [];

    d3.selectAll('#report-Version .statistics-data tbody tr').each(function () {
        var item = {
            version: d3.select(this).select(':nth-child(1)').text().replace(/(^\s*)|(\s*$)/g, ''),
            downloads: +(d3.select(this).select(':nth-child(2)').text().replace(/[^0-9]+/g, ''))
        };
        data[data.length] = item;
    });

    data.sort(sortByVersion);

    //  draw graph

    var reportTableWidth = $('#report-Version').width();

    var reportGraphWidth = 960 - reportTableWidth;

    reportGraphWidth = Math.min(reportGraphWidth, 590);

    var margin = { top: 20, right: 30, bottom: 160, left: 80 },
        width = reportGraphWidth - margin.left - margin.right,
        height = 450 - margin.top - margin.bottom;

    var xScale = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

    var yScale = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');

    var svg = d3.select('#statistics-graph-id')
        .append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    xScale.domain(data.map(function (d) { return d.version; }));
    yScale.domain([0, d3.max(data, function (d) { return d.downloads; })]);

    //  the use of dx attribute on the text element is correct, however, the negative shift doesn't appear to work on Firefox
    //  the workaround employed here is to add a translation to the rotation transform

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        //.attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65),translate(-10,0)"
        });

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Downloads");

    svg.selectAll(".bar")
        .data(data)
        .enter()
        .append("rect")
            .attr("class", "bar")
            .attr("x", function (d) { return xScale(d.version); })
            .attr("width", xScale.rangeBand())
            .attr("y", function (d) { return yScale(d.downloads); })
            .attr("height", function (d) { return height - yScale(d.downloads); });
}

var drawDownloadsByClientNameBarChart = function () {

    //  scrape data

    var data = [];

    d3.selectAll('#report-ClientName .statistics-data tbody tr').each(function () {
        var item = {
            clientName: d3.select(this).select(':nth-child(1)').text().replace(/(^\s*)|(\s*$)/g, ''),
            downloads: +(d3.select(this).select(':nth-child(2)').text().replace(/[^0-9]+/g, ''))
        };
        data[data.length] = item;
    });

    data.reverse();

    //  draw graph

    var margin = { top: 20, right: 30, bottom: 220, left: 100 },
        width = 420 - margin.left - margin.right,
        height = 550 - margin.top - margin.bottom;

    var xScale = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

    var yScale = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');

    var svg = d3.select('#statistics-graph-id')
        .append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    xScale.domain(data.map(function (d) { return d.clientName; }));
    yScale.domain([0, d3.max(data, function (d) { return d.downloads; })]);

    //  the use of dx attribute on the text element is correct, however, the negative shift doesn't appear to work on Firefox
    //  the workaround employed here is to add a translation to the rotation transform

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "end")
        //.attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", function (d) {
            return "rotate(-65),translate(-10,0)"
        });

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Downloads");

    svg.selectAll(".bar")
        .data(data)
        .enter()
        .append("rect")
            .attr("class", "bar")
            .attr("x", function (d) { return xScale(d.clientName); })
            .attr("width", xScale.rangeBand())
            .attr("y", function (d) { return yScale(d.downloads); })
            .attr("height", function (d) { return height - yScale(d.downloads); });
}
<|endoftext|>


<commit_before>
WebVRConfig = {
  // Flag to disabled the UI in VR Mode.
  CARDBOARD_UI_DISABLED: false, // Default: false

  // Forces availability of VR mode, even for non-mobile devices.
  FORCE_ENABLE_VR: true
}
var container, camera, scene, renderer, stats, effect;
var vrDisplay = null;

var gem, gui;

var tv1 = new THREE.Vector3();
var tv2 = new THREE.Vector3();
var repelerMeshes = [];
var repelersHidden = true;

var REPELERS = [];

// TODO: make into loader
var loaded = 0;
var neededToLoad = 1;

var loader = new Loader();
loader.liftCurtain();

var clock = new THREE.Clock();

var audioController = new AudioController();
//audioController.mute.gain.value = 0;

var stream = new Stream('audio/centerYourLove.mp3', audioController);
//stream.onStreamCreated = function(){
/// onLoad();
// }
/* var userAudio = new UserAudio( audioController );
  userAudio.onStreamCreated = function(){

    console.log('asds');
    onLoad();

  }*/
var shaders = new ShaderLoader('shaders');

shaders.load('ss-fire', 'fire', 'simulation');
shaders.load('ss-weird1', 'weird1', 'simulation');
shaders.load('vs-fire', 'fire', 'vertex');
shaders.load('fs-weird1', 'weird1', 'fragment');
shaders.load('fs-fire', 'fire', 'fragment');

shaders.shaderSetLoaded = function () {
  onLoad();
}

var G_UNIFORMS = {

  dT: { type: "f", value: 0 },
  time: { type: "f", value: 0 },
  t_audio: { type: "t", value: audioController.texture },

}

function init() {

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    50,
    window.innerWidth / window.innerHeight,
    1,
    5000
  );

  // placing our camera position so it can see everything
  camera.position.z = 1000;
  camera.lookAt(new THREE.Vector3());

  // Getting the container in the right location
  container = document.createElement('div');
  container.id = 'container';

  document.body.appendChild(container);

  // Getting the stats in the right position
  stats = new Stats();
  stats.domElement.id = 'stats';
  document.body.appendChild(stats.domElement);

  // Setting up our Renderer
  renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true });
  renderer.setClearColor(0x505050);
		renderer.setPixelRatio(window.devicePixelRatio);
  renderer.sortObjects = false;
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);
  renderer.domElement.style.background = "#000";

  /* //RIP CONTROLS */
  /*
  controls = new THREE.TrackballControls( camera , renderer.domElement);
  controls.minDistance = 1;
  controls.maxDistance = 3000;
  */

  controls = new THREE.VRControls(camera);
  console.log(controls);
		effect = new THREE.VREffect(renderer);

  // Making sure our renderer is always the right size
  window.addEventListener('resize', onWindowResize, false);
  //window.addEventListener( 'mousemove', onMouseMove , false );

  var g = new THREE.Mesh( //new THREE.IcosahedronGeometry( 400 , 7 )
    new THREE.SphereGeometry(300, 64, 64)
  );
  gem = new CurlMesh('Space Puppy', g, {

    soul: {

      noiseSize: { type: "f", value: .003, constraints: [.0001, .01] },
      noiseVariation: { type: "f", value: .8, constraints: [.01, 1.] },
      dampening: { type: "f", value: 1., constraints: [.8, .999] },
      noisePower: { type: "f", value: 100, constraints: [0, 200.] },
      returnPower: { type: "f", value: 1., constraints: [.0, 2.] },
      audioVelMultiplier: { type: "f", value: .8, constraints: [0, 1] },

    },

    body: {

      audioDisplacement: { type: "f", value: 30.0, constraints: [0, 100] },

      tmp_color1: { type: "color", value: 0xff0000 },
      tmp_color2: { type: "color", value: 0x00ffe1 },
      tmp_color3: { type: "color", value: 0x5500ff },

      color1: { type: "c", value: new THREE.Color(0xff0000) },
      color2: { type: "c", value: new THREE.Color(0x00ffe1) },
      color3: { type: "c", value: new THREE.Color(0x5500ff) },
    }

  });

  gem.toggle();

  if (navigator.getVRDisplays) {
    navigator.getVRDisplays()
						.then(function (displays) {
        console.log(displays);
        vrDisplay = displays[0];

        displayReady();

        controls.setVRDisplay(displays[0]);
        vrDisplay.requestPresent([{ source: renderer.domElement }]);
        vrDisplay.requestAnimationFrame(animate);
						})
						.catch(function () {
        // no displays
        console.log('NO DISPLAYS!');
						});
				//	document.body.appendChild( WEBVR.getButton( effect ) );
  }

}

function displayReady() {
  onDeviceReady();
  // device APIs are available
  //
  function onDeviceReady() {
    navigator.accelerometer.watchAcceleration(onSuccess, onError);
  }

  var onError = function(err){
    console.log(err);
  }
  // onSuccess: Get a snapshot of the current acceleration
  //
  function onSuccess(acceleration) {
    vrDisplay.poseSensor_.gyroscope.x = acceleration.x;
    vrDisplay.poseSensor_.gyroscope.y = acceleration.y;
    vrDisplay.poseSensor_.gyroscope.z = acceleration.z;

    console.log('Acceleration X: ' + acceleration.x + '\n' +
      'Acceleration Y: ' + acceleration.y + '\n' +
      'Acceleration Z: ' + acceleration.z + '\n' +
      'Timestamp: ' + acceleration.timestamp + '\n');
  }
}

function animate() {
  audioController.update();

  G_UNIFORMS.dT.value = clock.getDelta();
  G_UNIFORMS.time.value += G_UNIFORMS.dT.value;

  gem.update();

  stats.update();

  controls.update();

  effect.render(scene, camera);
  vrDisplay.requestAnimationFrame(animate);
}

/*
  function onMouseMove(e ){
  }
*/
// Resets the renderer to be the proper size
function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);

  var dpr = devicePixelRatio || 1;

  //camUniforms.SS.value.x = window.innerWidth * dpr;
  //camUniforms.SS.value.y = window.innerHeight * dpr;

}

function onLoad() {

  loaded++;

  console.log(loaded);
  if (loaded === neededToLoad) {

    if (stream) {
      // stream = window.URL.createObjectURL(stream);
      stream.play();
    }

    init();
    //goOn();
  }

}
$(document).ready(function () {
  $('#vrbutton').click(function () {
    vrDisplay.requestPresent([{ source: renderer.domElement }]);
  });
});
window.activateVR = function () {
  vrDisplay.requestPresent([{ source: renderer.domElement }]);
}
function toCart(r, t, p) {

  var x = r * (Math.sin(t)) * (Math.cos(p));
  var y = r * (Math.sin(t)) * (Math.sin(p));
  var z = r * (Math.cos(t));

  return new THREE.Vector3(x, y, z);

}<commit_msg>high freq<commit_after>
WebVRConfig = {
  // Flag to disabled the UI in VR Mode.
  CARDBOARD_UI_DISABLED: false, // Default: false

  // Forces availability of VR mode, even for non-mobile devices.
  FORCE_ENABLE_VR: true
}
var container, camera, scene, renderer, stats, effect;
var vrDisplay = null;

var gem, gui;

var tv1 = new THREE.Vector3();
var tv2 = new THREE.Vector3();
var repelerMeshes = [];
var repelersHidden = true;

var REPELERS = [];

// TODO: make into loader
var loaded = 0;
var neededToLoad = 1;

var loader = new Loader();
loader.liftCurtain();

var clock = new THREE.Clock();

var audioController = new AudioController();
//audioController.mute.gain.value = 0;

var stream = new Stream('audio/centerYourLove.mp3', audioController);
//stream.onStreamCreated = function(){
/// onLoad();
// }
/* var userAudio = new UserAudio( audioController );
  userAudio.onStreamCreated = function(){

    console.log('asds');
    onLoad();

  }*/
var shaders = new ShaderLoader('shaders');

shaders.load('ss-fire', 'fire', 'simulation');
shaders.load('ss-weird1', 'weird1', 'simulation');
shaders.load('vs-fire', 'fire', 'vertex');
shaders.load('fs-weird1', 'weird1', 'fragment');
shaders.load('fs-fire', 'fire', 'fragment');

shaders.shaderSetLoaded = function () {
  onLoad();
}

var G_UNIFORMS = {

  dT: { type: "f", value: 0 },
  time: { type: "f", value: 0 },
  t_audio: { type: "t", value: audioController.texture },

}

function init() {

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(
    50,
    window.innerWidth / window.innerHeight,
    1,
    5000
  );

  // placing our camera position so it can see everything
  camera.position.z = 1000;
  camera.lookAt(new THREE.Vector3());

  // Getting the container in the right location
  container = document.createElement('div');
  container.id = 'container';

  document.body.appendChild(container);

  // Getting the stats in the right position
  stats = new Stats();
  stats.domElement.id = 'stats';
  document.body.appendChild(stats.domElement);

  // Setting up our Renderer
  renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true });
  renderer.setClearColor(0x505050);
		renderer.setPixelRatio(window.devicePixelRatio);
  renderer.sortObjects = false;
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);
  renderer.domElement.style.background = "#000";

  /* //RIP CONTROLS */
  /*
  controls = new THREE.TrackballControls( camera , renderer.domElement);
  controls.minDistance = 1;
  controls.maxDistance = 3000;
  */

  controls = new THREE.VRControls(camera);
  console.log(controls);
		effect = new THREE.VREffect(renderer);

  // Making sure our renderer is always the right size
  window.addEventListener('resize', onWindowResize, false);
  //window.addEventListener( 'mousemove', onMouseMove , false );

  var g = new THREE.Mesh( //new THREE.IcosahedronGeometry( 400 , 7 )
    new THREE.SphereGeometry(300, 64, 64)
  );
  gem = new CurlMesh('Space Puppy', g, {

    soul: {

      noiseSize: { type: "f", value: .003, constraints: [.0001, .01] },
      noiseVariation: { type: "f", value: .8, constraints: [.01, 1.] },
      dampening: { type: "f", value: 1., constraints: [.8, .999] },
      noisePower: { type: "f", value: 100, constraints: [0, 200.] },
      returnPower: { type: "f", value: 1., constraints: [.0, 2.] },
      audioVelMultiplier: { type: "f", value: .8, constraints: [0, 1] },

    },

    body: {

      audioDisplacement: { type: "f", value: 30.0, constraints: [0, 100] },

      tmp_color1: { type: "color", value: 0xff0000 },
      tmp_color2: { type: "color", value: 0x00ffe1 },
      tmp_color3: { type: "color", value: 0x5500ff },

      color1: { type: "c", value: new THREE.Color(0xff0000) },
      color2: { type: "c", value: new THREE.Color(0x00ffe1) },
      color3: { type: "c", value: new THREE.Color(0x5500ff) },
    }

  });

  gem.toggle();

  if (navigator.getVRDisplays) {
    navigator.getVRDisplays()
						.then(function (displays) {
        console.log(displays);
        vrDisplay = displays[0];

        displayReady();

        controls.setVRDisplay(displays[0]);
        vrDisplay.requestPresent([{ source: renderer.domElement }]);
        vrDisplay.requestAnimationFrame(animate);
						})
						.catch(function () {
        // no displays
        console.log('NO DISPLAYS!');
						});
				//	document.body.appendChild( WEBVR.getButton( effect ) );
  }

}

function displayReady() {
  onDeviceReady();
  // device APIs are available
  //
  function onDeviceReady() {
    navigator.accelerometer.watchAcceleration(onSuccess, onError, {frequency: 20});
  }

  var onError = function(err){
    console.log(err);
  }
  // onSuccess: Get a snapshot of the current acceleration
  //
  function onSuccess(acceleration) {
    vrDisplay.poseSensor_.gyroscope.x = acceleration.x;
    vrDisplay.poseSensor_.gyroscope.y = acceleration.y;
    vrDisplay.poseSensor_.gyroscope.z = acceleration.z;

    console.log('Acceleration X: ' + acceleration.x + '\n' +
      'Acceleration Y: ' + acceleration.y + '\n' +
      'Acceleration Z: ' + acceleration.z + '\n' +
      'Timestamp: ' + acceleration.timestamp + '\n');
  }
}

function animate() {
  audioController.update();

  G_UNIFORMS.dT.value = clock.getDelta();
  G_UNIFORMS.time.value += G_UNIFORMS.dT.value;

  gem.update();

  stats.update();

  controls.update();

  effect.render(scene, camera);
  vrDisplay.requestAnimationFrame(animate);
}

/*
  function onMouseMove(e ){
  }
*/
// Resets the renderer to be the proper size
function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);

  var dpr = devicePixelRatio || 1;

  //camUniforms.SS.value.x = window.innerWidth * dpr;
  //camUniforms.SS.value.y = window.innerHeight * dpr;

}

function onLoad() {

  loaded++;

  console.log(loaded);
  if (loaded === neededToLoad) {

    if (stream) {
      // stream = window.URL.createObjectURL(stream);
      stream.play();
    }

    init();
    //goOn();
  }

}
$(document).ready(function () {
  $('#vrbutton').click(function () {
    vrDisplay.requestPresent([{ source: renderer.domElement }]);
  });
});
window.activateVR = function () {
  vrDisplay.requestPresent([{ source: renderer.domElement }]);
}
function toCart(r, t, p) {

  var x = r * (Math.sin(t)) * (Math.cos(p));
  var y = r * (Math.sin(t)) * (Math.sin(p));
  var z = r * (Math.cos(t));

  return new THREE.Vector3(x, y, z);

}<|endoftext|>


<commit_before>/**
 * Utility library for panthrBase.
 * @module utils
 * @author Haris Skiadas <skiadas@hanover.edu>, Barb Wahl <wahl@hanover.edu>
 */
(function(define) {'use strict';
define(function(require) {

   /**
    * A collection of utilities used by panthrBase.
    * Contains methods for:
    *   - handling missing values,
    *   - standard arithmetic operations,
    *   - equality tests,
    *   - number formatting.
    */
   var utils = {};

   /**
    * Mixes into the first object the key-value pairs from the other objects.
    * Shallow copy.
    */
   utils.mixin = function(target) {
      var rest = [].slice.call(arguments, 1);
      rest.forEach(function(o) {
         if (o) {
            Object.keys(o).forEach(function(key) {
               if (!target.hasOwnProperty(key)) { target[key] = o[key]; }
            });
         }
      });
      return target;
   };

   /** Value to be used for all missing values. */
   utils.missing = NaN;

   /** Return true if `val` is `undefined`, `null`, or `NaN`. **/
   utils.isMissing = function isMissing(val) {
      /* eslint-disable no-self-compare */
      return val == null || val !== val;
      /* eslint-enable */
   };

   /** Return true if `val` is not `undefined`, `null`, or `NaN`. **/
   utils.isNotMissing = function isNotMissing(val) {
      /* eslint-disable no-self-compare */
      return val != null && val === val;
      /* eslint-enable */
   };

   /** For an array, return whether the array has any missing values in it. */
   utils.hasMissing = function hasMissing(arr) {
      return arr.some(utils.isMissing);
   };

   /** Return `val` if it is non-missing; otherwise return `utils.missing`. */
   utils.singleMissing = function singleMissing(val) {
      return utils.isMissing(val) ? utils.missing : val;
   };

   /**
    * Return a new function `g` such that `g(any missing)` is `utils.missing`,
    * and `g(val)` is either `f(val)` or `utils.missing`, depending on whether
    * `f(val)` is a missing value.
    */
   utils.makePreserveMissing = function makePreserveMissing(f) {
      return function(val) {
         return utils.isMissing(val) ? utils.missing :
               utils.singleMissing(f.apply(null, [].slice.call(arguments)));
      };
   };

   /** Return true if all entries in the array are missing. */
   utils.allMissing = function allMissing(arr) {
     return arr.every(utils.isMissing);
   };

   /** If `val` is a missing value, return `deflt`, else return `val`. */
   utils.getDefault = function getDefault(val, deflt) {
     return utils.isMissing(val) ? deflt : val;
   };

   /**
    * If `val` is a missing value, return `utils.missing`. Otherwise return `f(val)`.
    */
   utils.optionMap = function optionMap(val, f) {
     return utils.isMissing(val) ? utils.missing : f(val);
   };

   /** Test for equality that respects missing values. */
   utils.equal = function equal(a, b) {
      return utils.isMissing(a) ? utils.isMissing(b)
                                : utils.isNotMissing(b) && a === b;
   };

   /**
    * Test for array element equality that respects missing values.
    * Makes a shallow comparison.
    */
   utils.areEqualArrays = function areEqualArrays(A, B) {
      var i;
      if (A.length !== B.length) { return false; }
      for (i = 0; i < A.length; i += 1) {
         if (!utils.equal(A[i], B[i])) { return false; }
      }
      return true;
   };

   /* "Reverse lookup" for an array. */
   utils.arrayToObject = function arrayToObject(arr) {
      var obj;
      obj = {};
      arr.forEach(function(k, i) { obj[k] = i + 1; });
      return obj;
   };

   /**
    * Test if `v` is of one of the listed `types` (an array of strings).
    */
   utils.isOfType = function isOfType(v, types) {
      return types.some(function(t) { return v instanceof t; });
   };

   /**
    * Create an array of sequential values. Similar options to `Variable.seq`.
    */
   utils.seq = function seq(from, to, step) {
      var arr = [];
      if (arguments.length === 1) { to = from; from = 1; }
      if (arguments.length < 3) { step = to >= from ? 1 : -1; }
      while ((to - from) * step >= 0) {
         arr.push(from);
         from += step;
      }
      return arr;
   };

   /** Arithmetic operators */
   utils.op = {};

   /** The function that adds two numbers. Also available as `utils.op['+']`. */
   utils.op.add = function add(a, b) { return a + b; };
   utils.op['+'] = utils.op.add;

   /** The function that subtracts two numbers. Also available as `utils.op['-']`. */
   utils.op.sub = function sub(a, b) { return a - b; };
   utils.op['-'] = utils.op.sub;

   /** The function that multiplies two numbers. Also available as `utils.op['*']`. */
   utils.op.mult = function mult(a, b) { return a * b; };
   utils.op['*'] = utils.op.mult;

   /** The function that divides two numbers. Also available as `utils.op['/']`. */
   utils.op.div = function divide(a, b) { return a / b; };
   utils.op['/'] = utils.op.div;

   /** The function that takes two values and returns the minimum. */
   utils.op.min2 = function min2(a, b) { return Math.min(a, b); };

   /** The function that takes two values and returns the maximum. */
   utils.op.max2 = function max2(a, b) { return Math.max(a, b); };

   /**
    * Take a user-provided option description `s` (a string) and an array `optList`
    * of allowable option settings.  Return the first element of the array that
    * has `s` as its initial substring.  Return `null` if no such match is found.
    *
    * If `s` is empty, `null` or `undefined`, return the default setting `deflt`.
    */
   utils.getOption = function getOption(s, optList, deflt) {
      var i;
      if (s == null || s === '') { return deflt; }
      s = s.toLowerCase();
      for (i = 0; i < optList.length; i += 1) {
         if (optList[i].toLowerCase().indexOf(s) === 0) { return optList[i]; }
      }
      return null;
   };

   /** An object containing formatting functions for numbers. */
   utils.format = {
      scientific: function(decimals) {
         return function(x) { return x.toExponential(decimals); };
      },
      fixed: function(decimals) {
         return function(x) { return x.toFixed(decimals); };
      }
   };

   return utils;

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<commit_msg>Properly place the doc for utils<commit_after>/**
 * Utility library for panthrBase.
 *
 * A collection of utilities used by panthrBase.
 * Contains methods for:
 *   - handling missing values,
 *   - standard arithmetic operations,
 *   - equality tests,
 *   - number formatting.
 * @module utils
 * @author Haris Skiadas <skiadas@hanover.edu>, Barb Wahl <wahl@hanover.edu>
 */
(function(define) {'use strict';
define(function(require) {

   var utils = {};

   /**
    * Mixes into the first object the key-value pairs from the other objects.
    * Shallow copy.
    */
   utils.mixin = function(target) {
      var rest = [].slice.call(arguments, 1);
      rest.forEach(function(o) {
         if (o) {
            Object.keys(o).forEach(function(key) {
               if (!target.hasOwnProperty(key)) { target[key] = o[key]; }
            });
         }
      });
      return target;
   };

   /** Value to be used for all missing values. */
   utils.missing = NaN;

   /** Return true if `val` is `undefined`, `null`, or `NaN`. **/
   utils.isMissing = function isMissing(val) {
      /* eslint-disable no-self-compare */
      return val == null || val !== val;
      /* eslint-enable */
   };

   /** Return true if `val` is not `undefined`, `null`, or `NaN`. **/
   utils.isNotMissing = function isNotMissing(val) {
      /* eslint-disable no-self-compare */
      return val != null && val === val;
      /* eslint-enable */
   };

   /** For an array, return whether the array has any missing values in it. */
   utils.hasMissing = function hasMissing(arr) {
      return arr.some(utils.isMissing);
   };

   /** Return `val` if it is non-missing; otherwise return `utils.missing`. */
   utils.singleMissing = function singleMissing(val) {
      return utils.isMissing(val) ? utils.missing : val;
   };

   /**
    * Return a new function `g` such that `g(any missing)` is `utils.missing`,
    * and `g(val)` is either `f(val)` or `utils.missing`, depending on whether
    * `f(val)` is a missing value.
    */
   utils.makePreserveMissing = function makePreserveMissing(f) {
      return function(val) {
         return utils.isMissing(val) ? utils.missing :
               utils.singleMissing(f.apply(null, [].slice.call(arguments)));
      };
   };

   /** Return true if all entries in the array are missing. */
   utils.allMissing = function allMissing(arr) {
     return arr.every(utils.isMissing);
   };

   /** If `val` is a missing value, return `deflt`, else return `val`. */
   utils.getDefault = function getDefault(val, deflt) {
     return utils.isMissing(val) ? deflt : val;
   };

   /**
    * If `val` is a missing value, return `utils.missing`. Otherwise return `f(val)`.
    */
   utils.optionMap = function optionMap(val, f) {
     return utils.isMissing(val) ? utils.missing : f(val);
   };

   /** Test for equality that respects missing values. */
   utils.equal = function equal(a, b) {
      return utils.isMissing(a) ? utils.isMissing(b)
                                : utils.isNotMissing(b) && a === b;
   };

   /**
    * Test for array element equality that respects missing values.
    * Makes a shallow comparison.
    */
   utils.areEqualArrays = function areEqualArrays(A, B) {
      var i;
      if (A.length !== B.length) { return false; }
      for (i = 0; i < A.length; i += 1) {
         if (!utils.equal(A[i], B[i])) { return false; }
      }
      return true;
   };

   /* "Reverse lookup" for an array. */
   utils.arrayToObject = function arrayToObject(arr) {
      var obj;
      obj = {};
      arr.forEach(function(k, i) { obj[k] = i + 1; });
      return obj;
   };

   /**
    * Test if `v` is of one of the listed `types` (an array of strings).
    */
   utils.isOfType = function isOfType(v, types) {
      return types.some(function(t) { return v instanceof t; });
   };

   /**
    * Create an array of sequential values. Similar options to `Variable.seq`.
    */
   utils.seq = function seq(from, to, step) {
      var arr = [];
      if (arguments.length === 1) { to = from; from = 1; }
      if (arguments.length < 3) { step = to >= from ? 1 : -1; }
      while ((to - from) * step >= 0) {
         arr.push(from);
         from += step;
      }
      return arr;
   };

   /** Arithmetic operators */
   utils.op = {};

   /** The function that adds two numbers. Also available as `utils.op['+']`. */
   utils.op.add = function add(a, b) { return a + b; };
   utils.op['+'] = utils.op.add;

   /** The function that subtracts two numbers. Also available as `utils.op['-']`. */
   utils.op.sub = function sub(a, b) { return a - b; };
   utils.op['-'] = utils.op.sub;

   /** The function that multiplies two numbers. Also available as `utils.op['*']`. */
   utils.op.mult = function mult(a, b) { return a * b; };
   utils.op['*'] = utils.op.mult;

   /** The function that divides two numbers. Also available as `utils.op['/']`. */
   utils.op.div = function divide(a, b) { return a / b; };
   utils.op['/'] = utils.op.div;

   /** The function that takes two values and returns the minimum. */
   utils.op.min2 = function min2(a, b) { return Math.min(a, b); };

   /** The function that takes two values and returns the maximum. */
   utils.op.max2 = function max2(a, b) { return Math.max(a, b); };

   /**
    * Take a user-provided option description `s` (a string) and an array `optList`
    * of allowable option settings.  Return the first element of the array that
    * has `s` as its initial substring.  Return `null` if no such match is found.
    *
    * If `s` is empty, `null` or `undefined`, return the default setting `deflt`.
    */
   utils.getOption = function getOption(s, optList, deflt) {
      var i;
      if (s == null || s === '') { return deflt; }
      s = s.toLowerCase();
      for (i = 0; i < optList.length; i += 1) {
         if (optList[i].toLowerCase().indexOf(s) === 0) { return optList[i]; }
      }
      return null;
   };

   /** An object containing formatting functions for numbers. */
   utils.format = {
      scientific: function(decimals) {
         return function(x) { return x.toExponential(decimals); };
      },
      fixed: function(decimals) {
         return function(x) { return x.toFixed(decimals); };
      }
   };

   return utils;

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<|endoftext|>


<commit_before>
import styles from './node-style'
import Node from '../motor/Node'
import Transformable from '../motor/Transformable'
import Sizeable from '../motor/Sizeable'
import MotorHTMLBase, {initMotorHTMLBase, proxyGettersSetters} from './base'

initMotorHTMLBase()

class MotorHTMLNode extends MotorHTMLBase {

    getStyles() {
        return styles
    }

    // this is called by DeclarativeBase#init, which is called by
    // WebComponent#connectedCallback, at which point this element has a
    // parentNode.
    // @override
    _makeImperativeCounterpart() {
        return new Node({
            _motorHtmlCounterpart: this
        })
    }

    async attributeChangedCallback(...args) {
        super.attributeChangedCallback(...args)
        await this._imperativeCounterpartPromise
        this._updateNodeProperty(...args)
    }

    _updateNodeProperty(attribute, oldValue, newValue) {
        // attributes on our HTML elements are the same name as those on
        // the Node class (the setters).
        if (newValue !== oldValue) {
            if (attribute.match(/opacity/i))
                this.imperativeCounterpart[attribute] = window.parseFloat(newValue)
            else if (attribute.match(/sizeMode/i))
                this.imperativeCounterpart[attribute] = parseStringArray(newValue)
            else if (
                attribute.match(/rotation/i)
                || attribute.match(/scale/i)
                || attribute.match(/position/i)
                || attribute.match(/absoluteSize/i)
                || attribute.match(/proportionalSize/i)
                || attribute.match(/align/i)
                || attribute.match(/mountPoint/i)
                || attribute.match(/origin/i)
                || attribute.match(/skew/i)
            ) {
                this.imperativeCounterpart[attribute] = parseNumberArray(newValue)
            }
            else {
                /* nothing, ignore other attributes */
            }
        }
    }
}

// This associates the Transformable getters/setters with the HTML-API classes,
// so that the same getters/setters can be called from HTML side of the API.
proxyGettersSetters(Transformable, MotorHTMLNode)
proxyGettersSetters(Sizeable, MotorHTMLNode)

function parseNumberArray(str) {
    checkIsNumberArrayString(str)
    const numbers = str.trim().split(/(?:\s*,\s*)|(?:\s+)/g)
    const length = numbers.length
    if (length > 0) numbers[0] = window.parseFloat(numbers[0])
    if (length > 1) numbers[1] = window.parseFloat(numbers[1])
    if (length > 2) numbers[2] = window.parseFloat(numbers[2])
    return numbers
}

function parseStringArray(str) {
    checkIsSizeArrayString(str)
    const strings = str.trim().toLowerCase().split(/(?:\s*,\s*)|(?:\s+)/g)
    const length = strings.length
    if (length > 0) strings[0] = window.parseFloat(strings[0])
    if (length > 1) strings[1] = window.parseFloat(strings[1])
    if (length > 2) strings[2] = window.parseFloat(strings[2])
    return strings
}

function checkIsNumberArrayString(str) {
    if (!str.match(/^\s*(((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))\s*,){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))))|((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))\s){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)))))\s*$/g))
        throw new Error(`Attribute must be a comma- or space-separated sequence of up to three numbers, for example "1 2.5 3". Yours was "${str}".`)
}

function checkIsSizeArrayString(str) {
    if (!str.match(/^\s*(((\s*([a-zA-Z]+)\s*,){0,2}(\s*([a-zA-Z]+)))|((\s*([a-zA-Z]+)\s*){1,3}))\s*$/g))
        throw new Error(`Attribute must be a comma- or space-separated sequence of up to three strings, for example "absolute absolute". Yours was "${str}".`)
}

import 'document-register-element'
MotorHTMLNode = document.registerElement('motor-node', MotorHTMLNode)

export {MotorHTMLNode as default}
<commit_msg>Fix: don't parseFloat the string values<commit_after>
import styles from './node-style'
import Node from '../motor/Node'
import Transformable from '../motor/Transformable'
import Sizeable from '../motor/Sizeable'
import MotorHTMLBase, {initMotorHTMLBase, proxyGettersSetters} from './base'

initMotorHTMLBase()

class MotorHTMLNode extends MotorHTMLBase {

    getStyles() {
        return styles
    }

    // this is called by DeclarativeBase#init, which is called by
    // WebComponent#connectedCallback, at which point this element has a
    // parentNode.
    // @override
    _makeImperativeCounterpart() {
        return new Node({
            _motorHtmlCounterpart: this
        })
    }

    async attributeChangedCallback(...args) {
        super.attributeChangedCallback(...args)
        await this._imperativeCounterpartPromise
        this._updateNodeProperty(...args)
    }

    _updateNodeProperty(attribute, oldValue, newValue) {
        // attributes on our HTML elements are the same name as those on
        // the Node class (the setters).
        if (newValue !== oldValue) {
            if (attribute.match(/opacity/i))
                this.imperativeCounterpart[attribute] = window.parseFloat(newValue)
            else if (attribute.match(/sizeMode/i))
                this.imperativeCounterpart[attribute] = parseStringArray(newValue)
            else if (
                attribute.match(/rotation/i)
                || attribute.match(/scale/i)
                || attribute.match(/position/i)
                || attribute.match(/absoluteSize/i)
                || attribute.match(/proportionalSize/i)
                || attribute.match(/align/i)
                || attribute.match(/mountPoint/i)
                || attribute.match(/origin/i)
                || attribute.match(/skew/i)
            ) {
                this.imperativeCounterpart[attribute] = parseNumberArray(newValue)
            }
            else {
                /* nothing, ignore other attributes */
            }
        }
    }
}

// This associates the Transformable getters/setters with the HTML-API classes,
// so that the same getters/setters can be called from HTML side of the API.
proxyGettersSetters(Transformable, MotorHTMLNode)
proxyGettersSetters(Sizeable, MotorHTMLNode)

function parseNumberArray(str) {
    checkIsNumberArrayString(str)
    const numbers = str.trim().split(/(?:\s*,\s*)|(?:\s+)/g)
    const length = numbers.length
    if (length > 0) numbers[0] = window.parseFloat(numbers[0])
    if (length > 1) numbers[1] = window.parseFloat(numbers[1])
    if (length > 2) numbers[2] = window.parseFloat(numbers[2])
    return numbers
}

function parseStringArray(str) {
    checkIsSizeArrayString(str)
    const strings = str.trim().toLowerCase().split(/(?:\s*,\s*)|(?:\s+)/g)
    const length = strings.length
    if (length > 0) strings[0] = strings[0]
    if (length > 1) strings[1] = strings[1]
    if (length > 2) strings[2] = strings[2]
    return strings
}

function checkIsNumberArrayString(str) {
    if (!str.match(/^\s*(((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))\s*,){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))))|((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+))\s){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)))))\s*$/g))
        throw new Error(`Attribute must be a comma- or space-separated sequence of up to three numbers, for example "1 2.5 3". Yours was "${str}".`)
}

function checkIsSizeArrayString(str) {
    if (!str.match(/^\s*(((\s*([a-zA-Z]+)\s*,){0,2}(\s*([a-zA-Z]+)))|((\s*([a-zA-Z]+)\s*){1,3}))\s*$/g))
        throw new Error(`Attribute must be a comma- or space-separated sequence of up to three strings, for example "absolute absolute". Yours was "${str}".`)
}

import 'document-register-element'
MotorHTMLNode = document.registerElement('motor-node', MotorHTMLNode)

export {MotorHTMLNode as default}
<|endoftext|>