Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Experimenting with custom selection/deselection events.

  • Loading branch information...
commit 4cc92374353d8088b7b8a2e428df4e752e11c7ad 1 parent 93b6d0c
Joseph Pearson authored January 30, 2012
17  test/experimental/selection/index.html
@@ -23,7 +23,7 @@
23 23
         padding: 6px;
24 24
         visibility: hidden;
25 25
         height: 5em;
26  
-        overflow: scroll;
  26
+        overflow: auto;
27 27
       }
28 28
     </style>
29 29
     <script src="test.js"></script>
@@ -50,9 +50,15 @@
50 50
     <hr />
51 51
 
52 52
     <p>
53  
-      Monocle needs a set of events for "some text has been selected",
54  
-      "the selection has changed", and "selection deselected". This is so
55  
-      we can build contextual menus for these cases.
  53
+      Monocle needs custom events for "text has been selected" and
  54
+      "text has been deselected". (These events could form the basis of
  55
+      annotations or selection context menus, perhaps.)
  56
+    </p>
  57
+
  58
+    <p>
  59
+      Custom events are working here if the yellow box appears and displays
  60
+      selected text from the iframe, then disappears when text in the iframe is
  61
+      deselected.
56 62
     </p>
57 63
 
58 64
     <p>
@@ -65,7 +71,8 @@
65 71
     <p>
66 72
       The <strong>Remove Selection</strong> button is wired to
67 73
       take effect on a tap or a swipe, on touch-based and cursor-based
68  
-      browsers.
  74
+      browsers. The best test is to drag from inside to outside the button
  75
+      &mdash; text in the frame should be deselected.
69 76
     </p>
70 77
 
71 78
   </body>
71  test/experimental/selection/test.js
... ...
@@ -1,31 +1,80 @@
1 1
 (function () {
2 2
 
3 3
   var SELECTION_POLLING_INTERVAL = 250;
  4
+  var lastSelection = {};
4 5
 
5 6
 
6 7
   function init() {
7  
-    setInterval(pollSelection, SELECTION_POLLING_INTERVAL);
  8
+    if (SELECTION_POLLING_INTERVAL) {
  9
+      setInterval(pollSelection, SELECTION_POLLING_INTERVAL);
  10
+    }
8 11
     var btn = document.getElementById('deselectBtn');
9 12
     btn.addEventListener('mousedown', deselectAction, false);
10 13
     btn.addEventListener('touchstart', deselectAction, false);
  14
+    var frame = document.getElementById('reader');
  15
+    frame.addEventListener('experimental:selection', textSelected, false);
  16
+    frame.addEventListener('experimental:deselection', textDeselected, false);
11 17
   }
12 18
 
13 19
 
14 20
   function pollSelection() {
15  
-    var st = document.getElementById('statusSelect');
16 21
     var frame = document.getElementById('reader');
17  
-    var sel = frame.contentWindow.getSelection().toString();
18  
-    if (sel) {
19  
-      if (sel != st.innerHTML) {
20  
-        st.innerHTML = sel;
21  
-        st.style.visibility = 'visible';
  22
+    var sel = frame.contentWindow.getSelection();
  23
+    var lm = lastSelection;
  24
+    var nm = lastSelection = {
  25
+      range: sel.rangeCount ? sel.getRangeAt(0) : null,
  26
+      string: sel.toString()
  27
+    };
  28
+    if (nm.range && nm.string) {
  29
+      // Gecko keeps returning the same range object as the selection changes,
  30
+      // so we have to store and compare start and end points directly.
  31
+      nm.rangeStartContainer = nm.range.startContainer;
  32
+      nm.rangeEndContainer = nm.range.endContainer;
  33
+      nm.rangeStartOffset = nm.range.startOffset;
  34
+      nm.rangeEndOffset = nm.range.endOffset;
  35
+      if (!sameRange(nm, lm)) {
  36
+        dispatchEvent(frame, 'experimental:selection', nm);
22 37
       }
23  
-    } else {
24  
-      st.style.visibility = 'hidden';
  38
+    } else if (lm.string) {
  39
+      dispatchEvent(frame, 'experimental:deselection', lm);
25 40
     }
26 41
   }
27 42
 
28 43
 
  44
+  function sameRange(m1, m2) {
  45
+    return (
  46
+      m1.rangeStartContainer == m2.rangeStartContainer &&
  47
+      m1.rangeEndContainer == m2.rangeEndContainer &&
  48
+      m1.rangeStartOffset == m2.rangeStartOffset &&
  49
+      m1.rangeEndOffset == m2.rangeEndOffset
  50
+    );
  51
+  }
  52
+
  53
+
  54
+  function dispatchEvent(elem, evtType, data) {
  55
+    var evt = document.createEvent("Events");
  56
+    evt.initEvent(evtType, false, false);
  57
+    evt.m = data;
  58
+    return elem.dispatchEvent(evt);
  59
+  }
  60
+
  61
+
  62
+  function textSelected(evt) {
  63
+    console.log("Selection: "+evt.m.string);
  64
+    var st = document.getElementById('statusSelect');
  65
+    var sel = evt.m.string;
  66
+    st.innerHTML = sel;
  67
+    st.style.visibility = 'visible';
  68
+  }
  69
+
  70
+
  71
+  function textDeselected(evt) {
  72
+    console.log("Deselection.");
  73
+    var st = document.getElementById('statusSelect');
  74
+    st.style.visibility = 'hidden';
  75
+  }
  76
+
  77
+
29 78
   function deselectAction(evt) {
30 79
     evt.preventDefault();
31 80
     deselect();
@@ -99,14 +148,14 @@
99 148
 
100 149
     if (ovp) {
101 150
       var ovpcontent = ovp.getAttribute('content');
102  
-      var re = /user-scalable\s*=\s*([^,$])*/;
  151
+      var re = /user-scalable\s*=\s*([^,$\s])*/;
103 152
       var result = ovpcontent.match(re);
104 153
       if (result && ['no', '0'].indexOf(result[1]) >= 0) {
105 154
         console.log("Existing viewport is already non-scalable.");
106 155
         fn();
107 156
       } else {
108 157
         console.log("Replacing existing viewport with non-scalable one.");
109  
-        var nvpcontent = ovpcontent.replace(/user-scalable\s*=\s*[^,$]*/, '');
  158
+        var nvpcontent = ovpcontent.replace(re, '');
110 159
         nvpcontent += nvpcontent ? ', ' : '';
111 160
         nvpcontent += 'user-scalable=no';
112 161
         head.removeChild(ovp);

0 notes on commit 4cc9237

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