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

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

Powered by Google App Engine
This is Rietveld 408576698