2014-01-07 22:49:54 +00:00
|
|
|
/**
|
|
|
|
* Handles user input events and modifies state.
|
2014-01-19 14:23:17 +00:00
|
|
|
*
|
2014-01-07 22:49:54 +00:00
|
|
|
* @constructor
|
2014-01-12 10:37:38 +00:00
|
|
|
* @param {ascii.View} view
|
|
|
|
* @param {ascii.State} state
|
2014-01-07 22:49:54 +00:00
|
|
|
*/
|
2014-01-11 16:40:01 +00:00
|
|
|
ascii.Controller = function(view, state) {
|
|
|
|
/** @type {ascii.View} */ this.view = view;
|
|
|
|
/** @type {ascii.State} */ this.state = state;
|
|
|
|
|
2014-01-19 14:23:17 +00:00
|
|
|
/** @type {ascii.DrawFunction} */ this.drawFunction =
|
|
|
|
new ascii.DrawBox(state);
|
2014-01-11 18:53:45 +00:00
|
|
|
|
2014-01-11 17:41:44 +00:00
|
|
|
/** @type {ascii.Vector} */ this.dragOrigin;
|
2014-01-11 16:40:01 +00:00
|
|
|
/** @type {ascii.Vector} */ this.pressVector;
|
2014-01-12 14:38:56 +00:00
|
|
|
/** @type {ascii.Vector} */ this.lastMoveCell;
|
2014-01-11 17:41:44 +00:00
|
|
|
/** @type {number} */ this.pressTimestamp;
|
2014-01-07 22:49:54 +00:00
|
|
|
|
2014-01-12 19:08:29 +00:00
|
|
|
this.installBindings();
|
2014-01-07 22:49:54 +00:00
|
|
|
};
|
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
/**
|
|
|
|
* @param {ascii.Vector} position
|
|
|
|
*/
|
|
|
|
ascii.Controller.prototype.handlePress = function(position) {
|
|
|
|
this.pressVector = position;
|
2014-01-11 17:41:44 +00:00
|
|
|
this.pressTimestamp = $.now();
|
2014-01-11 18:53:45 +00:00
|
|
|
|
|
|
|
// Check to see if a drag happened in the given allowed time.
|
|
|
|
window.setTimeout(function() {
|
2014-01-12 19:08:29 +00:00
|
|
|
if (this.dragOrigin == null && this.pressVector != null) {
|
2014-01-19 14:23:17 +00:00
|
|
|
this.drawFunction.start(this.view.screenToCell(position));
|
2014-01-12 16:51:16 +00:00
|
|
|
this.view.dirty = true;
|
2014-01-11 18:53:45 +00:00
|
|
|
}
|
|
|
|
// TODO: Skip this if release happens before timeout.
|
|
|
|
}.bind(this), DRAG_LATENCY);
|
2014-01-08 23:06:08 +00:00
|
|
|
};
|
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
/**
|
|
|
|
* @param {ascii.Vector} position
|
|
|
|
*/
|
|
|
|
ascii.Controller.prototype.handleMove = function(position) {
|
2014-01-20 21:36:58 +00:00
|
|
|
// Update the cursor pointer, depending on the draw function.
|
|
|
|
this.view.canvas.style.cursor = this.drawFunction.getCursor(
|
|
|
|
this.view.screenToCell(position));
|
|
|
|
|
2014-01-11 18:53:45 +00:00
|
|
|
// No clicks, so just ignore.
|
2014-01-12 10:37:38 +00:00
|
|
|
if (this.pressVector == null) { return; }
|
2014-01-11 17:41:44 +00:00
|
|
|
|
|
|
|
// Initiate a drag if we have moved enough, quickly enough.
|
|
|
|
if (this.dragOrigin == null &&
|
|
|
|
($.now() - this.pressTimestamp) < DRAG_LATENCY &&
|
|
|
|
position.subtract(this.pressVector).length() > DRAG_ACCURACY) {
|
|
|
|
this.dragOrigin = this.view.offset;
|
|
|
|
}
|
|
|
|
|
2014-01-12 14:38:56 +00:00
|
|
|
// Not dragging, so pass the mouse move on, but remove duplicates.
|
2014-01-11 18:53:45 +00:00
|
|
|
if (this.dragOrigin == null &&
|
2014-01-12 14:38:56 +00:00
|
|
|
($.now() - this.pressTimestamp) >= DRAG_LATENCY &&
|
|
|
|
(this.lastMoveCell == null ||
|
|
|
|
!this.view.screenToCell(position)
|
|
|
|
.equals(this.view.screenToCell(this.lastMoveCell)))) {
|
2014-01-19 14:23:17 +00:00
|
|
|
this.drawFunction.move(this.view.screenToCell(position));
|
2014-01-12 16:51:16 +00:00
|
|
|
this.view.dirty = true;
|
2014-01-12 14:38:56 +00:00
|
|
|
this.lastMoveCell = position;
|
2014-01-11 18:53:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 17:41:44 +00:00
|
|
|
// Drag in progress, update the view origin.
|
|
|
|
if (this.dragOrigin != null) {
|
2014-01-12 11:09:55 +00:00
|
|
|
this.view.setOffset(this.dragOrigin.add(
|
2014-01-11 17:41:44 +00:00
|
|
|
this.pressVector
|
2014-01-12 10:37:38 +00:00
|
|
|
.subtract(position)
|
2014-01-12 11:09:55 +00:00
|
|
|
.scale(1 / this.view.zoom)));
|
2014-01-11 17:41:44 +00:00
|
|
|
}
|
2014-01-08 23:06:08 +00:00
|
|
|
};
|
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
/**
|
|
|
|
* @param {ascii.Vector} position
|
|
|
|
*/
|
|
|
|
ascii.Controller.prototype.handleRelease = function(position) {
|
2014-01-11 17:41:44 +00:00
|
|
|
// Drag wasn't initiated in time, treat this as a drawing event.
|
|
|
|
if (this.dragOrigin == null &&
|
2014-01-11 18:53:45 +00:00
|
|
|
($.now() - this.pressTimestamp) >= DRAG_LATENCY) {
|
2014-01-19 14:23:17 +00:00
|
|
|
this.drawFunction.end(this.view.screenToCell(position));
|
2014-01-12 16:51:16 +00:00
|
|
|
this.view.dirty = true;
|
2014-01-09 22:38:08 +00:00
|
|
|
}
|
2014-01-11 16:40:01 +00:00
|
|
|
this.pressVector = null;
|
2014-01-11 17:41:44 +00:00
|
|
|
this.pressTimestamp = 0;
|
|
|
|
this.dragOrigin = null;
|
2014-01-12 14:38:56 +00:00
|
|
|
this.lastMoveCell = null;
|
2014-01-07 22:49:54 +00:00
|
|
|
};
|
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
/**
|
|
|
|
* @param {number} delta
|
|
|
|
*/
|
2014-01-09 20:18:46 +00:00
|
|
|
ascii.Controller.prototype.handleZoom = function(delta) {
|
2014-01-12 11:09:55 +00:00
|
|
|
var newzoom = this.view.zoom * (delta > 0 ? 1.1 : 0.9);
|
|
|
|
newzoom = Math.max(Math.min(newzoom, 5), 0.2);
|
|
|
|
this.view.setZoom(newzoom);
|
2014-01-07 22:49:54 +00:00
|
|
|
};
|
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
/**
|
|
|
|
* Installs input bindings for desktop devices.
|
|
|
|
*/
|
2014-01-12 19:08:29 +00:00
|
|
|
ascii.Controller.prototype.installBindings = function() {
|
2014-01-07 22:49:54 +00:00
|
|
|
var controller = this;
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-07 22:49:54 +00:00
|
|
|
$(this.view.canvas).bind('mousewheel', function(e) {
|
|
|
|
controller.handleZoom(e.originalEvent.wheelDelta);
|
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-08 23:06:08 +00:00
|
|
|
$(this.view.canvas).mousedown(function(e) {
|
2014-01-12 10:37:38 +00:00
|
|
|
controller.handlePress(new ascii.Vector(e.clientX, e.clientY));
|
2014-01-08 23:06:08 +00:00
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-08 23:06:08 +00:00
|
|
|
$(this.view.canvas).mouseup(function(e) {
|
2014-01-12 10:37:38 +00:00
|
|
|
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
|
2014-01-08 23:06:08 +00:00
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-12 16:51:16 +00:00
|
|
|
$(this.view.canvas).mouseleave(function(e) {
|
|
|
|
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
|
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-08 23:06:08 +00:00
|
|
|
$(this.view.canvas).mousemove(function(e) {
|
2014-01-12 10:37:38 +00:00
|
|
|
controller.handleMove(new ascii.Vector(e.clientX, e.clientY));
|
2014-01-07 22:49:54 +00:00
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-11 17:41:44 +00:00
|
|
|
$(window).resize(function(e) { controller.view.resizeCanvas() });
|
2014-01-11 21:02:14 +00:00
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
$(this.view.canvas).bind('touchstart', function(e) {
|
2014-01-11 23:46:00 +00:00
|
|
|
e.preventDefault();
|
2014-01-12 10:37:38 +00:00
|
|
|
controller.handlePress(new ascii.Vector(
|
2014-01-11 23:46:00 +00:00
|
|
|
e.originalEvent.touches[0].pageX,
|
2014-01-12 10:37:38 +00:00
|
|
|
e.originalEvent.touches[0].pageY));
|
2014-01-11 21:02:14 +00:00
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
$(this.view.canvas).bind('touchend', function(e) {
|
2014-01-11 23:46:00 +00:00
|
|
|
e.preventDefault();
|
2014-01-12 17:19:15 +00:00
|
|
|
// TODO: This works for now as we don't use a touchend position anywhere.
|
|
|
|
// Need to track last position from touchmove and use it here.
|
|
|
|
controller.handleRelease(new ascii.Vector(0, 0));
|
2014-01-11 21:02:14 +00:00
|
|
|
});
|
2014-01-12 19:08:29 +00:00
|
|
|
|
2014-01-12 10:37:38 +00:00
|
|
|
$(this.view.canvas).bind('touchmove', function(e) {
|
2014-01-11 23:46:00 +00:00
|
|
|
e.preventDefault();
|
2014-01-12 10:37:38 +00:00
|
|
|
controller.handleMove(new ascii.Vector(
|
2014-01-11 23:46:00 +00:00
|
|
|
e.originalEvent.touches[0].pageX,
|
2014-01-12 10:37:38 +00:00
|
|
|
e.originalEvent.touches[0].pageY));
|
2014-01-11 21:02:14 +00:00
|
|
|
});
|
2014-01-12 14:38:56 +00:00
|
|
|
// TODO: Handle pinch to zoom.
|
2014-01-19 14:23:17 +00:00
|
|
|
|
2014-01-21 23:31:58 +00:00
|
|
|
$('#buttons > button.tool').click(function(e) {
|
2014-01-19 23:21:53 +00:00
|
|
|
this.updateButtons(e.target.id);
|
2014-01-19 14:23:17 +00:00
|
|
|
}.bind(this));
|
2014-01-20 23:58:33 +00:00
|
|
|
|
2014-01-21 23:31:58 +00:00
|
|
|
$('#undo-button').click(function(e) {
|
|
|
|
this.state.undo();
|
|
|
|
this.view.dirty = true;
|
|
|
|
}.bind(this));
|
|
|
|
|
2014-01-22 22:32:16 +00:00
|
|
|
$('#import-submit-button').click(function(e) {
|
|
|
|
this.state.clearCanvas();
|
|
|
|
this.state.fromText($('#import-area').val(),
|
|
|
|
this.view.screenToCell(new ascii.Vector(
|
|
|
|
this.view.canvas.width / 2,
|
|
|
|
this.view.canvas.height / 3)));
|
|
|
|
$('#import-area').val('');
|
|
|
|
this.view.dirty = true;
|
|
|
|
}.bind(this));
|
|
|
|
|
2014-01-20 23:58:33 +00:00
|
|
|
$(window).keypress(function(e) {
|
2014-01-21 21:30:47 +00:00
|
|
|
this.handleKeyPress(e);
|
|
|
|
}.bind(this));
|
|
|
|
|
|
|
|
$(window).keydown(function(e) {
|
|
|
|
this.handleKeyDown(e);
|
2014-01-20 23:58:33 +00:00
|
|
|
}.bind(this));
|
2014-01-19 23:21:53 +00:00
|
|
|
};
|
2014-01-19 14:23:17 +00:00
|
|
|
|
2014-01-19 23:21:53 +00:00
|
|
|
/**
|
|
|
|
* Handles the buttons in the UI.
|
|
|
|
* @param {string} id The ID of the element clicked.
|
|
|
|
*/
|
|
|
|
ascii.Controller.prototype.updateButtons = function(id) {
|
2014-01-21 23:31:58 +00:00
|
|
|
$('#buttons > button.tool').removeClass('active');
|
2014-01-20 21:36:58 +00:00
|
|
|
$('.dialog').removeClass('visible');
|
|
|
|
|
2014-01-22 22:32:16 +00:00
|
|
|
$('#' + id).toggleClass('active');
|
|
|
|
$('#' + id + '-dialog').toggleClass('visible');
|
2014-01-20 21:36:58 +00:00
|
|
|
|
2014-01-19 23:21:53 +00:00
|
|
|
// Install the right draw tool based on button pressed.
|
|
|
|
if (id == 'box-button') {
|
|
|
|
this.drawFunction = new ascii.DrawBox(this.state);
|
|
|
|
}
|
|
|
|
if (id == 'line-button') {
|
2014-01-19 14:23:17 +00:00
|
|
|
this.drawFunction = new ascii.DrawLine(this.state);
|
2014-01-19 23:21:53 +00:00
|
|
|
}
|
|
|
|
if (id == 'freeform-button') {
|
2014-01-20 21:58:25 +00:00
|
|
|
this.drawFunction = new ascii.DrawFreeform(this.state, SPECIAL_VALUE);
|
2014-01-19 23:21:53 +00:00
|
|
|
}
|
|
|
|
if (id == 'erase-button') {
|
2014-01-20 21:58:25 +00:00
|
|
|
this.drawFunction = new ascii.DrawErase(this.state);
|
2014-01-19 23:21:53 +00:00
|
|
|
}
|
|
|
|
if (id == 'move-button') {
|
2014-01-19 14:23:17 +00:00
|
|
|
this.drawFunction = new ascii.DrawMove(this.state);
|
2014-01-19 23:21:53 +00:00
|
|
|
}
|
2014-01-20 23:58:33 +00:00
|
|
|
if (id == 'text-button') {
|
|
|
|
this.drawFunction = new ascii.DrawText(this.state);
|
|
|
|
}
|
2014-01-21 23:31:58 +00:00
|
|
|
if (id == 'export-button') {
|
|
|
|
$('#export-area').val(this.state.outputText());
|
|
|
|
}
|
2014-01-20 23:58:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles key presses.
|
2014-01-21 21:30:47 +00:00
|
|
|
* @param {Object} event
|
|
|
|
*/
|
|
|
|
ascii.Controller.prototype.handleKeyPress = function(event) {
|
|
|
|
if (!event.ctrlKey && !event.metaKey) {
|
|
|
|
this.drawFunction.handleKey(String.fromCharCode(event.keyCode));
|
|
|
|
this.view.dirty = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles key down events.
|
|
|
|
* @param {Object} event
|
2014-01-20 23:58:33 +00:00
|
|
|
*/
|
2014-01-21 21:30:47 +00:00
|
|
|
ascii.Controller.prototype.handleKeyDown = function(event) {
|
|
|
|
// Override some special characters so that they can be handled in one place.
|
|
|
|
var specialKeyCode = null;
|
|
|
|
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
|
|
|
if (event.keyCode == 67) { specialKeyCode = KEY_COPY; }
|
|
|
|
if (event.keyCode == 86) { specialKeyCode = KEY_PASTE; }
|
2014-01-21 22:34:56 +00:00
|
|
|
if (event.keyCode == 90) { this.state.undo(); this.view.dirty = true; }
|
2014-01-21 21:30:47 +00:00
|
|
|
// if (event.keyCode == 89) { this.state.redo(); }
|
|
|
|
if (event.keyCode == 88) { specialKeyCode = KEY_CUT; }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.keyCode == 8) { specialKeyCode = KEY_BACKSPACE; }
|
|
|
|
if (event.keyCode == 13) { specialKeyCode = KEY_RETURN; }
|
|
|
|
if (event.keyCode == 38) { specialKeyCode = KEY_UP; }
|
|
|
|
if (event.keyCode == 40) { specialKeyCode = KEY_DOWN; }
|
|
|
|
if (event.keyCode == 37) { specialKeyCode = KEY_LEFT; }
|
|
|
|
if (event.keyCode == 39) { specialKeyCode = KEY_RIGHT; }
|
|
|
|
|
|
|
|
if (specialKeyCode != null) {
|
2014-01-21 23:31:58 +00:00
|
|
|
//event.preventDefault();
|
|
|
|
//event.stopPropagation();
|
2014-01-21 21:30:47 +00:00
|
|
|
this.drawFunction.handleKey(specialKeyCode);
|
|
|
|
this.view.dirty = true;
|
2014-01-20 23:58:33 +00:00
|
|
|
}
|
2014-01-11 21:02:14 +00:00
|
|
|
};
|
2014-01-21 21:30:47 +00:00
|
|
|
|