diff --git a/Gruntfile.js b/Gruntfile.js index 9eff0078..c9a35b11 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,6 +9,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks("grunt-contrib-connect"); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks("grunt-contrib-clean"); + grunt.loadNpmTasks("grunt-git-describe"); // ---------- var distribution = "build/openseadragon/openseadragon.js", @@ -65,6 +66,7 @@ module.exports = function(grunt) { options: { banner: "//! <%= pkg.name %> <%= pkg.version %>\n" + "//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n" + + "//! Git commit: <%= gitInfo %>\n" + "//! http://openseadragon.github.com\n\n", process: true }, @@ -131,6 +133,13 @@ module.exports = function(grunt) { }, beforeconcat: sources, afterconcat: [ distribution ] + }, + "git-describe": { + build: { + options: { + prop: "gitInfo" + } + } } }); @@ -160,7 +169,7 @@ module.exports = function(grunt) { // Build task. // Cleans out the build folder and builds the code and images into it, checking lint. grunt.registerTask("build", [ - "clean:build", "jshint:beforeconcat", "concat", "jshint:afterconcat", "uglify", "copy:build" + "clean:build", "jshint:beforeconcat", "git-describe", "concat", "jshint:afterconcat", "uglify", "copy:build" ]); // ---------- diff --git a/README.md b/README.md index aaac7786..ae259f02 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,6 @@ This project is a fork of the OpenSeadragon project at http://openseadragon.code http://openseadragon.github.com/ -## We've upgraded to Grunt 0.4.0! Please follow these directions if you've built OpenSeadragon prior to Feb 22, 2013: - -If you're new here, you can skip down to the "First Time Setup" section. - -1. If you had already installed Grunt globally, you now need to uninstall it: `npm uninstall -g grunt` -1. Delete the `build` and `node_modules` folders in your openseadragon directory -1. On the command line, go in to the openseadragon folder -1. Run `npm install -g grunt-cli` (this will install the Grunt command line runner) -1. Run `npm install` - -For more information on the upgrade, see http://gruntjs.com/upgrading-from-0.3-to-0.4 - ## First Time Setup All command-line operations are scripted using [Grunt](http://gruntjs.com/) which is based on [Node.js](http://nodejs.org/). To get set up: diff --git a/package.json b/package.json index 71698851..49bcd9af 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "grunt-contrib-qunit": "~0.1.1", "grunt-contrib-connect": "~0.1.2", "grunt-contrib-watch": "~0.2.0", - "grunt-contrib-clean": "~0.4.0" + "grunt-contrib-clean": "~0.4.0", + "grunt-git-describe": "~2.0.0" }, "scripts": { "test": "grunt test" diff --git a/src/mousetracker.js b/src/mousetracker.js index 58d2e80e..2c677bec 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -176,8 +176,6 @@ * initiated inside the tracked element, otherwise false. * @param {Boolean} buttonDownAny * Was the button down anywhere in the screen during the event. - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ enterHandler: function(){}, @@ -194,8 +192,6 @@ * initiated inside the tracked element, otherwise false. * @param {Boolean} buttonDownAny * Was the button down anywhere in the screen during the event. - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ exitHandler: function(){}, @@ -207,8 +203,6 @@ * A reference to the tracker instance. * @param {OpenSeadragon.Point} position * The poistion of the event on the screen. - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ pressHandler: function(){}, @@ -226,8 +220,6 @@ * @param {Boolean} insideElementRelease * Was the mouse still inside the tracked element when the button * was released. - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ releaseHandler: function(){}, @@ -243,8 +235,6 @@ * The scroll delta for the event. * @param {Boolean} shift * Was the shift key being pressed during this event? - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ scrollHandler: function(){}, @@ -261,8 +251,6 @@ * both pased. Useful for ignoring events. * @param {Boolean} shift * Was the shift key being pressed during this event? - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ clickHandler: function(){}, @@ -279,8 +267,6 @@ * end drag. Usefule for ignoring or weighting the events. * @param {Boolean} shift * Was the shift key being pressed during this event? - * @param {Event} event - * The original mouse or wheel event that triggered this handler */ dragHandler: function(){}, @@ -294,8 +280,6 @@ * The key code that was pressed. * @param {Boolean} shift * Was the shift key being pressed during this event? - * @param {MouseEvent} event - * The original mouse event that triggered this handler */ keyHandler: function(){}, @@ -537,8 +521,7 @@ propagate = tracker.keyHandler( tracker, event.keyCode ? event.keyCode : event.charCode, - event.shiftKey, - event + event.shiftKey ); if( !propagate ){ $.cancelEvent( event ); @@ -585,8 +568,7 @@ tracker, getMouseRelative( event, tracker.element ), delegate.buttonDown, - IS_BUTTON_DOWN, - event + IS_BUTTON_DOWN ); if( propagate === false ){ $.cancelEvent( event ); @@ -633,8 +615,7 @@ tracker, getMouseRelative( event, tracker.element ), delegate.buttonDown, - IS_BUTTON_DOWN, - event + IS_BUTTON_DOWN ); if( propagate === false ){ @@ -667,8 +648,7 @@ if ( tracker.pressHandler ) { propagate = tracker.pressHandler( tracker, - getMouseRelative( event, tracker.element ), - event + getMouseRelative( event, tracker.element ) ); if( propagate === false ){ $.cancelEvent( event ); @@ -703,16 +683,7 @@ event.targetTouches.length == 1 && event.changedTouches.length == 1 ){ - THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; - -//DWK So onMouseOver and onMouseDown are both expecting an event for the second -//DWK parameter. This is passing a Touch point instead. Although a Touch -//DWK object is very similar to an event object that are not identical. -//DWK not sure if this is a problem. May need to include a comment in the -//DWK API notes, since this object as passed here is getting passed along. -//DWK Alternately, should there be some way to still pass the original -//DWK touch event along. - + THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; onMouseOver( tracker, event.changedTouches[ 0 ] ); onMouseDown( tracker, event.touches[ 0 ] ); } @@ -760,8 +731,7 @@ tracker, getMouseRelative( event, tracker.element ), insideElementPress, - insideElementRelease, - event + insideElementRelease ); if( propagate === false ){ $.cancelEvent( event ); @@ -785,7 +755,6 @@ event.changedTouches.length == 1 ){ THIS[ tracker.hash ].lastTouch = null; -//DWK See comment in onTouchStart onMouseUp( tracker, event.changedTouches[ 0 ] ); onMouseOut( tracker, event.changedTouches[ 0 ] ); } @@ -894,8 +863,7 @@ tracker, getMouseRelative( event, tracker.element ), nDelta, - event.shiftKey, - event + event.shiftKey ); if( propagate === false ){ $.cancelEvent( event ); @@ -929,8 +897,7 @@ tracker, getMouseRelative( event, tracker.element ), quick, - event.shiftKey, - event + event.shiftKey ); if( propagate === false ){ $.cancelEvent( event ); @@ -960,8 +927,7 @@ tracker, getMouseRelative( event, tracker.element ), delta, - event.shiftKey, - event + event.shiftKey ); if( propagate === false ){ $.cancelEvent( event ); @@ -983,7 +949,7 @@ event.targetTouches.length === 1 && event.changedTouches.length === 1 && THIS[ tracker.hash ].lastTouch.identifier === event.touches[ 0 ].identifier){ -//DWK See comment in onTouchStart + onMouseMove( tracker, event.touches[ 0 ] ); } else if ( event.touches.length === 2 ){ @@ -998,8 +964,6 @@ if( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ){ //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); -//DWK This is a variant on the onTouchStart comment, but instead of a Touch -//DWK object, a generic object with a handful of properties is passed onMouseWheelSpin( tracker, { shift: false, pageX: THIS[ tracker.hash ].pinchMidpoint.x, @@ -1096,4 +1060,4 @@ } })(); -}( OpenSeadragon )); +}( OpenSeadragon )); \ No newline at end of file diff --git a/test/basic.js b/test/basic.js index 5e99f6b7..ca4556c8 100644 --- a/test/basic.js +++ b/test/basic.js @@ -1,5 +1,8 @@ (function() { + // TODO: Tighten up springs and use "immediate" where possible, so tests run faster + // TODO: Test drag + var viewer = null; // ---------- @@ -17,7 +20,7 @@ var openHandler = function(eventSender, eventData) { viewer.removeHandler('open', openHandler); ok(true, 'Open event was sent'); - ok(eventSender === viewer, 'Sender of open event was viewer'); + equal(eventSender, viewer, 'Sender of open event was viewer'); ok(eventData, 'Handler also received event data'); ok(viewer.viewport, 'Viewport exists'); start(); @@ -30,11 +33,11 @@ // ---------- asyncTest('Zoom', function() { var viewport = viewer.viewport; - ok(viewport.getZoom() === 1, 'We start out unzoomed'); + equal(viewport.getZoom(), 1, 'We start out unzoomed'); var zoomHandler = function() { viewer.removeHandler('animationfinish', zoomHandler); - ok(viewport.getZoom() === 2, 'Zoomed correctly'); + equal(viewport.getZoom(), 2, 'Zoomed correctly'); start(); }; @@ -59,6 +62,44 @@ viewport.panTo(new OpenSeadragon.Point(0.1, 0.1)); }); + // ---------- + asyncTest('Home', function() { + var viewport = viewer.viewport; + var center = viewport.getCenter(); + ok(center.x !== 0.5 && center.y !== 0.5, 'We start out panned'); + notEqual(viewport.getZoom(), 1, 'We start out zoomed'); + + var homeHandler = function() { + viewer.removeHandler('animationfinish', homeHandler); + center = viewport.getCenter(); + ok(center.x === 0.5 && center.y === 0.5, 'We end up unpanned'); + equal(viewport.getZoom(), 1, 'We end up unzoomed'); + start(); + }; + + viewer.addHandler('animationfinish', homeHandler); + viewport.goHome(true); + }); + + // ---------- + asyncTest('Click', function() { + var viewport = viewer.viewport; + center = viewport.getCenter(); + ok(center.x === 0.5 && center.y === 0.5, 'We start out unpanned'); + equal(viewport.getZoom(), 1, 'We start out unzoomed'); + + var clickHandler = function() { + viewer.removeHandler('animationfinish', clickHandler); + center = viewport.getCenter(); + ok(center.x > 0.37 && center.x < 0.38 && center.y > 0.37 && center.y < 0.38, 'Panned correctly'); + equal(viewport.getZoom(), 2, 'Zoomed correctly'); + start(); + }; + + viewer.addHandler('animationfinish', clickHandler); + Util.simulateViewerClick(viewer, 0.25, 0.25); + }); + // ---------- asyncTest('Close', function() { var closeHandler = function() { @@ -68,7 +109,6 @@ }; viewer.addHandler('close', closeHandler); - viewer.close(); }); diff --git a/test/lib/jquery.simulate.js b/test/lib/jquery.simulate.js new file mode 100644 index 00000000..6e2f3ba8 --- /dev/null +++ b/test/lib/jquery.simulate.js @@ -0,0 +1,324 @@ + /*! + * jQuery Simulate v0.0.1 - simulate browser mouse and keyboard events + * https://github.com/jquery/jquery-simulate + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: Sun Dec 9 12:15:33 2012 -0500 + */ + +;(function( $, undefined ) { + +var rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/; + +$.fn.simulate = function( type, options ) { + return this.each(function() { + new $.simulate( this, type, options ); + }); +}; + +$.simulate = function( elem, type, options ) { + var method = $.camelCase( "simulate-" + type ); + + this.target = elem; + this.options = options; + + if ( this[ method ] ) { + this[ method ](); + } else { + this.simulateEvent( elem, type, options ); + } +}; + +$.extend( $.simulate, { + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + }, + + buttonCode: { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2 + } +}); + +$.extend( $.simulate.prototype, { + + simulateEvent: function( elem, type, options ) { + var event = this.createEvent( type, options ); + this.dispatchEvent( elem, type, event, options ); + }, + + createEvent: function( type, options ) { + if ( rkeyEvent.test( type ) ) { + return this.keyEvent( type, options ); + } + + if ( rmouseEvent.test( type ) ) { + return this.mouseEvent( type, options ); + } + }, + + mouseEvent: function( type, options ) { + var event, eventDoc, doc, body; + options = $.extend({ + bubbles: true, + cancelable: (type !== "mousemove"), + view: window, + detail: 0, + screenX: 0, + screenY: 0, + clientX: 1, + clientY: 1, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + button: 0, + relatedTarget: undefined + }, options ); + + if ( document.createEvent ) { + event = document.createEvent( "MouseEvents" ); + event.initMouseEvent( type, options.bubbles, options.cancelable, + options.view, options.detail, + options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.button, options.relatedTarget || document.body.parentNode ); + + // IE 9+ creates events with pageX and pageY set to 0. + // Trying to modify the properties throws an error, + // so we define getters to return the correct values. + if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) { + eventDoc = event.relatedTarget.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + Object.defineProperty( event, "pageX", { + get: function() { + return options.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + } + }); + Object.defineProperty( event, "pageY", { + get: function() { + return options.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + }); + } + } else if ( document.createEventObject ) { + event = document.createEventObject(); + $.extend( event, options ); + // standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx + // old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx + // so we actually need to map the standard back to oldIE + event.button = { + 0: 1, + 1: 4, + 2: 2 + }[ event.button ] || event.button; + } + + return event; + }, + + keyEvent: function( type, options ) { + var event; + options = $.extend({ + bubbles: true, + cancelable: true, + view: window, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0, + charCode: undefined + }, options ); + + if ( document.createEvent ) { + try { + event = document.createEvent( "KeyEvents" ); + event.initKeyEvent( type, options.bubbles, options.cancelable, options.view, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.keyCode, options.charCode ); + // initKeyEvent throws an exception in WebKit + // see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution + // and also https://bugs.webkit.org/show_bug.cgi?id=13368 + // fall back to a generic event until we decide to implement initKeyboardEvent + } catch( err ) { + event = document.createEvent( "Events" ); + event.initEvent( type, options.bubbles, options.cancelable ); + $.extend( event, { + view: options.view, + ctrlKey: options.ctrlKey, + altKey: options.altKey, + shiftKey: options.shiftKey, + metaKey: options.metaKey, + keyCode: options.keyCode, + charCode: options.charCode + }); + } + } else if ( document.createEventObject ) { + event = document.createEventObject(); + $.extend( event, options ); + } + + if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) { + event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode; + event.charCode = undefined; + } + + return event; + }, + + dispatchEvent: function( elem, type, event ) { + if ( elem.dispatchEvent ) { + elem.dispatchEvent( event ); + } else if ( elem.fireEvent ) { + elem.fireEvent( "on" + type, event ); + } + }, + + simulateFocus: function() { + var focusinEvent, + triggered = false, + element = $( this.target ); + + function trigger() { + triggered = true; + } + + element.bind( "focus", trigger ); + element[ 0 ].focus(); + + if ( !triggered ) { + focusinEvent = $.Event( "focusin" ); + focusinEvent.preventDefault(); + element.trigger( focusinEvent ); + element.triggerHandler( "focus" ); + } + element.unbind( "focus", trigger ); + }, + + simulateBlur: function() { + var focusoutEvent, + triggered = false, + element = $( this.target ); + + function trigger() { + triggered = true; + } + + element.bind( "blur", trigger ); + element[ 0 ].blur(); + + // blur events are async in IE + setTimeout(function() { + // IE won't let the blur occur if the window is inactive + if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) { + element[ 0 ].ownerDocument.body.focus(); + } + + // Firefox won't trigger events if the window is inactive + // IE doesn't trigger events if we had to manually focus the body + if ( !triggered ) { + focusoutEvent = $.Event( "focusout" ); + focusoutEvent.preventDefault(); + element.trigger( focusoutEvent ); + element.triggerHandler( "blur" ); + } + element.unbind( "blur", trigger ); + }, 1 ); + } +}); + + + +/** complex events **/ + +function findCenter( elem ) { + var offset, + document = $( elem.ownerDocument ); + elem = $( elem ); + offset = elem.offset(); + + return { + x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(), + y: offset.top + elem.outerHeight() / 2 - document.scrollTop() + }; +} + +function findCorner( elem ) { + var offset, + document = $( elem.ownerDocument ); + elem = $( elem ); + offset = elem.offset(); + + return { + x: offset.left - document.scrollLeft(), + y: offset.top - document.scrollTop() + }; +} + +$.extend( $.simulate.prototype, { + simulateDrag: function() { + var i = 0, + target = this.target, + options = this.options, + center = options.handle === "corner" ? findCorner( target ) : findCenter( target ), + x = Math.floor( center.x ), + y = Math.floor( center.y ), + coord = { clientX: x, clientY: y }, + dx = options.dx || ( options.x !== undefined ? options.x - x : 0 ), + dy = options.dy || ( options.y !== undefined ? options.y - y : 0 ), + moves = options.moves || 3; + + this.simulateEvent( target, "mousedown", coord ); + + for ( ; i < moves ; i++ ) { + x += dx / moves; + y += dy / moves; + + coord = { + clientX: Math.round( x ), + clientY: Math.round( y ) + }; + + this.simulateEvent( document, "mousemove", coord ); + } + + this.simulateEvent( target, "mouseup", coord ); + this.simulateEvent( target, "click", coord ); + } +}); + +})( jQuery ); diff --git a/test/test.html b/test/test.html index 72d07ae6..8266fb5f 100644 --- a/test/test.html +++ b/test/test.html @@ -19,8 +19,9 @@ - - + + + diff --git a/test/util.js b/test/util.js new file mode 100644 index 00000000..a426bd52 --- /dev/null +++ b/test/util.js @@ -0,0 +1,32 @@ +(function() { + + // ---------- + window.Util = { + // ---------- + simulateViewerClick: function(viewer, widthFactor, heightFactor) { + if (widthFactor === undefined) { + widthFactor = 0.5; + } + + if (heightFactor === undefined) { + heightFactor = 0.5; + } + + widthFactor = Math.min(1, Math.max(0, widthFactor)); + heightFactor = Math.min(1, Math.max(0, heightFactor)); + + var $canvas = $(viewer.element).find('.openseadragon-canvas').not('.navigator .openseadragon-canvas'); + var offset = $canvas.offset(); + var event = { + clientX: offset.left + Math.floor($canvas.width() * widthFactor), + clientY: offset.top + Math.floor($canvas.height() * heightFactor) + }; + + $canvas + .simulate('mouseover', event) + .simulate('mousedown', event) + .simulate('mouseup', event); + } + }; + +})();