Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Experimenting with a basic state machine for detecting drags, double-…

…taps, etc.

git-svn-id: https://modestmaps.googlecode.com/svn/trunk/js@899 a23cadb0-8de1-11de-82e5-a1837a67dc72
  • Loading branch information...
commit f5e7f7d82a216bd974b920d0358a1ef9f3f7ca18 1 parent 2deb098
migurski authored
229 examples/touch/index.html
@@ -4,22 +4,248 @@
4 4 <meta name="apple-mobile-web-app-status-bar-style" content="black" />
5 5 <meta name="apple-mobile-web-app-capable" content="yes" />
6 6 <title>Modest Maps JS - Touch Tester</title>
  7 +<script type="text/javascript" src="underscore-min.js"></script>
7 8 <script type="text/javascript" src="../../modestmaps.js"></script>
8 9 <script type="text/javascript">
9 10
  11 +function stderr(msg)
  12 +{
  13 + req = new XMLHttpRequest();
  14 + req.open('POST', 'trace.php', false);
  15 + req.send(msg);
  16 +}
  17 +
10 18 (function(MM){
11 19
  20 + function distance(t1, t2)
  21 + {
  22 + return Math.sqrt(Math.pow(t1.screenX - t2.screenX, 2) + Math.pow(t1.screenY - t2.screenY, 2));
  23 + }
  24 +
  25 + function Start(touch, time)
  26 + {
  27 + this.screenX = touch.screenX;
  28 + this.screenY = touch.screenY;
  29 +
  30 + this.touch = touch;
  31 + this.time = time;
  32 + }
  33 +
  34 + function End(touch, time)
  35 + {
  36 + this.screenX = touch.screenX;
  37 + this.screenY = touch.screenY;
  38 +
  39 + this.touch = touch;
  40 + this.time = time;
  41 + }
  42 +
  43 + function Tap(x, y, time)
  44 + {
  45 + this.x = x;
  46 + this.y = y;
  47 + this.time = time;
  48 + }
  49 +
  50 + function Hold(x, y, end, duration)
  51 + {
  52 + this.x = x;
  53 + this.y = y;
  54 + this.end = end;
  55 + this.duration = duration;
  56 + }
  57 +
  58 + function Drag(x1, y1, x2, y2, end, duration)
  59 + {
  60 + this.x1 = x1;
  61 + this.y1 = y1;
  62 + this.x2 = x2;
  63 + this.y2 = y2;
  64 + this.end = end;
  65 + this.duration = duration;
  66 + }
  67 +
  68 + function hasattr(obj, attr)
  69 + {
  70 + return _.any(_.keys(obj), function(key) { return key == attr });
  71 + }
  72 +
  73 + function sameTouch(state, touch)
  74 + {
  75 + if(typeof state == 'object' && hasattr(state, 'touch')) {
  76 + if(state.constructor == Start || state.constructor == End) {
  77 + return touch == state.touch;
  78 + }
  79 + }
  80 + }
  81 +
12 82 MM.TouchHandler = function() { }
13 83
14 84 MM.TouchHandler.prototype = {
15 85
16 86 init: function(map) {
17 87 this.map = map;
  88 + /*
18 89 MM.addEvent(map.parent, 'touchstart', this.getDoubleTap());
19 90 MM.addEvent(map.parent, 'touchstart', this.getTouchStart());
20 91 MM.addEvent(map.parent, 'gesturestart', this.getGestureStart());
  92 + */
  93 +
  94 + MM.addEvent(map.parent, 'touchstart', this.getTouchStartLog());
  95 + MM.addEvent(map.parent, 'touchmove', this.getTouchMoveLog());
  96 + //MM.addEvent(map.parent, 'touchend', this.getTouchEndLog());
  97 +
  98 + MM.addEvent(map.parent, 'touchstart', this.getTouchStartMachine());
  99 + MM.addEvent(map.parent, 'touchmove', this.getTouchMoveMachine());
  100 + MM.addEvent(map.parent, 'touchend', this.getTouchEndMachine());
  101 + },
  102 +
  103 + getTouchStartLogHandler: null,
  104 +
  105 + getTouchStartLog: function() {
  106 + if (!this.getTouchStartLogHandler) {
  107 + var theHandler = this;
  108 + this.getTouchStartLogHandler = function(e)
  109 + {
  110 + points = _.map(e.touches, function(t) { return '(' + t.screenX + ', ' + t.screenY + ')'; }).join(', ');
  111 + stderr('touch start: ' + e.changedTouches.length + '/' + e.touches.length + ', ' + points);
  112 + }
  113 + }
  114 + return this.getTouchStartLogHandler;
  115 + },
  116 +
  117 + getTouchStartLogHandler: null,
  118 +
  119 + getTouchMoveLog: function() {
  120 + if (!this.getTouchMoveLogHandler) {
  121 + var theHandler = this;
  122 + this.getTouchMoveLogHandler = function(e)
  123 + {
  124 + //stderr('touch move');
  125 + }
  126 + }
  127 + return this.getTouchMoveLogHandler;
  128 + },
  129 +
  130 + getTouchEndLogHandler: null,
  131 +
  132 + getTouchEndLog: function() {
  133 + if (!this.getTouchEndLogHandler) {
  134 + var theHandler = this;
  135 + this.getTouchEndLogHandler = function(e)
  136 + {
  137 + points = _.map(e.touches, function(t) { return '(' + t.screenX + ', ' + t.screenY + ')'; }).join(', ');
  138 + stderr('touch end, all: ' + e.changedTouches.length + '/' + e.touches.length + ', ' + points);
  139 + }
  140 + }
  141 + return this.getTouchEndLogHandler;
21 142 },
  143 +
  144 + maxTapTime: 150,
  145 + maxTapDistance: 10,
  146 + maxDoubleTapDelay: 150,
  147 + states: [],
  148 +
  149 + getTouchStartMachineHandler: null,
  150 +
  151 + getTouchStartMachine: function() {
  152 + var states = this.states;
  153 +
  154 + if (!this.getTouchStartMachineHandler) {
  155 + var theHandler = this;
  156 + this.getTouchStartMachineHandler = function(e)
  157 + {
  158 + var time = new Date().getTime();
  159 + _.each(e.changedTouches, function(touch) { states.push(new Start(touch, time)); });
  160 + }
  161 + }
  162 + return this.getTouchStartMachineHandler;
  163 + },
  164 +
  165 + getTouchStartMachineHandler: null,
  166 +
  167 + getTouchMoveMachine: function() {
  168 + if (!this.getTouchMoveMachineHandler) {
  169 + var theHandler = this;
  170 + this.getTouchMoveMachineHandler = function(e)
  171 + {
  172 + }
  173 + }
  174 + return this.getTouchMoveMachineHandler;
  175 + },
  176 +
  177 + getTouchEndMachineHandler: null,
  178 +
  179 + getTouchEndMachine: function() {
  180 + if (!this.getTouchEndMachineHandler) {
  181 + var theHandler = this;
  182 + this.getTouchEndMachineHandler = function(e)
  183 + {
  184 + var states = theHandler.states;
  185 + var now = new Date().getTime();
22 186
  187 + // look at each changed touch in turn.
  188 + for(var i = 0; i < e.changedTouches.length; i += 1)
  189 + {
  190 + var touch = e.changedTouches[i];
  191 +
  192 + for(var j = 0; j < states.length; j += 1)
  193 + {
  194 + var state = states[j];
  195 +
  196 + if(sameTouch(state, touch))
  197 + {
  198 + states.splice(j, 1);
  199 + j -= 1;
  200 +
  201 + // we now know we have a Start or End object and
  202 + // a matching touch that's just ended. Let's see
  203 + // what kind of event it is based on how long it
  204 + // lasted and how far it moved.
  205 +
  206 + var time = now - state.time;
  207 + var dist = distance(state, touch);
  208 +
  209 + if(dist > theHandler.maxTapDistance) {
  210 + var drag = new Drag(state.screenX, state.screenY, touch.screenX, touch.screenY, now, time);
  211 + theHandler.onDrag(drag);
  212 +
  213 + } else if(time > theHandler.maxTapTime) {
  214 + // close in time, but not in space: a hold
  215 + var hold = new Hold(touch.screenX, touch.screenY, now, time);
  216 + theHandler.onHold(hold);
  217 +
  218 + } else {
  219 + // close in both time and space: a tap
  220 + var tap = new Tap(touch.screenX, touch.screenY, now);
  221 + theHandler.onTap(tap);
  222 + }
  223 + }
  224 + }
  225 + }
  226 + }
  227 + }
  228 + return this.getTouchEndMachineHandler;
  229 + },
  230 +
  231 + onDrag: function(drag)
  232 + {
  233 + stderr('Drag: (' + drag.x1 + ', ' + drag.y1 + ') to (' + drag.x2 + ', ' + drag.y2 + ') for ' + drag.duration + ' msec');
  234 + },
  235 +
  236 + onHold: function(hold)
  237 + {
  238 + stderr('Hold: (' + hold.x + ', ' + hold.y + ') for ' + hold.duration + ' msec');
  239 + },
  240 +
  241 + onTap: function(tap)
  242 + {
  243 + stderr('Tap: (' + tap.x + ', ' + tap.y + ')');
  244 + }
  245 +
  246 +
  247 +
  248 + /*
23 249 gestureStart: null,
24 250 gestureChange: null,
25 251 gestureCenter: null,
@@ -178,6 +404,7 @@
178 404 }
179 405 return this.doubleTapHandler;
180 406 }
  407 + */
181 408
182 409 };
183 410
@@ -199,6 +426,8 @@
199 426 window.onresize = function() {
200 427 map.setSize(container.offsetWidth, container.offsetHeight);
201 428 };
  429 +
  430 + stderr('------------------------------------------------------------------------');
202 431 }
203 432
204 433 </script>
10 examples/touch/trace.php
... ... @@ -0,0 +1,10 @@
  1 +<?php
  2 +
  3 + if(($in = fopen('php://input', 'r')) && ($out = fopen('what.txt', 'a')))
  4 + {
  5 + fwrite($out, fread($in, 1024)."\n");
  6 + fclose($in);
  7 + fclose($out);
  8 + }
  9 +
  10 +?>
17 examples/touch/underscore-min.js
... ... @@ -0,0 +1,17 @@
  1 +(function(){var n=this,A=n._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",B=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},j=Array.prototype,l=Object.prototype,o=j.slice,C=j.unshift,D=l.toString,p=l.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,m=j.indexOf,z=j.lastIndexOf;l=Array.isArray;var E=Object.keys,b=function(a){return new k(a)};if(typeof exports!=="undefined")exports._=b;n._=b;b.VERSION="1.0.2";var i=b.forEach=
  2 +function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=0,f=a.length;e<f;e++)c.call(d,a[e],e,a);else for(e in a)p.call(a,e)&&c.call(d,a[e],e,a)}catch(g){if(g!=r)throw g;}return a};b.map=function(a,c,d){if(t&&a.map===t)return a.map(c,d);var e=[];i(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(u&&a.reduce===u)return a.reduce(b.bind(d,e),c);i(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(v&&
  3 +a.reduceRight===v)return a.reduceRight(b.bind(d,e),c);a=b.clone(b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.detect=function(a,c,d){var e;i(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.filter=function(a,c,d){if(w&&a.filter===w)return a.filter(c,d);var e=[];i(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];i(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.every=function(a,c,d){c=c||b.identity;if(x&&a.every===x)return a.every(c,
  4 +d);var e=true;i(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.some=function(a,c,d){c=c||b.identity;if(y&&a.some===y)return a.some(c,d);var e=false;i(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c)!=-1;var d=false;i(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=function(a,c){return b.map(a,
  5 +function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(e,f,g){return{value:e,
  6 +criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return o.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?o.call(a,0,c):a[0]};b.rest=function(a,
  7 +c,d){return o.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.filter(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,e)))d.push(e);return d})};
  8 +b.intersect=function(a){var c=b.rest(arguments);return b.filter(b.uniq(a),function(d){return b.every(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(z&&a.lastIndexOf===z)return a.lastIndexOf(c);for(var d=a.length;d--;)if(a[d]===
  9 +c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||{},d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);i(c,function(d){a[d]=b.bind(a[d],a)});return a};b.delay=function(a,
  10 +c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=E||function(a){if(b.isArray(a))return b.range(0,a.length);var c=[];for(var d in a)p.call(a,
  11 +d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){i(b.rest(arguments),function(c){for(var d in c)a[d]=c[d]});return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
  12 +if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a))return a.length===
  13 +0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=l||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return a&&a.callee};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||D.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&
  14 +a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){n._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e=0;e<a;e++)c.call(d,e)};b.breakLoop=function(){throw r;};b.mixin=function(a){i(b.functions(a),function(c){F(c,b[c]=a[c])})};
  15 +var G=0;b.uniqueId=function(a){var c=G++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+B(d.end)+")","g");a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(e,"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+
  16 +"');}return p.join('');");return c?a(c):a};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain():a},F=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments);C.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];
  17 +k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain=true;return this};k.prototype.value=function(){return this._wrapped}})();

0 comments on commit f5e7f7d

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