diff --git a/lib/module/hammer.js b/lib/module/hammer.js
index f76b3e39..a2679764 100644
--- a/lib/module/hammer.js
+++ b/lib/module/hammer.js
@@ -1,7 +1,9 @@
// Only load hammer.js when in a browser environment
// (loading hammer.js in a node.js environment gives errors)
if (typeof window !== 'undefined') {
- module.exports = window['Hammer'] || require('hammerjs');
+ var Hammer = window['Hammer'] || require('hammerjs');
+ var propagating = require('propagating-hammerjs');
+ module.exports = propagating(Hammer);
}
else {
module.exports = function () {
diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js
index a7121973..8bceb003 100644
--- a/lib/timeline/Core.js
+++ b/lib/timeline/Core.js
@@ -89,10 +89,6 @@ Core.prototype._create = function (container) {
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
this.on('rangechange', this.redraw.bind(this));
- this.on('touch', this._onTouch.bind(this));
- this.on('pinch', this._onPinch.bind(this));
- this.on('dragstart', this._onDragStart.bind(this));
- this.on('drag', this._onDrag.bind(this));
var me = this;
this.on('change', function (properties) {
@@ -113,44 +109,42 @@ Core.prototype._create = function (container) {
// create event listeners for all interesting events, these events will be
// emitted via emitter
- this.hammer = new Hammer(this.dom.root);
+ this.hammer = new Hammer(this.dom.root, {touchAction: 'pan-y'});
+ this.hammer.get('pinch').set({enable: true});
this.listeners = {};
var events = [
'tap', 'doubletap', 'press',
'pinch',
- 'panstart', 'panmove', 'panend'
- // TODO: mouse wheel?
+ 'pan', 'panstart', 'panmove', 'panend'
// TODO: cleanup
//'touch', 'pinch',
//'tap', 'doubletap', 'hold',
//'dragstart', 'drag', 'dragend',
//'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
];
- events.forEach(function (event) {
- var listener = function () {
- var args = [event].concat(Array.prototype.slice.call(arguments, 0));
+ events.forEach(function (type) {
+ var listener = function (event) {
if (me.isActive()) {
- me.emit.apply(me, args);
+ me.emit(type, event);
}
};
- me.hammer.on(event, listener);
- me.listeners[event] = listener;
+ me.hammer.on(type, listener);
+ me.listeners[type] = listener;
});
+ // emulate a touch event (emitted before the start of a pan, pinch, tap, or press)
this.hammer.on('hammer.input', function (event) {
if (event.isFirst) {
- var args = ['touch'].concat(Array.prototype.slice.call(arguments, 0));
if (me.isActive()) {
- me.emit.apply(me, args);
+ me.emit('touch', event);
}
}
}.bind(this));
- function onMouseWheel() {
- var args = ['mousewheel'].concat(Array.prototype.slice.call(arguments, 0));
+ function onMouseWheel(event) {
if (me.isActive()) {
- me.emit.apply(me, args);
+ me.emit('mousewheel', event);
}
}
this.dom.root.addEventListener('mousewheel', onMouseWheel);
@@ -172,7 +166,6 @@ Core.prototype._create = function (container) {
scrollTop: 0,
scrollTopMin: 0
};
- this.touch = {}; // store state information needed for touch events
this.redrawCount = 0;
@@ -794,55 +787,6 @@ Core.prototype._stopAutoResize = function () {
this._onResize = null;
};
-/**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
-Core.prototype._onTouch = function (event) {
- this.touch.allowDragging = true;
-};
-
-/**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
-Core.prototype._onPinch = function (event) {
- this.touch.allowDragging = false;
-};
-
-/**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
-Core.prototype._onDragStart = function (event) {
- this.touch.initialScrollTop = this.props.scrollTop;
-};
-
-/**
- * Move the timeline vertically
- * @param {Event} event
- * @private
- */
-Core.prototype._onDrag = function (event) {
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (!this.touch.allowDragging) return;
-
- var delta = event.deltaY;
-
- var oldScrollTop = this._getScrollTop();
- var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
-
-
- if (newScrollTop != oldScrollTop) {
- this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already
- this.emit("verticalDrag");
- }
-};
-
/**
* Apply a scrollTop
* @param {Number} scrollTop
diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js
index f35b76b3..996d580d 100644
--- a/lib/timeline/Range.js
+++ b/lib/timeline/Range.js
@@ -363,6 +363,8 @@ Range.prototype._onDragStart = function(event) {
if (this.body.dom.root) {
this.body.dom.root.style.cursor = 'move';
}
+
+ event.preventDefault();
};
/**
@@ -413,6 +415,8 @@ Range.prototype._onDrag = function (event) {
start: new Date(this.start),
end: new Date(this.end)
});
+
+ event.preventDefault();
};
/**
@@ -521,41 +525,41 @@ Range.prototype._onPinch = function (event) {
this.props.touch.allowDragging = false;
- if (event.touches.length > 1) {
- if (!this.props.touch.center) {
- this.props.touch.center = getPointer(event.center, this.body.dom.center);
- }
+ if (!this.props.touch.center) {
+ this.props.touch.center = getPointer(event.center, this.body.dom.center);
+ }
- var scale = 1 / (event.scale + this.scaleOffset);
- var centerDate = this._pointerToDate(this.props.touch.center);
-
- var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
- var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate);
- var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
-
- // calculate new start and end
- var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
- var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale;
-
- // snapping times away from hidden zones
- this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
- this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
-
- var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
- var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
- if (safeStart != newStart || safeEnd != newEnd) {
- this.props.touch.start = safeStart;
- this.props.touch.end = safeEnd;
- this.scaleOffset = 1 - event.scale;
- newStart = safeStart;
- newEnd = safeEnd;
- }
+ var scale = 1 / (event.scale + this.scaleOffset);
+ var centerDate = this._pointerToDate(this.props.touch.center);
- this.setRange(newStart, newEnd);
+ var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
+ var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate);
+ var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
+
+ // calculate new start and end
+ var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
+ var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale;
+
+ // snapping times away from hidden zones
+ this.startToFront = 1 - scale <= 0; // used to do the right auto correction with periodic hidden times
+ this.endToFront = scale - 1 <= 0; // used to do the right auto correction with periodic hidden times
- this.startToFront = false; // revert to default
- this.endToFront = true; // revert to default
+ var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
+ var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
+ if (safeStart != newStart || safeEnd != newEnd) {
+ this.props.touch.start = safeStart;
+ this.props.touch.end = safeEnd;
+ this.scaleOffset = 1 - event.scale;
+ newStart = safeStart;
+ newEnd = safeEnd;
}
+
+ this.setRange(newStart, newEnd);
+
+ this.startToFront = false; // revert to default
+ this.endToFront = true; // revert to default
+
+ event.preventDefault();
};
/**
diff --git a/lib/timeline/component/CustomTime.js b/lib/timeline/component/CustomTime.js
index a42e1edf..982295fd 100644
--- a/lib/timeline/component/CustomTime.js
+++ b/lib/timeline/component/CustomTime.js
@@ -70,8 +70,11 @@ CustomTime.prototype._create = function() {
// attach event listeners
this.hammer = new Hammer(drag);
this.hammer.on('panstart', this._onDragStart.bind(this));
- this.hammer.on('panmove', this._onDrag.bind(this));
+ this.hammer.on('panmove', this._onDrag.bind(this));
this.hammer.on('panend', this._onDragEnd.bind(this));
+ this.hammer.on('pan', function (event) {
+ event.preventDefault();
+ });
};
/**
@@ -147,7 +150,7 @@ CustomTime.prototype._onDragStart = function(event) {
this.eventParams.dragging = true;
this.eventParams.customTime = this.customTime;
- //event.stopPropagation();
+ event.stopPropagation();
event.preventDefault();
};
@@ -169,7 +172,7 @@ CustomTime.prototype._onDrag = function (event) {
time: new Date(this.customTime.valueOf())
});
- //event.stopPropagation();
+ event.stopPropagation();
event.preventDefault();
};
@@ -186,7 +189,7 @@ CustomTime.prototype._onDragEnd = function (event) {
time: new Date(this.customTime.valueOf())
});
- //event.stopPropagation();
+ event.stopPropagation();
event.preventDefault();
};
diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js
index 894aea4e..c74b7779 100644
--- a/lib/timeline/component/ItemSet.js
+++ b/lib/timeline/component/ItemSet.js
@@ -184,7 +184,6 @@ ItemSet.prototype._create = function(){
this.hammer = new Hammer(this.body.dom.centerContainer);
// drag items when selected
- //this.hammer.on('pandown', this._onTouch.bind(this)); // TODO
this.hammer.on('hammer.input', function (event) {
if (event.isFirst) {
this._onTouch(event);
@@ -1077,6 +1076,7 @@ ItemSet.prototype._onTouch = function (event) {
this.touchParams.item = ItemSet.itemFromTarget(event);
this.touchParams.dragLeftItem = event.target.dragLeftItem || false;
this.touchParams.dragRightItem = event.target.dragRightItem || false;
+ this.touchParams.itemProps = null;
};
/**
@@ -1147,7 +1147,8 @@ ItemSet.prototype._onDragStart = function (event) {
});
}
- //event.stopPropagation();
+ event.stopPropagation();
+ event.preventDefault();
}
};
@@ -1199,7 +1200,7 @@ ItemSet.prototype._onDrag = function (event) {
this.stackDirty = true; // force re-stacking of all items next redraw
this.body.emitter.emit('change');
- //event.stopPropagation();
+ event.stopPropagation();
}
};
@@ -1243,8 +1244,6 @@ ItemSet.prototype._moveToGroup = function(item, groupId) {
* @private
*/
ItemSet.prototype._onDragEnd = function (event) {
- event.preventDefault()
-
if (this.touchParams.itemProps) {
// prepare a change set for the changed items
var changes = [],
@@ -1297,7 +1296,7 @@ ItemSet.prototype._onDragEnd = function (event) {
dataset.update(changes);
}
- //event.stopPropagation();
+ event.stopPropagation();
}
};
diff --git a/package.json b/package.json
index a174e24b..6c222290 100644
--- a/package.json
+++ b/package.json
@@ -30,8 +30,9 @@
"dependencies": {
"emitter-component": "^1.1.1",
"hammerjs": "^2.0.4",
+ "keycharm": "^0.1.6",
"moment": "^2.7.0",
- "keycharm": "^0.1.6"
+ "propagating-hammerjs": "^1.2.0"
},
"devDependencies": {
"clean-css": "latest",
diff --git a/test/timeline.html b/test/timeline.html
index f15eed18..7e82c01b 100644
--- a/test/timeline.html
+++ b/test/timeline.html
@@ -61,6 +61,7 @@