diff --git a/examples/Animated/Flock.html b/examples/Animated/Flock.html
index 63c1cc17..1c217eea 100644
--- a/examples/Animated/Flock.html
+++ b/examples/Animated/Flock.html
@@ -287,7 +287,7 @@
var layer = document.activeLayer;
function onKeyDown(event) {
- if (event.keyCode == 'space')
+ if (event.key == 'space')
layer.selected = !layer.selected;
}
diff --git a/src/item/Item.js b/src/item/Item.js
index 9029eee6..7ca7feaf 100644
--- a/src/item/Item.js
+++ b/src/item/Item.js
@@ -937,7 +937,7 @@ var Item = this.Item = Base.extend({
if (!func || !func._installed) {
var hash = {};
hash[handler] = function(event) {
- // Always clear the drag set on mouse-up
+ // Always clear the drag set on mouseup
if (name === 'up')
sets.drag = {};
removeAll(sets[name]);
diff --git a/src/paper.js b/src/paper.js
index 18332d96..182d8029 100644
--- a/src/paper.js
+++ b/src/paper.js
@@ -59,11 +59,7 @@ Base.inject({
var start = start || 0,
length = length || list.length - start;
var obj = list[start];
- // As a convention, do not return objects that are owned, e.g.
- // LinkedPoint or SegmentPoint, although they are instances of Point,
- // since they override properties with beans. Convert these to pure
- // Points instead, further down.
- if (obj && !obj._owner && obj instanceof this
+ if (obj instanceof this
// If the class defines _readNull, return null when nothing
// was provided
|| this.prototype._readNull && obj == null && length <= 1)
@@ -122,8 +118,28 @@ Base.inject({
});
},
+ camelize: function(str) {
+ return str.replace(/-(\w)/g, function(all, chr) {
+ return chr.toUpperCase();
+ });
+ },
+
+ /**
+ * Utility function for rendering numbers to strings at a precision of up
+ * to 5 fractional digits.
+ */
formatNumber: function(num) {
return (Math.round(num * 100000) / 100000).toString();
+ },
+
+ /**
+ * Utility function for rendering objects to strings, in object literal
+ * notation.
+ */
+ formatObject: function(obj) {
+ return '{ ' + Base.each(obj, function(value, key) {
+ this.push(key + ': ' + value);
+ }, []).join(', ') + ' }';
}
});
diff --git a/src/tool/Tool.js b/src/tool/Tool.js
index 8d789df0..0717fccd 100644
--- a/src/tool/Tool.js
+++ b/src/tool/Tool.js
@@ -31,7 +31,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
this.events = {
mousedown: function(event) {
curPoint = viewToArtwork(event, that._document);
- that.onHandleEvent('mouse-down', curPoint, event);
+ that.onHandleEvent('mousedown', curPoint, event);
if (that.onMouseDown)
that._document.redraw();
if (that.eventInterval != null) {
@@ -54,9 +54,9 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
if (dragging && !onlyMove) {
curPoint = point || curPoint;
if (curPoint)
- that.onHandleEvent('mouse-drag', curPoint, event);
+ that.onHandleEvent('mousedrag', curPoint, event);
} else if (!dragging || onlyMove) {
- that.onHandleEvent('mouse-move', point, event);
+ that.onHandleEvent('mousemove', point, event);
}
if (that.onMouseMove || that.onMouseDrag)
that._document.redraw();
@@ -67,7 +67,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
curPoint = null;
if (that.eventInterval != null)
clearInterval(this.timer);
- that.onHandleEvent('mouse-up',
+ that.onHandleEvent('mouseup',
viewToArtwork(event, that._document), event);
if (that.onMouseUp)
that._document.redraw();
diff --git a/src/tool/ToolEvent.js b/src/tool/ToolEvent.js
index 5a0df5d1..b68b1fec 100644
--- a/src/tool/ToolEvent.js
+++ b/src/tool/ToolEvent.js
@@ -39,25 +39,13 @@ var ToolEvent = this.ToolEvent = Base.extend({
this.type = type;
this.event = event;
},
-
- toString: function() {
- return '{ type: ' + this.type
- + ', point: ' + this.point
- + ', count: ' + this.count
- + ', modifiers: ' + this.modifiers
- + ' }';
- },
/**
* Convenience method to allow local overrides of point values.
* See application below.
*/
- choosePoint: function(point, toolPoint) {
- if (point)
- return point;
- if (toolPoint)
- return new Point(toolPoint);
- return null;
+ _choosePoint: function(point, toolPoint) {
+ return point ? point : toolPoint ? toolPoint.clone() : null;
},
/**
@@ -78,7 +66,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
*
*/
getPoint: function() {
- return this.choosePoint(this._point, this.tool.point);
+ return this._choosePoint(this._point, this.tool.point);
},
setPoint: function(point) {
@@ -90,7 +78,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
* event was fired.
*/
getLastPoint: function() {
- return this.choosePoint(this._lastPoint, this.tool.lastPoint);
+ return this._choosePoint(this._lastPoint, this.tool.lastPoint);
},
setLastPoint: function(lastPoint) {
@@ -102,7 +90,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
* was last clicked.
*/
getDownPoint: function() {
- return this.choosePoint(this._downPoint, this.tool.downPoint);
+ return this._choosePoint(this._downPoint, this.tool.downPoint);
},
setDownPoint: function(downPoint) {
@@ -117,7 +105,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
*/
getMiddlePoint: function() {
// For explanations, see getDelta()
- if (this._middlePoint == null && this.tool.lastPoint != null) {
+ if (!this._middlePoint && this.tool.lastPoint) {
// (point + lastPoint) / 2
return this.tool.point.add(this.tool.lastPoint).divide(2);
}
@@ -130,8 +118,8 @@ var ToolEvent = this.ToolEvent = Base.extend({
/**
* The difference between the current position and the last position of the
- * mouse when the event was fired. In case of the mouse-up event, the
- * difference to the mouse-down position is returned.
+ * mouse when the event was fired. In case of the mouseup event, the
+ * difference to the mousedown position is returned.
*/
getDelta: function() {
// Do not put the calculated delta into delta, since this only reserved
@@ -139,10 +127,9 @@ var ToolEvent = this.ToolEvent = Base.extend({
// Instead, keep calculating the delta each time, so the result can be
// directly modified by the script without changing the internal values.
// We could cache this and use clone, but this is almost as fast...
- if (this._delta == null && this.tool.lastPoint != null) {
- return this.tool.point.subtract(this.tool.lastPoint);
- }
- return this._delta;
+ return this._delta && this.tool.lastPoint
+ ? this.tool.point.subtract(this.tool.lastPoint)
+ : this._delta;
},
setDelta: function(delta) {
@@ -168,32 +155,21 @@ var ToolEvent = this.ToolEvent = Base.extend({
*
*/
getCount: function() {
- switch (this.type) {
- case 'mouse-down':
- case 'mouse-up':
- // Return downCount for both mouse down and up, since
- // the count is the same.
- return this.tool.downCount;
- default:
- return this.tool.count;
- }
+ // Return downCount for both mouse down and up, since
+ // the count is the same.
+ return /^mouse(down|up)$/.test(this.type)
+ ? this.tool.downCount
+ : this.tool.count;
},
setCount: function(count) {
- switch (this.type) {
- case 'mouse-down':
- case 'mouse-up':
- this.tool.downCount = count;
- break;
- default:
- this.tool.count = count;
- break;
- }
+ this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
+ = count;
},
getModifiers: function() {
return Key.modifiers;
- }
+ },
// TODO: implement hitTest first
// getItem: function() {
@@ -215,4 +191,12 @@ var ToolEvent = this.ToolEvent = Base.extend({
// setItem: function(Item item) {
// this.item = item;
// }
+
+ toString: function() {
+ return '{ type: ' + this.type
+ + ', point: ' + this.getPoint()
+ + ', count: ' + this.getCount()
+ + ', modifiers: ' + this.getModifiers()
+ + ' }';
+ }
});
diff --git a/src/tool/ToolHandler.js b/src/tool/ToolHandler.js
index 87faec5f..8c164974 100644
--- a/src/tool/ToolHandler.js
+++ b/src/tool/ToolHandler.js
@@ -35,8 +35,8 @@ var ToolHandler = this.ToolHandler = Base.extend({
*
* Sample code:
*
- * // Fire the onMouseDrag event after the user has dragged
- * // more then 5 points from the last onMouseDrag event:
+ * // Fire the onMouseDrag event after the user has dragged more then 5
+ * // points from the last onMouseDrag event:
* tool.minDistance = 5;
*
*/
@@ -98,42 +98,38 @@ var ToolHandler = this.ToolHandler = Base.extend({
this.lastPoint = this.point;
this.point = pt;
switch (type) {
- case 'mouse-down':
+ case 'mousedown':
this.lastPoint = this.downPoint;
this.downPoint = this.point;
this.downCount++;
break;
- case 'mouse-up':
- // Mouse up events return the down point for last point,
- // so delta is spanning over the whole drag.
+ case 'mouseup':
+ // Mouse up events return the down point for last point, so delta is
+ // spanning over the whole drag.
this.lastPoint = this.downPoint;
break;
}
- if (start) {
- this.count = 0;
- } else {
- this.count++;
- }
+ this.count = start ? 0 : this.count + 1;
return true;
},
onHandleEvent: function(type, pt, event) {
switch (type) {
- case 'mouse-down':
+ case 'mousedown':
this.updateEvent(type, pt, null, null, true, false, false);
if (this.onMouseDown)
this.onMouseDown(new ToolEvent(this, type, event));
break;
- case 'mouse-drag':
- // In order for idleInterval drag events to work, we need to
- // not check the first call for a change of position.
- // Subsequent calls required by min/maxDistance functionality
- // will require it, otherwise this might loop endlessly.
+ case 'mousedrag':
+ // In order for idleInterval drag events to work, we need to not
+ // check the first call for a change of position. Subsequent calls
+ // required by min/maxDistance functionality will require it,
+ // otherwise this might loop endlessly.
var needsChange = false,
- // If the mouse is moving faster than maxDistance, do not
- // produce events for what is left after the first event is
- // generated in case it is shorter than maxDistance, as this
- // would produce weird results. matchMaxDistance controls this.
+ // If the mouse is moving faster than maxDistance, do not produce
+ // events for what is left after the first event is generated in
+ // case it is shorter than maxDistance, as this would produce weird
+ // results. matchMaxDistance controls this.
matchMaxDistance = false;
while (this.updateEvent(type, pt, this.minDistance,
this.maxDistance, false, needsChange, matchMaxDistance)) {
@@ -143,11 +139,11 @@ var ToolHandler = this.ToolHandler = Base.extend({
matchMaxDistance = true;
}
break;
- case 'mouse-up':
- // If the last mouse drag happened in a different place, call
- // mouse drag first, then mouse up.
+ case 'mouseup':
+ // If the last mouse drag happened in a different place, call mouse
+ // drag first, then mouse up.
if ((this.point.x != pt.x || this.point.y != pt.y)
- && this.updateEvent('mouse-drag', pt, this.minDistance,
+ && this.updateEvent('mousedrag', pt, this.minDistance,
this.maxDistance, false, false, false)) {
if (this.onMouseDrag)
this.onMouseDrag(new ToolEvent(this, type, event));
@@ -156,11 +152,11 @@ var ToolHandler = this.ToolHandler = Base.extend({
false, false);
if (this.onMouseUp)
this.onMouseUp(new ToolEvent(this, type, event));
- // Start with new values for 'mouse-move'
+ // Start with new values for 'mousemove'
this.updateEvent(type, pt, null, null, true, false, false);
this.firstMove = true;
break;
- case 'mouse-move':
+ case 'mousemove':
while (this.updateEvent(type, pt, this.minDistance,
this.maxDistance, this.firstMove, true, false)) {
if (this.onMouseMove)
diff --git a/src/ui/Event.js b/src/ui/Event.js
index 8a7d0919..f05b6242 100644
--- a/src/ui/Event.js
+++ b/src/ui/Event.js
@@ -15,6 +15,8 @@
*/
var Event = this.Event = Base.extend({
+ beans: true,
+
initialize: function(event) {
this.event = event;
},
diff --git a/src/ui/Key.js b/src/ui/Key.js
index 1342a8d7..6e2b2a08 100644
--- a/src/ui/Key.js
+++ b/src/ui/Key.js
@@ -16,18 +16,21 @@
var Key = this.Key = new function() {
// TODO: make sure the keys are called the same as in Scriptographer
- // Missing: tab, cancel, clear, pause, page-down, page-up, end, home, comma,
- // minus, period, slash, etc etc etc.
+ // Missing: tab, cancel, clear, page-down, page-up, comma, minus, period,
+ // slash, etc etc etc.
var keys = {
8: 'backspace',
13: 'enter',
16: 'shift',
17: 'control',
- 19: 'option', // was alt
- 20: 'capsLock',
+ 18: 'option',
+ 19: 'pause',
+ 20: 'caps-lock',
27: 'escape',
32: 'space',
+ 35: 'end',
+ 36: 'home',
37: 'left',
38: 'up',
39: 'right',
@@ -41,57 +44,84 @@ var Key = this.Key = new function() {
control: false,
option: false,
command: false,
- capsLock: false
+ capsLock: false,
+
+ toString: function() {
+ return Base.formatObject(this);
+ }
},
- keyCodes = {},
- downCode,
- downTimer;
+ // Since only keypress gets proper keyCodes that are actually representing
+ // characters, we need to perform a little trickery here to use these codes
+ // in onKeyDown/Up: keydown is used to store the downCode and handle
+ // modifiers and special keys such as arrows, space, etc, keypress fires the
+ // actual onKeyDown event and maps the keydown keyCode to the keypress
+ // charCode so keyup can do the right thing too.
+ charCodeMap = {}, // keyCode -> charCode mappings for pressed keys
+ keyMap = {}, // Map for currently pressed keys
+ downCode; // The last keyCode from keydown
- function handleKey(down, code, event) {
- var character = String.fromCharCode(code),
- keyCode = keys[code] || character.toLowerCase(),
+ function handleKey(down, keyCode, charCode, event) {
+ var character = String.fromCharCode(charCode),
+ key = keys[keyCode] || character.toLowerCase(),
handler = down ? 'onKeyDown' : 'onKeyUp';
- console.log(handler, keyCode, character);
- if (modifiers[keyCode] !== undefined) {
- modifiers[keyCode] = down;
- } else if (paper.tool && paper.tool[handler]) {
+ keyMap[key] = down;
+ if (paper.tool && paper.tool[handler]) {
// Call the onKeyDown or onKeyUp handler if present
// When the handler function returns false, prevent the
// default behaviour of the key event:
// PORT: Add to Sg
- var keyEvent = new KeyEvent(down, keyCode, character, event);
+ var keyEvent = new KeyEvent(down, key, character, event);
if (paper.tool[handler](keyEvent) === false) {
keyEvent.preventDefault();
}
}
}
- // Since only keypress gest proper keyCodes that are actually representing
- // characters, we need to add a little timeout to keydown events to see if
- // they are follow immediately by a keypress, and if so, map the keyCode
- // from the keydown to the one from keypress, so keyup still knows what
- // code has now been released.
DomEvent.add(document, {
keydown: function(event) {
- var code = downCode = event.which || event.keyCode;
- downTimer = setTimeout(function() {
- keyCodes[code] = code;
- handleKey(true, code, event);
- }, 1);
+ var code = event.which || event.keyCode;
+ // If the keyCode is in keys, it needs to be handled by keydown and
+ // not in keypress after (arrows for example wont be triggering
+ // a keypress, but space would).
+ var key = keys[code], name;
+ if (key) {
+ // Do not fire handleKey for modifiers, but for other keys such
+ // ass arrows, delete, backspace, etc.
+ if (modifiers[name = Base.camelize(key)] !== undefined) {
+ modifiers[name] = true;
+ } else {
+ // No char code for special keys, but mark as pressed
+ charCodeMap[code] = 0;
+ handleKey(true, code, null, event);
+ }
+ // Do not set downCode as we handled it already. Space would
+ // be handled twice otherwise, once here, once in keypress.
+ } else {
+ downCode = code;
+ }
},
keypress: function(event) {
- clearTimeout(downTimer);
- var code = event.which || event.keyCode;
- keyCodes[downCode] = code;
- handleKey(true, code, event);
+ if (downCode != null) {
+ var code = event.which || event.keyCode;
+ // Link the downCode from keydown with the code form keypress, so
+ // keyup can retrieve that code again.
+ charCodeMap[downCode] = code;
+ handleKey(true, downCode, code, event);
+ downCode = null;
+ }
},
keyup: function(event) {
- var code = event.which || event.keyCode;
- handleKey(false, keyCodes[code], event);
- delete keyCodes[code];
+ var code = event.which || event.keyCode,
+ key = keys[code], name;
+ if (key && modifiers[name = Base.camelize(key)] !== undefined) {
+ modifiers[name] = false
+ } else if (charCodeMap[code] != null) {
+ handleKey(false, code, charCodeMap[code], event);
+ delete charCodeMap[code];
+ }
}
});
@@ -99,7 +129,7 @@ var Key = this.Key = new function() {
modifiers: modifiers,
isDown: function(key) {
- return !!activeKeys[key];
+ return !!keyMap[key];
}
};
};
\ No newline at end of file
diff --git a/src/ui/KeyEvent.js b/src/ui/KeyEvent.js
index 3ea14f7c..d63eb315 100644
--- a/src/ui/KeyEvent.js
+++ b/src/ui/KeyEvent.js
@@ -16,11 +16,19 @@
var KeyEvent = this.KeyEvent = Event.extend(new function() {
return {
- initialize: function(down, keyCode, character, event) {
+ initialize: function(down, key, character, event) {
this.base(event);
- this.type = down ? 'key-down' : 'key-up';
- this.keyCode = keyCode;
+ this.type = down ? 'keydown' : 'keyup';
+ this.key = key;
this.character = character;
+ },
+
+ toString: function() {
+ return '{ type: ' + this.type
+ + ', key: ' + this.key
+ + ', character: ' + this.character
+ + ', modifiers: ' + this.getModifiers()
+ + ' }';
}
};
});
diff --git a/src/util/PaperScript.js b/src/util/PaperScript.js
index 4c7aefdc..121d0bd5 100644
--- a/src/util/PaperScript.js
+++ b/src/util/PaperScript.js
@@ -134,14 +134,13 @@ var PaperScript = this.PaperScript = new function() {
var doc = paper.document,
tool = paper.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)
&& new Tool(null, doc),
- onEditOptions, onOptions, onSelect, onDeselect, onReselect,
- onMouseDown, onMouseUp, onMouseDrag, onMouseMove, onKeyDown,
- onKeyUp,
+ onEditOptions, onSelect, onDeselect, onReselect, onMouseDown,
+ onMouseUp, onMouseDrag, onMouseMove, onKeyDown, onKeyUp,
res = eval(compile(code));
if (tool) {
- Base.each(['onEditOptions', 'onOptions', 'onSelect',
- 'onDeselect', 'onReselect', 'onMouseDown', 'onMouseUp',
- 'onMouseDrag', 'onMouseMove', 'onKeyDown', 'onKeyUp'],
+ Base.each(['onEditOptions', 'onSelect', 'onDeselect',
+ 'onReselect', 'onMouseDown', 'onMouseUp', 'onMouseDrag',
+ 'onMouseMove', 'onKeyDown', 'onKeyUp'],
function(key) {
tool[key] = eval(key);
}