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

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: 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 /**
18 * @constructor
arv (Not doing code reviews) 2011/03/10 19:25:59 Missing description
Rick Byers 2011/03/11 02:44:33 Done.
19 * @param {!Element} element The element that can be grabbed and moved.
20 */
21 function Grabber(element) {
22 /**
23 * @type {!Element}
24 * @private
25 */
26 this.element_ = element;
27
28 /**
29 * @type {!TouchHandler}
30 * @private
31 */
32 this.touchHandler_ = new TouchHandler(this.element_);
33
34 /**
35 * @type {boolean}
36 * @private
37 */
38 this.grabbed_ = false;
39
40 /**
41 * @type {boolean}
42 * @private
43 */
44 this.dragging_ = false;
45
46 /**
47 * @type {EventTracker}
48 * @private
49 */
50 this.events_ = new EventTracker();
51
52 this.touchHandler_.enable(/* opt_capture */ false);
arv (Not doing code reviews) 2011/03/10 19:25:59 this.touchHandler_.enabled = false; seems like a
Rick Byers 2011/03/11 02:44:33 enabled=false would suggest to me that the touchHa
arv (Not doing code reviews) 2011/03/11 20:08:46 OK, then it makes sense.
53
54 // Prevent any built-in drag-and-drop support from activating for the element.
55 // Note that we don't want details of how we're implementing dragging here to
56 // leak out of this file (eg. we may switch to using webkit drag-and-drop).
57 this.events_.add(this.element_, 'dragstart', function(e) {
58 e.preventDefault();
59 }, true);
60
61 // Add our TouchHandler event listeners
62 this.events_.add(this.element_, TouchHandler.EventType.TOUCH_START,
63 this.onTouchStart_.bind(this), false);
64 this.events_.add(this.element_, TouchHandler.EventType.LONG_PRESS,
65 this.onLongPress_.bind(this), false);
66 this.events_.add(this.element_, TouchHandler.EventType.DRAG_START,
67 this.onDragStart_.bind(this), false);
68 this.events_.add(this.element_, TouchHandler.EventType.DRAG_MOVE,
69 this.onDragMove_.bind(this), false);
70 this.events_.add(this.element_, TouchHandler.EventType.DRAG_END,
71 this.onDragEnd_.bind(this), false);
72 this.events_.add(this.element_, TouchHandler.EventType.TOUCH_END,
73 this.onTouchEnd_.bind(this), false);
74 }
75
76 /**
77 * Events fired by the grabber.
78 * Events are fired at the element affected (not the element being dragged).
arv (Not doing code reviews) 2011/03/10 19:25:59 @enum {string}
Rick Byers 2011/03/11 02:44:33 Done.
79 */
80 Grabber.EventType = {
81 // Fired at the grabber element when it is first grabbed
82 GRAB: 'grabber:grab',
83 // Fired at the grabber element when dragging begins (after GRAB)
84 DRAG_START: 'grabber:dragstart',
85 // Fired at an element when something is dragged over top of it.
86 DRAG_ENTER: 'grabber:dragenter',
87 // Fired at an element when something is no longer over top of it.
88 // Not fired at all in the case of a DROP
89 DRAG_LEAVE: 'grabber:drag',
90 // Fired at an element when something is dropped on top of it.
91 DROP: 'grabber:drop',
92 // Fired at the grabber element when dragging ends (successfully or not) -
93 // after any DROP or DRAG_LEAVE
94 DRAG_END: 'grabber:dragend',
95 // Fired at the grabber element when it is released (even if no drag
96 // occured) - after any DRAG_END event.
97 RELEASE: 'grabber:release'
98 };
99
100 /**
101 * The CSS class to apply when an element is touched but not yet
102 * grabbed.
103 * @type {string}
104 */
105 Grabber.PRESSED_CLASS = 'grabber-pressed';
106
107 /**
108 * The class to apply when an element has been held (including when it is
109 * being dragged.
110 * @type {string}
111 */
112 Grabber.GRAB_CLASS = 'grabber-grabbed';
113
114 /**
115 * The class to apply when a grabbed element is being dragged.
116 * @type {string}
117 */
118 Grabber.DRAGGING_CLASS = 'grabber-dragging';
119
120 /**
121 * The webkitTransform applied to the element when it first started being
122 * dragged.
123 * @type {string|undefined}
124 */
125 Grabber.prototype.baseTransform_;
126
127 /**
128 * The element for which a DRAG_ENTER event was last fired
129 * @type {Element|undefined}
130 */
131 Grabber.prototype.lastEnter_;
132
133 /**
134 * Clean up all event handlers (eg. if the underlying element will be removed)
135 */
136 Grabber.prototype.dispose = function() {
137 this.touchHandler_.disable();
138 this.events_.removeAll();
139
140 // Clean-up any active touch/drag
141 if (this.dragging_) {
arv (Not doing code reviews) 2011/03/10 19:25:59 Chrome style is to not use curly braces for single
Rick Byers 2011/03/11 02:44:33 Done. Does this apply to if/else blocks to? Eg. I
arv (Not doing code reviews) 2011/03/11 20:08:46 Yes. If either the if or else part is more than o
Rick Byers 2011/03/15 21:47:54 Ok, thanks. I believe I've updated for this braci
142 this.stopDragging_();
143 }
144 this.onTouchEnd_();
145 };
146
147 /**
148 * Invoked whenever this element is first touched
149 * @param {!CustomEvent} e The TouchHandler event.
arv (Not doing code reviews) 2011/03/10 19:25:59 CustomEvent is a useless DOM interface. Just use E
Rick Byers 2011/03/11 02:44:33 The only reason I used CustomEvent is that it adds
150 * @private
151 */
152 Grabber.prototype.onTouchStart_ = function(e) {
153 this.element_.classList.add(Grabber.PRESSED_CLASS);
154
155 // Always permit the touch to perhaps trigger a drag
156 e.detail.enableDrag = true;
157 };
158
159 /**
160 * Invoked whenever the element stops being touched.
161 * Can be called explicitly to cleanup any active touch.
162 * @param {!CustomEvent=} opt_e The TouchHandler event.
163 * @private
164 */
165 Grabber.prototype.onTouchEnd_ = function(opt_e) {
166 if (this.grabbed_) {
167 // Mark this element as no longer being grabbed
168 this.element_.classList.remove(Grabber.GRAB_CLASS);
169 this.grabbed_ = false;
170
171 this.dispatchEvent_(Grabber.EventType.RELEASE);
arv (Not doing code reviews) 2011/03/10 19:25:59 cr.dispatchSimpleEvent(this, Grabber.EventType.REL
Rick Byers 2011/03/11 02:44:33 I should have looked more closely at the utilities
arv (Not doing code reviews) 2011/03/11 20:08:46 (I misunderstood this. See more in next comment)
Rick Byers 2011/03/15 21:47:54 See below.
172 }
173 else
174 {
175 this.element_.classList.remove(Grabber.PRESSED_CLASS);
176 }
177 };
178
179 /**
180 * Handler for TouchHandler's LONG_PRESS event
181 * Invoked when the element is held (without being dragged)
182 * @param {!CustomEvent} e The TouchHandler event.
183 * @private
184 */
185 Grabber.prototype.onLongPress_ = function(e) {
186 assert(!this.grabbed_, 'Got longPress while still being held');
187
188 this.element_.classList.remove(Grabber.PRESSED_CLASS);
189 this.element_.classList.add(Grabber.GRAB_CLASS);
190 this.grabbed_ = true;
191
192 this.dispatchEvent_(Grabber.EventType.GRAB);
193 };
194
195 /**
196 * Invoked when the element is dragged.
197 * @param {!CustomEvent} e The TouchHandler event.
198 * @private
199 */
200 Grabber.prototype.onDragStart_ = function(e) {
201 var that = this;
202 assert(!this.lastEnter_, 'only expect one drag to occur at a time');
203 assert(!this.dragging_);
204
205 // We only want to drag the element if its been grabbed
206 if (this.grabbed_) {
207 // Mark the item as being dragged
208 // Ensures our translate transform won't be animated and cancels any
209 // outstanding animations.
210 this.element_.classList.add(Grabber.DRAGGING_CLASS);
211
212 // Determine the webkitTransform currently applied to the element.
213 // Note that it's important that we do this AFTER cancelling animation,
214 // otherwise we could see an intermediate value.
215 // We'll assume this value will be constant for the duration of the drag
216 // so that we can combine it with our translate3d transform.
217 this.baseTransform_ = window.getComputedStyle(this.element_, null).
arv (Not doing code reviews) 2011/03/10 19:25:59 is this the right window? this.ownerDocument.defa
arv (Not doing code reviews) 2011/03/10 19:25:59 skip null here?
Rick Byers 2011/03/11 02:44:33 Interesting - thanks. I had to read up on this a
Rick Byers 2011/03/11 02:44:33 Done. I thought JSCompiler was complaining if I o
arv (Not doing code reviews) 2011/03/11 20:08:46 I assume most of the Chrome JS would fail in the p
218 webkitTransform;
219
220 this.dispatchEvent_(Grabber.EventType.DRAG_START);
221 e.detail.enableDrag = true;
222 this.dragging_ = true;
223 } else {
224 // Hasn't been grabbed - don't drag, just unpress
225 this.element_.classList.remove(Grabber.PRESSED_CLASS);
226 e.detail.enableDrag = false;
227 }
228 };
229
230 /**
231 * Invoked when a grabbed element is being dragged
232 * @param {!CustomEvent} e The TouchHandler event.
233 * @private
234 */
235 Grabber.prototype.onDragMove_ = function(e) {
236 assert(this.grabbed_ && this.dragging_);
237
238 this.translateTo_(e.detail.dragDeltaX, e.detail.dragDeltaY);
239
240 var target = this.getCoveredElement_(e.detail);
241 if (target && target != this.lastEnter_) {
242 // Send the events
243 this.sendDragLeave_(e);
244 this.dispatchEventTo_(Grabber.EventType.DRAG_ENTER, target);
245 }
246 this.lastEnter_ = target;
247 };
248
249 /**
250 * Send DRAG_LEAVE to the element last sent a DRAG_ENTER if any.
251 * @param {!CustomEvent} e The event triggering this DRAG_LEAVE.
252 * @private
253 */
254 Grabber.prototype.sendDragLeave_ = function(e) {
255 if (this.lastEnter_) {
256 this.dispatchEventTo_(Grabber.EventType.DRAG_LEAVE, this.lastEnter_);
257 this.lastEnter_ = undefined;
258 }
259 };
260
261 /**
262 * Moves the element to the specified position.
263 * @param {number} x Horizontal position to move to.
264 * @param {number} y Vertical position to move to.
265 * @private
266 */
267 Grabber.prototype.translateTo_ = function(x, y) {
268 // Order is important here - we want to translate before doing the zoom
269 this.element_.style.webkitTransform = 'translate3d(' + x + 'px, ' +
270 y + 'px, 0) ' + this.baseTransform_;
arv (Not doing code reviews) 2011/03/10 19:25:59 +2 indentation
Rick Byers 2011/03/11 02:44:33 Done.
271 };
272
273 /**
274 * Get the element being covered by a given touch.
275 * @param {TouchHandler.EventDetail} touch The details of the touch event
276 * indicating the position to check.
arv (Not doing code reviews) 2011/03/10 19:25:59 indent 4 spaces
Rick Byers 2011/03/11 02:44:33 Done.
277 * @return {Element} The element under the touch or null.
278 * @private
279 */
280 Grabber.prototype.getCoveredElement_ = function(touch) {
281 // Ensure the element being dragged doesn't get in the way
282 // It's unfortunate that there's not a better way to do this.
283 // This will probably cause a re-layout, but shouldn't cause flicker as
284 // repaint shouldn't happen in the middle of this function.
285 // We could also use zIndex, but that's more complicated and harder to
286 // reliably get the element out of the way.
287 var origDisplay = this.element_.style.display || '';
288 this.element_.style.display = 'none';
arv (Not doing code reviews) 2011/03/10 19:25:59 I would prefer pointerEvents = 'none' since that d
Rick Byers 2011/03/11 02:44:33 elementFromPoint does appear to be aware of pointe
289 var target = document.elementFromPoint(touch.clientX, touch.clientY);
290 this.element_.style.display = origDisplay;
291
292 return target;
293 };
294
295 /**
296 * @private
297 * @param {CustomEvent} e The TouchHandler event.
298 */
299 Grabber.prototype.onDragEnd_ = function(e) {
300 // We should get this before the onTouchEnd. Don't change
301 // this.grabbed_ - it's onTouchEnd's responsibility to clear it.
302 assert(this.grabbed_ && this.dragging_);
303 var event;
304
305 // Try to determine what element is underneath us
306 var target = this.getCoveredElement_(e.detail);
307 if (target) {
308 this.dispatchEventTo_(Grabber.EventType.DROP, target);
309 }
310
311 // Cleanup and send DRAG_END
312 // Note that like HTML5 DND, we don't send DRAG_LEAVE on drop
313 this.stopDragging_();
314 };
315
316 /**
317 * Clean-up the active drag and send DRAG_LEAVE
318 * @private
319 */
320 Grabber.prototype.stopDragging_ = function() {
321 assert(this.dragging_);
322 this.lastEnter_ = undefined;
323
324 // Mark the element as no longer being dragged
325 this.element_.classList.remove(Grabber.DRAGGING_CLASS);
326 this.element_.style.webkitTransform = '';
327
328 this.dragging_ = false;
329 this.dispatchEvent_(Grabber.EventType.DRAG_END);
330 };
331
332 /**
333 * @return {!Element} The element the grabber is attached to.
334 */
335 Grabber.prototype.getElement = function() {
arv (Not doing code reviews) 2011/03/10 19:25:59 Use getters and setters for getters and setters G
Rick Byers 2011/03/11 02:44:33 Thanks, but JSCompiler doesn't yet support this ES
arv (Not doing code reviews) 2011/03/11 20:08:46 It is a such a relief to not have to be hamstrung
Rick Byers 2011/03/15 21:47:54 Ok. I agree we shouldn't be sacrificing good styl
336 return this.element_;
337 };
338
339 /**
340 * Send a Grabber event to a specific element
341 * @param {string} eventType The type of event to send.
342 * @param {!Element} target The element to send the event to.
343 * @private
344 */
345 Grabber.prototype.dispatchEventTo_ = function(eventType, target) {
346 var event = document.createEvent('Event');
arv (Not doing code reviews) 2011/03/10 19:25:59 cr.dispatchSimpleEvent
Rick Byers 2011/03/11 02:44:33 See above.
arv (Not doing code reviews) 2011/03/11 20:08:46 Ah, I was confused by the overloaded term, sender.
Rick Byers 2011/03/15 21:47:54 Darn - I forgot that I added a 'sender' property m
347 event.initEvent(eventType, true, true);
348 event.sender = this;
349 target.dispatchEvent(event);
350 };
351
352 /**
353 * Send a Grabber event to the grabbed element
354 * @param {string} eventType The type of event to send.
355 * @private
356 */
357 Grabber.prototype.dispatchEvent_ = function(eventType) {
358 this.dispatchEventTo_(eventType, this.element_);
359 };
360
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698