Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #7299 from mozilla-b2g/master

Merge nightly - 050113
  • Loading branch information...
commit b506bdbd5bcdb042e3a8ca5d62856ae5d683c176 2 parents 67b917f + 0a66aae
vingtetun authored
3  README.md
Source Rendered
@@ -40,9 +40,6 @@ To run all the unit tests with B2G Desktop:
40 40
    or `make test-agent-test APP=<APP>` to run unit tests for a
41 41
    specific app
42 42
 
43  
-   or `make test-agent-test TESTS=<PATH/TO/TESTFILE.JS>` to run unit
44  
-   tests in a specific file
45  
-
46 43
 More importantly, you can use test-agent-server to watch the files
47 44
 on the filesystem and execute relevant tests when they change:
48 45
 
1  apps/clock/index.html
@@ -22,6 +22,7 @@
22 22
 
23 23
     <!-- Specific code -->
24 24
     <script type="application/javascript" src="shared/js/gesture_detector.js"></script>
  25
+    <script type="application/javascript" src="shared/js/mouse_event_shim.js"></script>
25 26
     <script type="application/javascript" src="js/utils.js"></script>
26 27
     <script type="application/javascript" src="js/alarmsdb.js"></script>
27 28
     <script type="application/javascript" src="js/clock.js"></script>
6  apps/clock/js/alarm.js
@@ -767,7 +767,7 @@ var AlarmEditView = {
767 767
         }
768 768
         break;
769 769
       case 'repeat-menu':
770  
-        this.repeatSelect.focus();
  770
+        setTimeout(function(self) { self.repeatSelect.focus(); }, 0, this);
771 771
         break;
772 772
       case 'repeat-select':
773 773
         switch (evt.type) {
@@ -777,7 +777,7 @@ var AlarmEditView = {
777 777
         }
778 778
         break;
779 779
       case 'sound-menu':
780  
-        this.soundSelect.focus();
  780
+        setTimeout(function(self) { self.soundSelect.focus(); }, 0, this);
781 781
         break;
782 782
       case 'sound-select':
783 783
         switch (evt.type) {
@@ -791,7 +791,7 @@ var AlarmEditView = {
791 791
         }
792 792
         break;
793 793
       case 'snooze-menu':
794  
-        this.snoozeSelect.focus();
  794
+        setTimeout(function(self) { self.snoozeSelect.focus(); }, 0, this);
795 795
         break;
796 796
       case 'snooze-select':
797 797
         switch (evt.type) {
6  apps/clock/js/utils.js
@@ -251,7 +251,8 @@ var ValuePicker = (function() {
251 251
     if ('touches' in evt) {
252 252
       evt = evt.touches[0];
253 253
     }
254  
-    return { x: evt.pageX, y: evt.pageY, timestamp: evt.timeStamp };
  254
+    return { x: evt.pageX, y: evt.pageY,
  255
+             timestamp: MouseEventShim.getEventTimestamp(evt) };
255 256
   }
256 257
 
257 258
   //
@@ -287,7 +288,6 @@ var ValuePicker = (function() {
287 288
 
288 289
   function vp_mousemove(event) {
289 290
     event.stopPropagation();
290  
-    event.target.setCapture(true);
291 291
     currentEvent = cloneEvent(event);
292 292
 
293 293
     calcSpeed();
@@ -324,6 +324,8 @@ var ValuePicker = (function() {
324 324
 
325 325
   function vp_mousedown(event) {
326 326
     event.stopPropagation();
  327
+    event.target.setCapture(true);
  328
+    MouseEventShim.setCapture();
327 329
 
328 330
     // Stop animation
329 331
     this.element.classList.remove('animation-on');
2  apps/communications/contacts/index.html
@@ -42,7 +42,7 @@
42 42
     <link rel="resource" type="application/l10n" href="/shared/locales/date.ini"/>
43 43
     <script type="text/javascript" src="/shared/js/l10n.js"></script>
44 44
     <script type="text/javascript" src="/shared/js/l10n_date.js"></script>
45  
-    <script defer type="text/javascript" src="/shared/js/async_storage.js"></script>
  45
+    <script defer type="text/javascript" src="/shared/js/mouse_event_shim.js"></script>
46 46
 
47 47
     <script defer type="text/javascript" src="/shared/js/async_storage.js"></script>
48 48
     <script defer type="text/javascript" src="/contacts/js/contacts_settings.js"></script>
1  apps/communications/dialer/index.html
@@ -39,6 +39,7 @@
39 39
     <script defer type="application/javascript" src="/shared/js/settings_listener.js"></script>
40 40
     <script defer type="application/javascript" src="/shared/js/notification_helper.js"></script>
41 41
     <script defer type="application/javascript" src="/shared/js/mobile_operator.js"></script>
  42
+    <script defer type="application/javascript" src="/shared/js/mouse_event_shim.js"></script>
42 43
 
43 44
     <!-- App js -->
44 45
     <script defer type="application/javascript" src="/contacts/js/confirm_dialog.js"></script>
63  apps/communications/dialer/js/oncall.js
@@ -191,15 +191,12 @@ var OnCallHandler = (function onCallHandler() {
191 191
   });
192 192
 
193 193
   var screenLock;
194  
-  var cpuLock;
195 194
 
196 195
   /* === Setup === */
197 196
   function setup() {
198 197
     // Animating the screen in the viewport.
199 198
     toggleScreen();
200 199
 
201  
-    cpuLock = navigator.requestWakeLock('cpu');
202  
-
203 200
     if (telephony) {
204 201
       // Somehow the muted property appears to true after initialization.
205 202
       // Set it to false.
@@ -269,33 +266,7 @@ var OnCallHandler = (function onCallHandler() {
269 266
 
270 267
     if (handledCalls.length > 1) {
271 268
       // New incoming call, signaling the user.
272  
-      // Waiting for the screen to be turned on before vibrating.
273  
-      if (document.mozHidden) {
274  
-        window.addEventListener('mozvisibilitychange', function waitOn() {
275  
-          window.removeEventListener('mozvisibilitychange', waitOn);
276  
-          if (activateVibration) {
277  
-            navigator.vibrate([100, 100, 100]);
278  
-          }
279  
-        });
280  
-      } else {
281  
-        if (activateVibration) {
282  
-          navigator.vibrate([100, 100, 100]);
283  
-        }
284  
-      }
285  
-
286  
-      LazyL10n.get(function localized(_) {
287  
-        var number = (call.number.length ? call.number : _('unknown'));
288  
-        Contacts.findByNumber(number, function lookupContact(contact) {
289  
-          if (contact && contact.name) {
290  
-            CallScreen.incomingNumber.textContent = contact.name;
291  
-            return;
292  
-          }
293  
-
294  
-          CallScreen.incomingNumber.textContent = number;
295  
-        });
296  
-      });
297  
-
298  
-      CallScreen.showIncoming();
  269
+      handleCallWaiting(call);
299 270
     } else {
300 271
       if (window.location.hash === '#locked' &&
301 272
           (call.state == 'incoming')) {
@@ -384,6 +355,33 @@ var OnCallHandler = (function onCallHandler() {
384 355
     });
385 356
   }
386 357
 
  358
+  function handleCallWaiting(call) {
  359
+    LazyL10n.get(function localized(_) {
  360
+      var number = (call.number.length ? call.number : _('unknown'));
  361
+      Contacts.findByNumber(number, function lookupContact(contact) {
  362
+        if (contact && contact.name) {
  363
+          CallScreen.incomingNumber.textContent = contact.name;
  364
+          return;
  365
+        }
  366
+
  367
+        CallScreen.incomingNumber.textContent = number;
  368
+      });
  369
+    });
  370
+
  371
+    CallScreen.showIncoming();
  372
+
  373
+    var vibrateInterval = window.setInterval(function vibrate() {
  374
+      if ('vibrate' in navigator) {
  375
+        navigator.vibrate([200]);
  376
+      }
  377
+    }, 2000);
  378
+
  379
+    call.addEventListener('statechange', function callStateChange() {
  380
+      call.removeEventListener('statechange', callStateChange);
  381
+      window.clearInterval(vibrateInterval);
  382
+    });
  383
+  }
  384
+
387 385
   /* === Call Screen === */
388 386
   function toggleScreen() {
389 387
     displayed = !displayed;
@@ -421,11 +419,6 @@ var OnCallHandler = (function onCallHandler() {
421 419
       return;
422 420
     }
423 421
 
424  
-    if (cpuLock) {
425  
-      cpuLock.unlock();
426  
-      cpuLock = null;
427  
-    }
428  
-
429 422
     closing = true;
430 423
 
431 424
     if (Swiper) {
1  apps/communications/dialer/oncall.html
@@ -18,6 +18,7 @@
18 18
 
19 19
     <script defer type="application/javascript" src="/shared/js/settings_listener.js"></script>
20 20
     <script defer type="application/javascript" src="/shared/js/async_storage.js"></script>
  21
+    <script defer type="application/javascript" src="/shared/js/mouse_event_shim.js"></script>
21 22
 
22 23
     <script defer type="application/javascript" src="/shared/js/simple_phone_matcher.js"></script>
23 24
     <script defer type="application/javascript" src="/dialer/js/contacts.js"></script>
2  apps/communications/ftu/locales/ftu.en-US.properties
@@ -139,7 +139,7 @@ aboutYourPrivacy2 = Your Privacy
139 139
 mozillaCommunity2 = When you use {{brandShortName}}, you become part of a global community helping to build a brighter future for the Web. If you’d like to know more about the Mozilla community, our other products and events near you, please enter your email address below.
140 140
 collaborate = I want to help improve {{brandShortName}} by sharing data about my phone.
141 141
 learnMore = Learn more.
142  
-emailAddress = Email address
  142
+emailAddress.placeholder = Email address
143 143
 browserPrivacyChoices = {{brandShortName}} Privacy Choices
144 144
 privacy-b2g={{brandShortName}}
145 145
 privacy-b2g.title={{brandShortName}}
9  apps/email/js/ext/gaia-email-opt.js
@@ -35944,7 +35944,14 @@ Autoconfigurator.prototype = {
35944 35944
       callback('no-config-info', null, { status: 'error' });
35945 35945
     };
35946 35946
 
35947  
-    xhr.send();
  35947
+    // Gecko currently throws in send() if the file we're opening doesn't exist.
  35948
+    // This is almost certainly wrong, but let's just work around it for now.
  35949
+    try {
  35950
+      xhr.send();
  35951
+    }
  35952
+    catch(e) {
  35953
+      callback('no-config-info', null, { status: 404 });
  35954
+    }
35948 35955
   },
35949 35956
 
35950 35957
   /**
1  apps/fm/index.html
@@ -8,6 +8,7 @@
8 8
     <link rel="resource" type="application/l10n" href="locales/locales.ini" />
9 9
     <script type="text/javascript" src="shared/js/l10n.js"></script>
10 10
     <script type="text/javascript" src="shared/js/async_storage.js"></script>
  11
+    <script type="text/javascript" src="shared/js/mouse_event_shim.js"></script>
11 12
     <script type="text/javascript" src="js/fm.js"></script>
12 13
   </head>
13 14
 
3  apps/fm/js/fm.js
@@ -190,7 +190,8 @@ var frequencyDialer = {
190 190
       if ('touches' in evt) {
191 191
         evt = evt.touches[0];
192 192
       }
193  
-      return { x: evt.clientX, y: evt.clientX, timestamp: evt.timeStamp };
  193
+      return { x: evt.clientX, y: evt.clientX,
  194
+               timestamp: MouseEventShim.getEventTimestamp(evt) };
194 195
     }
195 196
 
196 197
     var self = this;
1  apps/gallery/index.html
@@ -12,6 +12,7 @@
3  apps/gallery/js/gallery.js
@@ -711,6 +711,9 @@ function setView(view) {
1  apps/homescreen/index.html
@@ -14,6 +14,7 @@
14 14
     <script type="text/javascript" src="shared/js/l10n.js"></script>
15 15
     <script type="text/javascript" src="shared/js/l10n_date.js"></script>
16 16
     <script type="text/javascript" defer src="shared/js/manifest_helper.js"></script>
  17
+    <script type="text/javascript" src="shared/js/mouse_event_shim.js"></script>
17 18
 
18 19
     <link rel="stylesheet" type="text/css" href="shared/style_unstable/progress_activity.css">
19 20
     <link rel="stylesheet" type="text/css" href="shared/style/buttons.css">
5  apps/homescreen/js/grid.js
@@ -37,7 +37,7 @@ const GridManager = (function() {
37 37
   function handleEvent(evt) {
38 38
     switch (evt.type) {
39 39
       case 'mousedown':
40  
-        touchStartTimestamp = evt.timeStamp;
  40
+        touchStartTimestamp = MouseEventShim.getEventTimestamp(evt);
41 41
         evt.stopPropagation();
42 42
         startEvent = evt;
43 43
         attachEvents();
@@ -144,10 +144,11 @@ const GridManager = (function() {
144 144
 
145 145
         var container = pages[index].container;
146 146
         container.setCapture(true);
  147
+        MouseEventShim.setCapture(container);
147 148
         container.addEventListener('mousemove', pan, true);
148 149
 
149 150
         window.addEventListener('mouseup', function removePanHandler(e) {
150  
-          touchEndTimestamp = e.timeStamp;
  151
+          touchEndTimestamp = MouseEventShim.getEventTimestamp(e);
151 152
           window.removeEventListener('mouseup', removePanHandler, true);
152 153
 
153 154
           container.removeEventListener('mousemove', pan, true);
1  apps/music/index.html
@@ -11,6 +11,7 @@
11 11
     <link rel="resource" type="application/l10n" href="locales/locales.ini">
12 12
     <!-- Shared code -->
13 13
     <script type="text/javascript" src="shared/js/l10n.js"></script>
  14
+    <script type="text/javascript" defer src="shared/js/mouse_event_shim.js"></script>
14 15
     <script type="text/javascript" defer src="shared/js/mediadb.js"></script>
15 16
     <script type="text/javascript" defer src="shared/js/blobview.js"></script>
16 17
     <script type="text/javascript" defer src="shared/js/async_storage.js"></script>
27  apps/music/js/Player.js
@@ -481,7 +481,7 @@ var PlayerView = {
481 481
   },
482 482
 
483 483
   seekAudio: function pv_seekAudio(seekTime) {
484  
-    if (seekTime)
  484
+    if (seekTime !== undefined)
485 485
       this.audio.currentTime = seekTime;
486 486
 
487 487
     // mp3 returns in microseconds
@@ -606,21 +606,18 @@ var PlayerView = {
606 606
       case 'mousemove':
607 607
         if (evt.type === 'mousedown') {
608 608
           target.setCapture(false);
  609
+          MouseEventShim.setCapture()
609 610
           this.isSeeking = true;
610 611
           this.seekIndicator.classList.add('highlight');
611 612
         }
612 613
         if (this.isSeeking && this.audio.duration > 0) {
613  
-          var x = 0;
614  
-
615  
-          if (evt.layerX < 0) {
  614
+          // target is the seek bar
  615
+          var x = (evt.clientX - target.offsetLeft) / target.offsetWidth;
  616
+          if (x < 0)
616 617
             x = 0;
617  
-          } else if (evt.layerX > target.clientWidth) {
618  
-            x = target.clientWidth;
619  
-          } else {
620  
-            x = evt.layerX;
621  
-          }
622  
-          // target is the seek bar, and evt.layerX is the moved position
623  
-          var seekTime = x / target.clientWidth * this.seekBar.max;
  618
+          if (x > 1)
  619
+            x = 1;
  620
+          var seekTime = x * this.seekBar.max;
624 621
           this.setSeekBar(this.audio.startTime, this.audio.duration, seekTime);
625 622
         }
626 623
         break;
@@ -629,8 +626,12 @@ var PlayerView = {
629 626
         this.seekIndicator.classList.remove('highlight');
630 627
 
631 628
         if (this.audio.duration > 0) {
632  
-          // target is the seek bar, and evt.layerX is the moved position
633  
-          var seekTime = evt.layerX / target.clientWidth * this.seekBar.max;
  629
+          var x = (evt.clientX - target.offsetLeft) / target.offsetWidth;
  630
+          if (x < 0)
  631
+            x = 0;
  632
+          if (x > 1)
  633
+            x = 1;
  634
+          var seekTime = x * this.seekBar.max;
634 635
           this.seekAudio(seekTime);
635 636
         }
636 637
         break;
3  apps/settings/index.html
@@ -38,6 +38,7 @@
38 38
     <link rel="resource" type="application/l10n" href="locales/locales.ini"/>
39 39
     <script type="application/javascript" src="shared/js/l10n.js"></script>
40 40
     <script type="application/javascript" src="shared/js/l10n_date.js"></script>
  41
+    <script type="application/javascript" defer src="shared/js/mouse_event_shim.js"></script>
41 42
     <script type="application/javascript" defer src="shared/js/manifest_helper.js"></script>
42 43
 
43 44
     <!-- Specific code -->
@@ -1374,7 +1375,7 @@ <h1 data-l10n-id="enter-passcode"   data-mode="confirm"> Enter Passcode    </h1>
1374 1375
 
1375 1376
       <div class="passcode-overlay">
1376 1377
         <input type="number" id="passcode-input" />
1377  
-        <div class="passcode-container">
  1378
+        <div class="passcode-container" id="passcode-container">
1378 1379
           <div class="passcode-error" data-l10n-id="incorrect-passcode" data-type="incorrect">Incorrect passcode!</div>
1379 1380
           <div class="passcode-error" data-l10n-id="passcode-doesnt-match" data-type="mismatch">Passcode doesn't match. Try again!</div>
1380 1381
           <span data-l10n-id="passcode">Passcode</span>
30  apps/settings/js/phone_lock.js
@@ -31,6 +31,7 @@ var PhoneLock = {
31 31
     this.phonelockDesc = document.getElementById('phoneLock-desc');
32 32
     this.lockscreenEnable = document.getElementById('lockscreen-enable');
33 33
     this.passcodeInput = document.getElementById('passcode-input');
  34
+    this.passcodeContainer = document.getElementById('passcode-container');
34 35
     this.passcodeDigits = document.querySelectorAll('.passcode-digit');
35 36
     this.passcodeEnable = document.getElementById('passcode-enable');
36 37
     this.passcodeEditButton = document.getElementById('passcode-edit');
@@ -47,7 +48,15 @@ var PhoneLock = {
47 48
     this.passcodeEditButton.addEventListener('click', this);
48 49
     this.createPasscodeButton.addEventListener('click', this);
49 50
     this.changePasscodeButton.addEventListener('click', this);
50  
-    this.passcodePanel.addEventListener('mousedown', this, true);
  51
+
  52
+    // If the pseudo-input loses focus, then allow the user to restore focus
  53
+    // by touching the container around the pseudo-input.
  54
+    var self = this;
  55
+    this.passcodeContainer.addEventListener('mousedown', function(evt) {
  56
+      self.passcodeInput.focus();
  57
+      evt.preventDefault();
  58
+    });
  59
+
51 60
     this.fetchSettings();
52 61
   },
53 62
 
@@ -119,26 +128,17 @@ var PhoneLock = {
119 128
     this.MODE = mode;
120 129
     this.passcodePanel.dataset.mode = mode;
121 130
     if (document.location.hash != 'phoneLock-passcode') {
122  
-      var self = this;
123 131
       document.location.hash = 'phoneLock-passcode'; // show dialog box
124  
-      this.passcodePanel.
125  
-           addEventListener('transitionend', function ontransitionend() {
126  
-        self.passcodePanel.
127  
-             removeEventListener('transitionend', ontransitionend);
128  
-        self.passcodeInput.focus();
129  
-      });
  132
+
  133
+      // Open the keyboard after the UI transition. We can't listen for the
  134
+      // ontransitionend event because some of the passcode mode changes, such
  135
+      // as edit->new, do not trigger transition events.
  136
+      setTimeout(function(self) { self.passcodeInput.focus(); }, 0, this);
130 137
     }
131 138
     this.updatePassCodeUI();
132 139
   },
133 140
 
134 141
   handleEvent: function pl_handleEvent(evt) {
135  
-    // Prevent mousedown event to avoid the keypad losing focus.
136  
-    if (evt.type == 'mousedown') {
137  
-      this.passcodeInput.focus();
138  
-      evt.preventDefault();
139  
-      return;
140  
-    }
141  
-
142 142
     switch (evt.target) {
143 143
       case this.passcodeEnable:
144 144
         evt.preventDefault();
3  apps/system/js/cards_view.js
@@ -625,6 +625,9 @@ var CardsView = (function() {
625 625
         break;
626 626
 
627 627
       case 'holdhome':
  628
+        if (LockScreen.locked)
  629
+          return;
  630
+
628 631
         SleepMenu.hide();
629 632
         showCardSwitcher();
630 633
         break;
30  apps/system/js/screen_manager.js
@@ -154,21 +154,24 @@ var ScreenManager = {
154 154
         break;
155 155
 
156 156
       case 'userproximity':
  157
+        this._screenOffByProximity = evt.near;
157 158
         if (evt.near) {
158 159
           this.turnScreenOff(true);
159 160
         } else {
160 161
           this.turnScreenOn();
161 162
         }
162  
-        this._screenOffByProximity = evt.near;
163 163
         break;
164 164
 
165 165
       case 'callschanged':
166 166
         var telephony = window.navigator.mozTelephony;
167 167
         if (!telephony.calls.length) {
168  
-          window.removeEventListener('userproximity', this);
169 168
           if (this._screenOffByProximity) {
170 169
             this.turnScreenOn();
171 170
           }
  171
+
  172
+          window.removeEventListener('userproximity', this);
  173
+          this._screenOffByProximity = false;
  174
+
172 175
           if (this._cpuWakeLock) {
173 176
            this._cpuWakeLock.unlock();
174 177
            this._cpuWakeLock = null;
@@ -225,6 +228,14 @@ var ScreenManager = {
225 228
     // we turn the screen back on.
226 229
     self._savedBrightness = navigator.mozPower.screenBrightness;
227 230
 
  231
+    // Remove the cpuWakeLock if screen is not turned off by
  232
+    // userproximity event.
  233
+    if (!this._screenOffByProximity && this._cpuWakeLock) {
  234
+      window.removeEventListener('userproximity', this);
  235
+      this._cpuWakeLock.unlock();
  236
+      this._cpuWakeLock = null;
  237
+    }
  238
+
228 239
     var screenOff = function scm_screenOff() {
229 240
       self._setIdleTimeout(0);
230 241
 
@@ -282,6 +293,20 @@ var ScreenManager = {
282 293
     // Set the brightness before the screen is on.
283 294
     this.setScreenBrightness(this._savedBrightness, instant);
284 295
 
  296
+    // If we are in a call and there is no cpuWakeLock,
  297
+    // we would have to get one here.
  298
+    var telephony = window.navigator.mozTelephony;
  299
+    if (!this._cpuWakeLock && telephony && telephony.calls.length) {
  300
+      telephony.calls.some(function checkCallConnection(call) {
  301
+        if (call.state == 'connected') {
  302
+          this._cpuWakeLock = navigator.requestWakeLock('cpu');
  303
+          window.addEventListener('userproximity', this);
  304
+          return true;
  305
+        }
  306
+        return false;
  307
+      }, this);
  308
+    }
  309
+
285 310
     // Actually turn the screen on.
286 311
     var power = navigator.mozPower;
287 312
     if (power)
@@ -298,6 +323,7 @@ var ScreenManager = {
298 323
 
299 324
     this._reconfigScreenTimeout();
300 325
     this.fireScreenChangeEvent();
  326
+
301 327
     return true;
302 328
   },
303 329
 
1  apps/video/index.html
@@ -66,6 +66,7 @@ <h1 id="overlay-title"></h1>
66 66
     <script type="text/javascript" src="shared/js/mediadb.js"></script>
67 67
     <script type="text/javascript" src="shared/js/blobview.js"></script>
68 68
     <script type="text/javascript" src="shared/js/media/get_video_rotation.js"></script>
  69
+    <script type="text/javascript" src="shared/js/mouse_event_shim.js"></script>
69 70
     <script type="text/javascript" src="js/video.js"></script>
70 71
 
71 72
   </body>
3  build/preferences.js
@@ -69,6 +69,9 @@ if (DEBUG) {
69 69
                      webapp.sourceDirectoryName);
70 70
   });
71 71
   prefs.push(["extensions.gaia.app_relative_path", appPathList.join(' ')]);
  72
+
  73
+  // Identity debug messages
  74
+  prefs.push(["toolkit.identity.debug", true]);
72 75
 }
73 76
 
74 77
 function writePrefs() {
282  shared/js/mouse_event_shim.js
... ...
@@ -0,0 +1,282 @@
  1
+/**
  2
+ * mouse_event_shim.js: generate mouse events from touch events.
  3
+ *
  4
+ *   This library listens for touch events and generates mousedown, mousemove
  5
+ *   mouseup, and click events to match them. It captures and dicards any
  6
+ *   real mouse events (non-synthetic events with isTrusted true) that are
  7
+ *   send by gecko so that there are not duplicates.
  8
+ *
  9
+ *   This library does emit mouseover/mouseout and mouseenter/mouseleave
  10
+ *   events. You can turn them off by setting MouseEventShim.trackMouseMoves to
  11
+ *   false. This means that mousemove events will always have the same target
  12
+ *   as the mousedown even that began the series. You can also call
  13
+ *   MouseEventShim.setCapture() from a mousedown event handler to prevent
  14
+ *   mouse tracking until the next mouseup event.
  15
+ *
  16
+ *   This library does not support multi-touch but should be sufficient
  17
+ *   to do drags based on mousedown/mousemove/mouseup events.
  18
+ *
  19
+ *   This library does not emit dblclick events or contextmenu events
  20
+ */
  21
+
  22
+'use strict';
  23
+
  24
+(function() {
  25
+  // Make sure we don't run more than once
  26
+  if (MouseEventShim)
  27
+    return;
  28
+
  29
+  // Bail if we're not on running on a platform that sends touch
  30
+  // events.  We don't need the shim code for mouse events.
  31
+  try {
  32
+    document.createEvent('TouchEvent');
  33
+  } catch (e) {
  34
+    return;
  35
+  }
  36
+
  37
+  var starttouch;  // The Touch object that we started with
  38
+  var target;      // The element the touch is currently over
  39
+  var emitclick;   // Will we be sending a click event after mouseup?
  40
+
  41
+  // Use capturing listeners to discard all mouse events from gecko
  42
+  window.addEventListener('mousedown', discardEvent, true);
  43
+  window.addEventListener('mouseup', discardEvent, true);
  44
+  window.addEventListener('mousemove', discardEvent, true);
  45
+  window.addEventListener('click', discardEvent, true);
  46
+
  47
+  function discardEvent(e) {
  48
+    if (e.isTrusted) {
  49
+      e.stopImmediatePropagation(); // so it goes no further
  50
+      if (e.type === 'click')
  51
+        e.preventDefault();         // so it doesn't trigger a change event
  52
+    }
  53
+  }
  54
+
  55
+  // Listen for touch events that bubble up to the window.
  56
+  // If other code has called stopPropagation on the touch events
  57
+  // then we'll never see them. Also, we'll honor the defaultPrevented
  58
+  // state of the event and will not generate synthetic mouse events
  59
+  window.addEventListener('touchstart', handleTouchStart);
  60
+  window.addEventListener('touchmove', handleTouchMove);
  61
+  window.addEventListener('touchend', handleTouchEnd);
  62
+  window.addEventListener('touchcancel', handleTouchEnd); // Same as touchend
  63
+
  64
+  function handleTouchStart(e) {
  65
+    // If we're already handling a touch, ignore this one
  66
+    if (starttouch)
  67
+      return;
  68
+
  69
+    // Ignore any event that has already been prevented
  70
+    if (e.defaultPrevented)
  71
+      return;
  72
+
  73
+    // Sometimes an unknown gecko bug causes us to get a touchstart event
  74
+    // for an iframe target that we can't use because it is cross origin.
  75
+    // Don't start handling a touch in that case
  76
+    try {
  77
+      e.changedTouches[0].target.ownerDocument;
  78
+    }
  79
+    catch (e) {
  80
+      // Ignore the event if we can't see the properties of the target
  81
+      return;
  82
+    }
  83
+
  84
+    // If there is more than one simultaneous touch, ignore all but the first
  85
+    starttouch = e.changedTouches[0];
  86
+    target = starttouch.target;
  87
+    emitclick = true;
  88
+
  89
+    // Move to the position of the touch
  90
+    emitEvent('mousemove', target, starttouch);
  91
+
  92
+    // Now send a synthetic mousedown
  93
+    var result = emitEvent('mousedown', target, starttouch);
  94
+
  95
+    // If the mousedown was prevented, pass that on to the touch event.
  96
+    // And remember not to send a click event
  97
+    if (!result) {
  98
+      e.preventDefault();
  99
+      emitclick = false;
  100
+    }
  101
+  }
  102
+
  103
+  function handleTouchEnd(e) {
  104
+    if (!starttouch)
  105
+      return;
  106
+
  107
+    // End a MouseEventShim.setCapture() call
  108
+    if (MouseEventShim.capturing) {
  109
+      MouseEventShim.capturing = false;
  110
+      MouseEventShim.captureTarget = null;
  111
+    }
  112
+
  113
+    for (var i = 0; i < e.changedTouches.length; i++) {
  114
+      var touch = e.changedTouches[i];
  115
+      // If the ended touch does not have the same id, skip it
  116
+      if (touch.identifier !== starttouch.identifier)
  117
+        continue;
  118
+
  119
+      emitEvent('mouseup', target, touch);
  120
+
  121
+      // If target is still the same element we started and the touch did not
  122
+      // move more than the threshold and if the user did not prevent
  123
+      // the mousedown, then send a click event, too.
  124
+      if (emitclick)
  125
+        emitEvent('click', starttouch.target, touch);
  126
+
  127
+      starttouch = null;
  128
+      return;
  129
+    }
  130
+  }
  131
+
  132
+  function handleTouchMove(e) {
  133
+    if (!starttouch)
  134
+      return;
  135
+
  136
+    for (var i = 0; i < e.changedTouches.length; i++) {
  137
+      var touch = e.changedTouches[i];
  138
+      // If the ended touch does not have the same id, skip it
  139
+      if (touch.identifier !== starttouch.identifier)
  140
+        continue;
  141
+
  142
+      // Don't send a mousemove if the touchmove was prevented
  143
+      if (e.defaultPrevented)
  144
+        return;
  145
+
  146
+      // See if we've moved too much to emit a click event
  147
+      var dx = Math.abs(touch.screenX - starttouch.screenX);
  148
+      var dy = Math.abs(touch.screenY - starttouch.screenY);
  149
+      if (dx > MouseEventShim.dragThresholdX ||
  150
+          dy > MouseEventShim.dragThresholdY) {
  151
+        emitclick = false;
  152
+      }
  153
+
  154
+      var tracking = MouseEventShim.trackMouseMoves &&
  155
+        !MouseEventShim.capturing;
  156
+
  157
+      if (tracking) {
  158
+        // If the touch point moves, then the element it is over
  159
+        // may have changed as well. Note that calling elementFromPoint()
  160
+        // forces a layout if one is needed.
  161
+        // XXX: how expensive is it to do this on each touchmove?
  162
+        // Can we listen for (non-standard) touchleave events instead?
  163
+        var oldtarget = target;
  164
+        var newtarget = document.elementFromPoint(touch.clientX, touch.clientY);
  165
+        if (newtarget === null) {
  166
+          // this can happen as the touch is moving off of the screen, e.g.
  167
+          newtarget = oldtarget;
  168
+        }
  169
+        if (newtarget !== oldtarget) {
  170
+          leave(oldtarget, newtarget, touch); // mouseout, mouseleave
  171
+          target = newtarget;
  172
+        }
  173
+      }
  174
+      else if (MouseEventShim.captureTarget) {
  175
+        target = MouseEventShim.captureTarget;
  176
+      }
  177
+
  178
+      emitEvent('mousemove', target, touch);
  179
+
  180
+      if (tracking && newtarget !== oldtarget) {
  181
+        enter(newtarget, oldtarget, touch);  // mouseover, mouseenter
  182
+      }
  183
+    }
  184
+  }
  185
+
  186
+  // Return true if element a contains element b
  187
+  function contains(a, b) {
  188
+    return (a.compareDocumentPosition(b) & 16) !== 0;
  189
+  }
  190
+
  191
+  // A touch has left oldtarget and entered newtarget
  192
+  // Send out all the events that are required
  193
+  function leave(oldtarget, newtarget, touch) {
  194
+    emitEvent('mouseout', oldtarget, touch, newtarget);
  195
+
  196
+    // If the touch has actually left oldtarget (and has not just moved
  197
+    // into a child of oldtarget) send a mouseleave event. mouseleave
  198
+    // events don't bubble, so we have to repeat this up the hierarchy.
  199
+    for (var e = oldtarget; !contains(e, newtarget); e = e.parentNode) {
  200
+      emitEvent('mouseleave', e, touch, newtarget);
  201
+    }
  202
+  }
  203
+
  204
+  // A touch has entered newtarget from oldtarget
  205
+  // Send out all the events that are required.
  206
+  function enter(newtarget, oldtarget, touch) {
  207
+    emitEvent('mouseover', newtarget, touch, oldtarget);
  208
+
  209
+    // Emit non-bubbling mouseenter events if the touch actually entered
  210
+    // newtarget and wasn't already in some child of it
  211
+    for (var e = newtarget; !contains(e, oldtarget); e = e.parentNode) {
  212
+      emitEvent('mouseenter', e, touch, oldtarget);
  213
+    }
  214
+  }
  215
+
  216
+  function emitEvent(type, target, touch, relatedTarget) {
  217
+    var synthetic = document.createEvent('MouseEvents');
  218
+    var bubbles = (type !== 'mouseenter' && type !== 'mouseleave');
  219
+    var count =
  220
+      (type === 'mousedown' || type === 'mouseup' || type === 'click') ? 1 : 0;
  221
+
  222
+    synthetic.initMouseEvent(type,
  223
+                             bubbles,     // canBubble
  224
+                             true,        // cancelable
  225
+                             window,
  226
+                             count,       // detail: click count
  227
+                             touch.screenX,
  228
+                             touch.screenY,
  229
+                             touch.clientX,
  230
+                             touch.clientY,
  231
+                             false,       // ctrlKey: we don't have one
  232
+                             false,       // altKey: we don't have one
  233
+                             false,       // shiftKey: we don't have one
  234
+                             false,       // metaKey: we don't have one
  235
+                             0,           // we're simulating the left button
  236
+                             relatedTarget || null);
  237
+
  238
+    try {
  239
+      return target.dispatchEvent(synthetic);
  240
+    }
  241
+    catch (e) {
  242
+      console.warn('Exception calling dispatchEvent', type, e);
  243
+      return true;
  244
+    }
  245
+  }
  246
+}());
  247
+
  248
+var MouseEventShim = {
  249
+  // It is a known gecko bug that synthetic events have timestamps measured
  250
+  // in microseconds while regular events have timestamps measured in
  251
+  // milliseconds. This utility function returns a the timestamp converted
  252
+  // to milliseconds, if necessary.
  253
+  getEventTimestamp: function(e) {
  254
+    if (e.isTrusted)             // XXX: Are real events always trusted?
  255
+      return e.timeStamp;
  256
+    else
  257
+      return e.timeStamp / 1000;
  258
+  },
  259
+
  260
+  // Set this to false if you don't care about mouseover/out events
  261
+  // and don't want the target of mousemove events to follow the touch
  262
+  trackMouseMoves: true,
  263
+
  264
+  // Call this function from a mousedown event handler if you want to guarantee
  265
+  // that the mousemove and mouseup events will go to the same element
  266
+  // as the mousedown even if they leave the bounds of the element. This is
  267
+  // like setting trackMouseMoves to false for just one drag. It is a
  268
+  // substitute for event.target.setCapture(true)
  269
+  setCapture: function(target) {
  270
+    this.capturing = true;  // Will be set back to false on mouseup
  271
+    if (target)
  272
+      this.captureTarget = target;
  273
+  },
  274
+
  275
+  capturing: false,
  276
+
  277
+  // Keep these in sync with ui.dragThresholdX and ui.dragThresholdY prefs.
  278
+  // If a touch ever moves more than this many pixels from its starting point
  279
+  // then we will not synthesize a click event when the touch ends.
  280
+  dragThresholdX: 25,
  281
+  dragThresholdY: 25
  282
+};

0 notes on commit b506bdb

Please sign in to comment.
Something went wrong with that request. Please try again.