Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: chrome/browser/resources/touch_ntp/grabber.js

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

Powered by Google App Engine
This is Rietveld 408576698