GPQAPP

Форк
0
841 строка · 25.4 Кб
1
;(function () {
2
	'use strict';
3

4
	/**
5
	 * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
6
	 *
7
	 * @codingstandard ftlabs-jsv2
8
	 * @copyright The Financial Times Limited [All Rights Reserved]
9
	 * @license MIT License (see LICENSE.txt)
10
	 */
11

12
	/*jslint browser:true, node:true*/
13
	/*global define, Event, Node*/
14

15

16
	/**
17
	 * Instantiate fast-clicking listeners on the specified layer.
18
	 *
19
	 * @constructor
20
	 * @param {Element} layer The layer to listen on
21
	 * @param {Object} [options={}] The options to override the defaults
22
	 */
23
	function FastClick(layer, options) {
24
		var oldOnClick;
25

26
		options = options || {};
27

28
		/**
29
		 * Whether a click is currently being tracked.
30
		 *
31
		 * @type boolean
32
		 */
33
		this.trackingClick = false;
34

35

36
		/**
37
		 * Timestamp for when click tracking started.
38
		 *
39
		 * @type number
40
		 */
41
		this.trackingClickStart = 0;
42

43

44
		/**
45
		 * The element being tracked for a click.
46
		 *
47
		 * @type EventTarget
48
		 */
49
		this.targetElement = null;
50

51

52
		/**
53
		 * X-coordinate of touch start event.
54
		 *
55
		 * @type number
56
		 */
57
		this.touchStartX = 0;
58

59

60
		/**
61
		 * Y-coordinate of touch start event.
62
		 *
63
		 * @type number
64
		 */
65
		this.touchStartY = 0;
66

67

68
		/**
69
		 * ID of the last touch, retrieved from Touch.identifier.
70
		 *
71
		 * @type number
72
		 */
73
		this.lastTouchIdentifier = 0;
74

75

76
		/**
77
		 * Touchmove boundary, beyond which a click will be cancelled.
78
		 *
79
		 * @type number
80
		 */
81
		this.touchBoundary = options.touchBoundary || 10;
82

83

84
		/**
85
		 * The FastClick layer.
86
		 *
87
		 * @type Element
88
		 */
89
		this.layer = layer;
90

91
		/**
92
		 * The minimum time between tap(touchstart and touchend) events
93
		 *
94
		 * @type number
95
		 */
96
		this.tapDelay = options.tapDelay || 200;
97

98
		/**
99
		 * The maximum time for a tap
100
		 *
101
		 * @type number
102
		 */
103
		this.tapTimeout = options.tapTimeout || 700;
104

105
		if (FastClick.notNeeded(layer)) {
106
			return;
107
		}
108

109
		// Some old versions of Android don't have Function.prototype.bind
110
		function bind(method, context) {
111
			return function() { return method.apply(context, arguments); };
112
		}
113

114

115
		var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
116
		var context = this;
117
		for (var i = 0, l = methods.length; i < l; i++) {
118
			context[methods[i]] = bind(context[methods[i]], context);
119
		}
120

121
		// Set up event handlers as required
122
		if (deviceIsAndroid) {
123
			layer.addEventListener('mouseover', this.onMouse, true);
124
			layer.addEventListener('mousedown', this.onMouse, true);
125
			layer.addEventListener('mouseup', this.onMouse, true);
126
		}
127

128
		layer.addEventListener('click', this.onClick, true);
129
		layer.addEventListener('touchstart', this.onTouchStart, false);
130
		layer.addEventListener('touchmove', this.onTouchMove, false);
131
		layer.addEventListener('touchend', this.onTouchEnd, false);
132
		layer.addEventListener('touchcancel', this.onTouchCancel, false);
133

134
		// Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
135
		// which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
136
		// layer when they are cancelled.
137
		if (!Event.prototype.stopImmediatePropagation) {
138
			layer.removeEventListener = function(type, callback, capture) {
139
				var rmv = Node.prototype.removeEventListener;
140
				if (type === 'click') {
141
					rmv.call(layer, type, callback.hijacked || callback, capture);
142
				} else {
143
					rmv.call(layer, type, callback, capture);
144
				}
145
			};
146

147
			layer.addEventListener = function(type, callback, capture) {
148
				var adv = Node.prototype.addEventListener;
149
				if (type === 'click') {
150
					adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
151
						if (!event.propagationStopped) {
152
							callback(event);
153
						}
154
					}), capture);
155
				} else {
156
					adv.call(layer, type, callback, capture);
157
				}
158
			};
159
		}
160

161
		// If a handler is already declared in the element's onclick attribute, it will be fired before
162
		// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
163
		// adding it as listener.
164
		if (typeof layer.onclick === 'function') {
165

166
			// Android browser on at least 3.2 requires a new reference to the function in layer.onclick
167
			// - the old one won't work if passed to addEventListener directly.
168
			oldOnClick = layer.onclick;
169
			layer.addEventListener('click', function(event) {
170
				oldOnClick(event);
171
			}, false);
172
			layer.onclick = null;
173
		}
174
	}
175

176
	/**
177
	* Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
178
	*
179
	* @type boolean
180
	*/
181
	var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
182

183
	/**
184
	 * Android requires exceptions.
185
	 *
186
	 * @type boolean
187
	 */
188
	var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
189

190

191
	/**
192
	 * iOS requires exceptions.
193
	 *
194
	 * @type boolean
195
	 */
196
	var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
197

198

199
	/**
200
	 * iOS 4 requires an exception for select elements.
201
	 *
202
	 * @type boolean
203
	 */
204
	var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
205

206

207
	/**
208
	 * iOS 6.0-7.* requires the target element to be manually derived
209
	 *
210
	 * @type boolean
211
	 */
212
	var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
213

214
	/**
215
	 * BlackBerry requires exceptions.
216
	 *
217
	 * @type boolean
218
	 */
219
	var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
220

221
	/**
222
	 * Determine whether a given element requires a native click.
223
	 *
224
	 * @param {EventTarget|Element} target Target DOM element
225
	 * @returns {boolean} Returns true if the element needs a native click
226
	 */
227
	FastClick.prototype.needsClick = function(target) {
228
		switch (target.nodeName.toLowerCase()) {
229

230
		// Don't send a synthetic click to disabled inputs (issue #62)
231
		case 'button':
232
		case 'select':
233
		case 'textarea':
234
			if (target.disabled) {
235
				return true;
236
			}
237

238
			break;
239
		case 'input':
240

241
			// File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
242
			if ((deviceIsIOS && target.type === 'file') || target.disabled) {
243
				return true;
244
			}
245

246
			break;
247
		case 'label':
248
		case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
249
		case 'video':
250
			return true;
251
		}
252

253
		return (/\bneedsclick\b/).test(target.className);
254
	};
255

256

257
	/**
258
	 * Determine whether a given element requires a call to focus to simulate click into element.
259
	 *
260
	 * @param {EventTarget|Element} target Target DOM element
261
	 * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
262
	 */
263
	FastClick.prototype.needsFocus = function(target) {
264
		switch (target.nodeName.toLowerCase()) {
265
		case 'textarea':
266
			return true;
267
		case 'select':
268
			return !deviceIsAndroid;
269
		case 'input':
270
			switch (target.type) {
271
			case 'button':
272
			case 'checkbox':
273
			case 'file':
274
			case 'image':
275
			case 'radio':
276
			case 'submit':
277
				return false;
278
			}
279

280
			// No point in attempting to focus disabled inputs
281
			return !target.disabled && !target.readOnly;
282
		default:
283
			return (/\bneedsfocus\b/).test(target.className);
284
		}
285
	};
286

287

288
	/**
289
	 * Send a click event to the specified element.
290
	 *
291
	 * @param {EventTarget|Element} targetElement
292
	 * @param {Event} event
293
	 */
294
	FastClick.prototype.sendClick = function(targetElement, event) {
295
		var clickEvent, touch;
296

297
		// On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
298
		if (document.activeElement && document.activeElement !== targetElement) {
299
			document.activeElement.blur();
300
		}
301

302
		touch = event.changedTouches[0];
303

304
		// Synthesise a click event, with an extra attribute so it can be tracked
305
		clickEvent = document.createEvent('MouseEvents');
306
		clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
307
		clickEvent.forwardedTouchEvent = true;
308
		targetElement.dispatchEvent(clickEvent);
309
	};
310

311
	FastClick.prototype.determineEventType = function(targetElement) {
312

313
		//Issue #159: Android Chrome Select Box does not open with a synthetic click event
314
		if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
315
			return 'mousedown';
316
		}
317

318
		return 'click';
319
	};
320

321

322
	/**
323
	 * @param {EventTarget|Element} targetElement
324
	 */
325
	FastClick.prototype.focus = function(targetElement) {
326
		var length;
327

328
		// Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
329
		if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
330
			length = targetElement.value.length;
331
			targetElement.setSelectionRange(length, length);
332
		} else {
333
			targetElement.focus();
334
		}
335
	};
336

337

338
	/**
339
	 * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
340
	 *
341
	 * @param {EventTarget|Element} targetElement
342
	 */
343
	FastClick.prototype.updateScrollParent = function(targetElement) {
344
		var scrollParent, parentElement;
345

346
		scrollParent = targetElement.fastClickScrollParent;
347

348
		// Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
349
		// target element was moved to another parent.
350
		if (!scrollParent || !scrollParent.contains(targetElement)) {
351
			parentElement = targetElement;
352
			do {
353
				if (parentElement.scrollHeight > parentElement.offsetHeight) {
354
					scrollParent = parentElement;
355
					targetElement.fastClickScrollParent = parentElement;
356
					break;
357
				}
358

359
				parentElement = parentElement.parentElement;
360
			} while (parentElement);
361
		}
362

363
		// Always update the scroll top tracker if possible.
364
		if (scrollParent) {
365
			scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
366
		}
367
	};
368

369

370
	/**
371
	 * @param {EventTarget} targetElement
372
	 * @returns {Element|EventTarget}
373
	 */
374
	FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
375

376
		// On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
377
		if (eventTarget.nodeType === Node.TEXT_NODE) {
378
			return eventTarget.parentNode;
379
		}
380

381
		return eventTarget;
382
	};
383

384

385
	/**
386
	 * On touch start, record the position and scroll offset.
387
	 *
388
	 * @param {Event} event
389
	 * @returns {boolean}
390
	 */
391
	FastClick.prototype.onTouchStart = function(event) {
392
		var targetElement, touch, selection;
393

394
		// Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
395
		if (event.targetTouches.length > 1) {
396
			return true;
397
		}
398

399
		targetElement = this.getTargetElementFromEventTarget(event.target);
400
		touch = event.targetTouches[0];
401

402
		if (deviceIsIOS) {
403

404
			// Only trusted events will deselect text on iOS (issue #49)
405
			selection = window.getSelection();
406
			if (selection.rangeCount && !selection.isCollapsed) {
407
				return true;
408
			}
409

410
			if (!deviceIsIOS4) {
411

412
				// Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
413
				// when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
414
				// with the same identifier as the touch event that previously triggered the click that triggered the alert.
415
				// Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
416
				// immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
417
				// Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
418
				// which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
419
				// random integers, it's safe to to continue if the identifier is 0 here.
420
				if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
421
					event.preventDefault();
422
					return false;
423
				}
424

425
				this.lastTouchIdentifier = touch.identifier;
426

427
				// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
428
				// 1) the user does a fling scroll on the scrollable layer
429
				// 2) the user stops the fling scroll with another tap
430
				// then the event.target of the last 'touchend' event will be the element that was under the user's finger
431
				// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
432
				// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
433
				this.updateScrollParent(targetElement);
434
			}
435
		}
436

437
		this.trackingClick = true;
438
		this.trackingClickStart = event.timeStamp;
439
		this.targetElement = targetElement;
440

441
		this.touchStartX = touch.pageX;
442
		this.touchStartY = touch.pageY;
443

444
		// Prevent phantom clicks on fast double-tap (issue #36)
445
		if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
446
			event.preventDefault();
447
		}
448

449
		return true;
450
	};
451

452

453
	/**
454
	 * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
455
	 *
456
	 * @param {Event} event
457
	 * @returns {boolean}
458
	 */
459
	FastClick.prototype.touchHasMoved = function(event) {
460
		var touch = event.changedTouches[0], boundary = this.touchBoundary;
461

462
		if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
463
			return true;
464
		}
465

466
		return false;
467
	};
468

469

470
	/**
471
	 * Update the last position.
472
	 *
473
	 * @param {Event} event
474
	 * @returns {boolean}
475
	 */
476
	FastClick.prototype.onTouchMove = function(event) {
477
		if (!this.trackingClick) {
478
			return true;
479
		}
480

481
		// If the touch has moved, cancel the click tracking
482
		if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
483
			this.trackingClick = false;
484
			this.targetElement = null;
485
		}
486

487
		return true;
488
	};
489

490

491
	/**
492
	 * Attempt to find the labelled control for the given label element.
493
	 *
494
	 * @param {EventTarget|HTMLLabelElement} labelElement
495
	 * @returns {Element|null}
496
	 */
497
	FastClick.prototype.findControl = function(labelElement) {
498

499
		// Fast path for newer browsers supporting the HTML5 control attribute
500
		if (labelElement.control !== undefined) {
501
			return labelElement.control;
502
		}
503

504
		// All browsers under test that support touch events also support the HTML5 htmlFor attribute
505
		if (labelElement.htmlFor) {
506
			return document.getElementById(labelElement.htmlFor);
507
		}
508

509
		// If no for attribute exists, attempt to retrieve the first labellable descendant element
510
		// the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
511
		return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
512
	};
513

514

515
	/**
516
	 * On touch end, determine whether to send a click event at once.
517
	 *
518
	 * @param {Event} event
519
	 * @returns {boolean}
520
	 */
521
	FastClick.prototype.onTouchEnd = function(event) {
522
		var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
523

524
		if (!this.trackingClick) {
525
			return true;
526
		}
527

528
		// Prevent phantom clicks on fast double-tap (issue #36)
529
		if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
530
			this.cancelNextClick = true;
531
			return true;
532
		}
533

534
		if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
535
			return true;
536
		}
537

538
		// Reset to prevent wrong click cancel on input (issue #156).
539
		this.cancelNextClick = false;
540

541
		this.lastClickTime = event.timeStamp;
542

543
		trackingClickStart = this.trackingClickStart;
544
		this.trackingClick = false;
545
		this.trackingClickStart = 0;
546

547
		// On some iOS devices, the targetElement supplied with the event is invalid if the layer
548
		// is performing a transition or scroll, and has to be re-detected manually. Note that
549
		// for this to function correctly, it must be called *after* the event target is checked!
550
		// See issue #57; also filed as rdar://13048589 .
551
		if (deviceIsIOSWithBadTarget) {
552
			touch = event.changedTouches[0];
553

554
			// In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
555
			targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
556
			targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
557
		}
558

559
		targetTagName = targetElement.tagName.toLowerCase();
560
		if (targetTagName === 'label') {
561
			forElement = this.findControl(targetElement);
562
			if (forElement) {
563
				this.focus(targetElement);
564
				if (deviceIsAndroid) {
565
					return false;
566
				}
567

568
				targetElement = forElement;
569
			}
570
		} else if (this.needsFocus(targetElement)) {
571

572
			// Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
573
			// Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
574
			if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
575
				this.targetElement = null;
576
				return false;
577
			}
578

579
			this.focus(targetElement);
580
			this.sendClick(targetElement, event);
581

582
			// Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
583
			// Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
584
			if (!deviceIsIOS || targetTagName !== 'select') {
585
				this.targetElement = null;
586
				event.preventDefault();
587
			}
588

589
			return false;
590
		}
591

592
		if (deviceIsIOS && !deviceIsIOS4) {
593

594
			// Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
595
			// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
596
			scrollParent = targetElement.fastClickScrollParent;
597
			if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
598
				return true;
599
			}
600
		}
601

602
		// Prevent the actual click from going though - unless the target node is marked as requiring
603
		// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
604
		if (!this.needsClick(targetElement)) {
605
			event.preventDefault();
606
			this.sendClick(targetElement, event);
607
		}
608

609
		return false;
610
	};
611

612

613
	/**
614
	 * On touch cancel, stop tracking the click.
615
	 *
616
	 * @returns {void}
617
	 */
618
	FastClick.prototype.onTouchCancel = function() {
619
		this.trackingClick = false;
620
		this.targetElement = null;
621
	};
622

623

624
	/**
625
	 * Determine mouse events which should be permitted.
626
	 *
627
	 * @param {Event} event
628
	 * @returns {boolean}
629
	 */
630
	FastClick.prototype.onMouse = function(event) {
631

632
		// If a target element was never set (because a touch event was never fired) allow the event
633
		if (!this.targetElement) {
634
			return true;
635
		}
636

637
		if (event.forwardedTouchEvent) {
638
			return true;
639
		}
640

641
		// Programmatically generated events targeting a specific element should be permitted
642
		if (!event.cancelable) {
643
			return true;
644
		}
645

646
		// Derive and check the target element to see whether the mouse event needs to be permitted;
647
		// unless explicitly enabled, prevent non-touch click events from triggering actions,
648
		// to prevent ghost/doubleclicks.
649
		if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
650

651
			// Prevent any user-added listeners declared on FastClick element from being fired.
652
			if (event.stopImmediatePropagation) {
653
				event.stopImmediatePropagation();
654
			} else {
655

656
				// Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
657
				event.propagationStopped = true;
658
			}
659

660
			// Cancel the event
661
			event.stopPropagation();
662
			event.preventDefault();
663

664
			return false;
665
		}
666

667
		// If the mouse event is permitted, return true for the action to go through.
668
		return true;
669
	};
670

671

672
	/**
673
	 * On actual clicks, determine whether this is a touch-generated click, a click action occurring
674
	 * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
675
	 * an actual click which should be permitted.
676
	 *
677
	 * @param {Event} event
678
	 * @returns {boolean}
679
	 */
680
	FastClick.prototype.onClick = function(event) {
681
		var permitted;
682

683
		// It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
684
		if (this.trackingClick) {
685
			this.targetElement = null;
686
			this.trackingClick = false;
687
			return true;
688
		}
689

690
		// Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
691
		if (event.target.type === 'submit' && event.detail === 0) {
692
			return true;
693
		}
694

695
		permitted = this.onMouse(event);
696

697
		// Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
698
		if (!permitted) {
699
			this.targetElement = null;
700
		}
701

702
		// If clicks are permitted, return true for the action to go through.
703
		return permitted;
704
	};
705

706

707
	/**
708
	 * Remove all FastClick's event listeners.
709
	 *
710
	 * @returns {void}
711
	 */
712
	FastClick.prototype.destroy = function() {
713
		var layer = this.layer;
714

715
		if (deviceIsAndroid) {
716
			layer.removeEventListener('mouseover', this.onMouse, true);
717
			layer.removeEventListener('mousedown', this.onMouse, true);
718
			layer.removeEventListener('mouseup', this.onMouse, true);
719
		}
720

721
		layer.removeEventListener('click', this.onClick, true);
722
		layer.removeEventListener('touchstart', this.onTouchStart, false);
723
		layer.removeEventListener('touchmove', this.onTouchMove, false);
724
		layer.removeEventListener('touchend', this.onTouchEnd, false);
725
		layer.removeEventListener('touchcancel', this.onTouchCancel, false);
726
	};
727

728

729
	/**
730
	 * Check whether FastClick is needed.
731
	 *
732
	 * @param {Element} layer The layer to listen on
733
	 */
734
	FastClick.notNeeded = function(layer) {
735
		var metaViewport;
736
		var chromeVersion;
737
		var blackberryVersion;
738
		var firefoxVersion;
739

740
		// Devices that don't support touch don't need FastClick
741
		if (typeof window.ontouchstart === 'undefined') {
742
			return true;
743
		}
744

745
		// Chrome version - zero for other browsers
746
		chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
747

748
		if (chromeVersion) {
749

750
			if (deviceIsAndroid) {
751
				metaViewport = document.querySelector('meta[name=viewport]');
752

753
				if (metaViewport) {
754
					// Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
755
					if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
756
						return true;
757
					}
758
					// Chrome 32 and above with width=device-width or less don't need FastClick
759
					if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
760
						return true;
761
					}
762
				}
763

764
			// Chrome desktop doesn't need FastClick (issue #15)
765
			} else {
766
				return true;
767
			}
768
		}
769

770
		if (deviceIsBlackBerry10) {
771
			blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
772

773
			// BlackBerry 10.3+ does not require Fastclick library.
774
			// https://github.com/ftlabs/fastclick/issues/251
775
			if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
776
				metaViewport = document.querySelector('meta[name=viewport]');
777

778
				if (metaViewport) {
779
					// user-scalable=no eliminates click delay.
780
					if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
781
						return true;
782
					}
783
					// width=device-width (or less than device-width) eliminates click delay.
784
					if (document.documentElement.scrollWidth <= window.outerWidth) {
785
						return true;
786
					}
787
				}
788
			}
789
		}
790

791
		// IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
792
		if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
793
			return true;
794
		}
795

796
		// Firefox version - zero for other browsers
797
		firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
798

799
		if (firefoxVersion >= 27) {
800
			// Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
801

802
			metaViewport = document.querySelector('meta[name=viewport]');
803
			if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
804
				return true;
805
			}
806
		}
807

808
		// IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
809
		// http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
810
		if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
811
			return true;
812
		}
813

814
		return false;
815
	};
816

817

818
	/**
819
	 * Factory method for creating a FastClick object
820
	 *
821
	 * @param {Element} layer The layer to listen on
822
	 * @param {Object} [options={}] The options to override the defaults
823
	 */
824
	FastClick.attach = function(layer, options) {
825
		return new FastClick(layer, options);
826
	};
827

828

829
	if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
830

831
		// AMD. Register as an anonymous module.
832
		define(function() {
833
			return FastClick;
834
		});
835
	} else if (typeof module !== 'undefined' && module.exports) {
836
		module.exports = FastClick.attach;
837
		module.exports.FastClick = FastClick;
838
	} else {
839
		window.FastClick = FastClick;
840
	}
841
}());
842

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.