<commit_before>/**
 * Created by apizzimenti on 5/19/16.
 */

"use strict";

/**
 * @author Anthony Pizzimenti
 *
 * @desc Each animal object contains a sprite and enhanced location information.
 *
 * @param game {object} Phaser game instance.
 * @param scope {object} Angular scope.
 * @param row {number} Initial sprite row.
 * @param col {number} Initial sprite column.
 * @param keys {string[]} Cached Phaser textures or images.
 * @param group {object} Phaser group.
 * @param map {Map} Game's Map object.
 * @param [species=sprite.key] {string} This animal's species.
 * @param [scale=1] {number | number[]} The desired sprite scale factor. Can be of the format x, [x], or [x, y].
 * @param [auto=true] {boolean} Do you want this sprite to be loaded automatically?
 *
 * @property game {object} Phaser game instance.
 * @property scope {object} Angular scope.
 * @property sprite {object} Phaser isoSprite.
 *
 * @property sprite.tile {object} Empty object that, on direction initialization, will be populated.
 * @property sprite.tile.center {sprite} Center tile.
 * @property sprite.tile.top {sprite} Tile to the relative top.
 * @property sprite.tile.left {sprite} Tile to the relative left.
 * @property sprite.tile.bottom {sprite} Tile to the relative bottom.
 * @property sprite.tile.right {sprite} Tile to the relative right.
 * @property sprite.scale {number | number[]} The sprite's scale factor.
 *
 * @property map {Map} This sprite's Map object.
 * @property row {number} Current tile row.
 * @property col {number} Current tile column.
 * @property direction {number} Current direction of travel.
 * @property scanned {boolean} Has this Animal been scanned?
 * @property scan {object} Phaser Signal that, on dispatch, is immediately destroyed.
 * @property species {string} This animal's species.
 * @property auto {boolean} Is this sprite loaded automatically?
 *
 * @class {object} Animal
 * @this Animal
 * @constructor
 *
 * @see Player
 * @see direction
 * @see Globals
 * @see Map
 */

function Animal (game, scope, row, col, keys, group, map, species, scale, auto) {

    this.type = "animal";
    this.species = species || keys[0];

    if (scale) {
        this.scale = Array.isArray(scale) ? scale : [scale];
    } else {
        this.scale = [1];
    }

    if (auto !== undefined) {
        this.auto = auto;
    } else {
        this.auto = true;
    }

    this.game = game;
    this.scope = scope;
    this.keys = keys;
    this.map = map;

    var x = row * map.tileSize,
        y = col * map.tileSize;

    this.sprite = this.game.add.isoSprite(x, y, 0, keys[0], null, group);
    this.sprite.tile = {};
    this.sprite.anchor.set(...Globals.anchor);
    this.game.physics.isoArcade.enable(this.sprite);
    this.sprite.enableBody = true;
    this.sprite.body.collideWorldBounds = true;
    this.sprite.visible = !this.map.fog;
    this.sprite.scale.setTo(...this.scale);

    this.scanned = false;
    this.scan = new Phaser.Signal();
    this.listen();

    direction(this);
}

/**
 * @author Anthony Pizzimenti
 *
 * @desc Determines whether the animal sprite is on a discovered tile or not.
 *
 * @param Player {Player} Sprite which sets discovered tile information.
 *
 * @this Animal
 */

Animal.prototype.isVisible = function (Player) {

    if (Player.auto) {

        var pRow = Player.row,
            pCol = Player.col,
            row = this.sprite.row,
            col = this.sprite.col,
            inRow = row >= pRow - 1 && row <= pRow + 1,
            inCol = col >= pCol - 1 && col <= pCol + 1;

        if (inRow && inCol) {
            this.sprite.visible = true;
        }

        this.sprite.tile.center.discovered ? this.sprite.visible = true : this.sprite.visible = false;
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Changes the target sprite's direction at intervals of 3 + n seconds, where n is a
 * random floating point value such that 0 < n < 3 and is changed at every event firing.
 *
 * @property rand {number} Current random time interval.
 * @property loopedMovement {object} Phaser looped event.
 *
 * @this Animal
 *
 * @see forceDirection
 */

Animal.prototype.timedMovement = function () {

    var dir = 0;

    this.rand = Math.random() * 3;

    this.loopedMovement = this.game.time.events.loop((Phaser.Timer.SECOND * 3) + this.rand, () => {
        dir = Math.floor(Math.random() * 4);
        this.rand = (Math.random() * 3);
        forceDirection(this, dir);
    })
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Listens for events.
 *
 * @this Animal
 */

Animal.prototype.listen = function () {

    this.scan.add(() => {
        this.scope.$emit("scanned", {animal: this});
    });

    this.scope.$on("pathfind", (e, data) => {
        this._pathfind(data.row, data.col);
    })
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Generates a walkable path from the Animal's current location to the target location.
 *
 * @private
 *
 * @param itemRow {number} Target item's row.
 * @param itemCol {number} Target item's column.
 *
 * @this Animal
 */

Animal.prototype._pathfind = function (itemRow, itemCol) {

    var e = new EasyStar.js(),
        row = this.sprite.row,
        col = this.sprite.col,
        dirs = [];

    e.setGrid(this.map.blocked);
    e.setAcceptableTiles([1]);
    e.setIterationsPerCalculation(1000);

    e.findPath(row, col, itemRow, itemCol, (path) => {

        if (!path) {
            console.log("path not found");
        } else {
            this.game.time.events.remove(this.loopedMovement);
            dirs = shrinkPath(path);
            resetSprite(this, true);
            this._followDirection(true, path, dirs);
        }
    });

    e.calculate();
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Recursive method for following a set of path instructions from EasyStar.js.
 *
 * @private
 *
 * @param begin {boolean} Is this the first iteration?
 * @param path {object[]} Array of coordinate objects.
 * @param dirs {number[]} Array of directions.
 *
 * @property sprite.tween {object} Phaser tween object that will be chained to.
 *
 * @this Animal
 */

Animal.prototype._followDirection = function (begin, path, dirs) {

    var dim = this.map.tileSize,
        r = path[0].x,
        c = path[0].y,
        x = r * dim,
        y = c * dim,
        tween,
        end = dirs.length === 0 || path.length === 1;

    if (begin) {
        this.sprite.tween = this.game.add.tween(this.sprite).to({isoX: x, isoY: y}, ...Globals.tween);

        this.sprite.tween.onComplete.add(() => {
            this.sprite.direction = dirs[0];
            path.splice(0, 1);
            dirs.splice(0, 1);

            this._followDirection(false, path, dirs)
        });

    } else if (!begin && !end) {
        tween = this.game.add.tween(this.sprite);
        this.sprite.tween.chain(tween.to({isoX: x, isoY: y}, ...Globals.tween));

        tween.onComplete.add(() => {
            this.sprite.direction = dirs[0];
            path.splice(0, 1);
            dirs.splice(0, 1);

            this._followDirection(false, path, dirs);
        });

    } else if (!begin && end) {
        resetSprite(this, false);
        this.timedMovement();
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Creates the specified number of Animal objects.
 *
 * @param scope {object} Angular scope.
 * @param num {number} Number of Animals to be created.
 * @param game {object} Current game instance.
 * @param keys {string[]} Array of cached Phaser texture IDs to be assigned.
 * @param group {object} Phaser group.
 * @param map {Map} Game's Map object.
 * @param species {string[]} Array of species.
 *
 * @returns {Animal[]} An array of Animal objects.
 *
 * @see Animal
 */

function createAnimals (scope, num, game, keys, group, map, species) {

    var animals = [],
        animal,
        row,
        col,
        dim = (map.tileMap.length - 1);

    for (var i = 0; i < num; i++) {

        row = Math.floor(Math.random() * dim);
        col = Math.floor(Math.random() * dim);

        animal = new Animal(game, scope, row, col, keys, group, map, species[i]);
        animal.id = i;
        animals.push(animal);
    }

    return animals;
}
<commit_msg>Animal<commit_after>/**
 * Created by apizzimenti on 5/19/16.
 */

"use strict";

/**
 * @author Anthony Pizzimenti
 *
 * @desc Each animal object contains a sprite and enhanced location information.
 *
 * @param game {object} Phaser game instance.
 * @param scope {object} Angular scope.
 * @param row {number} Initial sprite row.
 * @param col {number} Initial sprite column.
 * @param keys {string[]} Cached Phaser textures or images.
 * @param group {object} Phaser group.
 * @param map {Map} Game's Map object.
 * @param [species=sprite.key] {string} This animal's species.
 * @param [scale=1] {number | number[]} The desired sprite scale factor. Can be of the format x, [x], or [x, y].
 * @param [auto=true] {boolean} Do you want this sprite to be loaded automatically?
 *
 * @property game {object} Phaser game instance.
 * @property scope {object} Angular scope.
 * @property sprite {object} Phaser isoSprite.
 *
 * @property sprite.tile {object} Empty object that, on direction initialization, will be populated.
 * @property sprite.tile.center {sprite} Center tile.
 * @property sprite.tile.top {sprite} Tile to the relative top.
 * @property sprite.tile.left {sprite} Tile to the relative left.
 * @property sprite.tile.bottom {sprite} Tile to the relative bottom.
 * @property sprite.tile.right {sprite} Tile to the relative right.
 * @property sprite.scale {number | number[]} The sprite's scale factor.
 *
 * @property map {Map} This sprite's Map object.
 * @property row {number} Current tile row.
 * @property col {number} Current tile column.
 * @property direction {number} Current direction of travel.
 * @property scanned {boolean} Has this Animal been scanned?
 * @property scan {object} Phaser Signal that, on dispatch, is immediately destroyed.
 * @property species {string} This animal's species.
 * @property auto {boolean} Is this sprite loaded automatically?
 *
 * @class {object} Animal
 * @this Animal
 * @constructor
 *
 * @see Player
 * @see direction
 * @see Globals
 * @see Map
 */

function Animal (game, scope, row, col, keys, group, map, species, scale, auto) {

    this.type = "animal";
    this.species = species || keys[0];

    if (scale) {
        this.scale = Array.isArray(scale) ? scale : [scale];
    } else {
        this.scale = [1];
    }

    if (auto !== undefined) {
        this.auto = auto;
    } else {
        this.auto = true;
    }

    this.game = game;
    this.scope = scope;
    this.keys = keys;
    this.map = map;

    var x = row * map.tileSize,
        y = col * map.tileSize;

    // add isometric sprite
    this.sprite = this.game.add.isoSprite(x, y, 0, keys[0], null, group);

    // this object will be tied to the sprite; contains all tile information
    this.sprite.tile = {};

    // set anchor, enable physics, enable body
    this.sprite.anchor.set(...Globals.anchor);
    this.game.physics.isoArcade.enable(this.sprite);
    this.sprite.enableBody = true;

    // object will collide world bounds
    this.sprite.body.collideWorldBounds = true;

    // if fog of war is turned on, then sprite is invisible
    this.sprite.visible = !this.map.fog;

    // set object scale
    this.sprite.scale.setTo(...this.scale);

    // initialize sprite scanned value
    this.scanned = false;

    // handler for scanning
    this.scan = new Phaser.Signal();
    this.listen();

    // determine the direction of the sprite
    direction(this);
}

/**
 * @author Anthony Pizzimenti
 *
 * @desc Determines whether the animal sprite is on a discovered tile or not.
 *
 * @param Player {Player} Sprite which sets discovered tile information.
 *
 * @this Animal
 */

Animal.prototype.isVisible = function (Player) {

    if (Player.auto) {

            // current player position
        var pRow = Player.row,
            pCol = Player.col,

            // current animal position
            row = this.sprite.row,
            col = this.sprite.col,

            // is the animal sprite within a one-tile radius of the player?
            inRow = row >= pRow - 1 && row <= pRow + 1,
            inCol = col >= pCol - 1 && col <= pCol + 1;

        if (inRow && inCol) {
            // if yes, make the sprite visible
            this.sprite.visible = true;
        }

        // if this animal sprite returns to a discovered tile, then make it visible again
        this.sprite.tile.center.discovered ? this.sprite.visible = true : this.sprite.visible = false;
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Changes the target sprite's direction at intervals of 3 + n seconds, where n is a
 * random floating point value such that 0 < n < 3 and is changed at every event firing.
 *
 * @property rand {number} Current random time interval.
 * @property loopedMovement {object} Phaser looped event.
 *
 * @this Animal
 *
 * @see forceDirection
 */

Animal.prototype.timedMovement = function () {

    var dir = 0;

    this.rand = Math.random() * 3;

    this.loopedMovement = this.game.time.events.loop((Phaser.Timer.SECOND * 3) + this.rand, () => {
        dir = Math.floor(Math.random() * 4);
        this.rand = (Math.random() * 3);
        forceDirection(this, dir);
    })
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Listens for events.
 *
 * @this Animal
 */

Animal.prototype.listen = function () {

    this.scan.add(() => {
        this.scope.$emit("scanned", {animal: this});
    });

    this.scope.$on("pathfind", (e, data) => {
        this._pathfind(data.row, data.col);
    })
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Generates a walkable path from the Animal's current location to the target location.
 *
 * @private
 *
 * @param itemRow {number} Target item's row.
 * @param itemCol {number} Target item's column.
 *
 * @this Animal
 */

Animal.prototype._pathfind = function (itemRow, itemCol) {

    var e = new EasyStar.js(),
        row = this.sprite.row,
        col = this.sprite.col,
        dirs = [];

    e.setGrid(this.map.blocked);
    e.setAcceptableTiles([1]);
    e.setIterationsPerCalculation(1000);

    e.findPath(row, col, itemRow, itemCol, (path) => {

        if (!path) {
            console.log("path not found");
        } else {
            this.game.time.events.remove(this.loopedMovement);
            dirs = shrinkPath(path);
            resetSprite(this, true);
            this._followDirection(true, path, dirs);
        }
    });

    e.calculate();
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Recursive method for following a set of path instructions from EasyStar.js.
 *
 * @private
 *
 * @param begin {boolean} Is this the first iteration?
 * @param path {object[]} Array of coordinate objects.
 * @param dirs {number[]} Array of directions.
 *
 * @property sprite.tween {object} Phaser tween object that will be chained to.
 *
 * @this Animal
 */

Animal.prototype._followDirection = function (begin, path, dirs) {

        // dimensisons of map tiles
    var dim = this.map.tileSize,

        // get the next direction on the path, then map coordinates for direction
        r = path[0].x,
        c = path[0].y,
        x = r * dim,
        y = c * dim,

        tween,
        end = dirs.length === 0 || path.length === 1;

    if (begin) {

        this.sprite.tween = this.game.add.tween(this.sprite).to({isoX: x, isoY: y}, ...Globals.tween);

        this.sprite.tween.onComplete.add(() => {
            this.sprite.direction = dirs[0];
            path.splice(0, 1);
            dirs.splice(0, 1);

            this._followDirection(false, path, dirs)
        });

    } else if (!begin && !end) {

        tween = this.game.add.tween(this.sprite);
        this.sprite.tween.chain(tween.to({isoX: x, isoY: y}, ...Globals.tween));

        tween.onComplete.add(() => {
            this.sprite.direction = dirs[0];
            path.splice(0, 1);
            dirs.splice(0, 1);

            this._followDirection(false, path, dirs);
        });

    } else if (!begin && end) {

        resetSprite(this, false);
        this.timedMovement();
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Creates the specified number of Animal objects.
 *
 * @param scope {object} Angular scope.
 * @param num {number} Number of Animals to be created.
 * @param game {object} Current game instance.
 * @param keys {string[]} Array of cached Phaser texture IDs to be assigned.
 * @param group {object} Phaser group.
 * @param map {Map} Game's Map object.
 * @param species {string[]} Array of species.
 *
 * @returns {Animal[]} An array of Animal objects.
 *
 * @see Animal
 */

function createAnimals (scope, num, game, keys, group, map, species) {

    var animals = [],
        animal,
        row,
        col,
        dim = (map.tileMap.length - 1);

    for (var i = 0; i < num; i++) {

        row = Math.floor(Math.random() * dim);
        col = Math.floor(Math.random() * dim);

        animal = new Animal(game, scope, row, col, keys, group, map, species[i]);
        animal.id = i;
        animals.push(animal);
    }

    return animals;
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import { ToggleSwitch, ToggleSwitchItem } from '../../../src/toggle-switch';

export default class ToggleSwitchPage extends Component {
  constructor(props) {
    super(props);

    this.state = {
      activeKey: '3',
    };
  }

  handleSelect = (activeKey) => this.setState({ activeKey });

  render() {
    const { activeKey } = this.state;

    return (

Toggle Switch

          Now you can tell your users to flip the switch or switch off.

Basics

Importing the ToggleSwitch components:

{
`// Import with local scoped class names (via CSS Modules)
import { ToggleSwitch, ToggleSwitchItem } from 'react-foundation-components/lib/toggle-switch';

or

// Import with global scoped class names
import {
  ToggleSwitch,
  ToggleSwitchItem,
} from 'react-foundation-components/lib/global/toggle-switch';`
}

          ToggleSwitch component is a component to select from a list of items. The ToggleSwitchItem
          component contained in a ToggleSwitch must set a unique eventKey prop so that
          ToggleSwitch knows what ToggleSwitchItem is currently selected.

          By default, ToggleSwitch is an uncontrolled input initially set to have nothing selected.
          Set the defaultActiveKey prop to some child eventKey value to
          create an uncontrolled input with that child initially selected.

{
`<ToggleSwitch>
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch>
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

{
`<ToggleSwitch defaultActiveKey="2">
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch defaultActiveKey="2">
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

          Use the activeKey and onSelect props to create a controlled
          component.

{
`constructor(props) {
  super(props);

  this.state = { activeKey: '3' };
}

handleSelect = (activeKey) => this.setState({ activeKey });

render() {
  return (
    <ToggleSwitch activeKey={activeKey} onSelect={this.handleSelect}>
      <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
    </ToggleSwitch>
  );
}`
}

        <ToggleSwitch activeKey={activeKey} onSelect={this.handleSelect}>
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

Coloring

          Give a ToggleSwitch additional meaning by setting the color
          and/or paddleColor. Possible values are primary, secondary, success, alert
          and warning.

{
`<ToggleSwitch defaultActiveKey="2" color="success" paddleColor="alert">
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch defaultActiveKey="2" color="success" paddleColor="alert">
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

    );
  }
}
<commit_msg>doc update<commit_after>import React, { Component } from 'react';

import { ToggleSwitch, ToggleSwitchItem } from '../../../src/toggle-switch';

export default class ToggleSwitchPage extends Component {
  constructor(props) {
    super(props);

    this.state = {
      activeKey: '3',
    };
  }

  handleSelect = (activeKey) => this.setState({ activeKey });

  render() {
    const { activeKey } = this.state;

    return (

Toggle Switch

          Toggle between a set of options.

Basics

Importing the ToggleSwitch components:

{
`// Import with local scoped class names (via CSS Modules)
import { ToggleSwitch, ToggleSwitchItem } from 'react-foundation-components/lib/toggle-switch';

or

// Import with global scoped class names
import {
  ToggleSwitch,
  ToggleSwitchItem,
} from 'react-foundation-components/lib/global/toggle-switch';`
}

          ToggleSwitch component is a component to toggle between a list of items. It is based
          on css-toggle-switch.
          The ToggleSwitchItem components contained in a ToggleSwitch must set a
          unique eventKey prop so that ToggleSwitch knows what ToggleSwitchItem is
          currently selected.

          By default, ToggleSwitch is an uncontrolled input initially set to have nothing selected.
          Set the defaultActiveKey prop to some child eventKey value to
          create an uncontrolled input with that child initially selected.

{
`<ToggleSwitch>
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch>
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

{
`<ToggleSwitch defaultActiveKey="2">
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch defaultActiveKey="2">
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

          Use the activeKey and onSelect props to create a controlled
          component.

{
`constructor(props) {
  super(props);

  this.state = { activeKey: '3' };
}

handleSelect = (activeKey) => this.setState({ activeKey });

render() {
  return (
    <ToggleSwitch activeKey={activeKey} onSelect={this.handleSelect}>
      <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
      <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
    </ToggleSwitch>
  );
}`
}

        <ToggleSwitch activeKey={activeKey} onSelect={this.handleSelect}>
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

Coloring

          Give a ToggleSwitch additional meaning by setting the color
          and/or paddleColor. Possible values are primary, secondary, success, alert
          and warning.

{
`<ToggleSwitch defaultActiveKey="2" color="success" paddleColor="alert">
  <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
  <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
</ToggleSwitch>`
}

        <ToggleSwitch defaultActiveKey="2" color="success" paddleColor="alert">
          <ToggleSwitchItem eventKey="1">Hour</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="2">Day</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="3">Week</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="4">Month</ToggleSwitchItem>
          <ToggleSwitchItem eventKey="5">Year</ToggleSwitchItem>
        </ToggleSwitch>

    );
  }
}
<|endoftext|>


<commit_before>/**
 * @module  lang

 * - language enhancement for javascript
 * - adapter for mootools
 */

;(function(K){

// bind the this pointer of a function
function bind_method(fn, bind){
	return function(){
		return fn.apply(bind, arguments);
	}
};

/**
 * transform functions that have the signature fn(key, value)
 * to
 * functions that could accept object arguments
 * @adapter
 */
function batch_setter(fn){
	return fn.overloadSetter();
};

/**
 * memoize static result of a complicated method to save time
 */
function memoizeMethod(fn){
	var stack = {};

	return function(){
		var arg = array_join.call(arguments, '~^_^~');

		return (arg in stack) ? stack[arg] : (stack[arg] = fn.apply(null, arguments));
	}
};

/**
 * transform constructor functions to functions that could change a method of a instance or singleton
 */
function overload_for_instance_method(fn){
	var self = this;

	return function(methodname, instance){
		var arg = arguments;

		return K.isFunction(methodname) ?
			fn.apply(self, arg)
		:	instance[methodname] = fn.call(instance, instance[methodname], instance);
	};
};

function toQueryString(obj, splitter){
	var key, ret = [];

	for(key in obj){
		ret.push(key + '=' + obj[key]);
	}

	return ret.join(splitter || '&');
};

var	array_join = Array.prototype.join,

	/**
	 * a simple and faster typeOf method, and an adapter for mootools
	 * @adapter
	 */
	type = function(){

		/**
		 * @param useOrigin {Boolean} use origin method (of mootools)
		 */
		function _type(obj, useOrigin){
			return !useOrigin && type_map[ toString.call(obj) ] || adapter(obj);
		};

		var type_map = {},
			_K = K,

			// adapter for mootools 1.3
			adapter = typeOf,
			toString = Object.prototype.toString;

		'Boolean Number String Function Array Date RegExp Object'.split(' ').each(function(name){
			var nl = name.toLowerCase();

			type_map[ '[object ' + name + ']' ] = nl;

			_K['is' + name] = function(o){
				return _type(o) === nl;
			}
		});

		return _type;
	}();

K.mix(K, {

	/**
	 * @adapter
	 * ----------------------------------------------------------------------------------------------- /
	 * encapsulation for mootools.core
	 */

	/**
	 * Never use KM._type to test for a certain type in your javascript for business,
	 * since the returned string may be subject to change in a future version

	 * use KM.isXXX instead
	 */
	_type: type,

	merge: Object.merge, // Object.merge

	random: Number.random, // Number.random

	now: function(){
		return + new Date;
	},

	lambda: Function.from,

	/**
	 * language enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * bind 'this' pointer for a function
	 * or bind a method for a constructor
	 * @usage:
	 *
	   1. KM.bind(myFunction, {a:1});
	   2. KM.bind('method', {a:1, method: function(){ alert(this.a) }});

	 *
	 *
	 */
	bind: function(fn, bind){
		return K.isFunction(fn) ?
			bind_method(fn, bind)
		:
			(bind[fn] = bind_method(bind[fn], bind));
	},

	/**
	 * method to encapsulate the delayed function
	 */
	delay: function(fn, delay, isInterval){
		var timer;

		return {
			start: function(){
				this.cancel();
				return timer = isInterval ? setInterval(fn, delay) : setTimeout(fn, delay);
			},
			cancel: function(){
				isInterval ? clearInterval(timer) : clearTimeout(timer);
				return this;
			}
		}
	},

	/**
	 *
	 */
	each: function(obj, fn, stop){

	},

	makeArray: function(obj){
		return K.isArray(obj) ? obj : [obj];
	},

	toQueryString: function(obj, splitter){
		return K.isObject(obj) ? toQueryString(obj, splitter) : obj;
	},

	/**
	 * OOP Enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * overload a setter function or a setter method of a instance
	 */
	_overloadSetter: overload_for_instance_method( batch_setter ),

	/**
	 *
	 */
	_overloadInstanceMethod: overload_for_instance_method,

	/**
	 * lazy execute the initialization method before the real method called
	 *
	 * @example
	 * if KM.Overlay::show is the public api to show the overlay
	 * but it has a initialization method, which we want to be called just before the overlay shows, not the very moment when the instance of KM.Overlay created,
	 * so,
	 * we apply:
	 * initialization method 	-> KM.Overlay::show
	 * real show method 		-> KM.Overlay::_show
	 * and then:
	 *
	 * @usage

		 	KM._lazyInit('show', '_show', KM.Overlay.prototype);

	 */
	_lazyInit: function(init_method_name, real_method_name, belong){
		var init = belong[init_method_name],
			real = belong[real_method_name];

		belong[init_method_name] = function(){
			init.call(this);
			real.apply(this, arguments);

			belong[init_method_name] = real;
		};

		delete belong[real_method_name];
	},

	/**
	 @usage

			funcion myMethod(string){....}
			var memoizedMyMethod = KM._memoize(myMethod);

	 */
	_memoize: overload_for_instance_method( memoizeMethod )

});

})(KM);

/**
 * TODO:

 * change log:
 * 2011-06-09  Kael:
 * - 增加 KM.each
 * 2011-04-19  Kael:
 * - 增加 lazyInit 方法，修正 overloadInstanceMethod 的一个bug
 * - 增加 类型判断方法，将 KM._type 从主要api中移除
 * 2011-04-15  Kael:
 * - 增加 overloadSetter的adapter, 为普通函数与instance的方法，重构 overload 和 bind 的实现
 * 2011-04-1   Kael Zhang: 去除lang中的domready方法，增加KM.bind
 * 2010-12-31  Kael Zhang:
 * - 迁移domready方法由mt.js到lang.js，修改部分实现。
 * - 迁移data和delay方法至此
 */<commit_msg>add KM.isPlainObject method<commit_after>/**
 * @module  lang

 * - language enhancement for javascript
 * - adapter for mootools
 */

;(function(K){

// bind the this pointer of a function
function bind_method(fn, bind){
	return function(){
		return fn.apply(bind, arguments);
	}
};

/**
 * transform functions that have the signature fn(key, value)
 * to
 * functions that could accept object arguments
 * @adapter
 */
function batch_setter(fn){
	return fn.overloadSetter();
};

/**
 * memoize static result of a complicated method to save time
 */
function memoizeMethod(fn){
	var stack = {};

	return function(){
		var arg = array_join.call(arguments, '~^_^~');

		return (arg in stack) ? stack[arg] : (stack[arg] = fn.apply(null, arguments));
	}
};

/**
 * transform constructor functions to functions that could change a method of a instance or singleton
 */
function overload_for_instance_method(fn){
	var self = this;

	return function(methodname, instance){
		var arg = arguments;

		return K.isFunction(methodname) ?
			fn.apply(self, arg)
		:	instance[methodname] = fn.call(instance, instance[methodname], instance);
	};
};

function toQueryString(obj, splitter){
	var key, ret = [];

	for(key in obj){
		ret.push(key + '=' + obj[key]);
	}

	return ret.join(splitter || '&');
};

var	array_join = Array.prototype.join,

	/**
	 * a simple and faster typeOf method, and an adapter for mootools
	 * @adapter
	 */
	type = function(){

		/**
		 * @param useOrigin {Boolean} use origin method (of mootools)
		 */
		function _type(obj, useOrigin){
			return !useOrigin && type_map[ toString.call(obj) ] || adapter(obj);
		};

		var type_map = {},
			_K = K,

			// adapter for mootools 1.3
			adapter = typeOf,
			toString = Object.prototype.toString;

		'Boolean Number String Function Array Date RegExp Object'.split(' ').each(function(name){
			var nl = name.toLowerCase();

			type_map[ '[object ' + name + ']' ] = nl;

			_K['is' + name] = function(o){
				return _type(o) === nl;
			}
		});

		return _type;
	}();

K.mix(K, {

	/**
	 * @adapter
	 * ----------------------------------------------------------------------------------------------- /
	 * encapsulation for mootools.core
	 */

	/**
	 * Never use KM._type to test for a certain type in your javascript for business,
	 * since the returned string may be subject to change in a future version

	 * use KM.isXXX instead
	 */
	_type: type,

	isPlainObject: function(obj){
		return obj && K.isObject(obj) && 'isPrototypeOf' in obj;
	},

	merge: Object.merge, // Object.merge

	random: Number.random, // Number.random

	now: function(){
		return + new Date;
	},

	lambda: Function.from,

	/**
	 * language enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * bind 'this' pointer for a function
	 * or bind a method for a constructor
	 * @usage:
	 *
	   1. KM.bind(myFunction, {a:1});
	   2. KM.bind('method', {a:1, method: function(){ alert(this.a) }});

	 *
	 *
	 */
	bind: function(fn, bind){
		return K.isFunction(fn) ?
			bind_method(fn, bind)
		:
			(bind[fn] = bind_method(bind[fn], bind));
	},

	/**
	 * method to encapsulate the delayed function
	 */
	delay: function(fn, delay, isInterval){
		var timer;

		return {
			start: function(){
				this.cancel();
				return timer = isInterval ? setInterval(fn, delay) : setTimeout(fn, delay);
			},
			cancel: function(){
				isInterval ? clearInterval(timer) : clearTimeout(timer);
				return this;
			}
		}
	},

	/**
	 *
	 */
	each: function(obj, fn, stop){

	},

	makeArray: function(obj){
		return K.isArray(obj) ? obj : [obj];
	},

	toQueryString: function(obj, splitter){
		return K.isObject(obj) ? toQueryString(obj, splitter) : obj;
	},

	/**
	 * OOP Enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * overload a setter function or a setter method of a instance
	 */
	_overloadSetter: overload_for_instance_method( batch_setter ),

	/**
	 *
	 */
	_overloadInstanceMethod: overload_for_instance_method,

	/**
	 * lazy execute the initialization method before the real method called
	 *
	 * @example
	 * if KM.Overlay::show is the public api to show the overlay
	 * but it has a initialization method, which we want to be called just before the overlay shows, not the very moment when the instance of KM.Overlay created,
	 * so,
	 * we apply:
	 * initialization method 	-> KM.Overlay::show
	 * real show method 		-> KM.Overlay::_show
	 * and then:
	 *
	 * @usage

		 	KM._lazyInit('show', '_show', KM.Overlay.prototype);

	 */
	_lazyInit: function(init_method_name, real_method_name, belong){
		var init = belong[init_method_name],
			real = belong[real_method_name];

		belong[init_method_name] = function(){
			init.call(this);
			real.apply(this, arguments);

			belong[init_method_name] = real;
		};

		delete belong[real_method_name];
	},

	/**
	 @usage

			funcion myMethod(string){....}
			var memoizedMyMethod = KM._memoize(myMethod);

	 */
	_memoize: overload_for_instance_method( memoizeMethod )

});

})(KM);

/**
 * TODO:

 * change log:
 * 2011-06-09  Kael:
 * - 增加 KM.each
 * 2011-04-19  Kael:
 * - 增加 lazyInit 方法，修正 overloadInstanceMethod 的一个bug
 * - 增加 类型判断方法，将 KM._type 从主要api中移除
 * 2011-04-15  Kael:
 * - 增加 overloadSetter的adapter, 为普通函数与instance的方法，重构 overload 和 bind 的实现
 * 2011-04-1   Kael Zhang: 去除lang中的domready方法，增加KM.bind
 * 2010-12-31  Kael Zhang:
 * - 迁移domready方法由mt.js到lang.js，修改部分实现。
 * - 迁移data和delay方法至此
 */<|endoftext|>


<commit_before>const moment = require('moment');

const getHumanDate = dateAsText => {
  const dateToConvert = moment(dateAsText).locale('ru');
  const format = dateToConvert.isBefore(moment().startOf('year'))
    ? 'D MMM YYYY'
    : 'D MMM';

  return dateToConvert.format(format);
};
const getMeetupPath = dateAsText => moment(dateAsText).format('YYYY-MM-DD');
// LOL
module.exports = {
  getHumanDate,
  getMeetupPath,
};
<commit_msg>Update date.js<commit_after>const moment = require('moment');

const getHumanDate = dateAsText => {
  const dateToConvert = moment(dateAsText).locale('ru');
  const format = dateToConvert.isBefore(moment().startOf('year'))
    ? 'D MMM YYYY'
    : 'D MMM';

  return dateToConvert.format(format);
};
const getMeetupPath = dateAsText => moment(dateAsText).format('YYYY-MM-DD');
// LOL123
module.exports = {
  getHumanDate,
  getMeetupPath,
};
<|endoftext|>


<commit_before><commit_msg>v2/handler: unify call stat emission<commit_after><|endoftext|>


<commit_before>/**
 * @license MIT License
 *
 * Copyright (c) 2016 Rick Lancee @ricklancee
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');
    this.pages = null;
    this.canvasEl = null;
    this.canvas = null;

    if (!this.data) {
      return;
    }

    // options
    this._fitscreen = (this.hasAttribute('fitscreen') && this.getAttribute('fitscreen') !== 'false')
      || false;
    this._opacity = 0.025;
    this._fitPanels = false;

    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;
    this.pageOffsetY = 0;

    // Init
    this._createCanvas();

    // TODO: Show spinner?

    this._addEventListeners();

    // When data is loaded save it
    this.loaded = new Promise(resolve => {
      this._loadData().then(data => {
        this.data = data;
        this.pages = this.data.pages;

        this._createPagination();

        const hashPagination = this._getPaginationFromHash();
        if (hashPagination) {
          this.currentPageIndex = hashPagination[0];
          this.currentPanelIndex = hashPagination[1];
        }

        this._setPage(this.currentPageIndex)
          .then(_ => {
            this._recalcPage();
            this._drawPanels(this.currentPanelIndex);
            this._setPaginationHash();
            this._setActivePagination();
            this._positionView();

            resolve();
          });
      });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    Array.from(this.querySelectorAll('.pagination a')).forEach(link => link.classList.remove('active'));
    Array.from(this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]'))
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _recalcPage() {
    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    const BCR = this.canvasEl.getBoundingClientRect();
    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen(on = true) {
    if (on) {
      this.canvasEl.style.height = this.screenHeight + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _loadImage(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  previousPage() {
    if (this.currentPageIndex == 0) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const panel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];

    if (!panel) {
      return;
    }

    this._recalcPage();

    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (panel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (panel.x * this.pageDimensions.width / 100) + offsetX;

    const panelHeight = panel.height * this.pageDimensions.height / 100;

    if (this._fitscreen) {
      this.fitscreen(true);
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen(true);
    } else if (this._fitPanels && panelHeight > this.screenHeight) {
      console.log('auto resize');
      const desiredHeight = this.screenHeight;
      const resizeTo = this.pageDimensions.height * ((this.screenHeight - this.pageDimensions.top) / panelHeight);
      this.canvasEl.style.height = resizeTo + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }

    this._recalcPage();
    window.scrollTo(panelX, panelY);
  }
}
<commit_msg>add preloading of the next page<commit_after>/**
 * @license MIT License
 *
 * Copyright (c) 2016 Rick Lancee @ricklancee
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');
    this.pages = null;
    this.canvasEl = null;
    this.canvas = null;

    if (!this.data) {
      return;
    }

    // options
    this._fitscreen = (this.hasAttribute('fitscreen') && this.getAttribute('fitscreen') !== 'false')
      || false;
    this._opacity = 0.025;
    this._fitPanels = false;
    this._preloadPages = true;

    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;
    this.pageOffsetY = 0;

    // Init
    this._createCanvas();

    // TODO: Show spinner?

    this._addEventListeners();

    // When data is loaded save it
    this.loaded = new Promise(resolve => {
      this._loadData().then(data => {
        this.data = data;
        this.pages = this.data.pages;

        this._createPagination();

        const hashPagination = this._getPaginationFromHash();
        if (hashPagination) {
          this.currentPageIndex = hashPagination[0];
          this.currentPanelIndex = hashPagination[1];
        }

        this._setPage(this.currentPageIndex)
          .then(_ => {
            this._recalcPage();
            this._drawPanels(this.currentPanelIndex);
            this._setPaginationHash();
            this._setActivePagination();
            this._positionView();
            if (this._preloadPages) {
              this._preloadNextPage();
            }
            resolve();
          });
      });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    Array.from(this.querySelectorAll('.pagination a')).forEach(link => link.classList.remove('active'));
    Array.from(this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]'))
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _recalcPage() {
    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    const BCR = this.canvasEl.getBoundingClientRect();
    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen(on = true) {
    if (on) {
      this.canvasEl.style.height = this.screenHeight + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _loadImage(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _preloadNextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return;
    }
    const nextPage = this.currentPageIndex + 1;
    const nextPageImage = this.pages[nextPage].image;

    this._loadImage(nextPageImage).then(_ => {
      console.log('preloaded page: '+ (nextPage + 1));
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
      if (this._preloadPages) {
        this._preloadNextPage();
      }
    });
  }

  previousPage() {
    if (this.currentPageIndex == 0) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const panel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];

    if (!panel) {
      return;
    }

    this._recalcPage();

    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (panel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (panel.x * this.pageDimensions.width / 100) + offsetX;

    const panelHeight = panel.height * this.pageDimensions.height / 100;

    if (this._fitscreen) {
      this.fitscreen(true);
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen(true);
    } else if (this._fitPanels && panelHeight > this.screenHeight) {
      console.log('auto resize');
      const desiredHeight = this.screenHeight;
      const resizeTo = this.pageDimensions.height * ((this.screenHeight - this.pageDimensions.top) / panelHeight);
      this.canvasEl.style.height = resizeTo + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }

    this._recalcPage();
    window.scrollTo(panelX, panelY);
  }
}
<|endoftext|>


<commit_before>//const devMode = process.env.NODE_ENV !== 'production';

export default {
  UPLOAD_FLYER_PRESET: 'event-flyer-ex',
  CLOUDINARY_API_URI: 'https://api.cloudinary.com/v1_1/toakeeassets/image',
};
<commit_msg>Change preset<commit_after>//const devMode = process.env.NODE_ENV !== 'production';

export default {
  UPLOAD_FLYER_PRESET: 'event-flyer',
  CLOUDINARY_API_URI: 'https://api.cloudinary.com/v1_1/toakeeassets/image',
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DOM = {
  /** Instantiate FOAM Objects in a document. **/
  init: function(X) {
    if ( ! X.document.FOAM_OBJECTS ) X.document.FOAM_OBJECTS = {};

    var fs = X.document.querySelectorAll('foam');
    var models = [];
    for ( var i = 0 ; i < fs.length ; i++ ) {
      var e = fs[i];
      X.lookup(e.getAttribute('view'));
      X.lookup(e.getAttribute('model'));
      if ( e.getAttribute('view') ) models.push(X.arequire(e.getAttribute('view')));
      if ( e.getAttribute('model') ) models.push(X.arequire(e.getAttribute('model')));
    }
    for ( var key in USED_MODELS ) {
      models.push(X.arequire(key));
    }

    atime('DOMInit', aseq(apar.apply(null, models), function(ret) {
      for ( var i = 0 ; i < fs.length ; i++ ) {
        var e = fs[i];
        // Check that the node is still in the DOM
        // (It won't be if it was the child of another FOAM tag.)
        var node = e;
        var body = X.document.body;
        while ( node && node !== body ) node = node.parentNode;
        if ( node ) {
          this.initElement(e, X, X.document);
          e.innerHTML = '';
        }
        ret();
      }
    }.bind(this)))();
  },

  initElementChildren: function(e, X) {
    var a = [];

    for ( var i = 0 ; i < e.children.length ; i++ ) {
      var c = e.children[i];

      if ( c.tagName === 'FOAM' ) {
        a.push(DOM.initElement(c, X));
      }
    }

    return a;
  },

  /**
   * opt_document -- if supplied the object's view will be added to the document.
   **/
  initElement: function(e, X, opt_document) {
    X.arequire('foam.ui.FoamTagView')(function(t) {
      foam.ui.FoamTagView.create({ element: e }, X);
    });
  },

  setClass: function(e, className, opt_enabled) {
    var oldClassName = e.className || '';
    var enabled = opt_enabled === undefined ? true : opt_enabled;
    e.className = oldClassName.replace(' ' + className, '').replace(className, '');
    if ( enabled ) e.className = e.className + ' ' + className;
  }
};

window &&
  window.addEventListener &&
  window.addEventListener('load', function() { DOM.init(X); }, false);

// TODO: document and make non-global
/** Convert a style size to an Int.  Ex. '10px' to 10. **/
function toNum(p) { return p.replace ? parseInt(p.replace('px','')) : p; };

// TODO(kgr): replace all instances of DomValue with new modelled DOMValue.
var DomValue = {
  DEFAULT_EVENT:    'change',
  DEFAULT_PROPERTY: 'value',

  __isValue__: true,

  create: function(element, opt_event, opt_property) {
    if ( ! element ) {
      throw "Missing Element in DomValue";
    }

    return {
      __proto__: this,
      element:   element,
      event:     opt_event    || this.DEFAULT_EVENT,
      property:  opt_property || this.DEFAULT_PROPERTY };
  },

  setElement: function ( element ) { this.element = element; },

  get: function() { return this.element[this.property]; },

  set: function(value) {
    if ( this.element[this.property] !== value )
      this.element[this.property] = value;
  },

  addListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.addEventListener(this.event, listener, false);
    } catch (x) {
    }
  },

  removeListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.removeEventListener(this.event, listener, false);
    } catch (x) {
      // could be that the element has been removed
    }
  },

  toString: function() {
    return "DomValue(" + this.event + ", " + this.property + ")";
  }
};

CLASS({
  name: 'DOMValue',

  constants: {
    __isValue__: true
  },

  properties: [
    {
      name: 'element',
      required: true
    },
    {
      name: 'property',
      defaultValue: 'value'
    },
    {
      name: 'event',
      defaultValue: 'change'
    },
    {
      name: 'value',
      postSet: function(_, value) { this.element[this.property] = value; }
    },
    {
      name: 'firstListener_',
      defaultValue: true
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.value = this.element[this.property];
    },

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

    set: function(value) { this.value = value; },

    addListener: function(listener) {
      if ( this.firstListener_ ) {
        if ( this.event ) {
          this.element.addEventListener(
            this.event,
            function() { debugger; /* TODO */ },
            false);
        }

        this.firstListener_ = false;
      }
      this.value$.addListener(listener);
    },

    removeListener: function(listener) {
      this.value$.removeListener(listener);
    },

    toString: function() {
      return 'DOMValue(' + this.event + ', ' + this.property + ')';
    }
  }
});

// U2 Support

var __element_map__ = {
  INPUT:    'foam.u2.Input',
  TEXTAREA: 'foam.u2.TextArea',
  SELECT:   'foam.u2.Select'
};

function elementForName(nodeName) {
  nodeName = nodeName ? nodeName : 'div' ;
  var modelName = this.__element_map__[nodeName.toUpperCase()];
  if ( modelName ) return this.X.lookup(modelName).create(null, this.Y || X);

  var i = nodeName.indexOf(':');
  if ( i != -1 ) {
    return this.elementForFeature(nodeName.substring(0, i), nodeName.substring(i+1));
  }

  return null;
}

function elementForFeature(objName, featureName) {
  var x = this.Y || this;
  if ( objName ) x = x.sub({data: this[objName]});
  return this[objName || 'data'].model_.getFeature(featureName).toE(x);
}

function registerE(name, model) {
  var m = { __proto__: this.__element_map__ };
  m[name.toUpperCase()] = model;
  this.set('__element_map__', m);
  return this;
}

// Utility function for creating U2 elements in a short format. Expects to be
// run on a conteXt object.
function E(opt_nodeName) {
  if (this === X || this === window) {
    console.log('Deprecated global E() call', new Error());
  }
  var e = this.elementForName && this.elementForName(opt_nodeName);

  if ( ! e ) {
    e = this.lookup('foam.u2.Element').create(null, this);
    if ( opt_nodeName ) e.nodeName = opt_nodeName;
  }

  return e;
}

function start(opt_nodeName) {
  return this.E(opt_nodeName);
}
<commit_msg>Better error reporting in view.elementForName().<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DOM = {
  /** Instantiate FOAM Objects in a document. **/
  init: function(X) {
    if ( ! X.document.FOAM_OBJECTS ) X.document.FOAM_OBJECTS = {};

    var fs = X.document.querySelectorAll('foam');
    var models = [];
    for ( var i = 0 ; i < fs.length ; i++ ) {
      var e = fs[i];
      X.lookup(e.getAttribute('view'));
      X.lookup(e.getAttribute('model'));
      if ( e.getAttribute('view') ) models.push(X.arequire(e.getAttribute('view')));
      if ( e.getAttribute('model') ) models.push(X.arequire(e.getAttribute('model')));
    }
    for ( var key in USED_MODELS ) {
      models.push(X.arequire(key));
    }

    atime('DOMInit', aseq(apar.apply(null, models), function(ret) {
      for ( var i = 0 ; i < fs.length ; i++ ) {
        var e = fs[i];
        // Check that the node is still in the DOM
        // (It won't be if it was the child of another FOAM tag.)
        var node = e;
        var body = X.document.body;
        while ( node && node !== body ) node = node.parentNode;
        if ( node ) {
          this.initElement(e, X, X.document);
          e.innerHTML = '';
        }
        ret();
      }
    }.bind(this)))();
  },

  initElementChildren: function(e, X) {
    var a = [];

    for ( var i = 0 ; i < e.children.length ; i++ ) {
      var c = e.children[i];

      if ( c.tagName === 'FOAM' ) {
        a.push(DOM.initElement(c, X));
      }
    }

    return a;
  },

  /**
   * opt_document -- if supplied the object's view will be added to the document.
   **/
  initElement: function(e, X, opt_document) {
    X.arequire('foam.ui.FoamTagView')(function(t) {
      foam.ui.FoamTagView.create({ element: e }, X);
    });
  },

  setClass: function(e, className, opt_enabled) {
    var oldClassName = e.className || '';
    var enabled = opt_enabled === undefined ? true : opt_enabled;
    e.className = oldClassName.replace(' ' + className, '').replace(className, '');
    if ( enabled ) e.className = e.className + ' ' + className;
  }
};

window &&
  window.addEventListener &&
  window.addEventListener('load', function() { DOM.init(X); }, false);

// TODO: document and make non-global
/** Convert a style size to an Int.  Ex. '10px' to 10. **/
function toNum(p) { return p.replace ? parseInt(p.replace('px','')) : p; };

// TODO(kgr): replace all instances of DomValue with new modelled DOMValue.
var DomValue = {
  DEFAULT_EVENT:    'change',
  DEFAULT_PROPERTY: 'value',

  __isValue__: true,

  create: function(element, opt_event, opt_property) {
    if ( ! element ) {
      throw "Missing Element in DomValue";
    }

    return {
      __proto__: this,
      element:   element,
      event:     opt_event    || this.DEFAULT_EVENT,
      property:  opt_property || this.DEFAULT_PROPERTY };
  },

  setElement: function ( element ) { this.element = element; },

  get: function() { return this.element[this.property]; },

  set: function(value) {
    if ( this.element[this.property] !== value )
      this.element[this.property] = value;
  },

  addListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.addEventListener(this.event, listener, false);
    } catch (x) {
    }
  },

  removeListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.removeEventListener(this.event, listener, false);
    } catch (x) {
      // could be that the element has been removed
    }
  },

  toString: function() {
    return "DomValue(" + this.event + ", " + this.property + ")";
  }
};

CLASS({
  name: 'DOMValue',

  constants: {
    __isValue__: true
  },

  properties: [
    {
      name: 'element',
      required: true
    },
    {
      name: 'property',
      defaultValue: 'value'
    },
    {
      name: 'event',
      defaultValue: 'change'
    },
    {
      name: 'value',
      postSet: function(_, value) { this.element[this.property] = value; }
    },
    {
      name: 'firstListener_',
      defaultValue: true
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.value = this.element[this.property];
    },

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

    set: function(value) { this.value = value; },

    addListener: function(listener) {
      if ( this.firstListener_ ) {
        if ( this.event ) {
          this.element.addEventListener(
            this.event,
            function() { debugger; /* TODO */ },
            false);
        }

        this.firstListener_ = false;
      }
      this.value$.addListener(listener);
    },

    removeListener: function(listener) {
      this.value$.removeListener(listener);
    },

    toString: function() {
      return 'DOMValue(' + this.event + ', ' + this.property + ')';
    }
  }
});

// U2 Support

var __element_map__ = {
  INPUT:    'foam.u2.Input',
  TEXTAREA: 'foam.u2.TextArea',
  SELECT:   'foam.u2.Select'
};

function elementForName(nodeName) {
  nodeName = nodeName ? nodeName : 'div' ;
  var modelName = this.__element_map__[nodeName.toUpperCase()];
  if ( modelName ) {
    var model = this.X.lookup(modelName);
    console.assert(model, 'Missing Model, Add "' + modelName + '" to your requires: block.');
    return model.create(null, this.Y || X);
  }

  var i = nodeName.indexOf(':');
  if ( i != -1 ) {
    return this.elementForFeature(nodeName.substring(0, i), nodeName.substring(i+1));
  }

  return null;
}

function elementForFeature(objName, featureName) {
  var x = this.Y || this;
  if ( objName ) x = x.sub({data: this[objName]});
  return this[objName || 'data'].model_.getFeature(featureName).toE(x);
}

function registerE(name, model) {
  var m = { __proto__: this.__element_map__ };
  m[name.toUpperCase()] = model;
  this.set('__element_map__', m);
  return this;
}

// Utility function for creating U2 elements in a short format. Expects to be
// run on a conteXt object.
function E(opt_nodeName) {
  if (this === X || this === window) {
    console.log('Deprecated global E() call', new Error());
  }
  var e = this.elementForName && this.elementForName(opt_nodeName);

  if ( ! e ) {
    e = this.lookup('foam.u2.Element').create(null, this);
    if ( opt_nodeName ) e.nodeName = opt_nodeName;
  }

  return e;
}

function start(opt_nodeName) {
  return this.E(opt_nodeName);
}
<|endoftext|>