<commit_before>Molecule.module('Molecule.Sprite', function (require, p) {

    var Animation = require('Molecule.Animation');

	// Sprite var.
    function Sprite(_name, _width, _height) {
        this.name = _name;
        this.image = null;
        this.position = {x: 0, y: 0, absolute: {x: 0, y: 0}};
        this.rotation = 0;
        this.move = {x: 0, y: 0};
        this.flip = {x: false, y: false, offset: {x: 0, y: 0}, f: {x: 0, y: 0}};
        this.anchor = {x: 0, y: 0};
        this.visible = true;
        this.alpha = 1;
        this.frame = {width: _width, height: _height, offset: {width: 0, height: 0}};
        this.animation = new Animation(this.frame.width, this.frame.height);
        this.size = {width: 0, height: 0};
        this.collides = {sprite: true, map: true};
        this.scrollable = true;
        this.collidable = true;
        this.platform = false;
        this.bounciness = false;
        this.acceleration = {x: 0, y: 0};
        this.speed = {x: 0, y: 0, t: {x: 0, y: 0}, max: {x: 100, y: 100}, min: {x: 0, y: 0}, check: {x: false, y: false}, gravity: {x: 0, y: 0}};
        this.affects = {physics: {gravity: true, friction: true}};
        this.collision = {map: {up: false, down: false, left: false, right: false, tile: null}, sprite: {up: false, down: false, left: false, right: false, id: null}, check: {map: {up: true, down: true, left: true, right: true}}};
        this.scroll = {offset: {x: 0, y: 0}};
        this.overlap = false;
        this.kill = false;
        this.game = null;

        return this;
    };

    Sprite.prototype.getAnimation = function () {
        this.size = {width: this.image.width, height: this.image.height};
        this.frame.width = this.frame.width || this.size.width;
        this.frame.height = this.frame.height || this.size.height;
        this.animation.sliceFrames(this.image.width, this.image.height, this.frame.width, this.frame.height);
    };

	// Sprite prototype Method flipUpdate
    Sprite.prototype.flipUpdate = function () {
        this.flip.offset.x = this.flip.x ? -this.frame.width : 0;
        this.flip.offset.y = this.flip.y ? -this.frame.height : 0;
        this.flip.f.x = this.flip.x ? -1 : 1;
        this.flip.f.y = this.flip.y ? -1 : 1;
    };

	// Sprite prototype Method update
    Sprite.prototype.update = function () {
        this.position.x += this.move.x;
        this.position.y += this.move.y;
        this.position.x = parseFloat(this.position.x.toFixed(3));
        this.position.y = parseFloat(this.position.y.toFixed(3));
        this.position.absolute.x = this.position.x;
        this.position.absolute.y = this.position.y;
        if (this.game.map.getMainLayer() !== -1) {
            this.position.absolute.x += Math.abs(this.game.map.json.layers[this.game.map.getMainLayer()].x);
            this.position.absolute.y += Math.abs(this.game.map.json.layers[this.game.map.getMainLayer()].y);
        }
        this.size.width = this.frame.width - this.frame.offset.width;
        this.size.height = this.frame.height - this.frame.offset.height;
    };

	// Sprite prototype Method resetMove
    Sprite.prototype.resetMove = function () {
        this.move = {x: 0, y: 0};
    };

	// Sprite prototype Method reset acceleration
    Sprite.prototype.resetAcceleration = function () {
        this.acceleration = {x: 0, y: 0};
    };

	// Sprite prototype Method draw
    Sprite.prototype.draw = function (_overlap) {
        if (this.overlap === _overlap) {
            this.game.context.save();
            this.game.context.globalAlpha = this.alpha;
            this.game.context.scale(1 * this.flip.f.x, 1 * this.flip.f.y);
            this.game.context.translate(Math.round((this.position.x * this.flip.f.x) + this.flip.offset.x), Math.round((this.position.y * this.flip.f.y) + this.flip.offset.y));
            this.game.context.rotate(this.rotation * (Math.PI / 180));
            this.game.context.translate(Math.round(-this.anchor.x * this.flip.f.x), Math.round(-this.anchor.y * this.flip.f.y));
            this.game.context.drawImage(this.image, this.animation.frame[this.animation.id[this.animation.current.animation].frame[this.animation.current.frame]].x, this.animation.frame[this.animation.id[this.animation.current.animation].frame[this.animation.current.frame]].y, this.frame.width, this.frame.height, 0, 0, this.frame.width, this.frame.height);
            this.game.context.restore();
        }
    };

	// Sprite prototype Method is_touched
    Sprite.prototype.touch = function () {
        var _touch = this.game.input.touch;
        for (var i = 0; i < _touch.length; i++) {
            if (this.position.x - this.anchor.x <= _touch[i].x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width > _touch[i].x && this.position.y - this.anchor.y <= _touch[i].y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height > _touch[i].y) {
                return true;
            }
        }
        return false;
    };

	// Sprite prototype Method is_clicked
    Sprite.prototype.click = function (_button) {
        var _mouse = this.game.input.mouse;
        if (this.position.x - this.anchor.x <= _mouse.x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width > _mouse.x && this.position.y - this.anchor.y <= _mouse.y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height > _mouse.y && _button)
            return true;
        return false;
    };

	// Sprite prototype Method collidesWithSprite
    Sprite.prototype.collidesWithSprite = function (_object) {
        if (((this.position.x - this.anchor.x + this.move.x <= _object.position.x - _object.anchor.x + _object.move.x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width + this.move.x > _object.position.x - _object.anchor.x + _object.move.x) || (_object.position.x - _object.anchor.x + _object.move.x <= this.position.x - this.anchor.x + this.move.x && _object.position.x - _object.anchor.x + _object.move.x + _object.frame.width - _object.frame.offset.width > this.position.x - this.anchor.x + this.move.x)) && ((this.position.y - this.anchor.y + this.move.y <= _object.position.y - _object.anchor.y + _object.move.y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height + this.move.y > _object.position.y - _object.anchor.y + _object.move.y) || (_object.position.y - _object.anchor.y + _object.move.y <= this.position.y - this.anchor.y + this.move.y && _object.position.y - _object.anchor.y + _object.move.y + _object.frame.height - _object.frame.offset.height > this.position.y - this.anchor.y + this.move.y)))
            return true;
        return false;
    };

	// Sprite prototype Method collidesWithTile
    Sprite.prototype.collidesWithTile = function (_layer, _tile, _j) {
        var _lpx = Math.abs(_layer.x);
        var _lpy = Math.abs(_layer.y);

        _object = {position: {x: Math.floor(_tile % _layer.width) * this.game.map.json.tilewidth, y: Math.floor(_tile / _layer.width) * this.game.map.json.tilewidth}, width: this.game.map.json.tilesets[this.game.map.getTileset(_layer.data[_tile])].tilewidth, height: this.game.map.json.tilesets[this.game.map.getTileset(_layer.data[_tile])].tileheight};

        var px1 = this.position.x - this.anchor.x + this.move.x + _lpx;
        var px2 = this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width + this.move.x + _lpx;
        var px3 = this.position.x - this.anchor.x + this.move.x + _lpx;
        var px4 = this.position.x - this.anchor.x + this.move.x + _lpx;
        if (_layer.properties.scroll.infinite.x) {
            if (px1 >= this.game.map.canvas[_j].width) {
                px1 = Math.floor(px1 % this.game.map.canvas[_j].width);
            }
            if (px2 >= this.game.map.canvas[_j].width) {
                px2 = Math.floor(px2 % this.game.map.canvas[_j].width);
            }
            if (px3 >= this.game.map.canvas[_j].width) {
                px3 = Math.floor(px3 % this.game.map.canvas[_j].width);
            }
            if (px4 >= this.game.map.canvas[_j].width) {
                px4 = Math.floor(px4 % this.game.map.canvas[_j].width);
            }
        }

        var py1 = this.position.y - this.anchor.y + this.move.y + _lpy;
        var py2 = this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height + this.move.y + _lpy;
        var py3 = this.position.y - this.anchor.y + this.move.y + _lpy;
        var py4 = this.position.y - this.anchor.y + this.move.y + _lpy;
        if (_layer.properties.scroll.infinite.y) {
            if (py1 >= this.game.map.canvas[_j].height) {
                py1 = Math.floor(py1 % this.game.map.canvas[_j].height);
            }
            if (py2 >= this.game.map.canvas[_j].height) {
                py2 = Math.floor(py2 % this.game.map.canvas[_j].height);
            }
            if (py3 >= this.game.map.canvas[_j].height) {
                py3 = Math.floor(py3 % this.game.map.canvas[_j].height);
            }
            if (py4 >= this.game.map.canvas[_j].height) {
                py4 = Math.floor(py4 % this.game.map.canvas[_j].height);
            }
        }

        if (((px1 <= _object.position.x && px2 > _object.position.x) || (_object.position.x <= px3 && _object.position.x + _object.width > px4)) && ((py1 <= _object.position.y && py2 > _object.position.y) || (_object.position.y <= py3 && _object.position.y + _object.height > py4)))
            return true;
        return false;
    };

    return Sprite;

});<commit_msg>Added width & height property on sprite<commit_after>Molecule.module('Molecule.Sprite', function (require, p) {

    var Animation = require('Molecule.Animation');

	// Sprite var.
    function Sprite(_name, _width, _height) {
        this.name = _name;
        this.image = null;
        this.position = {x: 0, y: 0, absolute: {x: 0, y: 0}};
        this.rotation = 0;
        this.move = {x: 0, y: 0};
        this.flip = {x: false, y: false, offset: {x: 0, y: 0}, f: {x: 0, y: 0}};
        this.anchor = {x: 0, y: 0};
        this.visible = true;
        this.alpha = 1;
        this.frame = {width: _width, height: _height, offset: {width: 0, height: 0}};
        this.animation = new Animation(this.frame.width, this.frame.height);
        this.size = {width: 0, height: 0};
        this.collides = {sprite: true, map: true};
        this.scrollable = true;
        this.collidable = true;
        this.platform = false;
        this.bounciness = false;
        this.acceleration = {x: 0, y: 0};
        this.speed = {x: 0, y: 0, t: {x: 0, y: 0}, max: {x: 100, y: 100}, min: {x: 0, y: 0}, check: {x: false, y: false}, gravity: {x: 0, y: 0}};
        this.affects = {physics: {gravity: true, friction: true}};
        this.collision = {map: {up: false, down: false, left: false, right: false, tile: null}, sprite: {up: false, down: false, left: false, right: false, id: null}, check: {map: {up: true, down: true, left: true, right: true}}};
        this.scroll = {offset: {x: 0, y: 0}};
        this.overlap = false;
        this.kill = false;
        this.game = null;
        this.width = 0;
        this.height = 0;

        return this;
    };

    Sprite.prototype.getAnimation = function () {
        this.size = {width: this.image.width, height: this.image.height};
        this.frame.width = this.frame.width || this.size.width;
        this.frame.height = this.frame.height || this.size.height;
        this.width = this.frame.width;
        this.height = this.frame.height;
        this.animation.sliceFrames(this.image.width, this.image.height, this.frame.width, this.frame.height);
    };

	// Sprite prototype Method flipUpdate
    Sprite.prototype.flipUpdate = function () {
        this.flip.offset.x = this.flip.x ? -this.frame.width : 0;
        this.flip.offset.y = this.flip.y ? -this.frame.height : 0;
        this.flip.f.x = this.flip.x ? -1 : 1;
        this.flip.f.y = this.flip.y ? -1 : 1;
    };

	// Sprite prototype Method update
    Sprite.prototype.update = function () {
        this.position.x += this.move.x;
        this.position.y += this.move.y;
        this.position.x = parseFloat(this.position.x.toFixed(3));
        this.position.y = parseFloat(this.position.y.toFixed(3));
        this.position.absolute.x = this.position.x;
        this.position.absolute.y = this.position.y;
        if (this.game.map.getMainLayer() !== -1) {
            this.position.absolute.x += Math.abs(this.game.map.json.layers[this.game.map.getMainLayer()].x);
            this.position.absolute.y += Math.abs(this.game.map.json.layers[this.game.map.getMainLayer()].y);
        }
        this.size.width = this.frame.width - this.frame.offset.width;
        this.size.height = this.frame.height - this.frame.offset.height;
    };

	// Sprite prototype Method resetMove
    Sprite.prototype.resetMove = function () {
        this.move = {x: 0, y: 0};
    };

	// Sprite prototype Method reset acceleration
    Sprite.prototype.resetAcceleration = function () {
        this.acceleration = {x: 0, y: 0};
    };

	// Sprite prototype Method draw
    Sprite.prototype.draw = function (_overlap) {
        if (this.overlap === _overlap) {
            this.game.context.save();
            this.game.context.globalAlpha = this.alpha;
            this.game.context.scale(1 * this.flip.f.x, 1 * this.flip.f.y);
            this.game.context.translate(Math.round((this.position.x * this.flip.f.x) + this.flip.offset.x), Math.round((this.position.y * this.flip.f.y) + this.flip.offset.y));
            this.game.context.rotate(this.rotation * (Math.PI / 180));
            this.game.context.translate(Math.round(-this.anchor.x * this.flip.f.x), Math.round(-this.anchor.y * this.flip.f.y));
            this.game.context.drawImage(this.image, this.animation.frame[this.animation.id[this.animation.current.animation].frame[this.animation.current.frame]].x, this.animation.frame[this.animation.id[this.animation.current.animation].frame[this.animation.current.frame]].y, this.frame.width, this.frame.height, 0, 0, this.frame.width, this.frame.height);
            this.game.context.restore();
        }
    };

	// Sprite prototype Method is_touched
    Sprite.prototype.touch = function () {
        var _touch = this.game.input.touch;
        for (var i = 0; i < _touch.length; i++) {
            if (this.position.x - this.anchor.x <= _touch[i].x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width > _touch[i].x && this.position.y - this.anchor.y <= _touch[i].y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height > _touch[i].y) {
                return true;
            }
        }
        return false;
    };

	// Sprite prototype Method is_clicked
    Sprite.prototype.click = function (_button) {
        var _mouse = this.game.input.mouse;
        if (this.position.x - this.anchor.x <= _mouse.x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width > _mouse.x && this.position.y - this.anchor.y <= _mouse.y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height > _mouse.y && _button)
            return true;
        return false;
    };

	// Sprite prototype Method collidesWithSprite
    Sprite.prototype.collidesWithSprite = function (_object) {
        if (((this.position.x - this.anchor.x + this.move.x <= _object.position.x - _object.anchor.x + _object.move.x && this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width + this.move.x > _object.position.x - _object.anchor.x + _object.move.x) || (_object.position.x - _object.anchor.x + _object.move.x <= this.position.x - this.anchor.x + this.move.x && _object.position.x - _object.anchor.x + _object.move.x + _object.frame.width - _object.frame.offset.width > this.position.x - this.anchor.x + this.move.x)) && ((this.position.y - this.anchor.y + this.move.y <= _object.position.y - _object.anchor.y + _object.move.y && this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height + this.move.y > _object.position.y - _object.anchor.y + _object.move.y) || (_object.position.y - _object.anchor.y + _object.move.y <= this.position.y - this.anchor.y + this.move.y && _object.position.y - _object.anchor.y + _object.move.y + _object.frame.height - _object.frame.offset.height > this.position.y - this.anchor.y + this.move.y)))
            return true;
        return false;
    };

	// Sprite prototype Method collidesWithTile
    Sprite.prototype.collidesWithTile = function (_layer, _tile, _j) {
        var _lpx = Math.abs(_layer.x);
        var _lpy = Math.abs(_layer.y);

        _object = {position: {x: Math.floor(_tile % _layer.width) * this.game.map.json.tilewidth, y: Math.floor(_tile / _layer.width) * this.game.map.json.tilewidth}, width: this.game.map.json.tilesets[this.game.map.getTileset(_layer.data[_tile])].tilewidth, height: this.game.map.json.tilesets[this.game.map.getTileset(_layer.data[_tile])].tileheight};

        var px1 = this.position.x - this.anchor.x + this.move.x + _lpx;
        var px2 = this.position.x - this.anchor.x + this.frame.width - this.frame.offset.width + this.move.x + _lpx;
        var px3 = this.position.x - this.anchor.x + this.move.x + _lpx;
        var px4 = this.position.x - this.anchor.x + this.move.x + _lpx;
        if (_layer.properties.scroll.infinite.x) {
            if (px1 >= this.game.map.canvas[_j].width) {
                px1 = Math.floor(px1 % this.game.map.canvas[_j].width);
            }
            if (px2 >= this.game.map.canvas[_j].width) {
                px2 = Math.floor(px2 % this.game.map.canvas[_j].width);
            }
            if (px3 >= this.game.map.canvas[_j].width) {
                px3 = Math.floor(px3 % this.game.map.canvas[_j].width);
            }
            if (px4 >= this.game.map.canvas[_j].width) {
                px4 = Math.floor(px4 % this.game.map.canvas[_j].width);
            }
        }

        var py1 = this.position.y - this.anchor.y + this.move.y + _lpy;
        var py2 = this.position.y - this.anchor.y + this.frame.height - this.frame.offset.height + this.move.y + _lpy;
        var py3 = this.position.y - this.anchor.y + this.move.y + _lpy;
        var py4 = this.position.y - this.anchor.y + this.move.y + _lpy;
        if (_layer.properties.scroll.infinite.y) {
            if (py1 >= this.game.map.canvas[_j].height) {
                py1 = Math.floor(py1 % this.game.map.canvas[_j].height);
            }
            if (py2 >= this.game.map.canvas[_j].height) {
                py2 = Math.floor(py2 % this.game.map.canvas[_j].height);
            }
            if (py3 >= this.game.map.canvas[_j].height) {
                py3 = Math.floor(py3 % this.game.map.canvas[_j].height);
            }
            if (py4 >= this.game.map.canvas[_j].height) {
                py4 = Math.floor(py4 % this.game.map.canvas[_j].height);
            }
        }

        if (((px1 <= _object.position.x && px2 > _object.position.x) || (_object.position.x <= px3 && _object.position.x + _object.width > px4)) && ((py1 <= _object.position.y && py2 > _object.position.y) || (_object.position.y <= py3 && _object.position.y + _object.height > py4)))
            return true;
        return false;
    };

    return Sprite;

});<|endoftext|>


<commit_before>
import Finder from './core'

Object.assign( Finder.fn, {
  version:  '@VERSION',
  preset:   'prose',
  mode:     'retain',
  context:  undefined,
  phase:    [],
  selector: {
    avoid: new Set( Finder.ELMT.NON_TEXT ),
    bdry:  new Set( Finder.ELMT.BDRY ),
  },
})

<commit_msg>Minor adjustments<commit_after>
import Finder from './core'

Object.assign( Finder.fn, {
  version:     '@VERSION',
  portionMode: 'retain',
  context:     undefined,
  phase:       [],
  selector: {
    avoid: new Set( Finder.ELMT.NON_TEXT ),
    bdry:  new Set( Finder.ELMT.BDRY ),
  },
})

<|endoftext|>


<commit_before>import { all, any, append, curry, defaultTo } from 'ramda';

import * as tokenType from './model/tokenType';
import * as border from './model/border';
import * as direction from './model/direction';
import * as floor from './model/floor';
import * as turn from './model/turn';
import position from './model/position';
import gameBoard from './model/gameBoard';

const getSquareAt = (game, pos) => {
	return game.getGameBoard().getBoard().getSquareAt(pos);
};

const getTokenAt = (game, pos) => {
	return game.getGameBoard().getTokenAt(pos);
};

const hasTokenAt = (game, pos) => {
	return game.getGameBoard().hasTokenAt(pos);
};

const canPush = (game, dir, pos) => {
	const isNotAnchor = getTokenAt(game, pos).getTokenType() !== tokenType.ANCHOR;
	const hasOpenBorder = getSquareAt(game, pos).getBorder(dir) === border.OPEN_BORDER;

	return isNotAnchor && hasOpenBorder;
};

const getNextPositionFn = (dir) => {
	switch (dir) {
	case direction.NORTH:
		return (pos) => position(pos.x, pos.y + 1);

	case direction.EAST:
		return (pos) => position(pos.x + 1, pos.y);

	case direction.SOUTH:
		return (pos) => position(pos.x, pos.y - 1);

	case direction.WEST:
		return (pos) => position(pos.x - 1, pos.y);

	default:
		throw new Error('Invalid direction');
	}
};

const getLine = (game, nextPositionFn, pos, prepend) => {
	const line = defaultTo([], prepend);

	if (getSquareAt(game, pos).getFloorType() === floor.PIT) {
		return line;
	}

	if (!hasTokenAt(game, pos)) {
		return line;
	}

	return getLine(game, nextPositionFn(pos), nextPositionFn, append(pos, line));
};

const validatePush = (game, dir, pos) => {
	if (getTokenAt(game, pos).getTokenType() !== tokenType.BULLY) {
		return false;
	}

	const canPushInDirection = curry(canPush)(game, dir);
	const nextPositionFn = getNextPositionFn(dir);
	const line = getLine(game, nextPositionFn, pos);
	return line.length > 1 && all(canPushInDirection, line);
};

const getTokenPositionsAfterPush = (game, dir, pos) => {
	// TODO: Implement me
};

const isTokenPositionInPit = (gb, tokenPosition) => {
	return gb.getBoard().getSquareAt(tokenPosition.position).getFloorType() === floor.PIT;
};

const hasTokenInPit = (gb) => any(curry(isTokenPositionInPit), gb.getTokenPositions());

const isCurrentPlayerStuck = (gb, playerTurn) => {
	// TODO: Implement me
};

const isGameOver = (gb, playerTurn) => {
	return hasTokenInPit(gb)
		|| isCurrentPlayerStuck(gb, playerTurn);
};

const getNextPlayerTurn = (currentTurn) => {
	return currentTurn === turn.PLAYER_ONE_TURN
		? turn.PLAYER_TWO_TURN
		: turn.PLAYER_ONE_TURN;
};

export default (game, dir, pos) => {
	if (!validatePush(game, dir, pos)) {
		throw new Error('Invalid move');
	}

	const newBoard = gameBoard(
		game.getGameBoard().getBoard(),
		getTokenPositionsAfterPush(game, dir, pos));

	const nextPlayerTurn = getNextPlayerTurn(game.getTurn());

	const nextTurn = isGameOver(newBoard, nextPlayerTurn)
		? turn.GAME_OVER
		: nextPlayerTurn;

	return game(game.getPlayerOne(), game.getPlayerTwo(), newBoard, nextTurn);
};
<commit_msg>Implement getTokensPositionsAfterPush<commit_after>import { any, append, contains, curry, defaultTo } from 'ramda';

import * as tokenType from './model/tokenType';
import * as border from './model/border';
import * as direction from './model/direction';
import * as floor from './model/floor';
import * as turn from './model/turn';
import position from './model/position';
import gameBoard from './model/gameBoard';
import tokenPosition from './model/tokenPosition';

const getSquareAt = (pos, game) => {
	return game.getGameBoard().getBoard().getSquareAt(pos);
};

const getTokenAt = (pos, game) => {
	return game.getGameBoard().getTokenAt(pos);
};

const hasTokenAt = (pos, game) => {
	return game.getGameBoard().hasTokenAt(pos);
};

// TODO: Figure out a way to not have to call this so many times
const getNextPositionFn = (dir) => {
	switch (dir) {
	case direction.NORTH:
		return (pos) => position(pos.x, pos.y + 1);

	case direction.EAST:
		return (pos) => position(pos.x + 1, pos.y);

	case direction.SOUTH:
		return (pos) => position(pos.x, pos.y - 1);

	case direction.WEST:
		return (pos) => position(pos.x - 1, pos.y);

	default:
		throw new Error('Invalid direction');
	}
};

const canPush = (dir, pos, game) => {
	if (getTokenAt(pos, game).getTokenType() === tokenType.ANCHOR) {
		return false;
	}

	if (getSquareAt(pos, game).getBorder(dir) === border.WALL_BORDER) {
		return false;
	}

	if (!hasTokenAt(pos, game)) {
		return true;
	}

	return canPush(dir, getNextPositionFn(dir)(pos), game);
};

const validatePush = (dir, pos, game) => {
	// Can not push an empty space
	if (!hasTokenAt(pos, game)) {
		return false;
	}

	// If the next space is empty, it's a move not a push
	const nextPosition = getNextPositionFn(dir)(pos);
	if (!hasTokenAt(nextPosition)) {
		return false;
	}

	const startingToken = getTokenAt(pos, game);

	// Can only push a bully
	if (startingToken.getTokenType() !== tokenType.BULLY
			// TODO: How to equate these two different types?
		|| startingToken.getPlayerType() !== game.getTurn()) {
		return false;
	}

	return canPush(dir, pos);
};

const getPushedTokens = (dir, pos, game, tokens) => {
	const pushed = defaultTo([], tokens);
	if (!hasTokenAt(pos, game)) {
		return pushed;
	}

	return getPushedTokens(dir, getNextPositionFn(dir)(pos), game,
		append(getTokenAt(pos, game), pushed));
};

const getTokenPositionsAfterPush = (dir, pos, game) => {
	const pushedTokens = getPushedTokens(dir, pos, game);
	return game.getGameBoard().getTokenPositions().map((tp) => {
		if (contains(tp.token, pushedTokens)) {
			return tokenPosition(tp.token, getNextPositionFn(dir)(tp.position));
		}
		return tp;
	});
};

const isTokenPositionInPit = (game, tp) => {
	return getSquareAt(tp.position, game).getFloorType() === floor.PIT;
};

const hasTokenInPit = (game) => any(curry(isTokenPositionInPit)(game),
	game.getGameBoard().getTokenPositions());

const isCurrentPlayerStuck = (playerTurn, game) => {
	// TODO: Implement me

};

const isGameOver = (playerTurn, game) => {
	return hasTokenInPit(game)
		|| isCurrentPlayerStuck(playerTurn, game);
};

const getNextPlayerTurn = (currentTurn) => {
	return currentTurn === turn.PLAYER_ONE_TURN
		? turn.PLAYER_TWO_TURN
		: turn.PLAYER_ONE_TURN;
};

export default (game, dir, pos) => {
	if (!validatePush(dir, pos, game)) {
		throw new Error('Invalid move');
	}

	const newBoard = gameBoard(
		game.getGameBoard().getBoard(),
		getTokenPositionsAfterPush(dir, pos, game));

	const nextPlayerTurn = getNextPlayerTurn(game.getTurn());

	const nextTurn = isGameOver(nextPlayerTurn, game)
		? turn.GAME_OVER
		: nextPlayerTurn;

	return game(game.getPlayerOne(), game.getPlayerTwo(), newBoard, nextTurn);
};
<|endoftext|>


<commit_before>"use strict";

var puckt = puckt || {};
puckt.Game = (function () {
    var world, currentLevel = 1, currentGame, fps = 60;

    function Game () {
        // Create stage and enable touch
        stage = new createjs.Stage(puckt.canvas.elem);
        createjs.Touch.enable(stage);

        puckt.Level.successCallback = function (stars, collisions) {
            var currentLevel = this;
            puckt.ui.openPopup({
                content: "
Congratulations, you completed level " + currentLevel.number + " with " + stars + " star(s)!
",
                buttons: [
                    {
                        text: "Proceed →",
                        callback: function () {
                            puckt.ui.closePopup();
                            currentGame = new puckt.Level(world, ++currentLevel.number);
                            currentGame.boot(function () {
                                currentGame.begin();
                            }, function () {
                                console.log("Ah, man!");
                            });
                        }
                    },
                    {
                        text: "Retry",
                        callback: function () {
                            puckt.ui.closePopup();
                        }
                    },
                    {
                        text: "Quit",
                        callback: function () {
                            puckt.ui.closePopup();
                        }
                    }
                ]
            })
        }

        puckt.Level.failCallback = function (collisions) {
            console.log('You FAILED!');
        }

        // Set the scene
        createWorld();

        // Declare settings for scene ticker
        createjs.Ticker.addEventListener('tick', tickrolled);
        createjs.Ticker.setFPS(fps);
        createjs.Ticker.useRAF = true;
    }

    function createWorld () {
        // Create world with no gravity
        world = new box2d.b2World(new box2d.b2Vec2(0, 0), true);

        // Contact Listener
        var contactListener = new Box2D.Dynamics.b2ContactListener;
        contactListener.BeginContact = function(contact) {
           var shape = contact.GetFixtureA().GetBody().GetUserData();

           if (shape.type = "wall") {
               shape.collision(contact);
           }
        };
        world.SetContactListener(contactListener);

        // Initialise debugger
        puckt.debug.init(world);

        // Draw Level
        currentGame = new puckt.Level(world, currentLevel);
        currentGame.boot(function () {
            currentGame.begin();
        }, function () {
            console.log("Ah, man!");
        });
    }

    function tickrolled (e) {
        if (!e.paused) {
            // Update stage
            stage.update();

            puckt.debug.run(world.DrawDebugData.bind(world));

            world.Step(1/fps, 10, 10);
            world.ClearForces();
        }
    }

    return Game;
})();<commit_msg>Improved user feedback on level completion<commit_after>"use strict";

var puckt = puckt || {};
puckt.Game = (function () {
    var world, currentLevel = 1, currentGame, fps = 60;

    function Game () {
        // Create stage and enable touch
        stage = new createjs.Stage(puckt.canvas.elem);
        createjs.Touch.enable(stage);

        puckt.Level.successCallback = function (stars, collisions) {
            var currentLevel = this;
            puckt.ui.openPopup({
                content: "
Congratulations, you completed level " + currentLevel.number + " with " + stars + " star" + (stars != 1 ? "s" : "") + "!
",
                buttons: [
                    {
                        text: "Proceed →",
                        callback: function () {
                            puckt.ui.closePopup();
                            currentGame = new puckt.Level(world, ++currentLevel.number);
                            currentGame.boot(function () {
                                currentGame.begin();
                            }, function () {
                                console.log("Ah, man!");
                            });
                        }
                    },
                    {
                        text: "Retry",
                        callback: function () {
                            puckt.ui.closePopup();
                        }
                    },
                    {
                        text: "Quit",
                        callback: function () {
                            puckt.ui.closePopup();
                        }
                    }
                ]
            })
        }

        puckt.Level.failCallback = function (collisions) {
            console.log('You FAILED!');
        }

        // Set the scene
        createWorld();

        // Declare settings for scene ticker
        createjs.Ticker.addEventListener('tick', tickrolled);
        createjs.Ticker.setFPS(fps);
        createjs.Ticker.useRAF = true;
    }

    function createWorld () {
        // Create world with no gravity
        world = new box2d.b2World(new box2d.b2Vec2(0, 0), true);

        // Contact Listener
        var contactListener = new Box2D.Dynamics.b2ContactListener;
        contactListener.BeginContact = function(contact) {
           var shape = contact.GetFixtureA().GetBody().GetUserData();

           if (shape.type = "wall") {
               shape.collision(contact);
           }
        };
        world.SetContactListener(contactListener);

        // Initialise debugger
        puckt.debug.init(world);

        // Draw Level
        currentGame = new puckt.Level(world, currentLevel);
        currentGame.boot(function () {
            currentGame.begin();
        }, function () {
            console.log("Ah, man!");
        });
    }

    function tickrolled (e) {
        if (!e.paused) {
            // Update stage
            stage.update();

            puckt.debug.run(world.DrawDebugData.bind(world));

            world.Step(1/fps, 10, 10);
            world.ClearForces();
        }
    }

    return Game;
})();<|endoftext|>


<commit_before>/**
 * Base class for canvas objects
 * @namespace
 * @constructor
 */
vonline.Base = function() {
	// data and object needs to be always in sync
	/** default values and json format specification */
	this.data = {
		/** unique identifier */
		id: 0,
		/** 'rectangle', 'circle', 'path', 'image', 'annotation', 'connection' */
		type: null,
		/** SVG path or further differentation of type, e.g. 'arrow', 'line' */
		path: null,
		x: 0,
		y: 0,
		width: 50,
		height: 50,
		rotation: 0,
		/** see: http://raphaeljs.com/reference.html#colour */
		color: 'white',
		text: null,
		/** [width, color] */
		border: null,
		/** [[id, side], ...] */
		connect: null
	};
	this.obj = null;
}

vonline.Base.prototype.getId = function() {
	return this.data.id;
}

/**
 * Displays the element on the canvas
 * @param {vonline.Canvas} canvas
 */
vonline.Base.prototype.setCanvas = function(canvas) {
	if (!canvas && this.obj) {
		// remove
		this.obj.remove();
		this.text.remove();
		return;
	}
	this.canvas = canvas;
	this.obj = this.createObject(canvas.getPaper());
	this.setPosition(this.data.x, this.data.y);
	this.setRotation(this.data.rotation);
	this.setColor(this.data.color);
	this.createText(canvas);

	this.initClickEventHandler();
	this.initDragEventHandler();
}

/**
 * Set the position of the component
 * Note that this function sets the absolute position by the top-left corner of the bounding box
 * @param {integer} x
 * @param {integer} y
 */
vonline.Base.prototype.setPosition = function(x, y) {
	var bbox = this.obj.getBBox(),
		currentX = bbox.x,
		currentY = bbox.y;
	this.obj.translate(x - currentX, y - currentY);
	this.data.x = x;
	this.data.y = y;
}

/**
 * Translates the object by the given values
 * @param {integer} x
 * @param {integer} y
 */
vonline.Base.prototype.setTranslation = function(x, y) {
	this.obj.translate(x, y);
	this.data.x += x;
	this.data.y += y;
	$(this.obj.node).trigger('changed');
}

/**
 * @param {float} x
 * @param {float} y
 * @param {number} origX
 * @param {number} origY
 */
vonline.Base.prototype.setScale = function(x, y, origX, origY) {
	throw 'vonline.Base.setScale must be implemented in subclass';
}

/**
 * @param {int} deg Degree between 0...360°
 */
vonline.Base.prototype.setRotation = function(deg) {
	this.obj.rotate(deg, true);
	this.data.rotation = deg;
	$(this.obj.node).trigger('changed');
}

/**
 * @param {string} color
 * @see http://raphaeljs.com/reference.html#colour
 */
vonline.Base.prototype.setColor = function(color) {
	this.obj.attr({fill: color});
	this.data.color = color;
}

/**
 * craetes a text within the object
 */
vonline.Base.prototype.createText = function() {
	var that = this;
	this.textShowEvent = function() {
		that.text.show();
	};
	this.textHideEvent = function(event) {
		if (!$(event.relatedTarget).is('tspan')) {
			that.text.hide();
		}
	};

	if (this.data.text) {
		this.text = this.canvas.getPaper().text(this.data.x, this.data.y, this.data.text);
	}
	else {
		this.text = this.canvas.getPaper().text(this.data.x, this.data.y, 'dblclick\nto add');
		this.text.hide();
		$(this.obj.node).mouseenter(this.textShowEvent);
		$(this.obj.node).mouseout(this.textHideEvent);
	}

	// redirect events to object
	$(this.text.node).click(function(event) {
		$(that.obj.node).trigger(event);
	});
	$(this.text.node).mousedown(function(event) {
		$(that.obj.node).trigger(event);
	});

	// reposition on change
	this.adjustText();
	$(this.obj.node).bind('changed', function() {
		that.adjustText();
	});

	// text changing
	$(this.text.node).dblclick(function() {
		var entered = window.prompt('Enter a new value:');
		if (entered !== null) {
			var command = new vonline.TextChangeCommand(that, entered);
			command.execute();
			vonline.events.trigger('commandexec', command);
		}
	});
}

/**
 * @param {string} text
 */
vonline.Base.prototype.changeText = function(text) {
	this.data.text = text;
	this.text.remove();
	$(this.obj.node).unbind('mouseenter', this.textShowEvent);
	$(this.obj.node).unbind('mouseout', this.textHideEvent);
	this.createText();
}

/**
 * sets the correct position of the inlined text
 */
vonline.Base.prototype.adjustText = function() {
	var bbox = this.obj.getBBox();
	this.text.attr('x', bbox.x + bbox.width / 2);
	this.text.attr('y', bbox.y + bbox.height / 2);
	this.text.rotate(this.data.rotation, true);
}

/**
 * Returns the JSON representation of the object
 * @return {json} this.data
 */
vonline.Base.prototype.toJSON = function() {
	return this.data;
}

vonline.Base.prototype.initDragEventHandler = function() {
	var that = this;
	this.obj.attr('cursor', 'pointer');
	$(this.obj.node).mousedown(function(event) {
		// catch mouse movement

		that.obj.attr('cursor', 'move');

		// prevent selecting text
		event.preventDefault();
		event.stopPropagation();

		var origX = x = event.pageX,
			origY = y = event.pageY;
		var moveEvent = function(event) {
			// distinguish between click and drag-event, see initClickHandler
			that.wasDragging = true;
			event.preventDefault();
			event.stopPropagation();

			var deltaX = event.pageX - x,
				deltaY = event.pageY - y;
			if (!event.altKey) {
				var bbox = that.obj.getBBox();
				// adjust object
				that.obj.translate((Raphael.snapTo(vonline.GRIDSIZE, bbox.x) - bbox.x), (Raphael.snapTo(vonline.GRIDSIZE, bbox.y) - bbox.y));
				event.pageX = Raphael.snapTo(vonline.GRIDSIZE, event.pageX);
				deltaX = Raphael.snapTo(vonline.GRIDSIZE, event.pageX - x);
				event.pageY = Raphael.snapTo(vonline.GRIDSIZE, event.pageY);
				deltaY = Raphael.snapTo(vonline.GRIDSIZE, event.pageY - y);
			}
			that.setTranslation(deltaX, deltaY);
			// that.obj.translate(deltaX, deltaY);
			x = event.pageX;
			y = event.pageY;
		}
		$(window).mousemove(moveEvent);
		$(window).one('mouseup', function(event) {
			event.preventDefault();
			event.stopPropagation();

			$(window).unbind('mousemove', moveEvent);
			if (that.wasDragging) {
				var translateX = (x - origX),
					translateY = (y - origY);
				vonline.events.trigger('commandexec', new vonline.TranslateCommand(that, translateX, translateY));
			}
			that.obj.attr('cursor', 'pointer');
		});
	});
}

vonline.Base.prototype.initClickEventHandler = function() {
	var that = this;
	$(this.obj.node).click(function(event) {
		event.preventDefault();
		event.stopPropagation();

		if (!that.wasDragging) {
			vonline.document.canvas.selection.toggle(that);
		}
		else {
			that.wasDragging = false;
		}
	});
}

/**
 * @param {boolean} active
 */
vonline.Base.prototype.setRotationMode = function(active) {
	var bbox = this.obj.getBBox(),
	radius = Math.max(bbox.width / 2, bbox.height / 2) + 25,
	centerX = bbox.x + bbox.width / 2,
	centerY = bbox.y + bbox.height / 2,
	that = this;

	if (active) {
		/**
		 * updates rotation-circle and rotation-handle on object changes
		 */
		this.updateHandles = function() {
			var newBBox = that.obj.getBBox(),
			newRadius = Math.max(newBBox.width / 2, newBBox.height / 2) + 25,
			newCenterX = newBBox.x + newBBox.width / 2,
			newCenterY = newBBox.y + newBBox.height / 2;
			if (that.rotationCircle) {
				if (radius != newRadius) {
					that.rotationCircle.scale(newRadius/radius, newRadius/radius);
					that.rotationCircle.resetScale();
					that.rotationCircle.translate(newCenterX - centerX, newCenterY - centerY);
					that.rotationHandle.rotate(0, centerX, centerY);
					that.rotationHandle.translate(newCenterX - centerX, newCenterY - newRadius - (centerY - radius));
					that.rotationHandle.rotate(that.data.rotation, newCenterX, newCenterY);
					radius = newRadius;
					centerX = newCenterX;
					centerY = newCenterY;
				}
				else if (centerX != newCenterX || centerY != newCenterY) {
					that.rotationCircle.translate(newCenterX - centerX, newCenterY - centerY);
					that.rotationHandle.translate(newCenterX - centerX, newCenterY - newRadius - (centerY - radius));
					centerX = newCenterX;
					centerY = newCenterY;
				}
			}
		}
		$(this.obj.node).bind('changed', this.updateHandles);

		this.rotationCircle = this.canvas.getPaper().circle(centerX, centerY, radius).attr('stroke', 'orange').attr('stroke-width', '2');
		this.rotationHandle = this.canvas.getPaper().circle(centerX, centerY - radius, 4).attr('stroke', 'none').attr('fill', 'orange').attr('cursor', 'pointer').rotate(this.data.rotation, centerX, centerY);
		$(this.rotationHandle.node).mousedown(function(event) {
			event.stopPropagation();

			var deg = that.data.rotation,
			rotationEvent = function(event) {
				deg = Raphael.angle(centerX, centerY, event.offsetX, event.offsetY);
				deg -= 90;
				deg = deg < 0 ? 360 + deg : deg;
				that.rotationHandle.rotate(deg, centerX, centerY);
				that.obj.rotate(deg, true);
			};
			$(window).mousemove(rotationEvent);
			$(window).one('mouseup', function() {
				$(window).unbind('mousemove', rotationEvent);
				var command = new vonline.RotateCommand(that, deg);
				command.execute();
				vonline.events.trigger('commandexec', command);
			});
		});
	}
	else {
		$(this.obj.node).unbind('changed', this.updateHandles);
		this.rotationCircle.remove();
		this.rotationCircle = null;
		this.rotationHandle.remove();
		this.rotationHandle = null;
	}
}

/**
 * Checks if bounding contains the current object
 * @param {object} bounding Specified by (x, y, width, height)
 */
vonline.Base.prototype.fitsIn = function(bounding) {
	var bbox = this.obj.getBBox();
	return (bbox.x >= bounding.x && bbox.y >= bounding.y
			&& bbox.x + bbox.width <= bounding.x + bounding.width
			&& bbox.y + bbox.height <= bounding.y + bounding.height);
}<commit_msg>preparation for connecting<commit_after>/**
 * Base class for canvas objects
 * @namespace
 * @constructor
 */
vonline.Base = function() {
	// data and object needs to be always in sync
	/** default values and json format specification */
	this.data = {
		/** unique identifier */
		id: 0,
		/** 'rectangle', 'circle', 'path', 'image', 'annotation', 'connection' */
		type: null,
		/** SVG path or further differentation of type, e.g. 'arrow', 'line' */
		path: null,
		x: 0,
		y: 0,
		width: 50,
		height: 50,
		rotation: 0,
		/** see: http://raphaeljs.com/reference.html#colour */
		color: 'white',
		text: null,
		/** [width, color] */
		border: null,
		/** [[id, side], ...] */
		connect: null
	};
	this.obj = null;
}

vonline.Base.prototype.getId = function() {
	return this.data.id;
}

/**
 * Displays the element on the canvas
 * @param {vonline.Canvas} canvas
 */
vonline.Base.prototype.setCanvas = function(canvas) {
	if (!canvas && this.obj) {
		// remove
		this.obj.remove();
		this.text.remove();
		return;
	}
	this.canvas = canvas;
	this.obj = this.createObject(canvas.getPaper());
	this.setPosition(this.data.x, this.data.y);
	this.setRotation(this.data.rotation);
	this.setColor(this.data.color);
	this.createText(canvas);

	this.initClickEventHandler();
	this.initDragEventHandler();
}

/**
 * Set the position of the component
 * Note that this function sets the absolute position by the top-left corner of the bounding box
 * @param {integer} x
 * @param {integer} y
 */
vonline.Base.prototype.setPosition = function(x, y) {
	var bbox = this.obj.getBBox(),
		currentX = bbox.x,
		currentY = bbox.y;
	this.obj.translate(x - currentX, y - currentY);
	this.data.x = x;
	this.data.y = y;
}

/**
 * Translates the object by the given values
 * @param {integer} x
 * @param {integer} y
 */
vonline.Base.prototype.setTranslation = function(x, y) {
	this.obj.translate(x, y);
	this.data.x += x;
	this.data.y += y;
	$(this.obj.node).trigger('changed');
}

/**
 * @param {float} x
 * @param {float} y
 * @param {number} origX
 * @param {number} origY
 */
vonline.Base.prototype.setScale = function(x, y, origX, origY) {
	throw 'vonline.Base.setScale must be implemented in subclass';
}

/**
 * @param {int} deg Degree between 0...360°
 */
vonline.Base.prototype.setRotation = function(deg) {
	this.obj.rotate(deg, true);
	this.data.rotation = deg;
	$(this.obj.node).trigger('changed');
}

/**
 * @param {string} color
 * @see http://raphaeljs.com/reference.html#colour
 */
vonline.Base.prototype.setColor = function(color) {
	this.obj.attr({fill: color});
	this.data.color = color;
}

/**
 * craetes a text within the object
 */
vonline.Base.prototype.createText = function() {
	var that = this;
	this.textShowEvent = function() {
		that.text.show();
	};
	this.textHideEvent = function(event) {
		if (!$(event.relatedTarget).is('tspan')) {
			that.text.hide();
		}
	};

	if (this.data.text) {
		this.text = this.canvas.getPaper().text(this.data.x, this.data.y, this.data.text);
	}
	else {
		this.text = this.canvas.getPaper().text(this.data.x, this.data.y, 'dblclick\nto add');
		this.text.hide();
		$(this.obj.node).mouseenter(this.textShowEvent);
		$(this.obj.node).mouseout(this.textHideEvent);
	}

	// redirect events to object
	$(this.text.node).click(function(event) {
		$(that.obj.node).trigger(event);
	});
	$(this.text.node).mousedown(function(event) {
		$(that.obj.node).trigger(event);
	});

	// reposition on change
	this.adjustText();
	$(this.obj.node).bind('changed', function() {
		that.adjustText();
	});

	// text changing
	$(this.text.node).dblclick(function() {
		var entered = window.prompt('Enter a new value:');
		if (entered !== null) {
			var command = new vonline.TextChangeCommand(that, entered);
			command.execute();
			vonline.events.trigger('commandexec', command);
		}
	});
}

/**
 * @param {string} text
 */
vonline.Base.prototype.changeText = function(text) {
	this.data.text = text;
	this.text.remove();
	$(this.obj.node).unbind('mouseenter', this.textShowEvent);
	$(this.obj.node).unbind('mouseout', this.textHideEvent);
	this.createText();
}

/**
 * sets the correct position of the inlined text
 */
vonline.Base.prototype.adjustText = function() {
	var bbox = this.obj.getBBox();
	this.text.attr('x', bbox.x + bbox.width / 2);
	this.text.attr('y', bbox.y + bbox.height / 2);
	this.text.rotate(this.data.rotation, true);
}

/**
 * Returns the JSON representation of the object
 * @return {json} this.data
 */
vonline.Base.prototype.toJSON = function() {
	return this.data;
}

vonline.Base.prototype.initDragEventHandler = function() {
	var that = this;
	this.obj.attr('cursor', 'pointer');
	$(this.obj.node).mousedown(function(event) {
		// catch mouse movement

		that.obj.attr('cursor', 'move');

		// prevent selecting text
		event.preventDefault();
		event.stopPropagation();

		var origX = x = event.pageX,
			origY = y = event.pageY;
		var moveEvent = function(event) {
			// distinguish between click and drag-event, see initClickHandler
			that.wasDragging = true;
			event.preventDefault();
			event.stopPropagation();

			var deltaX = event.pageX - x,
				deltaY = event.pageY - y;
			if (!event.altKey) {
				var bbox = that.obj.getBBox();
				// adjust object
				that.obj.translate((Raphael.snapTo(vonline.GRIDSIZE, bbox.x) - bbox.x), (Raphael.snapTo(vonline.GRIDSIZE, bbox.y) - bbox.y));
				event.pageX = Raphael.snapTo(vonline.GRIDSIZE, event.pageX);
				deltaX = Raphael.snapTo(vonline.GRIDSIZE, event.pageX - x);
				event.pageY = Raphael.snapTo(vonline.GRIDSIZE, event.pageY);
				deltaY = Raphael.snapTo(vonline.GRIDSIZE, event.pageY - y);
			}
			that.setTranslation(deltaX, deltaY);
			// that.obj.translate(deltaX, deltaY);
			x = event.pageX;
			y = event.pageY;
		}
		$(window).mousemove(moveEvent);
		$(window).one('mouseup', function(event) {
			event.preventDefault();
			event.stopPropagation();

			$(window).unbind('mousemove', moveEvent);
			if (that.wasDragging) {
				var translateX = (x - origX),
					translateY = (y - origY);
				vonline.events.trigger('commandexec', new vonline.TranslateCommand(that, translateX, translateY));
			}
			that.obj.attr('cursor', 'pointer');
		});
	});
}

vonline.Base.prototype.initClickEventHandler = function() {
	var that = this;
	$(this.obj.node).click(function(event) {
		event.preventDefault();
		event.stopPropagation();

		if (!that.wasDragging) {
			vonline.document.canvas.selection.toggle(that);
		}
		else {
			that.wasDragging = false;
		}
	});
}

/**
 * @param {boolean} active
 */
vonline.Base.prototype.setRotationMode = function(active) {
	var bbox = this.obj.getBBox(),
	radius = Math.max(bbox.width / 2, bbox.height / 2) + 25,
	centerX = bbox.x + bbox.width / 2,
	centerY = bbox.y + bbox.height / 2,
	that = this;

	if (active) {
		/**
		 * updates rotation-circle and rotation-handle on object changes
		 */
		this.updateHandles = function() {
			var newBBox = that.obj.getBBox(),
			newRadius = Math.max(newBBox.width / 2, newBBox.height / 2) + 25,
			newCenterX = newBBox.x + newBBox.width / 2,
			newCenterY = newBBox.y + newBBox.height / 2;
			if (that.rotationCircle) {
				if (radius != newRadius) {
					that.rotationCircle.scale(newRadius/radius, newRadius/radius);
					that.rotationCircle.resetScale();
					that.rotationCircle.translate(newCenterX - centerX, newCenterY - centerY);
					that.rotationHandle.rotate(0, centerX, centerY);
					that.rotationHandle.translate(newCenterX - centerX, newCenterY - newRadius - (centerY - radius));
					that.rotationHandle.rotate(that.data.rotation, newCenterX, newCenterY);
					radius = newRadius;
					centerX = newCenterX;
					centerY = newCenterY;
				}
				else if (centerX != newCenterX || centerY != newCenterY) {
					that.rotationCircle.translate(newCenterX - centerX, newCenterY - centerY);
					that.rotationHandle.translate(newCenterX - centerX, newCenterY - newRadius - (centerY - radius));
					centerX = newCenterX;
					centerY = newCenterY;
				}
			}
		}
		$(this.obj.node).bind('changed', this.updateHandles);

		this.rotationCircle = this.canvas.getPaper().circle(centerX, centerY, radius).attr('stroke', 'orange').attr('stroke-width', '2');
		this.rotationHandle = this.canvas.getPaper().circle(centerX, centerY - radius, 4).attr('stroke', 'none').attr('fill', 'orange').attr('cursor', 'pointer').rotate(this.data.rotation, centerX, centerY);
		$(this.rotationHandle.node).mousedown(function(event) {
			event.stopPropagation();

			var deg = that.data.rotation,
			rotationEvent = function(event) {
				deg = Raphael.angle(centerX, centerY, event.offsetX, event.offsetY);
				deg -= 90;
				deg = deg < 0 ? 360 + deg : deg;
				that.rotationHandle.rotate(deg, centerX, centerY);
				that.obj.rotate(deg, true);
			};
			$(window).mousemove(rotationEvent);
			$(window).one('mouseup', function() {
				$(window).unbind('mousemove', rotationEvent);
				var command = new vonline.RotateCommand(that, deg);
				command.execute();
				vonline.events.trigger('commandexec', command);
			});
		});
	}
	else {
		$(this.obj.node).unbind('changed', this.updateHandles);
		this.rotationCircle.remove();
		this.rotationCircle = null;
		this.rotationHandle.remove();
		this.rotationHandle = null;
	}
}

/**
 * show connection handles
 */
vonline.Base.prototype.setConnectionMode = function(active) {
	if (active) {
		var bbox = this.obj.getBBox();
		this.connectionHandle = this.canvas.getPaper().path('M25.06,13.719c-0.944-5.172-5.461-9.094-10.903-9.094v4c3.917,0.006,7.085,3.176,7.094,7.094c-0.009,3.917-3.177,7.085-7.094,7.093v4.002c5.442-0.004,9.959-3.926,10.903-9.096h4.69v-3.999H25.06zM20.375,15.719c0-3.435-2.784-6.219-6.219-6.219c-2.733,0-5.05,1.766-5.884,4.218H1.438v4.001h6.834c0.833,2.452,3.15,4.219,5.884,4.219C17.591,21.938,20.375,19.153,20.375,15.719z').attr({fill: "green", stroke: "none"}).scale(.6,.6,0,0);
		var handleBBox = this.connectionHandle.getBBox();
		this.connectionHandle.translate(bbox.x + bbox.width - handleBBox.width - 3, bbox.y + bbox.height - handleBBox.height - 5);
	}
}

/**
 * Checks if bounding contains the current object
 * @param {object} bounding Specified by (x, y, width, height)
 */
vonline.Base.prototype.fitsIn = function(bounding) {
	var bbox = this.obj.getBBox();
	return (bbox.x >= bounding.x && bbox.y >= bounding.y
			&& bbox.x + bbox.width <= bounding.x + bounding.width
			&& bbox.y + bbox.height <= bounding.y + bounding.height);
}<|endoftext|>


<commit_before>//
// HLF Extensions Core
// ===================
// [Tests](../../tests/js/core.html)
//
(function(root, namespace) {
  //
  // § __UMD__
  // - When AMD, register the attacher as an anonymous module.
  // - When Node or Browserify, set module exports to the attach result.
  // - When browser globals (root is window), Just run the attach function.
  //
  if (typeof define === 'function' && define.amd) {
    define(namespace);
  } else if (typeof exports === 'object') {
    module.exports = namespace();
  } else {
    window.hlf = namespace();
  }
})(this, function() {
  'use strict';
  //
  // Namespace
  // ---------
  // It takes some more boilerplate and helpers to write DOM extensions. That
  // code and set of conventions is here in the root namespace __hlf__. Child
  // namespaces follow suit convention.
  //
  // - The __debug__ flag here toggles debug logging for everything in the library
  //   that doesn't have a custom debug flag in its namespace.
  //
  // - __toString__ is mainly for extension namespacing. For now, its base form
  //   is very simple.
  //
  // - __debugLog__ in its base form just wraps around `console.log` and links to
  //   the `debug` flag. However, `debugLog` conventionally becomes a no-op if
  //   the `debug` flag is off.
  //
  let hlf = {
    debug: true,
    toString() { return 'hlf'; },
  };
  hlf.debugLog = (hlf.debug === false) ? function(){} :
    (console.log.bind ? console.log.bind(console) : console.log);
  //
  // createExtension
  // ---------------
  // Further binding state and functionality to DOM elements is a common task
  // that should be abstracted away, with common patterns and conventions around
  // logging, namespacing, instance access, and sending actions. Also, instead
  // of API classes and instances inheriting from a base layer, that base layer
  // should be integrated on instantiation.
  //
  // __createExtension__ will return an appropriate, multi-purpose function for
  // the given `createOptions`, comprised of:
  //
  // - The __name__ of the method is required.
  //
  // - __namespace__ is required and must correctly implement `debug`,
  //   `toString`, and `defaults`.
  //
  // - An __apiClass__ definition. It will get modified with base API additions.
  //   Also note that `apiClass` will get published into the namespace, so more
  //   flexibility is possible.
  //
  function createExtension(args) {
    const { name, namespace } = args;

    const { apiClass, autoBind, autoListen, autoSelect } = args;
    let groups = args.baseMethodGroups || [];
    groups.push('action', 'naming');
    if (autoListen) {
      groups.push('event');
    }
    if (autoSelect) {
      groups.push('selection');
    }
    let baseMethods = createExtensionBaseMethods(namespace, groups);
    if (apiClass) {
      namespace.apiClass = apiClass;
      Object.assign(apiClass.prototype, baseMethods);
      if (apiClass.init) {
        apiClass.init();
      }
    }
    const { attrName } = baseMethods;

    const { asSharedInstance, compactOptions } = args;
    let idCounter = 0;
    let instances = {};
    //
    // The __extension__ function handles two variations of input. An action
    // `name` and `payload` can be passed in to trigger the action route. The
    // latter is typically additional, action-specific parameters. Otherwise,
    // if the first argument is an options collection, the normal route is
    // triggered.
    //
    // With the action route, if there is a extension instance and it can
    // __perform__ (`action` mixin), call the method. With the normal route,
    // if there is a extension instance and no arguments are provided we assume
    // the call is to access the instance, not reset it.
    //
    // Otherwise if the instance exists, it is returned. __asSharedInstance__
    // will decide what the extension instance's main element will be. The idea
    // is several elements all share the same extension instance.
    //
    // Otherwise, continue creating the instance by preparing the options and
    // deciding the main element before passing over to `_buildInstance`.
    //
    function extension(subject, ...args) {
      let { action, options, contextElement } = extension._parseArguments(args);
      contextElement = contextElement || document.body;
      let finalSubject = asSharedInstance ? contextElement : subject;

      if (action) {
        extension._dispatchAction(action, finalSubject);
        return;

      } else if (!options) {
        let result = extension._getInstanceOrInstances(subject);
        if (result) {
          return result;
        }

      } else if (Object.keys(instances).length) {
        instances = {};
      }

      options = Object.assign({}, namespace.defaults, options);
      if (finalSubject === contextElement) {
        extension._buildInstance(subject, options, contextElement);
      } else {
        if (finalSubject instanceof HTMLElement) {
          extension._buildInstance(finalSubject, options);
        } else {
          finalSubject.forEach((element) => {
            extension._buildInstance(element, options);
          });
        }
      }

      return extension.bind(null, finalSubject);
    }

    Object.assign(extension, {
      baseMethods,
      //
      // ___buildInstance__ is a subroutine that's part of `createExtension`,
      // which has more details on its required input.
      //
      // 1. Check if element has options set in its root data attribute. If
      //    so, merge those options into our own `finalOptions`.
      //
      // 2. Also decide the `rootElement` based on the situation. It's where the
      //    extension instance id gets stored and the root class gets added. A
      //    shared instance, for example, gets stored on the `contextElement`.
      //
      // 3. If we're provided with a class for the API, instantiate it. Decorate
      //    the instance with additional mixins if fitting.
      //
      // 4. If the `compactOptions` flag is toggled, `finalOptions` will be merged
      //    into the instance. This makes accessing options more convenient, but
      //    can cause conflicts with larger existing APIs that don't account for
      //    such naming conflicts, since _we don't handle conflicts here_. Else,
      //    just alias the conventional `selectors` and `classNames` option groups.
      //
      // 5. If the `autoListen` flag is toggled, add and call the `addEventListeners`
      //    method (ie. via `selection` mixin), to set up element event listening
      //    before initialization. The `eventListeners` property must be set.
      //
      // 6. If the `autoSelect` flag is toggled, add and call the `select` method
      //    (ie. via `selection` mixin), to set up element references before
      //    initialization.
      //
      // 7. If the `className` API addition exists and provides the root class,
      //    add the root class to the decided `rootElement` before initialization.
      //
      // 8. If an `init` method is provided, call it. Convention is to always
      //    provide it.
      //
      // 9. Lastly, store the instance id on `rootElement`.
      //
      _buildInstance(subject, options, contextElement) {
        let element, elements;
        if (subject instanceof HTMLElement) {
          element = subject;
        } else {
          elements = subject;
        }
        let attrOptions;
        let rootElement = asSharedInstance ? contextElement : element;
        if (rootElement.hasAttribute(attrName())) {
          try {
            attrOptions = JSON.parse(element.getAttribute(attrName()));
          } catch (error) {}
        }
        let finalOptions = Object.assign({}, options, attrOptions);
        let instance = new apiClass(element || elements, finalOptions, contextElement);
        instance.rootElement = rootElement;
        if (element) {
          instance.element = element;
        } else {
          instance.contextElement = contextElement;
          instance.elements = elements;
        }
        if (compactOptions) {
          Object.assign(instance, finalOptions);
          delete instance.options;
        } else {
          if (finalOptions.selectors) {
            instance.selectors = finalOptions.selectors;
          }
          if (finalOptions.classNames) {
            instance.classNames = finalOptions.classNames;
          }
        }
        let cleanupTasks = [];
        Object.assign(instance, {
          destructor() {
            cleanupTasks.forEach(task => task(this));
            if (this.deinit) {
              this.deinit();
            }
          }
        });
        if (autoBind) {
          Object.getOwnPropertyNames(apiClass.prototype)
            .filter(name => (
              typeof instance[name] === 'function' && name !== 'constructor'
            ))
            .forEach((name) => {
              instance[name] = instance[name].bind(instance);
            });
        }
        if (autoListen && instance.addEventListeners && instance.eventListeners) {
          const { eventListeners } = instance;
          Object.getOwnPropertyNames(eventListeners)
            .forEach((name) => {
              eventListeners[name] = eventListeners[name].bind(instance);
            });
          instance.addEventListeners(eventListeners);
          cleanupTasks.push(() => {
            instance.removeEventListeners(eventListeners);
          });
        }
        if (autoSelect && instance.selectToProperties) {
          instance.selectToProperties();
        }
        if (instance.className) {
          rootElement.classList.add(instance.className());
        }
        if (instance.init) {
          instance.init();
        }
        extension._setInstance(rootElement, instance);
        return instance;
      },
      _deleteInstance(element) {
        const id = element.getAttribute(attrName('instance-id'));
        delete instances[id];
      },
      _dispatchAction(action, target) {
        if (target instanceof HTMLElement) {
          extension._getInstance(target).perform(action);
        } else {
          Array.from(target).map(extension._getInstance)
            .forEach((instance) => instance.perform(action));
        }
      },
      _getInstance(element) {
        const id = element.getAttribute(attrName('instance-id'));
        return instances[id];
      },
      _getInstanceOrInstances(source) {
        let instance, instances;
        if (source instanceof HTMLElement &&
          (instance = extension._getInstance(source))
        ) {
          return instance;
        } else if ((instances = Array.from(source).map(extension._getInstance)
          .filter(i => i != null)) &&
          instances.length
        ) {
          return instances;
        }
      },
      _parseArguments(args) {
        let action, options, contextElement;
        const [first, second] = args;
        if (typeof first === 'string') {
          action = { name: first, payload: second };
        } else {
          if (first instanceof HTMLElement) {
            contextElement = first;
          } else {
            options = first;
            if (second) {
              contextElement = second;
            }
          }
        }
        return { action, options, contextElement };
      },
      _setInstance(element, instance) {
        const id = idCounter;
        idCounter += 1;
        instance.id = id;
        instances[id] = instance;
        element.setAttribute(attrName('instance-id'), id);
      },
    });

    namespace.extension = extension;

    return extension;
  }
  //
  // __createExtensionBaseMethods__ is an internal subroutine that selectively
  // applies a general mixin collection for writing DOM extensions. It's part of
  // `createExtension`, which has more details on its required input.
  //
  // - Add the __debugLog__ method and attach functionality instead of a no-op
  //   only if namespace `debug` is on.
  //
  // - __action__, allows performing actions by calling action methods.
  //
  // - __naming__, allows namespacing an `attrName`, `className`, or `eventName`.
  //
  // - __event__, sugar around mass-listening to `element` events and
  //   dispatching custom `element` events.
  //
  // - __selection__, sugar around selecting `element` descendants and selecting
  //   to properties based on `selectors`.
  //
  function createExtensionBaseMethods(namespace, groups) {
    let methods = {};
    methods.debugLog = (!namespace.debug ? function(){} :
      hlf.debugLog.bind(null, namespace.toString('log'))
    );
    if (groups.indexOf('action') !== -1) {
      Object.assign(methods, {
        perform(action) {
          const { name, payload } = action;
          let methodName = `perform${name[0].toUpperCase()}${name.substr(1)}`;
          if (!this[methodName]) { return; }
          this[methodName](payload);
        },
        performRemove() {
          namespace.extension._deleteInstance(this.rootElement);
          this.destructor();
        },
      });
    }
    if (groups.indexOf('naming') !== -1) {
      const naming = {
        attrName(name = '') {
          if (name.length) {
            name = `-${name}`;
          }
          return `data-${namespace.toString('data')}${name}`;
        },
        className(name = '') {
          if (name.length) {
            name = `-${name}`;
          }
          return `js-${namespace.toString('class')}${name}`;
        },
        eventName(name) {
          return `${namespace.toString('event')}${name}`;
        },
      };
      Object.assign(methods, naming);
      Object.assign(namespace, naming);
      if (groups.indexOf('css') !== -1) {
        Object.assign(methods, {
          cssVariable(name) {
            return getComputedStyle(this.rootElement)
              .getPropertyValue(`--${namespace.toString('class')}-${name}`);
          }
        });
      }
      if (groups.indexOf('event') !== -1) {
        let normalizeInfos = function(infos) {
          for (const type in infos) {
            if (!infos.hasOwnProperty(type)) { continue; }
            if (typeof infos[type] !== 'function') { continue; }
            infos[type] = [infos[type]];
          }
        };
        Object.assign(methods, {
          addEventListeners(infos, target) {
            target = target || this.rootElement;
            normalizeInfos(infos);
            for (const type in infos) {
              if (!infos.hasOwnProperty(type)) { continue; }
              const [handler, options] = infos[type];
              target.addEventListener(type, handler, options);
            }
          },
          removeEventListeners(infos, target) {
            target = target || this.rootElement;
            normalizeInfos(infos);
            for (const type in infos) {
              if (!infos.hasOwnProperty(type)) { continue; }
              const [handler, options] = infos[type];
              target.removeEventListener(type, handler, options);
            }
          },
          toggleEventListeners(on, infos, target) {
            this[`${on ? 'add' : 'remove'}EventListeners`](infos, target);
          },
          createCustomEvent(type, detail) {
            let initArgs = { detail };
            initArgs.bubbles = true;
            return new CustomEvent(this.eventName(type), initArgs);
          },
          dispatchCustomEvent(type, detail = {}) {
            return this.rootElement.dispatchEvent(this.createCustomEvent(type, detail));
          },
        });
      }
      if (groups.indexOf('selection') !== -1) {
        Object.assign(methods, {
          selectByClass(name) {
            return this.rootElement.querySelector(`.${this.className(name)}`);
          },
          selectAllByClass(name) {
            return this.rootElement.querySelectorAll(`.${this.className(name)}`);
          },
          selectToProperties() {
            if (!this.rootElement || !this.selectors) {
              throw 'Missing requirements.';
            }
            for (const name in this.selectors) {
              if (!this.selectors.hasOwnProperty(name)) { continue; }
              const selector = this.selectors[name];
              if (name.substr(-1) === 's') {
                this[name] = this.rootElement.querySelectorAll(selector);
              } else {
                this[name] = this.rootElement.querySelector(selector);
              }
            }
          },
        });
      }
    }
    return methods;
  }

  Object.assign(hlf, { createExtension });

  if (hlf.debug && typeof window === 'object') {
    Object.assign(window, { hlf });
  }

  return hlf;
});
<commit_msg>ES6 rewrite: minor fix-up.<commit_after>//
// HLF Extensions Core
// ===================
// [Tests](../../tests/js/core.html)
//
(function(root, namespace) {
  //
  // § __UMD__
  // - When AMD, register the attacher as an anonymous module.
  // - When Node or Browserify, set module exports to the attach result.
  // - When browser globals (root is window), Just run the attach function.
  //
  if (typeof define === 'function' && define.amd) {
    define(namespace);
  } else if (typeof exports === 'object') {
    module.exports = namespace();
  } else {
    window.hlf = namespace();
  }
})(this, function() {
  'use strict';
  //
  // Namespace
  // ---------
  // It takes some more boilerplate and helpers to write DOM extensions. That
  // code and set of conventions is here in the root namespace __hlf__. Child
  // namespaces follow suit convention.
  //
  // - The __debug__ flag here toggles debug logging for everything in the library
  //   that doesn't have a custom debug flag in its namespace.
  //
  // - __toString__ is mainly for extension namespacing. For now, its base form
  //   is very simple.
  //
  // - __debugLog__ in its base form just wraps around `console.log` and links to
  //   the `debug` flag. However, `debugLog` conventionally becomes a no-op if
  //   the `debug` flag is off.
  //
  let hlf = {
    debug: true,
    toString() { return 'hlf'; },
  };
  hlf.debugLog = (hlf.debug === false) ? function(){} :
    (console.log.bind ? console.log.bind(console) : console.log);
  //
  // createExtension
  // ---------------
  // Further binding state and functionality to DOM elements is a common task
  // that should be abstracted away, with common patterns and conventions around
  // logging, namespacing, instance access, and sending actions. Also, instead
  // of API classes and instances inheriting from a base layer, that base layer
  // should be integrated on instantiation.
  //
  // __createExtension__ will return an appropriate, multi-purpose function for
  // the given `createOptions`, comprised of:
  //
  // - The __name__ of the method is required.
  //
  // - __namespace__ is required and must correctly implement `debug`,
  //   `toString`, and `defaults`.
  //
  // - An __apiClass__ definition. It will get modified with base API additions.
  //   Also note that `apiClass` will get published into the namespace, so more
  //   flexibility is possible.
  //
  function createExtension(args) {
    const { name, namespace } = args;

    const { apiClass, autoBind, autoListen, autoSelect } = args;
    let groups = args.baseMethodGroups || [];
    groups.push('action', 'naming');
    if (autoListen) {
      groups.push('event');
    }
    if (autoSelect) {
      groups.push('selection');
    }
    let baseMethods = createExtensionBaseMethods(namespace, groups);
    if (apiClass) {
      namespace.apiClass = apiClass;
      Object.assign(apiClass.prototype, baseMethods);
      if (apiClass.init) {
        apiClass.init();
      }
    }
    const { attrName } = baseMethods;

    const { asSharedInstance, compactOptions } = args;
    let idCounter = 0;
    let instances = {};
    //
    // The __extension__ function handles two variations of input. An action
    // `name` and `payload` can be passed in to trigger the action route. The
    // latter is typically additional, action-specific parameters. Otherwise,
    // if the first argument is an options collection, the normal route is
    // triggered.
    //
    // With the action route, if there is a extension instance and it can
    // __perform__ (`action` mixin), call the method. With the normal route,
    // if there is a extension instance and no arguments are provided we assume
    // the call is to access the instance, not reset it.
    //
    // Otherwise if the instance exists, it is returned. __asSharedInstance__
    // will decide what the extension instance's main element will be. The idea
    // is several elements all share the same extension instance.
    //
    // Otherwise, continue creating the instance by preparing the options and
    // deciding the main element before passing over to `_buildInstance`.
    //
    function extension(subject, ...args) {
      let { action, options, contextElement } = extension._parseArguments(args);
      contextElement = contextElement || document.body;
      let finalSubject = asSharedInstance ? contextElement : subject;

      if (action) {
        extension._dispatchAction(action, finalSubject);
        return;

      } else if (!options) {
        let result = extension._getInstanceOrInstances(subject);
        if (result) {
          return result;
        }

      } else if (Object.keys(instances).length) {
        instances = {};
      }

      options = Object.assign({}, namespace.defaults, options);
      if (finalSubject === contextElement) {
        extension._buildInstance(subject, options, contextElement);
      } else {
        if (finalSubject instanceof HTMLElement) {
          extension._buildInstance(finalSubject, options);
        } else {
          finalSubject.forEach((element) => {
            extension._buildInstance(element, options);
          });
        }
      }

      return extension.bind(null, finalSubject);
    }

    Object.assign(extension, {
      baseMethods,
      //
      // ___buildInstance__ is a subroutine that's part of `createExtension`,
      // which has more details on its required input.
      //
      // 1. Check if element has options set in its root data attribute. If
      //    so, merge those options into our own `finalOptions`.
      //
      // 2. Also decide the `rootElement` based on the situation. It's where the
      //    extension instance id gets stored and the root class gets added. A
      //    shared instance, for example, gets stored on the `contextElement`.
      //
      // 3. If we're provided with a class for the API, instantiate it. Decorate
      //    the instance with additional mixins if fitting.
      //
      // 4. If the `compactOptions` flag is toggled, `finalOptions` will be merged
      //    into the instance. This makes accessing options more convenient, but
      //    can cause conflicts with larger existing APIs that don't account for
      //    such naming conflicts, since _we don't handle conflicts here_. Else,
      //    just alias the conventional `selectors` and `classNames` option groups.
      //
      // 5. If the `autoListen` flag is toggled, add and call the `addEventListeners`
      //    method (ie. via `selection` mixin), to set up element event listening
      //    before initialization. The `eventListeners` property must be set.
      //
      // 6. If the `autoSelect` flag is toggled, add and call the `select` method
      //    (ie. via `selection` mixin), to set up element references before
      //    initialization.
      //
      // 7. If the `className` API addition exists and provides the root class,
      //    add the root class to the decided `rootElement` before initialization.
      //
      // 8. If an `init` method is provided, call it. Convention is to always
      //    provide it.
      //
      // 9. Lastly, store the instance id on `rootElement`.
      //
      _buildInstance(subject, options, contextElement) {
        let element, elements;
        if (subject instanceof HTMLElement) {
          element = subject;
        } else {
          elements = subject;
        }
        let attrOptions;
        let rootElement = asSharedInstance ? contextElement : element;
        if (rootElement.hasAttribute(attrName())) {
          try {
            attrOptions = JSON.parse(element.getAttribute(attrName()));
          } catch (error) {}
        }
        let finalOptions = Object.assign({}, options, attrOptions);
        let instance = new apiClass(element || elements, finalOptions, contextElement);
        instance.rootElement = rootElement;
        if (element) {
          instance.element = element;
        } else {
          instance.contextElement = contextElement;
          instance.elements = elements;
        }
        if (compactOptions) {
          Object.assign(instance, finalOptions);
        } else {
          instance.options = finalOptions;
          if (finalOptions.selectors) {
            instance.selectors = finalOptions.selectors;
          }
          if (finalOptions.classNames) {
            instance.classNames = finalOptions.classNames;
          }
        }
        let cleanupTasks = [];
        Object.assign(instance, {
          destructor() {
            cleanupTasks.forEach(task => task(this));
            if (this.deinit) {
              this.deinit();
            }
          }
        });
        if (autoBind) {
          Object.getOwnPropertyNames(apiClass.prototype)
            .filter(name => (
              typeof instance[name] === 'function' && name !== 'constructor'
            ))
            .forEach((name) => {
              instance[name] = instance[name].bind(instance);
            });
        }
        if (autoListen && instance.addEventListeners && instance.eventListeners) {
          const { eventListeners } = instance;
          Object.getOwnPropertyNames(eventListeners)
            .forEach((name) => {
              eventListeners[name] = eventListeners[name].bind(instance);
            });
          instance.addEventListeners(eventListeners);
          cleanupTasks.push(() => {
            instance.removeEventListeners(eventListeners);
          });
        }
        if (autoSelect && instance.selectToProperties) {
          instance.selectToProperties();
        }
        if (instance.className) {
          rootElement.classList.add(instance.className());
        }
        if (instance.init) {
          instance.init();
        }
        extension._setInstance(rootElement, instance);
        return instance;
      },
      _deleteInstance(element) {
        const id = element.getAttribute(attrName('instance-id'));
        delete instances[id];
      },
      _dispatchAction(action, target) {
        if (target instanceof HTMLElement) {
          extension._getInstance(target).perform(action);
        } else {
          Array.from(target).map(extension._getInstance)
            .forEach((instance) => instance.perform(action));
        }
      },
      _getInstance(element) {
        const id = element.getAttribute(attrName('instance-id'));
        return instances[id];
      },
      _getInstanceOrInstances(source) {
        let instance, instances;
        if (source instanceof HTMLElement &&
          (instance = extension._getInstance(source))
        ) {
          return instance;
        } else if ((instances = Array.from(source).map(extension._getInstance)
          .filter(i => i != null)) &&
          instances.length
        ) {
          return instances;
        }
      },
      _parseArguments(args) {
        let action, options, contextElement;
        const [first, second] = args;
        if (typeof first === 'string') {
          action = { name: first, payload: second };
        } else {
          if (first instanceof HTMLElement) {
            contextElement = first;
          } else {
            options = first;
            if (second) {
              contextElement = second;
            }
          }
        }
        return { action, options, contextElement };
      },
      _setInstance(element, instance) {
        const id = idCounter;
        idCounter += 1;
        instance.id = id;
        instances[id] = instance;
        element.setAttribute(attrName('instance-id'), id);
      },
    });

    namespace.extension = extension;

    return extension;
  }
  //
  // __createExtensionBaseMethods__ is an internal subroutine that selectively
  // applies a general mixin collection for writing DOM extensions. It's part of
  // `createExtension`, which has more details on its required input.
  //
  // - Add the __debugLog__ method and attach functionality instead of a no-op
  //   only if namespace `debug` is on.
  //
  // - __action__, allows performing actions by calling action methods.
  //
  // - __naming__, allows namespacing an `attrName`, `className`, or `eventName`.
  //
  // - __event__, sugar around mass-listening to `element` events and
  //   dispatching custom `element` events.
  //
  // - __selection__, sugar around selecting `element` descendants and selecting
  //   to properties based on `selectors`.
  //
  function createExtensionBaseMethods(namespace, groups) {
    let methods = {};
    methods.debugLog = (!namespace.debug ? function(){} :
      hlf.debugLog.bind(null, namespace.toString('log'))
    );
    if (groups.indexOf('action') !== -1) {
      Object.assign(methods, {
        perform(action) {
          const { name, payload } = action;
          let methodName = `perform${name[0].toUpperCase()}${name.substr(1)}`;
          if (!this[methodName]) { return; }
          this[methodName](payload);
        },
        performRemove() {
          namespace.extension._deleteInstance(this.rootElement);
          this.destructor();
        },
      });
    }
    if (groups.indexOf('naming') !== -1) {
      const naming = {
        attrName(name = '') {
          if (name.length) {
            name = `-${name}`;
          }
          return `data-${namespace.toString('data')}${name}`;
        },
        className(name = '') {
          if (name.length) {
            name = `-${name}`;
          }
          return `js-${namespace.toString('class')}${name}`;
        },
        eventName(name) {
          return `${namespace.toString('event')}${name}`;
        },
      };
      Object.assign(methods, naming);
      Object.assign(namespace, naming);
      if (groups.indexOf('css') !== -1) {
        Object.assign(methods, {
          cssVariable(name) {
            return getComputedStyle(this.rootElement)
              .getPropertyValue(`--${namespace.toString('class')}-${name}`);
          }
        });
      }
      if (groups.indexOf('event') !== -1) {
        let normalizeInfos = function(infos) {
          for (const type in infos) {
            if (!infos.hasOwnProperty(type)) { continue; }
            if (typeof infos[type] !== 'function') { continue; }
            infos[type] = [infos[type]];
          }
        };
        Object.assign(methods, {
          addEventListeners(infos, target) {
            target = target || this.rootElement;
            normalizeInfos(infos);
            for (const type in infos) {
              if (!infos.hasOwnProperty(type)) { continue; }
              const [handler, options] = infos[type];
              target.addEventListener(type, handler, options);
            }
          },
          removeEventListeners(infos, target) {
            target = target || this.rootElement;
            normalizeInfos(infos);
            for (const type in infos) {
              if (!infos.hasOwnProperty(type)) { continue; }
              const [handler, options] = infos[type];
              target.removeEventListener(type, handler, options);
            }
          },
          toggleEventListeners(on, infos, target) {
            this[`${on ? 'add' : 'remove'}EventListeners`](infos, target);
          },
          createCustomEvent(type, detail) {
            let initArgs = { detail };
            initArgs.bubbles = true;
            return new CustomEvent(this.eventName(type), initArgs);
          },
          dispatchCustomEvent(type, detail = {}) {
            return this.rootElement.dispatchEvent(this.createCustomEvent(type, detail));
          },
        });
      }
      if (groups.indexOf('selection') !== -1) {
        Object.assign(methods, {
          selectByClass(name) {
            return this.rootElement.querySelector(`.${this.className(name)}`);
          },
          selectAllByClass(name) {
            return this.rootElement.querySelectorAll(`.${this.className(name)}`);
          },
          selectToProperties() {
            if (!this.rootElement || !this.selectors) {
              throw 'Missing requirements.';
            }
            for (const name in this.selectors) {
              if (!this.selectors.hasOwnProperty(name)) { continue; }
              const selector = this.selectors[name];
              if (name.substr(-1) === 's') {
                this[name] = this.rootElement.querySelectorAll(selector);
              } else {
                this[name] = this.rootElement.querySelector(selector);
              }
            }
          },
        });
      }
    }
    return methods;
  }

  Object.assign(hlf, { createExtension });

  if (hlf.debug && typeof window === 'object') {
    Object.assign(window, { hlf });
  }

  return hlf;
});
<|endoftext|>


<commit_before>import mat4 from "gl-matrix/src/gl-matrix/mat4";

import { waitUntilAnimationFrame } from "./lib";
import GLWrapper from "./GLWrapper";

/**
 * @method draw
 * @param {WebGLRenderingContext} gl - WebGL Context
 * @param {Object[]} shaders - GLSL Shader Object
 */
export async function draw(gl, shaders) {
    // glWrapper を作る
    const glWrapper = new GLWrapper(gl);

    // クリア色の指定
    gl.clearColor(0, 0, 0, 1);

    // 深度テストを有効化
    gl.enable(gl.DEPTH_TEST);

    // メインシェーダーの取得
    const { vertex, fragment } = shaders[0];

    // シェーダーからプログラムを取得
    const program = glWrapper.compileShaderAndLink({vertex, fragment});

    // uniform の Location を取得
    const pLocation = gl.getUniformLocation(program, "projectionMatrix");
    const mvLocation = gl.getUniformLocation(program, "modelviewMatrix");

    // attribute の Location を取得
    const vLocation = gl.getAttribLocation(program, "vertex");
    const nLocation = gl.getAttribLocation(program, "normal");

    // 指定した attribute の Location のレジスタを有効にする
    glWrapper.enableAttributes([vLocation, nLocation]);

    // Vertex Buffer Object(VBO) を作る
    const vBuffer = glWrapper.createVertexBuffer(Float32Array.of(-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0));
    const nBuffer = glWrapper.createVertexBuffer(Float32Array.of(0, 0, 1, 0, 0, 1, 0, 0, 1));

    // attribute で VBO の指定
    glWrapper.setAttribute(vBuffer, vLocation, 3);
    glWrapper.setAttribute(nBuffer, nLocation, 3);

    // 描画処理
    const startTime = performance.now();
    for(;;) {
        // 描画開始からの時間
        const delta = await waitUntilAnimationFrame() - startTime;

        // WebGL の canvas の内容をクリア
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        // シェーダーに uniform を与える
        {
            // frustum 行列の生成
            const pMat = mat4.create();
            mat4.frustum(pMat, -1, 1, -1, 1, 3, 10);

            // 移動回転行列の生成
            const mvMat = mat4.create();
            mat4.translate(mvMat, mvMat, [0, 0, -6]);
            mat4.rotate(mvMat, mvMat, -0.002 * delta, [0, 1, 0]); // 軸 [0, 1, 0] で回転

            // uniform で頂点シェーダに送信
            gl.uniformMatrix4fv(pLocation, false, pMat);
            gl.uniformMatrix4fv(mvLocation, false, mvMat);
        }

        // 今まで設定した内容で WebGL に送信
        gl.drawArrays(gl.TRIANGLES, 0, 3);

        // キューの即時実行
        gl.flush();
    }
}<commit_msg>double polygons<commit_after>import mat4 from "gl-matrix/src/gl-matrix/mat4";

import { waitUntilAnimationFrame } from "./lib";
import GLWrapper from "./GLWrapper";

/**
 * @method draw
 * @param {WebGLRenderingContext} gl - WebGL Context
 * @param {Object[]} shaders - GLSL Shader Object
 */
export async function draw(gl, shaders) {
    // glWrapper を作る
    const glWrapper = new GLWrapper(gl);

    // クリア色の指定
    gl.clearColor(0, 0, 0, 1);

    // 深度テストを有効化
    gl.enable(gl.DEPTH_TEST);

    // メインシェーダーの取得
    const { vertex, fragment } = shaders[0];

    // シェーダーからプログラムを取得
    const program = glWrapper.compileShaderAndLink({vertex, fragment});

    // uniform の Location を取得
    const pLocation = gl.getUniformLocation(program, "projectionMatrix");
    const mvLocation = gl.getUniformLocation(program, "modelviewMatrix");

    // シェーダーに attribute を与える
    {
        // attribute の Location を取得
        const vLocation = gl.getAttribLocation(program, "vertex");
        const nLocation = gl.getAttribLocation(program, "normal");

        // 指定した attribute の Location のレジスタを有効にする
        glWrapper.enableAttributes([vLocation, nLocation]);

        // Vertex Buffer Object(VBO) を作る
        const vBuffer = glWrapper.createVertexBuffer(Float32Array.of(-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0));
        const nBuffer = glWrapper.createVertexBuffer(Float32Array.of(0, 0, 1, 0, 0, 1, 0, 0, 1));

        // attribute で VBO の指定
        glWrapper.setAttribute(vBuffer, vLocation, 3);
        glWrapper.setAttribute(nBuffer, nLocation, 3);
    }

    // 描画処理
    const startTime = performance.now();
    for(;;) {
        // 描画開始からの時間
        const delta = await waitUntilAnimationFrame() - startTime;

        // WebGL の canvas の内容をクリア
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        // シェーダーに uniform を与える
        {
            // frustum 行列
            const pMat = mat4.create();
            mat4.frustum(pMat, -1, 1, -1, 1, 3, 10);

            // 一つ目
            {
                // 移動回転行列
                const mvMat = mat4.create();
                mat4.translate(mvMat, mvMat, [-1, 0, -6]);
                mat4.rotate(mvMat, mvMat, -0.002 * delta, [0, 1, 0]); // 軸 [0, 1, 0] で回転

                // uniform で頂点シェーダに送信
                gl.uniformMatrix4fv(pLocation, false, pMat);
                gl.uniformMatrix4fv(mvLocation, false, mvMat);

                // 今まで設定した内容で WebGL に送信
                gl.drawArrays(gl.TRIANGLES, 0, 3);
            }

            // 二つ目
            {
                // 移動回転行列
                const mvMat = mat4.create();
                mat4.translate(mvMat, mvMat, [1, 0, -6]);
                mat4.rotate(mvMat, mvMat, 0.002 * delta + Math.PI, [0, 1, 0]); // 軸 [0, 1, 0] で回転

                // uniform で頂点シェーダに送信
                gl.uniformMatrix4fv(pLocation, false, pMat);
                gl.uniformMatrix4fv(mvLocation, false, mvMat);

                // 今まで設定した内容で WebGL に送信
                gl.drawArrays(gl.TRIANGLES, 0, 3);
            }
        }
        // キューの即時実行
        gl.flush();
    }
}<|endoftext|>