jxdnsong
2020-10-23 a7929e6b3ec9ac17233f39e55a2b8ac63ea75f42
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
define([
    'dojo/on',
    'dojo/query'
], function (on, query) {
    // This module exposes useful functions for working with touch devices.
 
    var util = {
        // Overridable defaults related to extension events defined below.
        tapRadius: 10,
        dbltapTime: 250,
 
        selector: function (selector, eventType, children) {
            // summary:
            //        Reimplementation of on.selector, taking an iOS quirk into account
            return function (target, listener) {
                var bubble = eventType.bubble;
                if (bubble) {
                    // the event type doesn't naturally bubble, but has a bubbling form, use that
                    eventType = bubble;
                }
                else if (children !== false) {
                    // for normal bubbling events we default to allowing children of the selector
                    children = true;
                }
                return on(target, eventType, function (event) {
                    var eventTarget = event.target;
 
                    // iOS tends to report the text node an event was fired on, rather than
                    // the top-level element; this may end up causing errors in selector engines
                    if (eventTarget.nodeType === 3) {
                        eventTarget = eventTarget.parentNode;
                    }
 
                    // there is a selector, so make sure it matches
                    while (!query.matches(eventTarget, selector, target)) {
                        if (eventTarget === target || !children || !(eventTarget = eventTarget.parentNode)) {
                            return;
                        }
                    }
                    return listener.call(eventTarget, event);
                });
            };
        },
 
        countCurrentTouches: function (evt, node) {
            // summary:
            //        Given a touch event and a DOM node, counts how many current touches
            //        presently lie within that node.  Useful in cases where an accurate
            //        count is needed but tracking changedTouches won't suffice because
            //        other handlers stop events from bubbling high enough.
 
            if (!('touches' in evt)) {
                // Not a touch event (perhaps called from a mouse event on a
                // platform supporting touch events)
                return -1;
            }
 
            var i, numTouches, touch;
            for (i = 0, numTouches = 0; (touch = evt.touches[i]); ++i) {
                if (node.contains(touch.target)) {
                    ++numTouches;
                }
            }
            return numTouches;
        }
    };
 
    function handleTapStart(target, listener, evt, prevent) {
        // Common function for handling tap detection.
        // The passed listener will only be fired when and if a touchend is fired
        // which confirms the overall gesture resembled a tap.
 
        if (evt.targetTouches.length > 1) {
            return; // ignore multitouch
        }
 
        var start = evt.changedTouches[0],
            startX = start.screenX,
            startY = start.screenY;
 
        prevent && evt.preventDefault();
 
        var endListener = on(target, 'touchend', function (evt) {
            var end = evt.changedTouches[0];
            if (!evt.targetTouches.length) {
                // only call listener if this really seems like a tap
                if (Math.abs(end.screenX - startX) < util.tapRadius &&
                        Math.abs(end.screenY - startY) < util.tapRadius) {
                    prevent && evt.preventDefault();
                    listener.call(this, evt);
                }
                endListener.remove();
            }
        });
    }
 
    function tap(target, listener) {
        // Function usable by dojo/on as a synthetic tap event.
        return on(target, 'touchstart', function (evt) {
            handleTapStart(target, listener, evt);
        });
    }
 
    function dbltap(target, listener) {
        // Function usable by dojo/on as a synthetic double-tap event.
        var first, timeout;
 
        return on(target, 'touchstart', function (evt) {
            if (!first) {
                // first potential tap: detect as usual, but with specific logic
                handleTapStart(target, function (evt) {
                    first = evt.changedTouches[0];
                    timeout = setTimeout(function () {
                        first = timeout = null;
                    }, util.dbltapTime);
                }, evt);
            }
            else {
                handleTapStart(target, function (evt) {
                    // bail out if first was cleared between 2nd touchstart and touchend
                    if (!first) {
                        return;
                    }
                    var second = evt.changedTouches[0];
                    // only call listener if both taps occurred near the same place
                    if (Math.abs(second.screenX - first.screenX) < util.tapRadius &&
                            Math.abs(second.screenY - first.screenY) < util.tapRadius) {
                        timeout && clearTimeout(timeout);
                        first = timeout = null;
                        listener.call(this, evt);
                    }
                }, evt, true);
            }
        });
    }
 
    util.tap = tap;
    util.dbltap = dbltap;
 
    return util;
});