Fix bad merge (#79)
* Fix bad merge Some classes were still prefixed by ascii, and constants not prefixed by c. * More es6 changes and refactorings Added helper methods to vector for creating vector from touch event and mouse event. Added up, down, left, right helper methods. * Refactor draw.js into one module per draw function
This commit is contained in:
parent
4097ee2322
commit
8277399c61
|
@ -1,5 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
java -client -jar closure-compiler.jar \
|
||||
--js js-lib/*.js \
|
||||
--js js-lib/**.js \
|
||||
--js js-lib/**/*.js \
|
||||
--warning_level=VERBOSE --formatting=PRETTY_PRINT --language_in=ECMASCRIPT6 --compilation_level=ADVANCED_OPTIMIZATIONS \
|
||||
--externs=jquery-3.1-externs.js \
|
||||
> js-compiled.js
|
||||
|
||||
|
|
|
@ -20,12 +20,12 @@ export class Box {
|
|||
/** type {number} */ this.endY = Math.max(a.y, b.y);
|
||||
}
|
||||
|
||||
/** @return {Vector} */
|
||||
/** @return {!Vector} */
|
||||
topLeft() {
|
||||
return new Vector(this.startX, this.startY);
|
||||
}
|
||||
|
||||
/** @return {Vector} */
|
||||
/** @return {!Vector} */
|
||||
bottomRight() {
|
||||
return new Vector(this.endX, this.endY);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export class Cell {
|
|||
|
||||
/** @return {boolean} */
|
||||
isSpecial() {
|
||||
return ALL_SPECIAL_VALUES.indexOf(this.getRawValue()) != -1;
|
||||
return ALL_SPECIAL_VALUES.includes(this.getRawValue());
|
||||
}
|
||||
|
||||
/** @return {boolean} */
|
||||
|
|
|
@ -2,7 +2,6 @@ import * as c from './constants';
|
|||
import Vector from './vector';
|
||||
import View from './view';
|
||||
import State from './state';
|
||||
import DrawSelect from './draw-select';
|
||||
import {
|
||||
DrawFunction,
|
||||
DrawBox,
|
||||
|
@ -11,7 +10,8 @@ import {
|
|||
DrawErase,
|
||||
DrawMove,
|
||||
DrawText,
|
||||
} from './draw';
|
||||
DrawSelect,
|
||||
} from './draw/index';
|
||||
|
||||
|
||||
/**
|
||||
|
|
536
js-lib/draw.js
536
js-lib/draw.js
|
@ -1,536 +0,0 @@
|
|||
import * as c from './constants';
|
||||
import State from './state';
|
||||
import Vector from './vector';
|
||||
import { Box } from './common';
|
||||
|
||||
/**
|
||||
* All drawing classes and functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Draws a line on the diagram state.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Vector} startPosition
|
||||
* @param {Vector} endPosition
|
||||
* @param {boolean} clockwise
|
||||
* @param {string=} value
|
||||
*/
|
||||
function drawLine(state, startPosition, endPosition, clockwise, value = c.SPECIAL_VALUE) {
|
||||
var box = new Box(startPosition, endPosition);
|
||||
var startX = box.startX;
|
||||
var startY = box.startY;
|
||||
var endX = box.endX;
|
||||
var endY = box.endY;
|
||||
|
||||
var midX = clockwise ? endPosition.x : startPosition.x;
|
||||
var midY = clockwise ? startPosition.y : endPosition.y;
|
||||
|
||||
while (startX++ < endX) {
|
||||
var position = new Vector(startX, midY);
|
||||
var context = state.getContext(new Vector(startX, midY));
|
||||
// Don't erase any lines that we cross.
|
||||
if (value != ' ' || context.up + context.down != 2) {
|
||||
state.drawValueIncremental(position, value);
|
||||
}
|
||||
}
|
||||
while (startY++ < endY) {
|
||||
var position = new Vector(midX, startY);
|
||||
var context = state.getContext(new Vector(midX, startY));
|
||||
// Don't erase any lines that we cross.
|
||||
if (value != ' ' || context.left + context.right != 2) {
|
||||
state.drawValueIncremental(position, value);
|
||||
}
|
||||
}
|
||||
|
||||
state.drawValue(startPosition, value);
|
||||
state.drawValue(endPosition, value);
|
||||
state.drawValueIncremental(new Vector(midX, midY), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface for different drawing functions, e.g. box, line, etc.
|
||||
* @interface
|
||||
*/
|
||||
export class DrawFunction {
|
||||
/** Start of drawing. @param {Vector} position */
|
||||
start(position) {};
|
||||
/** Drawing move. @param {Vector} position */
|
||||
move(position) {};
|
||||
/** End of drawing. */
|
||||
end() {};
|
||||
/** Cursor for given cell.
|
||||
* @param {Vector} position
|
||||
* @return {string}
|
||||
*/
|
||||
getCursor(position) {};
|
||||
/** Handle the key with given value being pressed. @param {string} value */
|
||||
handleKey(value) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawBox {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
/** @type {Vector} */ this.startPosition = null;
|
||||
/** @type {Vector} */ this.endPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.endPosition = position;
|
||||
this.state.clearDraw();
|
||||
drawLine(this.state, this.startPosition, position, true);
|
||||
drawLine(this.state, this.startPosition, position, false);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawLine {
|
||||
/**
|
||||
* @param {State} state
|
||||
* @param {boolean} isArrow
|
||||
*/
|
||||
constructor(state, isArrow) {
|
||||
this.state = state;
|
||||
this.isArrow = isArrow;
|
||||
/** @type {Vector} */ this.startPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
|
||||
// Try to infer line orientation.
|
||||
// TODO: Split the line into two lines if we can't satisfy both ends.
|
||||
var startContext = this.state.getContext(this.startPosition);
|
||||
var endContext = this.state.getContext(position);
|
||||
var clockwise = (startContext.up && startContext.down) ||
|
||||
(endContext.left && endContext.right);
|
||||
|
||||
drawLine(this.state, this.startPosition, position, clockwise);
|
||||
if (this.isArrow) {
|
||||
this.state.drawValue(position, c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawFreeform {
|
||||
/**
|
||||
* @param {State} state
|
||||
* @param {?string} value
|
||||
*/
|
||||
constructor(state, value) {
|
||||
this.state = state;
|
||||
this.value = value;
|
||||
if (c.TOUCH_ENABLED) {
|
||||
$('#freeform-tool-input').val('');
|
||||
$('#freeform-tool-input').hide(0, function() {$('#freeform-tool-input').show(0, function() {$('#freeform-tool-input').focus();});});
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.state.drawValue(position, this.value);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.drawValue(position, this.value);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {
|
||||
if (c.TOUCH_ENABLED) {
|
||||
this.value = $('#freeform-tool-input').val().substr(0, 1);
|
||||
$('#freeform-tool-input').blur();
|
||||
$('#freeform-tool-input').hide(0);
|
||||
}
|
||||
if (value.length == 1) {
|
||||
// The value is not a special character, so lets use it.
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawText {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state, view) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
this.endPosition = null;
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.state.commitDraw();
|
||||
$('#text-tool-input').val('');
|
||||
this.startPosition = position;
|
||||
|
||||
// Not working yet, needs fixing so that it can remove the underlying text completely.
|
||||
//this.loadExistingText(position);
|
||||
|
||||
// Effectively highlights the starting cell.
|
||||
var currentValue = this.state.getCell(this.startPosition).getRawValue();
|
||||
this.state.drawValue(this.startPosition,
|
||||
currentValue == null ? c.ERASE_CHAR : currentValue);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
if (this.startPosition != null) {
|
||||
this.endPosition = this.startPosition;
|
||||
this.startPosition = null;
|
||||
// Valid end click/press, show the textbox and focus it.
|
||||
$('#text-tool-widget').hide(0, function() {$('#text-tool-widget').show(0, function() {$('#text-tool-input').focus();});});
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'pointer';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {
|
||||
var text = $('#text-tool-input').val();
|
||||
this.state.clearDraw();
|
||||
var x = 0, y = 0;
|
||||
for(var i = 0; i < text.length; i++) {
|
||||
if (text[i] == '\n') {
|
||||
y++;
|
||||
x = 0;
|
||||
continue;
|
||||
}
|
||||
this.state.drawValue(this.endPosition.add(new Vector(x, y)), text[i]);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads any existing text if it is present.
|
||||
* TODO: This is horrible, and does not quite work, fix it.
|
||||
*/
|
||||
loadExistingText(position) {
|
||||
var currentPosition = new Vector(position.x, position.y);
|
||||
var cell = this.state.getCell(position);
|
||||
var spacesCount = 0;
|
||||
// Go back to find the start of the line.
|
||||
while ((!cell.isSpecial() && cell.getRawValue() != null) || spacesCount < 1) {
|
||||
if (cell.getRawValue() == null) {
|
||||
spacesCount++;
|
||||
} else if (!cell.isSpecial()) {
|
||||
spacesCount = 0;
|
||||
}
|
||||
currentPosition.x--;
|
||||
cell = this.state.getCell(currentPosition);
|
||||
}
|
||||
this.startPosition = currentPosition.add(new Vector(spacesCount + 1, 0));
|
||||
var text = '';
|
||||
spacesCount = 0;
|
||||
currentPosition = this.startPosition.clone();
|
||||
// Go forward to load the text.
|
||||
while ((!cell.isSpecial() && cell.getRawValue() != null) || spacesCount < 1) {
|
||||
cell = this.state.getCell(currentPosition);
|
||||
if (cell.getRawValue() == null) {
|
||||
spacesCount++;
|
||||
text += ' ';
|
||||
} else if (!cell.isSpecial()) {
|
||||
spacesCount = 0;
|
||||
text += cell.getRawValue();
|
||||
this.state.drawValue(currentPosition, cell.getRawValue());
|
||||
}
|
||||
currentPosition.x++;
|
||||
}
|
||||
$('#text-tool-input').val(text.substr(0, text.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawErase {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
this.endPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
this.move(position);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
this.endPosition = position;
|
||||
|
||||
var startX = Math.min(this.startPosition.x, this.endPosition.x);
|
||||
var startY = Math.min(this.startPosition.y, this.endPosition.y);
|
||||
var endX = Math.max(this.startPosition.x, this.endPosition.x);
|
||||
var endY = Math.max(this.startPosition.y, this.endPosition.y);
|
||||
|
||||
for (var i = startX; i <= endX; i++) {
|
||||
for (var j = startY; j <= endY; j++) {
|
||||
this.state.drawValue(new Vector(i, j), c.ERASE_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export class DrawMove {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
/** @type {!Array<{position, clockwise, startIsAlt, midPointIsAlt, endIsAlt}>} */
|
||||
this.ends = [];
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition =
|
||||
c.TOUCH_ENABLED ? this.snapToNearest(position) : position;
|
||||
this.ends = [];
|
||||
|
||||
// If this isn't a special cell then quit, or things get weird.
|
||||
if (!this.state.getCell(this.startPosition).isSpecial()) {
|
||||
return;
|
||||
}
|
||||
var context = this.state.getContext(this.startPosition);
|
||||
|
||||
var ends = [];
|
||||
for (var i of c.DIRECTIONS) {
|
||||
var midPoints = this.followLine(this.startPosition, i);
|
||||
for (var midPoint of midPoints) {
|
||||
// Clockwise is a lie, it is true if we move vertically first.
|
||||
var clockwise = (i.x != 0);
|
||||
var startIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(position).getRawValue()) != -1;
|
||||
var midPointIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(midPoint).getRawValue()) != -1;
|
||||
|
||||
var midPointContext = this.state.getContext(midPoint);
|
||||
// Special case, a straight line with no turns.
|
||||
if (midPointContext.sum() == 1) {
|
||||
ends.push({position: midPoint, clockwise, startIsAlt, endIsAlt: midPointIsAlt});
|
||||
continue;
|
||||
}
|
||||
// Continue following lines from the midpoint.
|
||||
for (var j of c.DIRECTIONS) {
|
||||
if (i.add(j).length() == 0 || i.add(j).length() == 2) {
|
||||
// Don't go back on ourselves, or don't carry on in same direction.
|
||||
continue;
|
||||
}
|
||||
var secondEnds = this.followLine(midPoint, j);
|
||||
// Ignore any directions that didn't go anywhere.
|
||||
if (secondEnds.length == 0) {
|
||||
continue;
|
||||
}
|
||||
var secondEnd = secondEnds[0];
|
||||
var endIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(secondEnd).getRawValue()) != -1;
|
||||
// On the second line we don't care about multiple
|
||||
// junctions, just the last.
|
||||
ends.push({position: secondEnd, clockwise, startIsAlt, midPointIsAlt, endIsAlt});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.ends = ends;
|
||||
// Redraw the new lines after we have cleared the existing ones.
|
||||
this.move(this.startPosition);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
// Clear all the lines so we can draw them afresh.
|
||||
for (var end of this.ends) {
|
||||
drawLine(this.state, this.startPosition, end.position, end.clockwise, ' ');
|
||||
}
|
||||
for (var i in this.ends) {
|
||||
drawLine(this.state, position, end.position, end.clockwise);
|
||||
}
|
||||
for (var end of this.ends) {
|
||||
// If the ends or midpoint of the line was a alt character (arrow), need to preserve that.
|
||||
if (end.startIsAlt) {
|
||||
this.state.drawValue(position, c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
if (end.endIsAlt) {
|
||||
this.state.drawValue(end.position, c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
if (end.midPointIsAlt) {
|
||||
var midX = end.clockwise ? end.position.x : position.x;
|
||||
var midY = end.clockwise ? position.y : end.position.y;
|
||||
this.state.drawValue(new Vector(midX, midY), c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Follows a line in a given direction from the startPosition.
|
||||
* Returns a list of positions that were line 'junctions'. This is a bit of a
|
||||
* loose definition, but basically means a point around which we resize things.
|
||||
* @param {Vector} startPosition
|
||||
* @param {Vector} direction
|
||||
* @return {!Array<Vector>}
|
||||
*/
|
||||
followLine(startPosition, direction) {
|
||||
var endPosition = startPosition.clone();
|
||||
var junctions = [];
|
||||
while (true) {
|
||||
var nextEnd = endPosition.add(direction);
|
||||
if (!this.state.getCell(nextEnd).isSpecial()) {
|
||||
// Junctions: Right angles and end T-Junctions.
|
||||
if (!startPosition.equals(endPosition)) {
|
||||
junctions.push(endPosition);
|
||||
}
|
||||
return junctions;
|
||||
}
|
||||
|
||||
endPosition = nextEnd;
|
||||
var context = this.state.getContext(endPosition);
|
||||
// Junctions: Side T-Junctions.
|
||||
if (context.sum() == 3) {
|
||||
junctions.push(endPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given position, finds the nearest cell that is of any interest to the
|
||||
* move tool, e.g. a corner or a line. Will look up to 1 cell in each direction
|
||||
* including diagonally.
|
||||
* @param {Vector} position
|
||||
* @return {Vector}
|
||||
*/
|
||||
snapToNearest(position) {
|
||||
if (this.state.getCell(position).isSpecial()) {
|
||||
return position;
|
||||
}
|
||||
var allDirections = c.DIRECTIONS.concat([
|
||||
c.DIR_LEFT.add(c.DIR_UP),
|
||||
c.DIR_LEFT.add(c.DIR_DOWN),
|
||||
c.DIR_RIGHT.add(c.DIR_UP),
|
||||
c.DIR_RIGHT.add(c.DIR_DOWN)]);
|
||||
|
||||
var bestDirection = null;
|
||||
var bestContextSum = 0;
|
||||
for (var direction of allDirections) {
|
||||
// Find the most connected cell, essentially.
|
||||
var newPos = position.add(direction);
|
||||
var contextSum = this.state.getContext(newPos).sum();
|
||||
if (this.state.getCell(newPos).isSpecial() &&
|
||||
contextSum > bestContextSum) {
|
||||
bestDirection = direction;
|
||||
bestContextSum = contextSum;
|
||||
}
|
||||
}
|
||||
if (bestDirection == null) {
|
||||
// Didn't find anything, so just return the current cell.
|
||||
return position;
|
||||
}
|
||||
return position.add(bestDirection);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
if (this.state.getCell(position).isSpecial()) {
|
||||
return 'pointer';
|
||||
} else {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
import DrawFunction from './function';
|
||||
import { drawLine } from './utils';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawBox {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
/** @type {Vector} */ this.startPosition = null;
|
||||
/** @type {Vector} */ this.endPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.endPosition = position;
|
||||
this.state.clearDraw();
|
||||
drawLine(this.state, this.startPosition, position, true);
|
||||
drawLine(this.state, this.startPosition, position, false);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {};
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { DrawFunction } from './function';
|
||||
import { ERASE_CHAR } from '../constants';
|
||||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawErase {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
this.endPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
this.move(position);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
this.endPosition = position;
|
||||
|
||||
var startX = Math.min(this.startPosition.x, this.endPosition.x);
|
||||
var startY = Math.min(this.startPosition.y, this.endPosition.y);
|
||||
var endX = Math.max(this.startPosition.x, this.endPosition.x);
|
||||
var endY = Math.max(this.startPosition.y, this.endPosition.y);
|
||||
|
||||
for (var i = startX; i <= endX; i++) {
|
||||
for (var j = startY; j <= endY; j++) {
|
||||
this.state.drawValue(new Vector(i, j), ERASE_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { DrawFunction } from './function';
|
||||
import { TOUCH_ENABLED } from '../constants';
|
||||
import State from '../state';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawFreeform {
|
||||
/**
|
||||
* @param {State} state
|
||||
* @param {?string} value
|
||||
*/
|
||||
constructor(state, value) {
|
||||
this.state = state;
|
||||
this.value = value;
|
||||
if (TOUCH_ENABLED) {
|
||||
$('#freeform-tool-input').val('');
|
||||
$('#freeform-tool-input').hide(0, function() {$('#freeform-tool-input').show(0, function() {$('#freeform-tool-input').focus();});});
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.state.drawValue(position, this.value);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.drawValue(position, this.value);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {
|
||||
if (TOUCH_ENABLED) {
|
||||
this.value = $('#freeform-tool-input').val().substr(0, 1);
|
||||
$('#freeform-tool-input').blur();
|
||||
$('#freeform-tool-input').hide(0);
|
||||
}
|
||||
if (value.length == 1) {
|
||||
// The value is not a special character, so lets use it.
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import Vector from '../vector';
|
||||
|
||||
/**
|
||||
* Common interface for different drawing functions, e.g. box, line, etc.
|
||||
* @interface
|
||||
*/
|
||||
export default class DrawFunction {
|
||||
/** Start of drawing. @param {Vector} position */
|
||||
start(position) {};
|
||||
/** Drawing move. @param {Vector} position */
|
||||
move(position) {};
|
||||
/** End of drawing. */
|
||||
end() {};
|
||||
/** Cursor for given cell.
|
||||
* @param {Vector} position
|
||||
* @return {string}
|
||||
*/
|
||||
getCursor(position) {};
|
||||
/** Handle the key with given value being pressed. @param {string} value */
|
||||
handleKey(value) {};
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export { default as DrawFunction } from './function';
|
||||
export { default as DrawBox } from './box';
|
||||
export { default as DrawBoxText } from './boxtext';
|
||||
export { default as DrawErase } from './erase';
|
||||
export { default as DrawLine } from './line';
|
||||
export { default as DrawSelect } from './select';
|
||||
export { default as DrawText } from './text';
|
||||
export { default as DrawMove } from './move';
|
||||
export { default as DrawFreeform } from './freeform';
|
|
@ -0,0 +1,55 @@
|
|||
import { DrawFunction } from './function';
|
||||
import { drawLine } from './utils';
|
||||
import { ALT_SPECIAL_VALUE } from '../constants';
|
||||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawLine {
|
||||
/**
|
||||
* @param {State} state
|
||||
* @param {boolean} isArrow
|
||||
*/
|
||||
constructor(state, isArrow) {
|
||||
this.state = state;
|
||||
this.isArrow = isArrow;
|
||||
/** @type {Vector} */ this.startPosition = null;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition = position;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
|
||||
// Try to infer line orientation.
|
||||
// TODO: Split the line into two lines if we can't satisfy both ends.
|
||||
var startContext = this.state.getContext(this.startPosition);
|
||||
var endContext = this.state.getContext(position);
|
||||
var clockwise = (startContext.up && startContext.down) ||
|
||||
(endContext.left && endContext.right);
|
||||
|
||||
drawLine(this.state, this.startPosition, position, clockwise);
|
||||
if (this.isArrow) {
|
||||
this.state.drawValue(position, ALT_SPECIAL_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'crosshair';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {};
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
import { DrawFunction } from './function';
|
||||
import { drawLine } from './utils';
|
||||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
import * as c from '../constants';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawMove {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
/** @type {!Array<{position, clockwise, startIsAlt, midPointIsAlt, endIsAlt}>} */
|
||||
this.ends = [];
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.startPosition =
|
||||
c.TOUCH_ENABLED ? this.snapToNearest(position) : position;
|
||||
this.ends = [];
|
||||
|
||||
// If this isn't a special cell then quit, or things get weird.
|
||||
if (!this.state.getCell(this.startPosition).isSpecial()) {
|
||||
return;
|
||||
}
|
||||
var context = this.state.getContext(this.startPosition);
|
||||
|
||||
var ends = [];
|
||||
for (var i of c.DIRECTIONS) {
|
||||
var midPoints = this.followLine(this.startPosition, i);
|
||||
for (var midPoint of midPoints) {
|
||||
// Clockwise is a lie, it is true if we move vertically first.
|
||||
var clockwise = (i.x != 0);
|
||||
var startIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(position).getRawValue()) != -1;
|
||||
var midPointIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(midPoint).getRawValue()) != -1;
|
||||
|
||||
var midPointContext = this.state.getContext(midPoint);
|
||||
// Special case, a straight line with no turns.
|
||||
if (midPointContext.sum() == 1) {
|
||||
ends.push({position: midPoint, clockwise, startIsAlt, endIsAlt: midPointIsAlt});
|
||||
continue;
|
||||
}
|
||||
// Continue following lines from the midpoint.
|
||||
for (var j of c.DIRECTIONS) {
|
||||
if (i.add(j).length() == 0 || i.add(j).length() == 2) {
|
||||
// Don't go back on ourselves, or don't carry on in same direction.
|
||||
continue;
|
||||
}
|
||||
var secondEnds = this.followLine(midPoint, j);
|
||||
// Ignore any directions that didn't go anywhere.
|
||||
if (secondEnds.length == 0) {
|
||||
continue;
|
||||
}
|
||||
var secondEnd = secondEnds[0];
|
||||
var endIsAlt = c.ALT_SPECIAL_VALUES.indexOf(this.state.getCell(secondEnd).getRawValue()) != -1;
|
||||
// On the second line we don't care about multiple
|
||||
// junctions, just the last.
|
||||
ends.push({position: secondEnd, clockwise, startIsAlt, midPointIsAlt, endIsAlt});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.ends = ends;
|
||||
// Redraw the new lines after we have cleared the existing ones.
|
||||
this.move(this.startPosition);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {
|
||||
this.state.clearDraw();
|
||||
// Clear all the lines so we can draw them afresh.
|
||||
for (var end of this.ends) {
|
||||
drawLine(this.state, this.startPosition, end.position, end.clockwise, ' ');
|
||||
}
|
||||
for (var i in this.ends) {
|
||||
drawLine(this.state, position, end.position, end.clockwise);
|
||||
}
|
||||
for (var end of this.ends) {
|
||||
// If the ends or midpoint of the line was a alt character (arrow), need to preserve that.
|
||||
if (end.startIsAlt) {
|
||||
this.state.drawValue(position, c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
if (end.endIsAlt) {
|
||||
this.state.drawValue(end.position, c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
if (end.midPointIsAlt) {
|
||||
var midX = end.clockwise ? end.position.x : position.x;
|
||||
var midY = end.clockwise ? position.y : end.position.y;
|
||||
this.state.drawValue(new Vector(midX, midY), c.ALT_SPECIAL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
this.state.commitDraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Follows a line in a given direction from the startPosition.
|
||||
* Returns a list of positions that were line 'junctions'. This is a bit of a
|
||||
* loose definition, but basically means a point around which we resize things.
|
||||
* @param {Vector} startPosition
|
||||
* @param {Vector} direction
|
||||
* @return {!Array<Vector>}
|
||||
*/
|
||||
followLine(startPosition, direction) {
|
||||
var endPosition = startPosition.clone();
|
||||
var junctions = [];
|
||||
while (true) {
|
||||
var nextEnd = endPosition.add(direction);
|
||||
if (!this.state.getCell(nextEnd).isSpecial()) {
|
||||
// Junctions: Right angles and end T-Junctions.
|
||||
if (!startPosition.equals(endPosition)) {
|
||||
junctions.push(endPosition);
|
||||
}
|
||||
return junctions;
|
||||
}
|
||||
|
||||
endPosition = nextEnd;
|
||||
var context = this.state.getContext(endPosition);
|
||||
// Junctions: Side T-Junctions.
|
||||
if (context.sum() == 3) {
|
||||
junctions.push(endPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given position, finds the nearest cell that is of any interest to the
|
||||
* move tool, e.g. a corner or a line. Will look up to 1 cell in each direction
|
||||
* including diagonally.
|
||||
* @param {Vector} position
|
||||
* @return {Vector}
|
||||
*/
|
||||
snapToNearest(position) {
|
||||
if (this.state.getCell(position).isSpecial()) {
|
||||
return position;
|
||||
}
|
||||
var allDirections = c.DIRECTIONS.concat([
|
||||
c.DIR_LEFT.add(c.DIR_UP),
|
||||
c.DIR_LEFT.add(c.DIR_DOWN),
|
||||
c.DIR_RIGHT.add(c.DIR_UP),
|
||||
c.DIR_RIGHT.add(c.DIR_DOWN)]);
|
||||
|
||||
var bestDirection = null;
|
||||
var bestContextSum = 0;
|
||||
for (var direction of allDirections) {
|
||||
// Find the most connected cell, essentially.
|
||||
var newPos = position.add(direction);
|
||||
var contextSum = this.state.getContext(newPos).sum();
|
||||
if (this.state.getCell(newPos).isSpecial() &&
|
||||
contextSum > bestContextSum) {
|
||||
bestDirection = direction;
|
||||
bestContextSum = contextSum;
|
||||
}
|
||||
}
|
||||
if (bestDirection == null) {
|
||||
// Didn't find anything, so just return the current cell.
|
||||
return position;
|
||||
}
|
||||
return position.add(bestDirection);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
if (this.state.getCell(position).isSpecial()) {
|
||||
return 'pointer';
|
||||
} else {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import * as c from './constants';
|
||||
import Vector from './vector';
|
||||
import State from './state';
|
||||
import { MappedValue, Box } from './common';
|
||||
import { DrawFunction, DrawErase } from './draw';
|
||||
import DrawFunction from './function';
|
||||
import { ERASE_CHAR, KEY_COPY, KEY_CUT, KEY_PASTE } from '../constants';
|
||||
import Vector from '../vector';
|
||||
import State from '../state';
|
||||
import { MappedValue, Box } from '../common';
|
||||
import DrawErase from './erase';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
|
@ -53,7 +54,7 @@ export default class DrawSelect {
|
|||
copyArea() {
|
||||
var nonEmptyCells = this.state.scratchCells.filter(function(value) {
|
||||
var rawValue = value.cell.getRawValue();
|
||||
return value.cell.getRawValue() != null && value.cell.getRawValue() != c.ERASE_CHAR;
|
||||
return value.cell.getRawValue() != null && value.cell.getRawValue() != ERASE_CHAR;
|
||||
});
|
||||
var topLeft = this.getSelectedBox().topLeft();
|
||||
this.selectedCells = nonEmptyCells.map(function(value) {
|
||||
|
@ -82,7 +83,7 @@ export default class DrawSelect {
|
|||
// Effectively highlights the cell.
|
||||
var currentValue = this.state.getCell(current).getRawValue();
|
||||
this.state.drawValue(current,
|
||||
currentValue == null ? c.ERASE_CHAR : currentValue);
|
||||
currentValue == null ? ERASE_CHAR : currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,17 +130,17 @@ export default class DrawSelect {
|
|||
handleKey(value) {
|
||||
if (this.startPosition != null &&
|
||||
this.endPosition != null) {
|
||||
if (value == c.KEY_COPY || value == c.KEY_CUT) {
|
||||
if (value == KEY_COPY || value == KEY_CUT) {
|
||||
this.copyArea();
|
||||
}
|
||||
if (value == c.KEY_CUT) {
|
||||
if (value == KEY_CUT) {
|
||||
var eraser = new DrawErase(this.state);
|
||||
eraser.start(this.startPosition);
|
||||
eraser.move(this.endPosition);
|
||||
this.state.commitDraw();
|
||||
}
|
||||
}
|
||||
if (value == c.KEY_PASTE) {
|
||||
if (value == KEY_PASTE) {
|
||||
this.drawSelected(this.startPosition);
|
||||
this.state.commitDraw();
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
import { DrawFunction } from './function';
|
||||
import { ERASE_CHAR } from '../constants';
|
||||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
import { drawText } from './utils';
|
||||
|
||||
/**
|
||||
* @implements {DrawFunction}
|
||||
*/
|
||||
export default class DrawText {
|
||||
/**
|
||||
* @param {State} state
|
||||
*/
|
||||
constructor(state, view) {
|
||||
this.state = state;
|
||||
this.startPosition = null;
|
||||
this.endPosition = null;
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
start(position) {
|
||||
this.state.commitDraw();
|
||||
$('#text-tool-input').val('');
|
||||
this.startPosition = position;
|
||||
|
||||
// TODO: Not working yet, needs fixing so that it can
|
||||
// remove the underlying text completely.
|
||||
// this.loadExistingText(position);
|
||||
|
||||
// Effectively highlights the starting cell.
|
||||
var currentValue = this.state.getCell(this.startPosition).getRawValue();
|
||||
this.state.drawValue(this.startPosition,
|
||||
currentValue == null ? ERASE_CHAR : currentValue);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
move(position) {}
|
||||
|
||||
/** @inheritDoc */
|
||||
end() {
|
||||
if (this.startPosition != null) {
|
||||
this.endPosition = this.startPosition;
|
||||
this.startPosition = null;
|
||||
// Valid end click/press, show the textbox and focus it.
|
||||
$('#text-tool-widget').hide(0, () => {
|
||||
$('#text-tool-widget').show(0, () => {
|
||||
$('#text-tool-input').focus();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
getCursor(position) {
|
||||
return 'pointer';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
handleKey(value) {
|
||||
var text = /** @type {string} */ ($('#text-tool-input').val());
|
||||
this.state.clearDraw();
|
||||
drawText(this.state, this.endPosition, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads any existing text if it is present.
|
||||
* TODO: This is horrible, and does not quite work, fix it.
|
||||
*/
|
||||
loadExistingText(position) {
|
||||
var currentPosition = position.clone();
|
||||
var cell = this.state.getCell(position);
|
||||
var spacesCount = 0;
|
||||
// Go back to find the start of the line.
|
||||
while ((!cell.isSpecial() && cell.getRawValue() != null) || spacesCount < 1) {
|
||||
if (cell.getRawValue() == null) {
|
||||
spacesCount++;
|
||||
} else if (!cell.isSpecial()) {
|
||||
spacesCount = 0;
|
||||
}
|
||||
currentPosition.x--;
|
||||
cell = this.state.getCell(currentPosition);
|
||||
}
|
||||
this.startPosition = currentPosition.right(spacesCount + 1);
|
||||
var text = '';
|
||||
spacesCount = 0;
|
||||
currentPosition = this.startPosition.clone();
|
||||
// Go forward to load the text.
|
||||
while ((!cell.isSpecial() && cell.getRawValue() != null) || spacesCount < 1) {
|
||||
cell = this.state.getCell(currentPosition);
|
||||
if (cell.getRawValue() == null) {
|
||||
spacesCount++;
|
||||
text += ' ';
|
||||
} else if (!cell.isSpecial()) {
|
||||
spacesCount = 0;
|
||||
text += cell.getRawValue();
|
||||
this.state.drawValue(currentPosition, cell.getRawValue());
|
||||
}
|
||||
currentPosition.x++;
|
||||
}
|
||||
$('#text-tool-input').val(text.substr(0, text.length - 1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
import { SPECIAL_VALUE } from '../constants';
|
||||
import { Box } from '../common';
|
||||
import State from '../state';
|
||||
import Vector from '../vector';
|
||||
|
||||
/**
|
||||
* Draws a line on the diagram state.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Vector} startPosition
|
||||
* @param {Vector} endPosition
|
||||
* @param {boolean} clockwise
|
||||
* @param {string=} value
|
||||
*/
|
||||
export function drawLine(
|
||||
state, startPosition, endPosition, clockwise, value = SPECIAL_VALUE) {
|
||||
|
||||
var box = new Box(startPosition, endPosition);
|
||||
var startX = box.startX;
|
||||
var startY = box.startY;
|
||||
var endX = box.endX;
|
||||
var endY = box.endY;
|
||||
|
||||
var midX = clockwise ? endPosition.x : startPosition.x;
|
||||
var midY = clockwise ? startPosition.y : endPosition.y;
|
||||
|
||||
while (startX++ < endX) {
|
||||
var position = new Vector(startX, midY);
|
||||
var context = state.getContext(new Vector(startX, midY));
|
||||
// Don't erase any lines that we cross.
|
||||
if (value != ' ' || context.up + context.down != 2) {
|
||||
state.drawValueIncremental(position, value);
|
||||
}
|
||||
}
|
||||
while (startY++ < endY) {
|
||||
var position = new Vector(midX, startY);
|
||||
var context = state.getContext(new Vector(midX, startY));
|
||||
// Don't erase any lines that we cross.
|
||||
if (value != ' ' || context.left + context.right != 2) {
|
||||
state.drawValueIncremental(position, value);
|
||||
}
|
||||
}
|
||||
|
||||
state.drawValue(startPosition, value);
|
||||
state.drawValue(endPosition, value);
|
||||
state.drawValueIncremental(new Vector(midX, midY), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cells scratch (uncommitted) values to the given text.
|
||||
* Handles newlines appropriately.
|
||||
* @param {State} state
|
||||
* @param {Vector} position
|
||||
* @param {string} text
|
||||
*/
|
||||
export function drawText(state, position, text) {
|
||||
let x = 0, y = 0;
|
||||
for (const char of text) {
|
||||
if (char == '\n') {
|
||||
y++;
|
||||
x = 0;
|
||||
continue;
|
||||
}
|
||||
state.drawValue(position.add(new Vector(x, y)), char);
|
||||
x++;
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ export default class DriveController {
|
|||
|
||||
this.loopSave();
|
||||
|
||||
$(window).on('hashchange', this.loadFromHash);
|
||||
$(window).on('hashchange', () => { this.loadFromHash(); });
|
||||
|
||||
$('#drive-new-file-button').click(() => {
|
||||
this.file = null;
|
||||
|
@ -60,14 +60,14 @@ export default class DriveController {
|
|||
window['gapi']['auth']['authorize']({
|
||||
'client_id': CLIENT_ID,
|
||||
'scope': SCOPES,
|
||||
'immediate': immediate},
|
||||
immediate },
|
||||
result => {
|
||||
if (result && !result.error && !this.driveEnabled) {
|
||||
this.driveEnabled = true;
|
||||
$('#drive-button').addClass('active');
|
||||
// We are authorized, so let's se if we can load from the URL hash.
|
||||
// This seems to fail if we do it too early.
|
||||
window.setTimeout(this.loadFromHash, 500);
|
||||
window.setTimeout(() => { this.loadFromHash(); }, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ export default class DriveController {
|
|||
safeExecute(request, callback) {
|
||||
// Could make the API call, don't blow up tho (mobiles n stuff).
|
||||
try {
|
||||
request['execute'](function(result) {
|
||||
request['execute'](result => {
|
||||
if (!result['error']) {
|
||||
callback(result);
|
||||
}
|
||||
|
@ -220,8 +220,8 @@ export default class DriveController {
|
|||
var method = this.file == null ? 'POST' : 'PUT';
|
||||
|
||||
return window['gapi']['client']['request']({
|
||||
method,
|
||||
'path': '/upload/drive/v2/files' + fileId,
|
||||
'method': method,
|
||||
'params': {'uploadType': 'multipart'},
|
||||
'headers': {
|
||||
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
|
||||
|
|
|
@ -29,31 +29,31 @@ export class DesktopController {
|
|||
* Installs input bindings associated with keyboard controls.
|
||||
*/
|
||||
installBindings() {
|
||||
var canvas = this.controller.view.canvas;
|
||||
$(canvas).on('mousewheel', e => {
|
||||
var canvas = $(this.controller.view.canvas);
|
||||
canvas.on('mousewheel', e => {
|
||||
this.handleZoom(e.originalEvent.wheelDelta);
|
||||
});
|
||||
|
||||
$(canvas).mousedown(e => {
|
||||
canvas.mousedown(e => {
|
||||
// Can drag by holding either the control or meta (Apple) key.
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
this.controller.startDrag(new Vector(e.clientX, e.clientY));
|
||||
this.controller.startDrag(Vector.fromMouseEvent(e));
|
||||
} else {
|
||||
this.controller.startDraw(new Vector(e.clientX, e.clientY));
|
||||
this.controller.startDraw(Vector.fromMouseEvent(e));
|
||||
}
|
||||
});
|
||||
|
||||
// Pass these events through to the main controller.
|
||||
$(canvas).mouseup(e => {
|
||||
canvas.mouseup(e => {
|
||||
this.controller.endAll();
|
||||
});
|
||||
|
||||
$(canvas).mouseleave(e => {
|
||||
canvas.mouseleave(e => {
|
||||
this.controller.endAll();
|
||||
});
|
||||
|
||||
$(canvas).mousemove(e => {
|
||||
this.controller.handleMove(new Vector(e.clientX, e.clientY));
|
||||
canvas.mousemove(e => {
|
||||
this.controller.handleMove(Vector.fromMouseEvent(e));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -152,42 +152,32 @@ export class TouchController {
|
|||
* Installs input bindings associated with touch controls.
|
||||
*/
|
||||
installBindings() {
|
||||
var canvas = this.controller.view.canvas;
|
||||
var canvas = $(this.controller.view.canvas);
|
||||
|
||||
$(canvas).on('touchstart', e => {
|
||||
canvas.on('touchstart', e => {
|
||||
e.preventDefault();
|
||||
if (e.originalEvent.touches.length == 1) {
|
||||
this.handlePress(new Vector(
|
||||
e.originalEvent.touches[0].pageX,
|
||||
e.originalEvent.touches[0].pageY));
|
||||
this.handlePress(Vector.fromTouchEvent(e));
|
||||
} else if (e.originalEvent.touches.length > 1) {
|
||||
this.handlePressMulti(new Vector(
|
||||
e.originalEvent.touches[0].pageX,
|
||||
e.originalEvent.touches[0].pageY),
|
||||
new Vector(
|
||||
e.originalEvent.touches[1].pageX,
|
||||
e.originalEvent.touches[1].pageY));
|
||||
this.handlePressMulti(
|
||||
Vector.fromTouchEvent(e, 0),
|
||||
Vector.fromTouchEvent(e, 1));
|
||||
}
|
||||
});
|
||||
|
||||
$(canvas).on('touchmove', e => {
|
||||
canvas.on('touchmove', e => {
|
||||
e.preventDefault();
|
||||
if (e.originalEvent.touches.length == 1) {
|
||||
this.handleMove(new Vector(
|
||||
e.originalEvent.touches[0].pageX,
|
||||
e.originalEvent.touches[0].pageY));
|
||||
this.handleMove(Vector.fromTouchEvent(e));
|
||||
} else if (e.originalEvent.touches.length > 1) {
|
||||
this.handleMoveMulti(new Vector(
|
||||
e.originalEvent.touches[0].pageX,
|
||||
e.originalEvent.touches[0].pageY),
|
||||
new Vector(
|
||||
e.originalEvent.touches[1].pageX,
|
||||
e.originalEvent.touches[1].pageY));
|
||||
this.handleMoveMulti(
|
||||
Vector.fromTouchEvent(e, 0),
|
||||
Vector.fromTouchEvent(e, 1));
|
||||
}
|
||||
});
|
||||
|
||||
// Pass through, no special handling.
|
||||
$(canvas).on('touchend', e => {
|
||||
canvas.on('touchend', e => {
|
||||
e.preventDefault();
|
||||
this.reset();
|
||||
this.controller.endAll();
|
||||
|
|
|
@ -98,8 +98,8 @@ export default class State {
|
|||
getDrawValue(position) {
|
||||
var cell = this.getCell(position);
|
||||
var value = cell.scratchValue != null ? cell.scratchValue : cell.value;
|
||||
var isSpecial = c.SPECIAL_VALUES.indexOf(value) != -1;
|
||||
var isAltSpecial = c.ALT_SPECIAL_VALUES.indexOf(value) != -1;
|
||||
var isSpecial = c.SPECIAL_VALUES.includes(value);
|
||||
var isAltSpecial = c.ALT_SPECIAL_VALUES.includes(value);
|
||||
if (!isSpecial && !isAltSpecial) {
|
||||
return value;
|
||||
}
|
||||
|
@ -146,13 +146,13 @@ export default class State {
|
|||
if (!context.up && context.rightdown && context.leftdown) {
|
||||
return c.SPECIAL_LINE_H;
|
||||
}
|
||||
var leftupempty = this.getCell(position.add(c.DIR_LEFT).add(c.DIR_UP)).isEmpty();
|
||||
var rightupempty = this.getCell(position.add(c.DIR_RIGHT).add(c.DIR_UP)).isEmpty();
|
||||
var leftupempty = this.getCell(position.left().up()).isEmpty();
|
||||
var rightupempty = this.getCell(position.right().up()).isEmpty();
|
||||
if (context.up && context.left && context.right && (!leftupempty || !rightupempty)) {
|
||||
return c.SPECIAL_LINE_H;
|
||||
}
|
||||
var leftdownempty = this.getCell(position.add(c.DIR_LEFT).add(c.DIR_DOWN)).isEmpty();
|
||||
var rightdownempty = this.getCell(position.add(c.DIR_RIGHT).add(c.DIR_DOWN)).isEmpty();
|
||||
var leftdownempty = this.getCell(position.left().down()).isEmpty();
|
||||
var rightdownempty = this.getCell(position.right().down()).isEmpty();
|
||||
if (context.down && context.left && context.right && (!leftdownempty || !rightdownempty)) {
|
||||
return c.SPECIAL_LINE_H;
|
||||
}
|
||||
|
@ -181,10 +181,10 @@ export default class State {
|
|||
* @return {CellContext}
|
||||
*/
|
||||
getContext(position) {
|
||||
var left = this.getCell(position.add(c.DIR_LEFT)).isSpecial();
|
||||
var right = this.getCell(position.add(c.DIR_RIGHT)).isSpecial();
|
||||
var up = this.getCell(position.add(c.DIR_UP)).isSpecial();
|
||||
var down = this.getCell(position.add(c.DIR_DOWN)).isSpecial();
|
||||
var left = this.getCell(position.left()).isSpecial();
|
||||
var right = this.getCell(position.right()).isSpecial();
|
||||
var up = this.getCell(position.up()).isSpecial();
|
||||
var down = this.getCell(position.down()).isSpecial();
|
||||
return new CellContext(left, right, up, down);
|
||||
}
|
||||
|
||||
|
@ -193,10 +193,10 @@ export default class State {
|
|||
* @param {CellContext} context
|
||||
*/
|
||||
extendContext(position, context) {
|
||||
context.leftup = this.getCell(position.add(c.DIR_LEFT).add(c.DIR_UP)).isSpecial();
|
||||
context.rightup = this.getCell(position.add(c.DIR_RIGHT).add(c.DIR_UP)).isSpecial();
|
||||
context.leftdown = this.getCell(position.add(c.DIR_LEFT).add(c.DIR_DOWN)).isSpecial();
|
||||
context.rightdown = this.getCell(position.add(c.DIR_RIGHT).add(c.DIR_DOWN)).isSpecial();
|
||||
context.leftup = this.getCell(position.left().up()).isSpecial();
|
||||
context.rightup = this.getCell(position.right().up()).isSpecial();
|
||||
context.leftdown = this.getCell(position.left().down()).isSpecial();
|
||||
context.rightdown = this.getCell(position.right().down()).isSpecial();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,11 +207,10 @@ export default class State {
|
|||
var oldValues = [];
|
||||
|
||||
// Dedupe the scratch values, or this causes havoc for history management.
|
||||
var positions = this.scratchCells.map(function(value) {
|
||||
var positions = this.scratchCells.map(value => {
|
||||
return value.position.x.toString() + value.position.y.toString();
|
||||
});
|
||||
var scratchCellsUnique =
|
||||
this.scratchCells.filter(function(value, index, arr) {
|
||||
var scratchCellsUnique = this.scratchCells.filter((value, index, arr) => {
|
||||
return positions.indexOf(positions[index]) == index;
|
||||
});
|
||||
|
||||
|
@ -253,9 +252,8 @@ export default class State {
|
|||
if (this.undoStates.length == 0) { return; }
|
||||
|
||||
var lastState = this.undoStates.pop();
|
||||
for (var i in lastState) {
|
||||
var mappedValue = lastState[i];
|
||||
this.drawValue(mappedValue.position, mappedValue.value);
|
||||
for (var { position, value } of lastState) {
|
||||
this.drawValue(position, value);
|
||||
}
|
||||
this.commitDraw(true);
|
||||
}
|
||||
|
@ -267,8 +265,8 @@ export default class State {
|
|||
if (this.redoStates.length == 0) { return; }
|
||||
|
||||
var lastState = this.redoStates.pop();
|
||||
for (var mappedValue of lastState) {
|
||||
this.drawValue(mappedValue.position, mappedValue.value);
|
||||
for (var { position, value } of lastState) {
|
||||
this.drawValue(position, value);
|
||||
}
|
||||
this.commitDraw();
|
||||
}
|
||||
|
@ -320,7 +318,7 @@ export default class State {
|
|||
*/
|
||||
fromText(value, offset) {
|
||||
var lines = value.split('\n');
|
||||
var middle = new ascii.Vector(0, Math.round(lines.length / 2));
|
||||
var middle = new Vector(0, Math.round(lines.length / 2));
|
||||
for (var j = 0; j < lines.length; j++) {
|
||||
middle.x = Math.max(middle.x, Math.round(lines[j].length / 2));
|
||||
}
|
||||
|
@ -331,10 +329,10 @@ export default class State {
|
|||
// Convert special output back to special chars.
|
||||
// TODO: This is a horrible hack, need to handle multiple special chars
|
||||
// correctly and preserve them through line drawing etc.
|
||||
if (SPECIAL_VALUES.indexOf(char) != -1) {
|
||||
char = SPECIAL_VALUE;
|
||||
if (c.SPECIAL_VALUES.includes(char)) {
|
||||
char = c.SPECIAL_VALUE;
|
||||
}
|
||||
this.drawValue(new ascii.Vector(i, j).add(offset).subtract(middle), char);
|
||||
this.drawValue(new Vector(i, j).add(offset).subtract(middle), char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,25 @@ export default class Vector {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {Vector} other
|
||||
* @param {jQuery.Event} event
|
||||
* @return {!Vector}
|
||||
*/
|
||||
static fromMouseEvent(event) {
|
||||
return new Vector(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {jQuery.Event} event
|
||||
* @param {number=} index
|
||||
* @return {!Vector}
|
||||
*/
|
||||
static fromTouchEvent(event, index = 0) {
|
||||
const { pageX, pageY } = event.originalEvent.touches[index];
|
||||
return new Vector(pageX, pageY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?Vector} other
|
||||
* @return {boolean}
|
||||
*/
|
||||
equals(other) {
|
||||
|
@ -36,7 +54,7 @@ export default class Vector {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return {Vector}
|
||||
* @return {!Vector}
|
||||
*/
|
||||
clone() {
|
||||
return new Vector(this.x, this.y);
|
||||
|
@ -54,4 +72,40 @@ export default class Vector {
|
|||
scale(scale) {
|
||||
return new Vector(this.x * scale, this.y * scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move up by value. Defaults to 1.
|
||||
* @param {number=} value
|
||||
* @return {Vector}
|
||||
*/
|
||||
up(value = 1) {
|
||||
return new Vector(this.x, this.y - value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move down by value. Defaults to 1.
|
||||
* @param {number=} value
|
||||
* @return {Vector}
|
||||
*/
|
||||
down(value = 1) {
|
||||
return new Vector(this.x, this.y + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move left by value. Defaults to 1.
|
||||
* @param {number=} value
|
||||
* @return {Vector}
|
||||
*/
|
||||
left(value = 1) {
|
||||
return new Vector(this.x - value, this.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move right by value. Defaults to 1.
|
||||
* @param {number=} value
|
||||
* @return {Vector}
|
||||
*/
|
||||
right(value = 1) {
|
||||
return new Vector(this.x + value, this.y);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,7 @@ export default class View {
|
|||
this.state.dirty = false;
|
||||
this.render();
|
||||
}
|
||||
var view = this;
|
||||
window.requestAnimationFrame(function() { view.animate(); });
|
||||
window.requestAnimationFrame(() => { this.animate(); });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue