diff options
Diffstat (limited to 'misc/openlayers/tests/Handler/Click.html')
-rw-r--r-- | misc/openlayers/tests/Handler/Click.html | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/misc/openlayers/tests/Handler/Click.html b/misc/openlayers/tests/Handler/Click.html new file mode 100644 index 0000000..be508ca --- /dev/null +++ b/misc/openlayers/tests/Handler/Click.html @@ -0,0 +1,735 @@ +<html> +<head> + <script src="../OLLoader.js"></script> + <script type="text/javascript"> + + function px(x, y) { + return new OpenLayers.Pixel(x, y); + } + + function test_Handler_Click_constructor(t) { + t.plan(3); + var control = new OpenLayers.Control(); + control.id = Math.random(); + var callbacks = {foo: "bar"}; + var options = {bar: "foo"}; + + var oldInit = OpenLayers.Handler.prototype.initialize; + + OpenLayers.Handler.prototype.initialize = function(con, call, opt) { + t.eq(con.id, control.id, + "constructor calls parent with the correct control"); + t.eq(call, callbacks, + "constructor calls parent with the correct callbacks"); + t.eq(opt, options, + "constructor calls parent with the correct options"); + } + var handler = new OpenLayers.Handler.Click(control, callbacks, options); + + OpenLayers.Handler.prototype.initialize = oldInit; + } + + function test_Handler_Click_activate(t) { + t.plan(2); + var control = { + map: new OpenLayers.Map('map') + }; + var handler = new OpenLayers.Handler.Click(control); + handler.active = true; + var activated = handler.activate(); + t.ok(!activated, + "activate returns false if the handler was already active"); + handler.active = false; + handler.dragging = true; + activated = handler.activate(); + t.ok(activated, + "activate returns true if the handler was not already active"); + + } + + function test_Handler_Click_events(t) { + t.plan(80); + + var map = new OpenLayers.Map('map'); + var control = { + map: map + }; + map.events.registerPriority = function(type, obj, func) { + var f = OpenLayers.Function.bind(func, obj) + var r = f({xy:null}); + if(typeof r == "string") { + // this is one of the mock handler methods + t.eq(OpenLayers.Util.indexOf(nonevents, type), -1, + "registered method is not one of the events " + + "that should not be handled"); + t.ok(OpenLayers.Util.indexOf(events, type) > -1, + "activate calls registerPriority with browser event: " + type); + t.eq(typeof func, "function", + "activate calls registerPriority with a function"); + t.eq(func(), type, + "activate calls registerPriority with the correct method"); + t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Click", + "activate calls registerPriority with the handler"); + } + } + function setMethod(key) { + handler[key] = function() {return key}; + } + + // list below events that should be handled (events) and those + // that should not be handled (nonevents) by the handler + var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"]; + var nonevents = ["mousemove", "resize", "focus", "blur"]; + var handler = new OpenLayers.Handler.Click(control); + // set browser event like properties on the handler + for(var i=0; i<events.length; ++i) { + setMethod(events[i]); + } + handler.activate(); + + // different listeners registered for pixelTolerance option + var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"]; + var nonevents = ["mousemove", "resize", "focus", "blur"]; + var handler = new OpenLayers.Handler.Click(control, {}, { + pixelTolerance: 2 + }); + for(var i=0; i<events.length; ++i) { + setMethod(events[i]); + } + handler.activate(); + + } + + var callbackMap; + function callbackSetup(log, options) { + callbackMap = new OpenLayers.Map({ + div: "map", + controls: [], // no controls here because these tests use a custom setTimeout and we only want setTimeout calls from a single handler + layers: [new OpenLayers.Layer(null, {isBaseLayer: true})], + center: new OpenLayers.LonLat(0, 0), + zoom: 1 + }); + var control = new OpenLayers.Control(); + callbackMap.addControl(control); + + var callbacks = { + "click": function(evt) { + log.push({callback: "click", evt: evt}); + }, + "dblclick": function(evt) { + log.push({callback: "dblclick", evt: evt}); + } + }; + var handler = new OpenLayers.Handler.Click(control, callbacks, options); + handler.activate(); + + + var timers = {}; + window._setTimeout = window.setTimeout; + window.setTimeout = function(func, delay) { + log.push({method: "setTimeout", func: func, delay: delay}); + var key = (new Date).getTime() + "-" + Math.random(); + timers[key] = true; + // execute function that is supposed to be delayed + func(); + return key; + } + window._clearTimeout = window.clearTimeout; + window.clearTimeout = function(key) { + log.push({ + method: "clearTimeout", + keyExists: (key in timers) + }); + delete timers[key]; + } + return handler; + } + + function callbackTeardown() { + window.setTimeout = window._setTimeout; + window.clearTimeout = window._clearTimeout; + callbackMap.destroy(); + callbackMap = null; + } + + function test_callbacks_click_default(t) { + t.plan(6); + + var log = []; + var handler = callbackSetup(log); + + // set up for single click - three tests here + var testEvt = {id: Math.random()}; + handler.map.events.triggerEvent("click", testEvt); + t.eq(log.length, 2, "(click w/ single true) two items logged"); + + // first item logged is setTimeout call + t.eq(log[0].method, "setTimeout", "setTimeout called"); + t.eq(typeof log[0].func, "function", "setTimeout called with a function"); + t.eq(log[0].delay, handler.delay, "setTimeout called with proper delay"); + + // second item logged is from click callback + t.eq(log[1].callback, "click", "click callback called"); + t.eq(log[1].evt.id, testEvt.id, "got correct event"); + + callbackTeardown(); + } + + function test_callbacks_dblclick_default(t) { + t.plan(1); + + var log = []; + var handler = callbackSetup(log); + var testEvt = {id: Math.random()}; + handler.map.events.triggerEvent("dblclick", testEvt); + + t.eq(log.length, 0, "nothing happens by default with dblclick (double is false)"); + + callbackTeardown(); + + } + + function test_callbacks_dblclick_double(t) { + t.plan(3); + + var log = []; + var handler = callbackSetup(log, {"double": true}); + var testEvt = {id: Math.random()}; + handler.map.events.triggerEvent("dblclick", testEvt); + + t.eq(log.length, 1, "one item logged"); + t.eq(log[0].callback, "dblclick", "dblclick callback called") + t.eq(log[0].evt.id, testEvt.id, "dblclick callback called with event"); + + callbackTeardown(); + + } + + function test_callbacks_dblclick_sequence(t) { + t.plan(8); + + var log = []; + var handler = callbackSetup(log, {"double": true}); + var testEvt = {id: Math.random()}; + + // first click - set timer for next + handler.map.events.triggerEvent("click", testEvt); + t.ok(handler.timerId != null, "timer is set"); + log.pop(); // because the test setTimeout is synchronous we get the click callback immediately + t.eq(log.length, 1, "one item logged (after pop due to synchronous setTimeout call in our tests"); + t.eq(log[0].method, "setTimeout", "setTimeout called first"); + + // second click - timer cleared + handler.map.events.triggerEvent("click", testEvt); + t.ok(handler.timerId == null, "timer is cleared"); + t.eq(log.length, 2, "two items logged after second click"); + t.eq(log[1].method, "clearTimeout", "clearTimeout called second"); + + // dblclick event - callback called + handler.map.events.triggerEvent("dblclick", testEvt); + t.eq(log.length, 3, "three items logged"); + t.eq(log[2].callback, "dblclick", "dblclick callback called third"); + + callbackTeardown(); + + } + + function test_callbacks_within_pixelTolerance(t) { + t.plan(1); + + var log = []; + var handler = callbackSetup(log, {"double": true, pixelTolerance: 2}); + + var down = { + xy: px(0, 0) + }; + var up = { + xy: px(0, 1) + }; + + handler.map.events.triggerEvent("mousedown", down); + handler.map.events.triggerEvent("mouseup", up); + handler.map.events.triggerEvent("click", up); + + t.eq(log[log.length-1].callback, "click", "click callback called"); + + callbackTeardown(); + + } + + function test_callbacks_outside_pixelTolerance(t) { + t.plan(2); + + var log = []; + var handler = callbackSetup(log, {pixelTolerance: 2}); + + var down = { + xy: px(0, 0) + }; + var up = { + xy: px(2, 3) + }; + + handler.map.events.triggerEvent("mousedown", down); + t.ok(handler.down && handler.down.xy.equals(down.xy), "down position set"); + + handler.map.events.triggerEvent("mouseup", up); + handler.map.events.triggerEvent("click", up); + t.eq(log.length, 0, "nothing logged - event outside tolerance"); + + callbackTeardown(); + + } + + function test_callbacks_within_dblclickTolerance(t) { + t.plan(6); + + var log = []; + var handler = callbackSetup(log, {single: false, "double": true, dblclickTolerance: 8}); + + var first = { + xy: px(0, 0) + }; + var second = { + xy: px(0, 5) + }; + + handler.map.events.triggerEvent("mousedown", first); + handler.map.events.triggerEvent("mouseup", first); + handler.map.events.triggerEvent("click", first); + t.eq(log.length, 1, "one item logged"); + t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called"); + + handler.map.events.triggerEvent("mousedown", second); + handler.map.events.triggerEvent("mouseup", second); + handler.map.events.triggerEvent("click", second); + t.eq(log.length, 2, "two events logged"); + t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called"); + + handler.map.events.triggerEvent("dblclick", second); + t.eq(log.length, 3, "three items logged"); + t.eq(log[2] && log[2].callback, "dblclick", "dblclick callback called"); + + callbackTeardown(); + } + + function test_callbacks_outside_dblclickTolerance(t) { + t.plan(5); + + var log = []; + // default dblclickTolerance is 13 + var handler = callbackSetup(log, {single: false, "double": true}); + + var first = { + xy: px(0, 0) + }; + var second = { + xy: px(13.5, 0) + }; + + handler.map.events.triggerEvent("mousedown", first); + handler.map.events.triggerEvent("mouseup", first); + handler.map.events.triggerEvent("click", first); + t.eq(log.length, 1, "one item logged"); + t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called"); + + handler.map.events.triggerEvent("mousedown", second); + handler.map.events.triggerEvent("mouseup", second); + handler.map.events.triggerEvent("click", second); + t.eq(log.length, 2, "two items logged"); + t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called"); + + handler.map.events.triggerEvent("dblclick", second); + t.eq(log.length, 2, "still two items logged - dblclick callback is not called"); + + callbackTeardown(); + } + + function test_callbacks_multitouch_single(t) { + + t.plan(2); + + var log = []; + + var callbacks = { + click: function(evt) { + log.push({callback: "click", type: evt.type}); + }, + dblclick: function(evt) { + log.push({callback: "dblclick", type: evt.type}); + } + }; + + var map = new OpenLayers.Map("map"); + var layer = new OpenLayers.Layer(null, {isBaseLayer: true}); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 1); + var control = new OpenLayers.Control(); + map.addControl(control); + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {"double": true, single: true, pixelTolerance: 2} + ); + + // we override here so we don't have to wait for the timeout + handler.queuePotentialClick = function(evt) { + log.push({potential: true, evt: evt}); + OpenLayers.Handler.Click.prototype.queuePotentialClick.call(this, evt); + } + + handler.activate(); + + function handle(o) { + var touches = []; + if (("x0" in o) && ("y0" in o)) { + touches.push({ + clientX: o.x0, clientY: o.y0 + }); + } + if (("x1" in o) && ("y1" in o)) { + touches.push({ + clientX: o.x1, clientY: o.y1 + }); + } + handler.map.events.handleBrowserEvent({ + type: o.type, touches: touches + }); + } + + // a typical multitouch sequence goes like this: + // touchstart, touchstart, touchend, touchend + handle({type: "touchstart", x0: 10, y0: 10}); + handle({type: "touchstart", x0: 10, y0: 10, x1: 30, y1: 15}); + handle({type: "touchend"}); + handle({type: "touchend"}); + + t.eq(log.length, 1, "one item logged"); + t.eq(log[0] && log[0].potential, true, "click in queue - no dblclick called"); + + map.destroy(); + } + + function test_Handler_Click_deactivate(t) { + t.plan(6); + var control = { + map: new OpenLayers.Map('map') + }; + var handler = new OpenLayers.Handler.Click(control); + handler.active = false; + var deactivated = handler.deactivate(); + t.ok(!deactivated, + "deactivate returns false if the handler was not already active"); + handler.active = true; + handler.down = true; + handler.timerId = true; + handler.touch = true; + handler.last = true; + deactivated = handler.deactivate(); + t.ok(deactivated, + "deactivate returns true if the handler was active already"); + t.eq(handler.down, null, + "deactivate sets down to null"); + t.eq(handler.timerId, null, + "deactivate sets timerId to null"); + t.eq(handler.touch, false, + "deactivate sets touch to false"); + t.eq(handler.last, null, + "deactivate sets last to null"); + + } + + function test_Handler_Click_mouseup(t) { + t.plan(11); + + var map = new OpenLayers.Map("map"); + var control = new OpenLayers.Control(); + map.addControl(control); + var handler = new OpenLayers.Handler.Click(control); + + var testEvent = {id: Math.random()}; + var propagate = true; + var log, got, modMatch, rightClick; + + // override methods to log what is called + var temp = OpenLayers.Event.isRightClick; + OpenLayers.Event.isRightClick = function(e) { + log.push({method: "isRightClick", evt: e}); + return rightClick; + }; + handler.checkModifiers = function(e) { + log.push({method: "checkModifiers", evt: e}); + return modMatch; + }; + handler.rightclick = function(e) { + log.push({method: "rightclick", evt: e}); + return propagate; + }; + + + // simulate an event with non-matching modifiers + log = []; + modMatch = false; + rightClick = false; + got = handler.mouseup(testEvent); + t.eq(log.length, 1, "one item logged"); + t.eq(log[0] && log[0].method, "checkModifiers", "a) checkModifiers called first"); + t.eq(log[0] && log[0].evt, testEvent, "a) first method called with correct event"); + + + // modifiers, handlerightclicks, and isrightclick + log = []; + rightClick = true; + modMatch = true; + handler.control.handleRightClicks = true; + got = handler.mouseup(testEvent); + t.eq(log.length, 3, "three items logged"); + t.eq(log[0] && log[0].method, "checkModifiers", "b) checkModifiers called first"); + t.eq(log[0] && log[0].evt, testEvent, "b) first method called with correct event"); + t.eq(log[1] && log[1].method, "isRightClick", "b) isRightClick called second"); + t.eq(log[1] && log[1].evt, testEvent, "b) second method called with correct event"); + t.eq(log[2] && log[2].method, "rightclick", "b) rightclick called third"); + t.eq(log[2] && log[2].evt, testEvent, "b) third method called with correct event"); + t.eq(got, propagate, "b) return from handler's rightclick returned from mouseup"); + + OpenLayers.Event.isRightClick = temp; + map.destroy(); + } + + function test_touch_click(t) { + t.plan(5); + + // set up + + var log; + + var map = new OpenLayers.Map('map'); + var control = {map: map}; + + var callbacks = { + 'click': function(e) { + log = {x: e.xy.x, y: e.xy.y, + lastTouches: e.lastTouches}; + } + }; + + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {'single': true, pixelTolerance: null}); + + // test + + // the common case: a touchstart followed by a touchend + log = null; + handler.touchstart({xy: px(1, 1), touches: ["foo"]}); + handler.touchend({touches: ["foo"]}); + + t.delay_call(1, function() { + t.ok(log != null, "click callback called"); + if(log != null) { + t.eq(log.x, 1, "evt.xy.x as expected"); + t.eq(log.y, 1, "evt.xy.y as expected"); + t.ok(log.lastTouches, "evt.lastTouches as expected"); + } + + // now emulate a touch where touchstart doesn't propagate + // to the click handler, i.e. the click handler gets a + // touchend only + log = null; + handler.touchend({touches: ["foo"]}); + + t.delay_call(1, function() { + t.ok(log == null, "click callback not called"); + + // tear down + map.destroy(); + }); + }); + } + + function test_touch_within_dblclickTolerance(t) { + t.plan(4); + + var log; + + var callbacks = { + click: function(evt) { + log.push({callback: "click", type: evt.type}); + }, + dblclick: function(evt) { + log.push({callback: "dblclick", type: evt.type}); + } + }; + + var map = new OpenLayers.Map("map"); + var layer = new OpenLayers.Layer(null, {isBaseLayer: true}); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 1); + var control = new OpenLayers.Control(); + map.addControl(control); + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {"double": true, single: true, pixelTolerance: 2} + ); + handler.activate(); + + function handle(type, x, y) { + map.events.handleBrowserEvent({ + type: type, + touches: [ + {clientX: x, clientY: y} + ] + }); + } + + // test + log = []; + // sequence of two clicks on a touch device + // click 1 + handle("touchstart", 10, 10); + handle("touchend", 11, 10); + handle("mousemove", 11, 10); + handle("mousedown", 10, 10); + handle("mouseup", 11, 10); + handle("click", 11, 10); + // click 2 + handle("touchstart", 12, 10); + handle("touchend", 12, 10); + handle("mousedown", 12, 10); + handle("mouseup", 12, 10); + handle("click", 12, 10); + + t.eq(log.length, 1, "one callback called"); + t.eq(log[0] && log[0].callback, "dblclick", "click callback called"); + t.eq(log[0] && log[0].type, "touchend", "click callback called with touchend event"); + t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click") + + // tear down + map.destroy(); + } + + function test_touch_outside_dblclickTolerance(t) { + t.plan(2); + + var log; + + var callbacks = { + click: function(evt) { + log.push({callback: "click", type: evt.type}); + }, + dblclick: function(evt) { + log.push({callback: "dblclick", type: evt.type}); + } + }; + + var map = new OpenLayers.Map("map"); + var layer = new OpenLayers.Layer(null, {isBaseLayer: true}); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 1); + var control = new OpenLayers.Control(); + map.addControl(control); + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {"double": true, single: true, pixelTolerance: 2, dblclickTolerance: 8} + ); + handler.activate(); + + function handle(type, x, y) { + var touches = []; + if (x !== undefined && y !== undefined) { + touches.push({ + clientX: x, clientY: y + }); + } + map.events.handleBrowserEvent({ + type: type, touches: touches + }); + } + + // test + log = []; + // sequence of two clicks on a touch device + // click 1 + handle("touchstart", 10, 10); + handle("touchend"); + handle("mousemove", 11, 10); + handle("mousedown", 10, 10); + handle("mouseup", 11, 10); + handle("click", 11, 10); + // click 2 + handle("touchstart", 20, 10); + handle("touchend"); + handle("mousedown", 20, 10); + handle("mouseup", 20, 10); + handle("click", 20, 10); + + t.eq(log.length, 0, "no callbacks called"); + t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click") + + // tear down + map.destroy(); + } + + function test_touchstart(t) { + // a test to verify that the touchstart function does + // unregister the mouse listeners when it's called the + // first time + + t.plan(7); + + // set up + + var map = new OpenLayers.Map("map", { + controls: [] + }); + var control = new OpenLayers.Control({}); + var handler = new OpenLayers.Handler.Click(control, {}); + control.handler = handler; + map.addControl(control); + handler.activate(); + + function allRegistered() { + var eventTypes = ['mousedown', 'mouseup', 'click', 'dblclick'], + eventType, + listeners, + listener, + flag; + for(var i=0, ilen=eventTypes.length; i<ilen; i++) { + flag = false; + eventType = eventTypes[i]; + listeners = map.events.listeners[eventType]; + for(var j=0, jlen=listeners.length; j<jlen; j++) { + listener = listeners[j]; + if(listener.func === handler[eventType] && listener.obj === handler) { + flag = true; + break; + } + } + if(!flag) { + return false; + } + } + return true; + } + + // test + + t.ok(allRegistered(), 'mouse listeners are registered'); + handler.touchstart({xy: new OpenLayers.Pixel(0, 0)}); + t.eq(map.events.listeners.mousedown.length, 0,"mousedown is not registered"); + t.eq(map.events.listeners.mouseup.length, 0,"mouseup is not registered"); + t.eq(map.events.listeners.click.length, 0,"click is not registered"); + t.eq(map.events.listeners.dblclick.length, 0,"dblclick is not registered"); + + t.ok(handler.touch, 'handler.touch is set'); + + handler.deactivate(); + t.ok(!handler.touch, 'handler.touch is not set'); + + map.destroy(); + } + </script> +</head> +<body> + <div id="map" style="width: 300px; height: 150px;"/> +</body> +</html> |