1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file. 4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/** 6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @fileoverview Grabber implementation. 7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Allows you to pick up objects (with a long-press) and drag them around the 8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * screen. 9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * 10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Note: This should perhaps really use standard drag-and-drop events, but there 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * is no standard for them on touch devices. We could define a model for 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * activating touch-based dragging of elements (programatically and/or with 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * CSS attributes) and use it here (even have a JS library to generate such 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * events when the browser doesn't support them). 15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use an anonymous function to enable strict mode just for this file (which 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// will be concatenated with other files when embedded in Chrome) 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar Grabber = (function() { 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'use strict'; 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Create a Grabber object to enable grabbing and dragging a given element. 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Element} element The element that can be grabbed and moved. 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function Grabber(element) { 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The element the grabber is attached to. 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element} 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element_ = element; 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The TouchHandler responsible for firing lower-level touch events when the 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * element is manipulated. 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!TouchHandler} 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_ = new TouchHandler(this.element); 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Tracks all event listeners we have created. 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {EventTracker} 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_ = new EventTracker(); 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Enable the generation of events when the element is touched (but no need 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // to use the early capture phase of event processing). 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_.enable(/* opt_capture */ false); 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Prevent any built-in drag-and-drop support from activating for the 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // element. Note that we don't want details of how we're implementing 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // dragging here to leak out of this file (eg. we may switch to using webkit 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // drag-and-drop). 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, 'dragstart', function(e) { 59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.preventDefault(); 60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, true); 61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Add our TouchHandler event listeners 63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.TOUCH_START, 64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onTouchStart_.bind(this), false); 65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.LONG_PRESS, 66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onLongPress_.bind(this), false); 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.DRAG_START, 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragStart_.bind(this), false); 69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.DRAG_MOVE, 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragMove_.bind(this), false); 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.DRAG_END, 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragEnd_.bind(this), false); 73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.add(this.element, TouchHandler.EventType.TOUCH_END, 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onTouchEnd_.bind(this), false); 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Events fired by the grabber. 79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Events are fired at the element affected (not the element being dragged). 80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @enum {string} 81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.EventType = { 83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at the grabber element when it is first grabbed 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen GRAB: 'grabber:grab', 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at the grabber element when dragging begins (after GRAB) 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DRAG_START: 'grabber:dragstart', 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at an element when something is dragged over top of it. 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DRAG_ENTER: 'grabber:dragenter', 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at an element when something is no longer over top of it. 90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Not fired at all in the case of a DROP 91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DRAG_LEAVE: 'grabber:drag', 92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at an element when something is dropped on top of it. 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DROP: 'grabber:drop', 94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at the grabber element when dragging ends (successfully or not) - 95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // after any DROP or DRAG_LEAVE 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DRAG_END: 'grabber:dragend', 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired at the grabber element when it is released (even if no drag 98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // occured) - after any DRAG_END event. 99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen RELEASE: 'grabber:release' 100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The type of Event sent by Grabber 104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor 105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} type The type of event (one of Grabber.EventType). 106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Element!} grabbedElement The element being dragged. 107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.Event = function(type, grabbedElement) { 109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var event = document.createEvent('Event'); 110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen event.initEvent(type, true, true); 111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen event.__proto__ = Grabber.Event.prototype; 112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The element which is being dragged. For some events this will be the 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * same as 'target', but for events like DROP that are fired at another 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * element it will be different. 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element} 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen event.grabbedElement = grabbedElement; 120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return event; 122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.Event.prototype = { 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen __proto__: Event.prototype 126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The CSS class to apply when an element is touched but not yet 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * grabbed. 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {string} 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.PRESSED_CLASS = 'grabber-pressed'; 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The class to apply when an element has been held (including when it is 138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * being dragged. 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {string} 140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.GRAB_CLASS = 'grabber-grabbed'; 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The class to apply when a grabbed element is being dragged. 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {string} 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.DRAGGING_CLASS = 'grabber-dragging'; 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Grabber.prototype = { 150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {!Element} The element that can be grabbed. 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen get element() { 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return this.element_; 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Clean up all event handlers (eg. if the underlying element will be 159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * removed) 160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dispose: function() { 162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_.disable(); 163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.events_.removeAll(); 164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Clean-up any active touch/drag 166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (this.dragging_) 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.stopDragging_(); 168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onTouchEnd_(); 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked whenever this element is first touched 173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onTouchStart_: function(e) { 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.add(Grabber.PRESSED_CLASS); 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Always permit the touch to perhaps trigger a drag 180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.enableDrag = true; 181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked whenever the element stops being touched. 185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Can be called explicitly to cleanup any active touch. 186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event=} opt_e The TouchHandler event. 187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onTouchEnd_: function(opt_e) { 190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (this.grabbed_) { 191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Mark this element as no longer being grabbed 192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.remove(Grabber.GRAB_CLASS); 193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.style.pointerEvents = ''; 194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.grabbed_ = false; 195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.RELEASE, this.element); 197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else { 198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.remove(Grabber.PRESSED_CLASS); 199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Handler for TouchHandler's LONG_PRESS event 204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when the element is held (without being dragged) 205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onLongPress_: function(e) { 209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!this.grabbed_, 'Got longPress while still being held'); 210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.remove(Grabber.PRESSED_CLASS); 212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.add(Grabber.GRAB_CLASS); 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Disable mouse events from the element - we care only about what's 215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // under the element after it's grabbed (since we're getting move events 216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // from the body - not the element itself). Note that we can't wait until 217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // onDragStart to do this because it won't have taken effect by the first 218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // onDragMove. 219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.style.pointerEvents = 'none'; 220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.grabbed_ = true; 222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.GRAB, this.element); 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when the element is dragged. 228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragStart_: function(e) { 232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!this.lastEnter_, 'only expect one drag to occur at a time'); 233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(!this.dragging_); 234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We only want to drag the element if its been grabbed 236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (this.grabbed_) { 237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Mark the item as being dragged 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ensures our translate transform won't be animated and cancels any 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // outstanding animations. 240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.add(Grabber.DRAGGING_CLASS); 241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Determine the webkitTransform currently applied to the element. 243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that it's important that we do this AFTER cancelling animation, 244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // otherwise we could see an intermediate value. 245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We'll assume this value will be constant for the duration of the drag 246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // so that we can combine it with our translate3d transform. 247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.baseTransform_ = this.element.ownerDocument.defaultView. 248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen getComputedStyle(this.element).webkitTransform; 249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.DRAG_START, this.element); 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.enableDrag = true; 252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.dragging_ = true; 253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else { 255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Hasn't been grabbed - don't drag, just unpress 256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.remove(Grabber.PRESSED_CLASS); 257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.enableDrag = false; 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when a grabbed element is being dragged 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragMove_: function(e) { 267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(this.grabbed_ && this.dragging_); 268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.translateTo_(e.dragDeltaX, e.dragDeltaY); 270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var target = e.touchedElement; 272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (target && target != this.lastEnter_) { 273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Send the events 274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendDragLeave_(e); 275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.DRAG_ENTER, target); 276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.lastEnter_ = target; 278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Send DRAG_LEAVE to the element last sent a DRAG_ENTER if any. 282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The event triggering this DRAG_LEAVE. 283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sendDragLeave_: function(e) { 286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (this.lastEnter_) { 287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.DRAG_LEAVE, this.lastEnter_); 288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.lastEnter_ = undefined; 289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Moves the element to the specified position. 294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} x Horizontal position to move to. 295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} y Vertical position to move to. 296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen translateTo_: function(x, y) { 299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Order is important here - we want to translate before doing the zoom 300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.style.WebkitTransform = 'translate3d(' + x + 'px, ' + 301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen y + 'px, 0) ' + this.baseTransform_; 302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Invoked when the element is no longer being dragged. 306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {TouchHandler.Event} e The TouchHandler event. 307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragEnd_: function(e) { 310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We should get this before the onTouchEnd. Don't change 311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // this.grabbed_ - it's onTouchEnd's responsibility to clear it. 312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(this.grabbed_ && this.dragging_); 313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var event; 314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Send the drop event to the element underneath the one we're dragging. 316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var target = e.touchedElement; 317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (target) 318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.DROP, target); 319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Cleanup and send DRAG_END 321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note that like HTML5 DND, we don't send DRAG_LEAVE on drop 322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.stopDragging_(); 323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Clean-up the active drag and send DRAG_LEAVE 327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen stopDragging_: function() { 330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(this.dragging_); 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.lastEnter_ = undefined; 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Mark the element as no longer being dragged 334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.classList.remove(Grabber.DRAGGING_CLASS); 335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.element.style.webkitTransform = ''; 336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 337ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.dragging_ = false; 338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.sendEvent_(Grabber.EventType.DRAG_END, this.element); 339ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 340ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 341ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Send a Grabber event to a specific element 343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} eventType The type of event to send. 344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Element} target The element to send the event to. 345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sendEvent_: function(eventType, target) { 348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var event = new Grabber.Event(eventType, this.element); 349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen target.dispatchEvent(event); 350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Whether or not the element is currently grabbed. 354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {boolean} 355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen grabbed_: false, 358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Whether or not the element is currently being dragged. 361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {boolean} 362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen dragging_: false, 365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The webkitTransform applied to the element when it first started being 368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * dragged. 369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {string|undefined} 370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen baseTransform_: undefined, 373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The element for which a DRAG_ENTER event was last fired 376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {Element|undefined} 377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen lastEnter_: undefined 380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return Grabber; 383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen})(); 384