+
+ Wine Gums
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/SVG Import/Arcs.html b/examples/SVG Import/Arcs.html
new file mode 100644
index 00000000..ee600c17
--- /dev/null
+++ b/examples/SVG Import/Arcs.html
@@ -0,0 +1,76 @@
+
+
+
+
+ Arcs Testing
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/basic/Point.js b/src/basic/Point.js
index 8714e417..d84dc79c 100644
--- a/src/basic/Point.js
+++ b/src/basic/Point.js
@@ -673,7 +673,7 @@ var Point = Base.extend(/** @lends Point# */{
c = Math.cos(angle);
point = new Point(
point.x * c - point.y * s,
- point.y * c + point.x * s
+ point.x * s + point.y * c
);
return center ? point.add(center) : point;
},
diff --git a/src/core/Base.js b/src/core/Base.js
index a79e655d..0d112b61 100644
--- a/src/core/Base.js
+++ b/src/core/Base.js
@@ -201,6 +201,13 @@ Base.inject(/** @lends Base# */{
return list[list.__index = start || list.__index || 0];
},
+ /**
+ * Returns how many arguments remain to be read in the argument list.
+ */
+ remain: function(list) {
+ return list.length - (list.__index || 0);
+ },
+
/**
* Reads all readable arguments from the list, handling nested arrays
* separately.
diff --git a/src/dom/DomElement.js b/src/dom/DomElement.js
index 624df695..9c66ec22 100644
--- a/src/dom/DomElement.js
+++ b/src/dom/DomElement.js
@@ -46,6 +46,24 @@ var DomElement = new function() {
return res;
}
+ // Handles both getting and setting of vendor prefix values
+ function handlePrefix(el, name, set, value) {
+ var prefixes = ['webkit', 'moz', 'Moz', 'ms', 'o', ''],
+ suffix = name[0].toUpperCase() + name.substring(1);
+ for (var i = 0; i < 6; i++) {
+ var prefix = prefixes[i],
+ key = prefix ? prefix + suffix : name;
+ if (key in el) {
+ if (set) {
+ el[key] = value;
+ } else {
+ return el[key];
+ }
+ break;
+ }
+ }
+ }
+
return /** @lends DomElement */{
create: function(nodes, parent) {
var isArray = Array.isArray(nodes),
@@ -203,13 +221,17 @@ var DomElement = new function() {
* Gets the given property from the element, trying out all browser
* prefix variants.
*/
- getPrefixValue: function(el, name) {
- var value = el[name],
- prefixes = ['webkit', 'moz', 'ms', 'o'],
- suffix = name[0].toUpperCase() + name.substring(1);
- for (var i = 0; i < 4 && value == null; i++)
- value = el[prefixes[i] + suffix];
- return value;
+ getPrefixed: function(el, name) {
+ return handlePrefix(el, name);
+ },
+
+ setPrefixed: function(el, name, value) {
+ if (typeof name === 'object') {
+ for (var key in name)
+ handlePrefix(el, key, true, name[key]);
+ } else {
+ handlePrefix(el, name, true, value);
+ }
}
};
};
diff --git a/src/dom/DomEvent.js b/src/dom/DomEvent.js
index 3426edfa..33dc0bbb 100644
--- a/src/dom/DomEvent.js
+++ b/src/dom/DomEvent.js
@@ -17,13 +17,21 @@
*/
var DomEvent = /** @lends DomEvent */{
add: function(el, events) {
- for (var type in events)
- el.addEventListener(type, events[type], false);
+ for (var type in events) {
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++)
+ el.addEventListener(parts[i], func, false);
+ }
},
remove: function(el, events) {
- for (var type in events)
- el.removeEventListener(type, events[type], false);
+ for (var type in events) {
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++)
+ el.removeEventListener(parts[i], func, false);
+ }
},
getPoint: function(event) {
@@ -59,8 +67,7 @@ var DomEvent = /** @lends DomEvent */{
};
DomEvent.requestAnimationFrame = new function() {
- var nativeRequest = DomElement.getPrefixValue(window,
- 'requestAnimationFrame'),
+ var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
requested = false,
callbacks = [],
focused = true,
diff --git a/src/path/Path.js b/src/path/Path.js
index 3888c3a2..ed265fba 100644
--- a/src/path/Path.js
+++ b/src/path/Path.js
@@ -2295,58 +2295,126 @@ var Path = PathItem.extend(/** @lends Path# */{
this.quadraticCurveTo(handle, to);
},
- arcTo: function(/* to, clockwise | through, to */) {
+ arcTo: function(/* to, clockwise | through, to
+ | to, radius, rotation, large, sweep */) {
// Get the start point:
var current = getCurrentSegment(this),
from = current._point,
- through,
to = Point.read(arguments),
- // Peek at next value to see if it's clockwise,
- // with true as default value.
- clockwise = Base.pick(Base.peek(arguments), true);
+ through,
+ // Peek at next value to see if it's clockwise, with true as the
+ // default value.
+ peek = Base.peek(arguments),
+ clockwise = Base.pick(peek, true),
+ center, extent, vector, matrix;
+ // We're handling three different approaches to drawing arcs in one
+ // large function:
if (typeof clockwise === 'boolean') {
- // arcTo(to, clockwise)
+ // #1: arcTo(to, clockwise)
var middle = from.add(to).divide(2),
through = middle.add(middle.subtract(from).rotate(
clockwise ? -90 : 90));
- } else {
- // arcTo(through, to)
+ } else if (Base.remain(arguments) <= 2) {
+ // #2: arcTo(through, to)
through = to;
to = Point.read(arguments);
+ } else {
+ // #3: arcTo(to, radius, rotation, large, sweep)
+ // Drawing arcs in SVG style:
+ var radius = Size.read(arguments);
+ // If rx = 0 or ry = 0 then this arc is treated as a
+ // straight line joining the endpoints.
+ if (radius.isZero())
+ return this.lineTo(to);
+ // See for an explanation of the following calculations:
+ // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ var rotation = Base.read(arguments),
+ large = !!Base.read(arguments),
+ sweep = !!Base.read(arguments),
+ middle = from.add(to).divide(2),
+ pt = from.subtract(middle).rotate(-rotation),
+ x = pt.x,
+ y = pt.y,
+ abs = Math.abs,
+ EPSILON = /*#=*/ Numerical.EPSILON,
+ rx = abs(radius.width),
+ ry = abs(radius.height),
+ rxSq = rx * rx,
+ rySq = ry * ry,
+ xSq = x * x,
+ ySq = y * y;
+ // "...ensure radii are large enough"
+ var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
+ if (factor > 1) {
+ rx *= factor;
+ ry *= factor;
+ rxSq = rx * rx;
+ rySq = ry * ry;
+ }
+ factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
+ (rxSq * ySq + rySq * xSq);
+ if (abs(factor) < EPSILON)
+ factor = 0;
+ if (factor < 0)
+ throw new Error(
+ 'Cannot create an arc with the given arguments');
+ center = new Point(rx * y / ry, -ry * x / rx)
+ // "...where the + sign is chosen if fA != fS,
+ // and the − sign is chosen if fA = fS."
+ .multiply((large == sweep ? -1 : 1) * Math.sqrt(factor))
+ .rotate(rotation).add(middle);
+ // Now create a matrix that maps the unit circle to the ellipse,
+ // for easier construction below.
+ matrix = new Matrix().translate(center).rotate(rotation)
+ .scale(rx, ry);
+ // Transform from and to to the unit circle coordinate space
+ // and calculcate start vector and extend from there.
+ vector = matrix._inverseTransform(from);
+ extent = vector.getDirectedAngle(matrix._inverseTransform(to));
+ // "...in other words, if sweep = 0 and extent is > 0, subtract
+ // 360, whereas if sweep = 1 and extent < 0, then add 360."
+ if (!sweep && extent > 0)
+ extent -= 360;
+ else if (sweep && extent < 0)
+ extent += 360;
}
- // Construct the two perpendicular middle lines to (from, through)
- // and (through, to), and intersect them to get the center
- var l1 = new Line(from.add(through).divide(2),
- through.subtract(from).rotate(90), true),
- l2 = new Line(through.add(to).divide(2),
- to.subtract(through).rotate(90), true),
- center = l1.intersect(l2, true),
- line = new Line(from, to),
- throughSide = line.getSide(through);
- if (!center) {
+ if (through) {
+ // Calculate center, vector and extend for non SVG versions:
+ // Construct the two perpendicular middle lines to
+ // (from, through) and (through, to), and intersect them to get
+ // the center.
+ var l1 = new Line(from.add(through).divide(2),
+ through.subtract(from).rotate(90), true),
+ l2 = new Line(through.add(to).divide(2),
+ to.subtract(through).rotate(90), true),
+ line = new Line(from, to),
+ throughSide = line.getSide(through);
+ center = l1.intersect(l2, true);
// If the two lines are colinear, there cannot be an arc as the
// circle is infinitely big and has no center point. If side is
// 0, the connecting arc line of this huge circle is a line
// between the two points, so we can use #lineTo instead.
// Otherwise we bail out:
- if (!throughSide)
- return this.lineTo(to);
- throw new Error('Cannot put an arc through the given points: '
- + [from, through, to]);
- }
- var vector = from.subtract(center),
- extent = vector.getDirectedAngle(to.subtract(center)),
- centerSide = line.getSide(center);
- if (centerSide == 0) {
- // If the center is lying on the line, we might have gotten the
- // wrong sign for extent above. Use the sign of the side of the
- // through point.
- extent = throughSide * Math.abs(extent);
- } else if (throughSide == centerSide) {
- // If the center is on the same side of the line (from, to) as
- // the through point, we're extending bellow 180 degrees and
- // need to adapt extent.
- extent -= 360 * (extent < 0 ? -1 : 1);
+ if (!center) {
+ if (!throughSide)
+ return this.lineTo(to);
+ throw new Error(
+ 'Cannot create an arc with the given arguments');
+ }
+ vector = from.subtract(center);
+ extent = vector.getDirectedAngle(to.subtract(center));
+ var centerSide = line.getSide(center);
+ if (centerSide === 0) {
+ // If the center is lying on the line, we might have gotten
+ // the wrong sign for extent above. Use the sign of the side
+ // of the through point.
+ extent = throughSide * Math.abs(extent);
+ } else if (throughSide === centerSide) {
+ // If the center is on the same side of the line (from, to)
+ // as the through point, we're extending bellow 180 degrees
+ // and need to adapt extent.
+ extent -= 360 * (extent < 0 ? -1 : 1);
+ }
}
var ext = Math.abs(extent),
count = ext >= 360 ? 4 : Math.ceil(ext / 90),
@@ -2357,15 +2425,29 @@ var Path = PathItem.extend(/** @lends Path# */{
for (var i = 0; i <= count; i++) {
// Explicitely use to point for last segment, since depending
// on values the calculation adds imprecision:
- var pt = i < count ? center.add(vector) : to;
- var out = i < count ? vector.rotate(90).multiply(z) : null;
- if (i == 0) {
+ var pt = to,
+ out = null;
+ if (i < count) {
+ out = vector.rotate(90).multiply(z);
+ if (matrix) {
+ pt = matrix._transformPoint(vector);
+ out = matrix._transformPoint(vector.add(out))
+ .subtract(pt);
+ } else {
+ pt = center.add(vector);
+ }
+ }
+ if (i === 0) {
// Modify startSegment
current.setHandleOut(out);
} else {
// Add new Segment
- segments.push(
- new Segment(pt, vector.rotate(-90).multiply(z), out));
+ var _in = vector.rotate(-90).multiply(z);
+ if (matrix) {
+ _in = matrix._transformPoint(vector.add(_in))
+ .subtract(pt);
+ }
+ segments.push(new Segment(pt, _in, out));
}
vector = vector.rotate(inc);
}
diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js
index a85f2765..dec87986 100644
--- a/src/path/PathItem.Boolean.js
+++ b/src/path/PathItem.Boolean.js
@@ -214,7 +214,9 @@ PathItem.inject(new function() {
var loc = intersections[i],
t = loc._parameter;
// Check if we are splitting same curve multiple times
- if (prevLoc && prevLoc._curve === loc._curve) {
+ if (prevLoc && prevLoc._curve === loc._curve
+ // Avoid dividing with zero
+ && prevLoc._parameter > 0) {
// Scale parameter after previous split.
t /= prevLoc._parameter;
} else {
@@ -404,8 +406,8 @@ PathItem.inject(new function() {
seg = interSeg;
dir = 1;
} else if (w3 * w4 !== 0) {
- // Do not attempt to switch contours if we aren't absolutely
- // sure that there is a possible candidate.
+ // Do not attempt to switch contours if we aren't
+ // absolutely sure that there is a possible candidate.
var curve = w3 < w4 ? c3 : c4,
nextCurve = operator(curve._segment1._winding)
? curve
@@ -469,8 +471,8 @@ PathItem.inject(new function() {
*
* @param {Point} point the location for which to determine the winding
* direction
- * @param {Boolean} horizontal whether we need to consider this point as
- * part of a horizontal curve
+ * @param {Boolean} horizontal whether we need to consider this point
+ * as part of a horizontal curve
* @param {Boolean} testContains whether we need to consider this point
* as part of stationary points on the curve itself, used when checking
* the winding about a point.
@@ -665,4 +667,4 @@ CompoundPath.inject(/** @lends CompoundPath# */{
monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
return monoCurves;
}
-});
\ No newline at end of file
+});
diff --git a/src/path/PathItem.js b/src/path/PathItem.js
index f2e5b3a9..12bf2453 100644
--- a/src/path/PathItem.js
+++ b/src/path/PathItem.js
@@ -192,10 +192,11 @@ var PathItem = Item.extend(/** @lends PathItem# */{
relative = false,
previous,
control,
- current = new Point();
+ current = new Point(),
+ start = new Point();
function getCoord(index, coord) {
- var val = parseFloat(coords[index]);
+ var val = +coords[index];
if (relative)
val += current[coord];
return val;
@@ -219,6 +220,8 @@ var PathItem = Item.extend(/** @lends PathItem# */{
coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
var length = coords && coords.length;
relative = command === lower;
+ if (previous === 'z' && lower !== 'z')
+ this.moveTo(current = start);
switch (lower) {
case 'm':
case 'l':
@@ -226,6 +229,8 @@ var PathItem = Item.extend(/** @lends PathItem# */{
this[j === 0 && lower === 'm' ? 'moveTo' : 'lineTo'](
current = getPoint(j));
control = current;
+ if(lower == 'm')
+ start = current;
break;
case 'h':
case 'v':
@@ -248,12 +253,12 @@ var PathItem = Item.extend(/** @lends PathItem# */{
// Smooth cubicCurveTo
for (var j = 0; j < length; j += 4) {
this.cubicCurveTo(
- /[cs]/i.test(previous)
+ /[cs]/.test(previous)
? current.multiply(2).subtract(control)
: current,
control = getPoint(j),
current = getPoint(j + 2));
- previous = command;
+ previous = lower;
}
break;
case 'q':
@@ -266,23 +271,26 @@ var PathItem = Item.extend(/** @lends PathItem# */{
case 't':
// Smooth quadraticCurveTo
for (var j = 0; j < length; j += 2) {
- console.log(previous, /[qt]/i.test(previous));
this.quadraticCurveTo(
- control = (/[qt]/i.test(previous)
+ control = (/[qt]/.test(previous)
? current.multiply(2).subtract(control)
: current),
current = getPoint(j));
- previous = command;
+ previous = lower;
}
break;
case 'a':
- // TODO: Implement Arcs!
+ for (var j = 0; j < length; j += 7) {
+ this.arcTo(current = getPoint(j + 5),
+ new Size(+coords[0], +coords[1]),
+ +coords[2], +coords[3], +coords[4]);
+ }
break;
case 'z':
this.closePath();
break;
}
- previous = command;
+ previous = lower;
}
},
diff --git a/src/path/Segment.js b/src/path/Segment.js
index 13345d38..6a5d7b85 100644
--- a/src/path/Segment.js
+++ b/src/path/Segment.js
@@ -150,25 +150,27 @@ var Segment = Base.extend(/** @lends Segment# */{
},
_changed: function(point) {
- if (!this._path)
+ var path = this._path;
+ if (!path)
return;
- // Delegate changes to affected curves if they exist. Check _curves
- // first to make sure we're not creating it by calling this.getCurve().
- var curve = this._path._curves && this.getCurve(),
- other;
- if (curve) {
- curve._changed();
- // Get the other affected curve, which is the previous one for
- // _point or _handleIn changing when this segment is _segment1 of
- // the curve, for all other cases it's the next (e.g. _handleOut
- // when this segment is _segment2)
- if (other = (curve[point == this._point
- || point == this._handleIn && curve._segment1 == this
- ? 'getPrevious' : 'getNext']())) {
- other._changed();
- }
+ // Delegate changes to affected curves if they exist.
+ var curves = path._curves,
+ index = this._index,
+ curveIn, curveOut;
+ if (curves) {
+ // Updated the neighboring affected curves, depending on which point
+ // is changing.
+ // TODO: Consider exposing these curves too, through #curveIn,
+ // and #curveOut, next to #curve?
+ if ((!point || point === this._point || point === this._handleIn)
+ && (curveIn = curves[index - 1]
+ || path._closed && curves[curves.length - 1]))
+ curveIn._changed();
+ if ((!point || point === this._point || point === this._handleOut)
+ && (curveOut = curves[index]))
+ curveOut._changed();
}
- this._path._changed(/*#=*/ Change.GEOMETRY);
+ path._changed(/*#=*/ Change.GEOMETRY);
},
/**
@@ -373,7 +375,8 @@ var Segment = Base.extend(/** @lends Segment# */{
},
/**
- * The curve that the segment belongs to.
+ * The curve that the segment belongs to. For the last segment of an open
+ * path, the previous segment is returned.
*
* @type Curve
* @bean
@@ -474,6 +477,16 @@ var Segment = Base.extend(/** @lends Segment# */{
return '{ ' + parts.join(', ') + ' }';
},
+ /**
+ * Transform the segment by the specified matrix.
+ *
+ * @param {Matrix} matrix the matrix to transform the segment by
+ */
+ transform: function(matrix) {
+ this._transformCoordinates(matrix, new Array(6), true);
+ this._changed();
+ },
+
_transformCoordinates: function(matrix, coords, change) {
// Use matrix.transform version() that takes arrays of multiple
// points for largely improved performance, as no calls to
diff --git a/src/style/Color.js b/src/style/Color.js
index 23c5438b..0ab82def 100644
--- a/src/style/Color.js
+++ b/src/style/Color.js
@@ -71,7 +71,7 @@ var Color = Base.extend(new function() {
// RGB / RGBA
components = match[1].split(',');
for (var i = 0, l = components.length; i < l; i++) {
- var value = parseFloat(components[i]);
+ var value = +components[i];
components[i] = i < 3 ? value / 255 : value;
}
} else {
diff --git a/src/svg/SVGImport.js b/src/svg/SVGImport.js
index 1dc82912..a1656e63 100644
--- a/src/svg/SVGImport.js
+++ b/src/svg/SVGImport.js
@@ -130,14 +130,10 @@ new function() {
}
function importPath(node) {
- // Get the path data, and determine whether it is a compound path or a
- // normal path based on the amount of moveTo commands inside it.
- var data = node.getAttribute('d'),
- path = data.match(/m/gi).length > 1
- ? new CompoundPath()
- : new Path();
- path.setPathData(data);
- return path;
+ return new CompoundPath({
+ pathData: node.getAttribute('d'),
+ insert: false
+ }).reduce();
}
function importGradient(node, type) {
diff --git a/src/ui/CanvasView.js b/src/ui/CanvasView.js
index 4923aafc..ed88d459 100644
--- a/src/ui/CanvasView.js
+++ b/src/ui/CanvasView.js
@@ -51,7 +51,7 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
// Hi-DPI Canvas support based on:
// http://www.html5rocks.com/en/tutorials/canvas/hidpi/
var deviceRatio = window.devicePixelRatio || 1,
- backingStoreRatio = DomElement.getPrefixValue(this._context,
+ backingStoreRatio = DomElement.getPrefixed(this._context,
'backingStorePixelRatio') || 1;
this._pixelRatio = deviceRatio / backingStoreRatio;
}
diff --git a/src/ui/View.js b/src/ui/View.js
index b1a1c753..6a8d2439 100644
--- a/src/ui/View.js
+++ b/src/ui/View.js
@@ -38,7 +38,20 @@ var View = Base.extend(Callback, /** @lends View# */{
if (this._id == null)
element.setAttribute('id', this._id = 'view-' + View._id++);
// Install event handlers
- DomEvent.add(element, this._viewHandlers);
+ DomEvent.add(element, this._viewEvents);
+ // Borrowed from Hammer.js:
+ var none = 'none';
+ DomElement.setPrefixed(element.style, {
+ userSelect: none,
+ // This makes the element blocking in IE10+
+ // You could experiment with the value, see this issue:
+ // https://github.com/EightMedia/hammer.js/issues/241
+ touchAction: none,
+ touchCallout: none,
+ contentZooming: none,
+ userDrag: none,
+ tapHighlightColor: 'rgba(0,0,0,0)'
+ });
// If the element has the resize attribute, resize the it to fill the
// window and resize it again whenever the user resizes the window.
if (PaperScope.hasAttribute(element, 'resize')) {
@@ -48,7 +61,7 @@ var View = Base.extend(Callback, /** @lends View# */{
that = this;
size = DomElement.getViewportBounds(element)
.getSize().subtract(offset);
- this._windowHandlers = {
+ this._windowEvents = {
resize: function() {
// Only update element offset if it's not invisible, as
// otherwise the offset would be wrong.
@@ -60,7 +73,7 @@ var View = Base.extend(Callback, /** @lends View# */{
.getSize().subtract(offset));
}
};
- DomEvent.add(window, this._windowHandlers);
+ DomEvent.add(window, this._windowEvents);
} else {
// Try visible size first, since that will help handling previously
// scaled canvases (e.g. when dealing with pixel-ratio)
@@ -130,8 +143,8 @@ var View = Base.extend(Callback, /** @lends View# */{
this._project.view = null;
/*#*/ if (__options.environment == 'browser') {
// Uninstall event handlers again for this view.
- DomEvent.remove(this._element, this._viewHandlers);
- DomEvent.remove(window, this._windowHandlers);
+ DomEvent.remove(this._element, this._viewEvents);
+ DomEvent.remove(window, this._windowEvents);
/*#*/ } // __options.environment == 'browser'
this._element = this._project = null;
// Remove all onFrame handlers.
@@ -687,7 +700,68 @@ var View = Base.extend(Callback, /** @lends View# */{
}
}
- function mousedown(event) {
+ function handleMouseMove(view, point, event) {
+ view._handleEvent('mousemove', point, event);
+ var tool = view._scope.tool;
+ if (tool) {
+ // If there's no onMouseDrag, fire onMouseMove while dragging.
+ tool._handleEvent(dragging && tool.responds('mousedrag')
+ ? 'mousedrag' : 'mousemove', point, event);
+ }
+ view.update();
+ return tool;
+ }
+
+ // Touch handling inspired by Hammer.js
+ var navigator = window.navigator,
+ mousedown, mousemove, mouseup;
+ if (navigator.pointerEnabled || navigator.msPointerEnabled) {
+ // HTML5 / MS pointer events
+ mousedown = 'pointerdown MSPointerDown';
+ mousemove = 'pointermove MSPointerMove';
+ mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
+ } else {
+ mousedown = 'touchstart';
+ mousemove = 'touchmove';
+ mouseup = 'touchend touchcancel';
+ // Do not add mouse events on mobile and tablet devices
+ if (!('ontouchstart' in window && navigator.userAgent.match(
+ /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
+ // For non pointer events browsers and mixed browsers, like chrome
+ // on Windows8 touch laptop.
+ mousedown += ' mousedown';
+ mousemove += ' mousemove';
+ mouseup += ' mouseup';
+ }
+ }
+
+ var viewEvents = {
+ 'selectstart dragstart': function(event) {
+ // Only stop this even if we're dragging already, since otherwise no
+ // text whatsoever can be selected on the page.
+ if (dragging)
+ event.preventDefault();
+ }
+ };
+
+ var docEvents = {
+ mouseout: function(event) {
+ // When the moues leaves the document, fire one last mousemove
+ // event, to give items the change to receive a mouseleave, etc.
+ var view = View._focused,
+ target = DomEvent.getRelatedTarget(event);
+ if (view && (!target || target.nodeName === 'HTML'))
+ handleMouseMove(view, viewToProject(view, event), event);
+ },
+
+ scroll: updateFocus
+ };
+
+ // mousemove and mouseup events need to be installed on document, not the
+ // view element, since we want to catch the end of drag events even outside
+ // our view. Only the mousedown events are installed on the view, as defined
+ // by _viewEvents below.
+ viewEvents[mousedown] = function(event) {
// Get the view from the event, and store a reference to the view that
// should receive keyboard input.
var view = View._focused = getView(event),
@@ -701,29 +775,17 @@ var View = Base.extend(Callback, /** @lends View# */{
// In the end we always call update(), which only updates the view if
// anything has changed in the above calls.
view.update();
- }
+ };
- function handleMouseMove(view, point, event) {
- view._handleEvent('mousemove', point, event);
- var tool = view._scope.tool;
- if (tool) {
- // If there's no onMouseDrag, fire onMouseMove while dragging.
- tool._handleEvent(dragging && tool.responds('mousedrag')
- ? 'mousedrag' : 'mousemove', point, event);
- }
- view.update();
- return tool;
- }
-
- function mousemove(event) {
+ docEvents[mousemove] = function(event) {
var view = View._focused;
if (!dragging) {
// See if we can get the view from the current event target, and
// handle the mouse move over it.
var target = getView(event);
if (target) {
- // Temporarily focus this view without making it sticky, so
- // Key events are handled too during the mouse over
+ // Temporarily focus this view without making it sticky, so Key
+ // events are handled too during the mouse over.
// If we switch view, fire one last mousemove in the old view,
// to give items the change to receive a mouseleave, etc.
if (view !== target)
@@ -741,18 +803,9 @@ var View = Base.extend(Callback, /** @lends View# */{
if (dragging || view.getBounds().contains(point))
tool = handleMouseMove(view, point, event);
}
- }
+ };
- function mouseout(event) {
- // When the moues leaves the document, fire one last mousemove event,
- // to give items the change to receive a mouseleave, etc.
- var view = View._focused,
- target = DomEvent.getRelatedTarget(event);
- if (view && (!target || target.nodeName === 'HTML'))
- handleMouseMove(view, viewToProject(view, event), event);
- }
-
- function mouseup(event) {
+ docEvents[mouseup] = function(event) {
var view = View._focused;
if (!view || !dragging)
return;
@@ -763,40 +816,16 @@ var View = Base.extend(Callback, /** @lends View# */{
if (tool)
tool._handleEvent('mouseup', point, event);
view.update();
- }
+ };
- function selectstart(event) {
- // Only stop this even if we're dragging already, since otherwise no
- // text whatsoever can be selected on the page.
- if (dragging)
- event.preventDefault();
- }
-
- // mousemove and mouseup events need to be installed on document, not the
- // view element, since we want to catch the end of drag events even outside
- // our view. Only the mousedown events are installed on the view, as handled
- // by _createHandlers below.
-
- DomEvent.add(document, {
- mousemove: mousemove,
- mouseout: mouseout,
- mouseup: mouseup,
- touchmove: mousemove,
- touchend: mouseup,
- selectstart: selectstart,
- scroll: updateFocus
- });
+ DomEvent.add(document, docEvents);
DomEvent.add(window, {
load: updateFocus
});
return {
- _viewHandlers: {
- mousedown: mousedown,
- touchstart: mousedown,
- selectstart: selectstart
- },
+ _viewEvents: viewEvents,
// To be defined in subclasses
_handleEvent: function(/* type, point, event */) {},