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

Side by Side Diff: third_party/polymer/v1_0/components-chromium/iron-dropdown/iron-dropdown-scroll-manager-extracted.js

Issue 2158913007: Roll Polymer from 1.5.0 -> 1.6.0 to pick up native CSS custom props (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 4 years, 4 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
OLDNEW
1 (function() { 1 (function() {
2 'use strict'; 2 'use strict';
3 // Used to calculate the scroll direction during touch events.
4 var LAST_TOUCH_POSITION = {
5 pageX: 0,
6 pageY: 0
7 };
8 // Used to avoid computing event.path and filter scrollable nodes (better pe rf).
9 var ROOT_TARGET = null;
10 var SCROLLABLE_NODES = [];
3 11
4 /** 12 /**
5 * The IronDropdownScrollManager is intended to provide a central source 13 * The IronDropdownScrollManager is intended to provide a central source
6 * of authority and control over which elements in a document are currently 14 * of authority and control over which elements in a document are currently
7 * allowed to scroll. 15 * allowed to scroll.
8 */ 16 */
9 17
10 Polymer.IronDropdownScrollManager = { 18 Polymer.IronDropdownScrollManager = {
11 19
12 /** 20 /**
13 * The current element that defines the DOM boundaries of the 21 * The current element that defines the DOM boundaries of the
14 * scroll lock. This is always the most recently locking element. 22 * scroll lock. This is always the most recently locking element.
15 */ 23 */
16 get currentLockingElement() { 24 get currentLockingElement() {
17 return this._lockingElements[this._lockingElements.length - 1]; 25 return this._lockingElements[this._lockingElements.length - 1];
18 }, 26 },
19 27
20
21 /** 28 /**
22 * Returns true if the provided element is "scroll locked," which is to 29 * Returns true if the provided element is "scroll locked", which is to
23 * say that it cannot be scrolled via pointer or keyboard interactions. 30 * say that it cannot be scrolled via pointer or keyboard interactions.
24 * 31 *
25 * @param {HTMLElement} element An HTML element instance which may or may 32 * @param {HTMLElement} element An HTML element instance which may or may
26 * not be scroll locked. 33 * not be scroll locked.
27 */ 34 */
28 elementIsScrollLocked: function(element) { 35 elementIsScrollLocked: function(element) {
29 var currentLockingElement = this.currentLockingElement; 36 var currentLockingElement = this.currentLockingElement;
30 37
31 if (currentLockingElement === undefined) 38 if (currentLockingElement === undefined)
32 return false; 39 return false;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 this._unlockScrollInteractions(); 112 this._unlockScrollInteractions();
106 } 113 }
107 }, 114 },
108 115
109 _lockingElements: [], 116 _lockingElements: [],
110 117
111 _lockedElementCache: null, 118 _lockedElementCache: null,
112 119
113 _unlockedElementCache: null, 120 _unlockedElementCache: null,
114 121
115 _originalBodyStyles: {},
116
117 _isScrollingKeypress: function(event) { 122 _isScrollingKeypress: function(event) {
118 return Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys( 123 return Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(
119 event, 'pageup pagedown home end up left down right'); 124 event, 'pageup pagedown home end up left down right');
120 }, 125 },
121 126
122 _hasCachedLockedElement: function(element) { 127 _hasCachedLockedElement: function(element) {
123 return this._lockedElementCache.indexOf(element) > -1; 128 return this._lockedElementCache.indexOf(element) > -1;
124 }, 129 },
125 130
126 _hasCachedUnlockedElement: function(element) { 131 _hasCachedUnlockedElement: function(element) {
(...skipping 27 matching lines...) Expand all
154 if (this._composedTreeContains(distributedNodes[nodeIndex], child)) { 159 if (this._composedTreeContains(distributedNodes[nodeIndex], child)) {
155 return true; 160 return true;
156 } 161 }
157 } 162 }
158 } 163 }
159 164
160 return false; 165 return false;
161 }, 166 },
162 167
163 _scrollInteractionHandler: function(event) { 168 _scrollInteractionHandler: function(event) {
164 var scrolledElement = 169 // Avoid canceling an event with cancelable=false, e.g. scrolling is in
165 /** @type {HTMLElement} */(Polymer.dom(event).rootTarget); 170 // progress and cannot be interrupted.
166 if (Polymer 171 if (event.cancelable && this._shouldPreventScrolling(event)) {
167 .IronDropdownScrollManager
168 .elementIsScrollLocked(scrolledElement)) {
169 if (event.type === 'keydown' &&
170 !Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) {
171 return;
172 }
173
174 event.preventDefault(); 172 event.preventDefault();
175 } 173 }
174 // If event has targetTouches (touch event), update last touch position.
175 if (event.targetTouches) {
176 var touch = event.targetTouches[0];
177 LAST_TOUCH_POSITION.pageX = touch.pageX;
178 LAST_TOUCH_POSITION.pageY = touch.pageY;
179 }
176 }, 180 },
177 181
178 _lockScrollInteractions: function() { 182 _lockScrollInteractions: function() {
179 // Memoize body inline styles: 183 this._boundScrollHandler = this._boundScrollHandler ||
180 this._originalBodyStyles.overflow = document.body.style.overflow; 184 this._scrollInteractionHandler.bind(this);
181 this._originalBodyStyles.overflowX = document.body.style.overflowX;
182 this._originalBodyStyles.overflowY = document.body.style.overflowY;
183
184 // Disable overflow scrolling on body:
185 // TODO(cdata): It is technically not sufficient to hide overflow on
186 // body alone. A better solution might be to traverse all ancestors of
187 // the current scroll locking element and hide overflow on them. This
188 // becomes expensive, though, as it would have to be redone every time
189 // a new scroll locking element is added.
190 document.body.style.overflow = 'hidden';
191 document.body.style.overflowX = 'hidden';
192 document.body.style.overflowY = 'hidden';
193
194 // Modern `wheel` event for mouse wheel scrolling: 185 // Modern `wheel` event for mouse wheel scrolling:
195 document.addEventListener('wheel', this._scrollInteractionHandler, true) ; 186 document.addEventListener('wheel', this._boundScrollHandler, true);
196 // Older, non-standard `mousewheel` event for some FF: 187 // Older, non-standard `mousewheel` event for some FF:
197 document.addEventListener('mousewheel', this._scrollInteractionHandler, true); 188 document.addEventListener('mousewheel', this._boundScrollHandler, true);
198 // IE: 189 // IE:
199 document.addEventListener('DOMMouseScroll', this._scrollInteractionHandl er, true); 190 document.addEventListener('DOMMouseScroll', this._boundScrollHandler, tr ue);
191 // Save the SCROLLABLE_NODES on touchstart, to be used on touchmove.
192 document.addEventListener('touchstart', this._boundScrollHandler, true);
200 // Mobile devices can scroll on touch move: 193 // Mobile devices can scroll on touch move:
201 document.addEventListener('touchmove', this._scrollInteractionHandler, t rue); 194 document.addEventListener('touchmove', this._boundScrollHandler, true);
202 // Capture keydown to prevent scrolling keys (pageup, pagedown etc.) 195 // Capture keydown to prevent scrolling keys (pageup, pagedown etc.)
203 document.addEventListener('keydown', this._scrollInteractionHandler, tru e); 196 document.addEventListener('keydown', this._boundScrollHandler, true);
204 }, 197 },
205 198
206 _unlockScrollInteractions: function() { 199 _unlockScrollInteractions: function() {
207 document.body.style.overflow = this._originalBodyStyles.overflow; 200 document.removeEventListener('wheel', this._boundScrollHandler, true);
208 document.body.style.overflowX = this._originalBodyStyles.overflowX; 201 document.removeEventListener('mousewheel', this._boundScrollHandler, tru e);
209 document.body.style.overflowY = this._originalBodyStyles.overflowY; 202 document.removeEventListener('DOMMouseScroll', this._boundScrollHandler, true);
203 document.removeEventListener('touchstart', this._boundScrollHandler, tru e);
204 document.removeEventListener('touchmove', this._boundScrollHandler, true );
205 document.removeEventListener('keydown', this._boundScrollHandler, true);
206 },
210 207
211 document.removeEventListener('wheel', this._scrollInteractionHandler, tr ue); 208 /**
212 document.removeEventListener('mousewheel', this._scrollInteractionHandle r, true); 209 * Returns true if the event causes scroll outside the current locking
213 document.removeEventListener('DOMMouseScroll', this._scrollInteractionHa ndler, true); 210 * element, e.g. pointer/keyboard interactions, or scroll "leaking"
214 document.removeEventListener('touchmove', this._scrollInteractionHandler , true); 211 * outside the locking element when it is already at its scroll boundaries .
215 document.removeEventListener('keydown', this._scrollInteractionHandler, true); 212 * @param {!Event} event
213 * @return {boolean}
214 * @private
215 */
216 _shouldPreventScrolling: function(event) {
217 // Avoid expensive checks if the event is not one of the observed keys.
218 if (event.type === 'keydown') {
219 // Prevent event if it is one of the scrolling keys.
220 return this._isScrollingKeypress(event);
221 }
222
223 // Update if root target changed. For touch events, ensure we don't
224 // update during touchmove.
225 var target = Polymer.dom(event).rootTarget;
226 if (event.type !== 'touchmove' && ROOT_TARGET !== target) {
227 ROOT_TARGET = target;
228 SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path);
229 }
230
231 // Prevent event if no scrollable nodes.
232 if (!SCROLLABLE_NODES.length) {
233 return true;
234 }
235 // Don't prevent touchstart event inside the locking element when it has
236 // scrollable nodes.
237 if (event.type === 'touchstart') {
238 return false;
239 }
240 // Get deltaX/Y.
241 var info = this._getScrollInfo(event);
242 // Prevent if there is no child that can scroll.
243 return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.delta Y);
244 },
245
246 /**
247 * Returns an array of scrollable nodes up to the current locking element,
248 * which is included too if scrollable.
249 * @param {!Array<Node>} nodes
250 * @return {Array<Node>} scrollables
251 * @private
252 */
253 _getScrollableNodes: function(nodes) {
254 var scrollables = [];
255 var lockingIndex = nodes.indexOf(this.currentLockingElement);
256 // Loop from root target to locking element (included).
257 for (var i = 0; i <= lockingIndex; i++) {
258 var node = nodes[i];
259 // Skip document fragments.
260 if (node.nodeType === 11) {
261 continue;
262 }
263 // Check inline style before checking computed style.
264 var style = node.style;
265 if (style.overflow !== 'scroll' && style.overflow !== 'auto') {
266 style = window.getComputedStyle(node);
267 }
268 if (style.overflow === 'scroll' || style.overflow === 'auto') {
269 scrollables.push(node);
270 }
271 }
272 return scrollables;
273 },
274
275 /**
276 * Returns the node that is scrolling. If there is no scrolling,
277 * returns undefined.
278 * @param {!Array<Node>} nodes
279 * @param {number} deltaX Scroll delta on the x-axis
280 * @param {number} deltaY Scroll delta on the y-axis
281 * @return {Node|undefined}
282 * @private
283 */
284 _getScrollingNode: function(nodes, deltaX, deltaY) {
285 // No scroll.
286 if (!deltaX && !deltaY) {
287 return;
288 }
289 // Check only one axis according to where there is more scroll.
290 // Prefer vertical to horizontal.
291 var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX);
292 for (var i = 0; i < nodes.length; i++) {
293 var node = nodes[i];
294 var canScroll = false;
295 if (verticalScroll) {
296 // delta < 0 is scroll up, delta > 0 is scroll down.
297 canScroll = deltaY < 0 ? node.scrollTop > 0 :
298 node.scrollTop < node.scrollHeight - node.clientHeight;
299 } else {
300 // delta < 0 is scroll left, delta > 0 is scroll right.
301 canScroll = deltaX < 0 ? node.scrollLeft > 0 :
302 node.scrollLeft < node.scrollWidth - node.clientWidth;
303 }
304 if (canScroll) {
305 return node;
306 }
307 }
308 },
309
310 /**
311 * Returns scroll `deltaX` and `deltaY`.
312 * @param {!Event} event The scroll event
313 * @return {{
314 * deltaX: number The x-axis scroll delta (positive: scroll right,
315 * negative: scroll left, 0: no scroll),
316 * deltaY: number The y-axis scroll delta (positive: scroll down,
317 * negative: scroll up, 0: no scroll)
318 * }} info
319 * @private
320 */
321 _getScrollInfo: function(event) {
322 var info = {
323 deltaX: event.deltaX,
324 deltaY: event.deltaY
325 };
326 // Already available.
327 if ('deltaX' in event) {
328 // do nothing, values are already good.
329 }
330 // Safari has scroll info in `wheelDeltaX/Y`.
331 else if ('wheelDeltaX' in event) {
332 info.deltaX = -event.wheelDeltaX;
333 info.deltaY = -event.wheelDeltaY;
334 }
335 // Firefox has scroll info in `detail` and `axis`.
336 else if ('axis' in event) {
337 info.deltaX = event.axis === 1 ? event.detail : 0;
338 info.deltaY = event.axis === 2 ? event.detail : 0;
339 }
340 // On mobile devices, calculate scroll direction.
341 else if (event.targetTouches) {
342 var touch = event.targetTouches[0];
343 // Touch moves from right to left => scrolling goes right.
344 info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX;
345 // Touch moves from down to up => scrolling goes down.
346 info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY;
347 }
348 return info;
216 } 349 }
217 }; 350 };
218 })(); 351 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698