├── .editorconfig ├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── karma.conf.js ├── package.json ├── simulate-event.d.ts ├── simulate-event.js └── test ├── events.js ├── key-events.js ├── mouse-events.js └── ui-events.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | indent_style = space 11 | indent_size = 2 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | indent_size = 4 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | coverage/ 4 | dist/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | sudo: false 5 | 6 | notifications: 7 | email: 8 | on_success: never 9 | on_failure: change 10 | 11 | before_script: 12 | - "export DISPLAY=:99.0" 13 | - "sh -e /etc/init.d/xvfb start" 14 | - sleep 3 # give xvfb some time to start 15 | after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" 16 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Standard options for the Browserify builds. 3 | * 4 | * @type {Object} 5 | */ 6 | var debugOptions = { 7 | debug: true, 8 | transform: [], 9 | standalone: 'simulateEvent' 10 | } 11 | 12 | /** 13 | * Options for a minified Browserify build. 14 | * 15 | * @type {Object} 16 | */ 17 | var minifyOptions = { 18 | debug: false, 19 | transform: ['uglifyify'], 20 | standalone: 'simulateEvent' 21 | } 22 | 23 | /** 24 | * Initialize the grunt configuration script. 25 | * 26 | * @param {Object} grunt 27 | */ 28 | module.exports = function (grunt) { 29 | require('load-grunt-tasks')(grunt) 30 | 31 | grunt.initConfig({ 32 | /** 33 | * Compile browser-side modules for simplified consumption. 34 | * 35 | * @type {Object} 36 | */ 37 | browserify: { 38 | debug: { 39 | src: 'simulate-event.js', 40 | dest: 'dist/simulate-event.js', 41 | options: debugOptions 42 | }, 43 | minify: { 44 | src: 'simulate-event.js', 45 | dest: 'dist/simulate-event.min.js', 46 | options: minifyOptions 47 | } 48 | }, 49 | 50 | /** 51 | * Uglify the output of the minified Browserified files. 52 | * 53 | * @type {Object} 54 | */ 55 | uglify: { 56 | minify: { 57 | files: { 58 | 'dist/simulate-event.min.js': ['dist/simulate-event.min.js'] 59 | } 60 | } 61 | }, 62 | 63 | /** 64 | * Execute the test suite using Karma. 65 | * 66 | * @type {Object} 67 | */ 68 | karma: { 69 | options: { 70 | configFile: 'karma.conf.js' 71 | }, 72 | unit: { 73 | singleRun: false, 74 | background: true 75 | }, 76 | ci: { 77 | singleRun: true 78 | } 79 | }, 80 | 81 | /** 82 | * Watch for any file changes and run the supporting processes. 83 | * 84 | * @type {Object} 85 | */ 86 | watch: { 87 | build: { 88 | files: ['<%= jshint.all.src %>'], 89 | tasks: ['build'] 90 | }, 91 | lint: { 92 | files: ['<%= jshint.all.src %>'], 93 | tasks: ['newer:jshint:all'] 94 | }, 95 | karma: { 96 | files: ['lib/**/*.js', 'test/**/*.js'], 97 | tasks: ['karma:unit:run'] 98 | } 99 | } 100 | }) 101 | 102 | grunt.registerTask('test', ['jshint', 'karma:ci']) 103 | grunt.registerTask('build', ['browserify', 'uglify']) 104 | grunt.registerTask('default', ['build', 'karma:unit', 'watch']) 105 | } 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Blake Embrey (hello@blakeembrey.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simulate Event 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![NPM downloads][downloads-image]][downloads-url] 5 | [![Build status][travis-image]][travis-url] 6 | [![Test coverage][coveralls-image]][coveralls-url] 7 | 8 | > Simply trigger DOM events on any element. 9 | 10 | ## Installation 11 | 12 | ```shell 13 | npm install simulate-event --save-dev 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```javascript 19 | // Simulate an event on an element 20 | simulateEvent.simulate(document.body, 'click'); 21 | 22 | // Generate an event for custom use 23 | var evt = simulateEvent.generate('click', { clientX: 10 }); 24 | ``` 25 | 26 | ## License 27 | 28 | MIT 29 | 30 | [npm-image]: https://img.shields.io/npm/v/simulate-event.svg?style=flat 31 | [npm-url]: https://npmjs.org/package/simulate-event 32 | [downloads-image]: https://img.shields.io/npm/dm/simulate-event.svg?style=flat 33 | [downloads-url]: https://npmjs.org/package/simulate-event 34 | [travis-image]: https://img.shields.io/travis/blakeembrey/simulate-event.svg?style=flat 35 | [travis-url]: https://travis-ci.org/blakeembrey/simulate-event 36 | [coveralls-image]: https://img.shields.io/coveralls/blakeembrey/simulate-event.svg?style=flat 37 | [coveralls-url]: https://coveralls.io/r/blakeembrey/simulate-event?branch=master 38 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | /** 4 | * The base path that will be used to resolve files. 5 | * 6 | * @type {String} 7 | */ 8 | basePath: '', 9 | 10 | /** 11 | * Test frameworks to use. 12 | * 13 | * @type {Array} 14 | */ 15 | frameworks: ['mocha', 'sinon-chai'], 16 | 17 | /** 18 | * Preprocess files matching a given pattern. 19 | * 20 | * @type {Object} 21 | */ 22 | preprocessors: { 23 | 'dist/**/*.js': ['coverage'] 24 | }, 25 | 26 | /** 27 | * List of files and patterns to load in the browser. 28 | * 29 | * @type {Array} 30 | */ 31 | files: [ 32 | 'dist/simulate-event.js', 33 | 'test/**/*.js' 34 | ], 35 | 36 | /** 37 | * List of files and patterns to exclude in the browser. 38 | * 39 | * @type {Array} 40 | */ 41 | exclude: [], 42 | 43 | /** 44 | * Test results reporter to use. 45 | * Possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 46 | * 47 | * @type {Array} 48 | */ 49 | reporters: ['progress', 'coverage'], 50 | 51 | /** 52 | * Web server port number. 53 | * 54 | * @type {Number} 55 | */ 56 | port: 9876, 57 | 58 | /** 59 | * Enable or disable colors in the output (reporters and logs). 60 | * 61 | * @type {Boolean} 62 | */ 63 | colors: true, 64 | 65 | /** 66 | * Level of logging. 67 | * Possible values: 68 | * - config.LOG_DISABLE 69 | * - config.LOG_ERROR 70 | * - config.LOG_WARN 71 | * - config.LOG_INFO 72 | * - config.LOG_DEBUG 73 | * 74 | * @type {Number} 75 | */ 76 | logLevel: config.LOG_INFO, 77 | 78 | /** 79 | * Enable and disable file watching and execution of tests when a 80 | * file changes. 81 | * 82 | * @type {Boolean} 83 | */ 84 | autoWatch: true, 85 | 86 | /** 87 | * Start these browsers automatically. Currently available: 88 | * - Chrome 89 | * - ChromeCanary 90 | * - Firefox 91 | * - Opera 92 | * - Safari (only Mac) 93 | * - PhantomJS 94 | * - IE (only Windows) 95 | * 96 | * @type {Array} 97 | */ 98 | browsers: ['Chrome', 'Firefox'], 99 | 100 | /** 101 | * If a browser does not capture within a given timeout, kill it. 102 | * 103 | * @type {Number} 104 | */ 105 | captureTimeout: 60000, 106 | 107 | /** 108 | * Continuous Integration mode. If true, it will capture browsers, run 109 | * tests and exit. 110 | * 111 | * @type {Boolean} 112 | */ 113 | singleRun: true 114 | }) 115 | } 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simulate-event", 3 | "version": "1.4.0", 4 | "description": "A library for triggering DOM events.", 5 | "main": "simulate-event.js", 6 | "typings": "simulate-event.d.ts", 7 | "files": [ 8 | "simulate-event.js", 9 | "simulate-event.d.ts" 10 | ], 11 | "scripts": { 12 | "build": "grunt build", 13 | "dev": "grunt watch:build & karma start", 14 | "lint": "standard", 15 | "test:debug": "npm run build && karma start --browsers=Firefox --singleRun=false --debug=true", 16 | "test:chrome": "npm run build && karma start --browsers=Chrome", 17 | "test:ie": "npm run build && karma start --browsers=IE", 18 | "test": "npm run build && npm run lint && karma start --browsers=Firefox" 19 | }, 20 | "standard": { 21 | "ignore": [ 22 | "dist/", 23 | "node_modules/", 24 | "coverage/" 25 | ] 26 | }, 27 | "repository": "https://github.com/blakeembrey/simulate-event", 28 | "keywords": [ 29 | "simulate", 30 | "event", 31 | "dom", 32 | "mouse", 33 | "keyboard" 34 | ], 35 | "author": { 36 | "name": "Blake Embrey", 37 | "email": "hello@blakeembrey.com", 38 | "url": "http://blakeembrey.me" 39 | }, 40 | "license": "MIT", 41 | "devDependencies": { 42 | "browserify": "^3.18.0", 43 | "grunt": "^0.4.2", 44 | "grunt-browserify": "^1.3.0", 45 | "grunt-cli": "^0.1.11", 46 | "grunt-contrib-jshint": "^0.8.0", 47 | "grunt-contrib-uglify": "^0.2.7", 48 | "grunt-contrib-watch": "^0.5.3", 49 | "grunt-karma": "^0.10.1", 50 | "grunt-newer": "^0.6.0", 51 | "karma": "^0.12.37", 52 | "karma-chrome-launcher": "^0.1.2", 53 | "karma-coffee-preprocessor": "^0.1.2", 54 | "karma-coverage": "^0.1.4", 55 | "karma-firefox-launcher": "^0.1.3", 56 | "karma-html2js-preprocessor": "^0.1.0", 57 | "karma-ie-launcher": "^0.2.0", 58 | "karma-jasmine": "^0.1.5", 59 | "karma-mocha": "^0.1.1", 60 | "karma-requirejs": "^0.2.1", 61 | "karma-script-launcher": "^0.1.0", 62 | "karma-sinon-chai": "^0.1.4", 63 | "load-grunt-tasks": "^0.2.1", 64 | "mocha": "^1.16.2", 65 | "requirejs": "^2.1.9", 66 | "standard": "^7.0.1", 67 | "uglifyify": "^1.0.1" 68 | }, 69 | "dependencies": { 70 | "xtend": "^4.0.1" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /simulate-event.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export 3 | function generate(type: string, options?: any): Event; 4 | 5 | export 6 | function simulate(element: EventTarget, type: string, options?: any): boolean; 7 | -------------------------------------------------------------------------------- /simulate-event.js: -------------------------------------------------------------------------------- 1 | var extend = require('xtend/mutable') 2 | 3 | /** 4 | * Set some default options. 5 | * 6 | * @type {Object} 7 | */ 8 | var eventOptions = { 9 | UIEvent: function () { 10 | return { 11 | view: document.defaultView 12 | } 13 | }, 14 | FocusEvent: function () { 15 | return eventOptions.UIEvent.apply(this, arguments) 16 | }, 17 | MouseEvent: function (type) { 18 | return { 19 | button: 0, 20 | bubbles: (type !== 'mouseenter' && type !== 'mouseleave'), 21 | cancelable: (type !== 'mouseenter' && type !== 'mouseleave'), 22 | ctrlKey: false, 23 | altKey: false, 24 | shiftKey: false, 25 | metaKey: false, 26 | clientX: 1, 27 | clientY: 1, 28 | screenX: 0, 29 | screenY: 0, 30 | view: document.defaultView, 31 | relatedTarget: document.documentElement 32 | } 33 | }, 34 | WheelEvent: function (type) { 35 | return eventOptions.MouseEvent.apply(this, arguments) 36 | }, 37 | KeyboardEvent: function () { 38 | return { 39 | view: document.defaultView, 40 | ctrlKey: false, 41 | altKey: false, 42 | shiftKey: false, 43 | metaKey: false, 44 | keyCode: 0 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * Map event names to constructor names. 51 | * 52 | * @type {Object} 53 | */ 54 | var eventTypes = { 55 | beforeprint: 'Event', 56 | afterprint: 'Event', 57 | beforeunload: 'Event', 58 | abort: 'Event', 59 | error: 'Event', 60 | change: 'Event', 61 | submit: 'Event', 62 | reset: 'Event', 63 | cached: 'Event', 64 | canplay: 'Event', 65 | canplaythrough: 'Event', 66 | chargingchange: 'Event', 67 | chargingtimechange: 'Event', 68 | checking: 'Event', 69 | close: 'Event', 70 | downloading: 'Event', 71 | durationchange: 'Event', 72 | emptied: 'Event', 73 | ended: 'Event', 74 | fullscreenchange: 'Event', 75 | fullscreenerror: 'Event', 76 | invalid: 'Event', 77 | levelchange: 'Event', 78 | loadeddata: 'Event', 79 | loadedmetadata: 'Event', 80 | noupdate: 'Event', 81 | obsolete: 'Event', 82 | offline: 'Event', 83 | online: 'Event', 84 | open: 'Event', 85 | orientationchange: 'Event', 86 | pause: 'Event', 87 | pointerlockchange: 'Event', 88 | pointerlockerror: 'Event', 89 | copy: 'Event', 90 | cut: 'Event', 91 | paste: 'Event', 92 | play: 'Event', 93 | playing: 'Event', 94 | ratechange: 'Event', 95 | readystatechange: 'Event', 96 | seeked: 'Event', 97 | seeking: 'Event', 98 | stalled: 'Event', 99 | success: 'Event', 100 | suspend: 'Event', 101 | timeupdate: 'Event', 102 | updateready: 'Event', 103 | visibilitychange: 'Event', 104 | volumechange: 'Event', 105 | waiting: 'Event', 106 | load: 'UIEvent', 107 | unload: 'UIEvent', 108 | resize: 'UIEvent', 109 | scroll: 'UIEvent', 110 | select: 'UIEvent', 111 | drag: 'MouseEvent', 112 | dragenter: 'MouseEvent', 113 | dragleave: 'MouseEvent', 114 | dragover: 'MouseEvent', 115 | dragstart: 'MouseEvent', 116 | dragend: 'MouseEvent', 117 | drop: 'MouseEvent', 118 | touchcancel: 'UIEvent', 119 | touchend: 'UIEvent', 120 | touchenter: 'UIEvent', 121 | touchleave: 'UIEvent', 122 | touchmove: 'UIEvent', 123 | touchstart: 'UIEvent', 124 | blur: 'UIEvent', 125 | focus: 'UIEvent', 126 | focusin: 'UIEvent', 127 | focusout: 'UIEvent', 128 | input: 'UIEvent', 129 | show: 'MouseEvent', 130 | click: 'MouseEvent', 131 | dblclick: 'MouseEvent', 132 | mouseenter: 'MouseEvent', 133 | mouseleave: 'MouseEvent', 134 | mousedown: 'MouseEvent', 135 | mouseup: 'MouseEvent', 136 | mouseover: 'MouseEvent', 137 | mousemove: 'MouseEvent', 138 | mouseout: 'MouseEvent', 139 | contextmenu: 'MouseEvent', 140 | wheel: 'WheelEvent', 141 | message: 'MessageEvent', 142 | storage: 'StorageEvent', 143 | timeout: 'StorageEvent', 144 | keydown: 'KeyboardEvent', 145 | keypress: 'KeyboardEvent', 146 | keyup: 'KeyboardEvent', 147 | progress: 'ProgressEvent', 148 | loadend: 'ProgressEvent', 149 | loadstart: 'ProgressEvent', 150 | popstate: 'PopStateEvent', 151 | hashchange: 'HashChangeEvent', 152 | transitionend: 'TransitionEvent', 153 | compositionend: 'CompositionEvent', 154 | compositionstart: 'CompositionEvent', 155 | compositionupdate: 'CompositionEvent', 156 | pagehide: 'PageTransitionEvent', 157 | pageshow: 'PageTransitionEvent' 158 | } 159 | 160 | /** 161 | * Map the event type constructor to the initialization method. 162 | * 163 | * @type {Object} 164 | */ 165 | var eventInit = { 166 | Event: 'initEvent', 167 | UIEvent: 'initUIEvent', 168 | FocusEvent: 'initUIEvent', 169 | MouseEvent: 'initMouseEvent', 170 | WheelEvent: 'initMouseEvent', 171 | MessageEvent: 'initMessageEvent', 172 | StorageEvent: 'initStorageEvent', 173 | KeyboardEvent: 'initKeyboardEvent', 174 | ProgressEvent: 'initEvent', 175 | PopStateEvent: 'initEvent', 176 | TransitionEvent: 'initEvent', 177 | HashChangeEvent: 'initHashChangeEvent', 178 | CompositionEvent: 'initCompositionEvent', 179 | DeviceMotionEvent: 'initDeviceMotionEvent', 180 | PageTransitionEvent: 'initEvent', 181 | DeviceOrientationEvent: 'initDeviceOrientationEvent' 182 | } 183 | 184 | /** 185 | * Map the options object to initialization parameters. 186 | * 187 | * @type {Object} 188 | */ 189 | var eventParameters = { 190 | initEvent: [], 191 | initUIEvent: [ 192 | 'view', 193 | 'detail' 194 | ], 195 | initKeyboardEvent: [ 196 | 'view', 197 | 'char', 198 | 'key', 199 | 'location', 200 | 'modifiersList', 201 | 'repeat', 202 | 'locale' 203 | ], 204 | initKeyEvent: [ 205 | 'view', 206 | 'ctrlKey', 207 | 'altKey', 208 | 'shiftKey', 209 | 'metaKey', 210 | 'keyCode', 211 | 'charCode' 212 | ], 213 | initMouseEvent: [ 214 | 'view', 215 | 'detail', 216 | 'screenX', 217 | 'screenY', 218 | 'clientX', 219 | 'clientY', 220 | 'ctrlKey', 221 | 'altKey', 222 | 'shiftKey', 223 | 'metaKey', 224 | 'button', 225 | 'relatedTarget' 226 | ], 227 | initHashChangeEvent: [ 228 | 'oldURL', 229 | 'newURL' 230 | ], 231 | initCompositionEvent: [ 232 | 'view', 233 | 'data', 234 | 'locale' 235 | ], 236 | initDeviceMotionEvent: [ 237 | 'acceleration', 238 | 'accelerationIncludingGravity', 239 | 'rotationRate', 240 | 'interval' 241 | ], 242 | initDeviceOrientationEvent: [ 243 | 'alpha', 244 | 'beta', 245 | 'gamma', 246 | 'absolute' 247 | ], 248 | initMessageEvent: [ 249 | 'data', 250 | 'origin', 251 | 'lastEventId', 252 | 'source' 253 | ], 254 | initStorageEvent: [ 255 | 'key', 256 | 'oldValue', 257 | 'newValue', 258 | 'url', 259 | 'storageArea' 260 | ] 261 | } 262 | 263 | /** 264 | * Map the event types to constructors. 265 | * 266 | * @type {Object} 267 | */ 268 | var eventConstructors = { 269 | UIEvent: window.UIEvent, 270 | FocusEvent: window.FocusEvent, 271 | MouseEvent: window.MouseEvent, 272 | WheelEvent: window.MouseEvent, 273 | KeyboardEvent: window.KeyboardEvent 274 | } 275 | 276 | /** 277 | * Get attributes which must be overriden manually. 278 | * 279 | * @param {String} eventType 280 | * @param {Object} options. 281 | */ 282 | function getOverrides (eventType, options) { 283 | if (eventType === 'KeyboardEvent' && options) { 284 | return { 285 | keyCode: options.keyCode || 0, 286 | key: options.key || 0, 287 | which: options.which || options.keyCode || 0 288 | } 289 | } 290 | } 291 | 292 | /** 293 | * Generate an event. 294 | * 295 | * @param {String} type 296 | * @param {Object} options 297 | * @return {Event} 298 | */ 299 | exports.generate = function (type, options) { 300 | // Immediately throw an error when the event name does not translate. 301 | if (!eventTypes.hasOwnProperty(type)) { 302 | throw new SyntaxError('Unsupported event type') 303 | } 304 | 305 | var eventType = eventTypes[type] 306 | var event 307 | var key 308 | 309 | // Handle parameters which must be manually overridden using 310 | // `Object.defineProperty`. 311 | var overrides = getOverrides(eventType, options) 312 | 313 | // Extend a new object with the default and passed in options. 314 | // Existing events already have all of their defaults set. 315 | if (!(options instanceof window.Event)) { 316 | // Check for extra defaults to pass in. 317 | if (eventType in eventOptions) { 318 | options = extend({ 319 | bubbles: true, 320 | cancelable: true 321 | }, eventOptions[eventType](type, options), options) 322 | } else { 323 | options = extend({ 324 | bubbles: true, 325 | cancelable: true 326 | }, options) 327 | } 328 | } 329 | 330 | // Attempt the Event Constructors DOM API. 331 | var Constructor = eventConstructors[eventType] || window.Event 332 | 333 | try { 334 | event = new Constructor(type, options) 335 | 336 | // Add the override properties. 337 | for (key in overrides) { 338 | Object.defineProperty(event, key, { 339 | value: overrides[key] 340 | }) 341 | } 342 | 343 | return event 344 | } catch (e) { 345 | // Continue. 346 | } 347 | 348 | // In IE11, the Keyboard event does not allow setting the 349 | // keyCode property, even with Object.defineProperty, 350 | // so we have to use UIEvent. 351 | var ua = window.navigator.userAgent.toLowerCase() 352 | var msie = Math.max(ua.indexOf('msie'), ua.indexOf('trident')) 353 | 354 | if (msie >= 0 && eventType === 'KeyboardEvent') { 355 | eventType = 'UIEvent' 356 | } 357 | 358 | var initEvent = eventInit[eventType] 359 | 360 | // In < IE9, the `createEvent` function is not available and we have to 361 | // resort to using `fireEvent`. 362 | if (!document.createEvent) { 363 | event = extend(document.createEventObject(), options) 364 | 365 | // Add the override properties. 366 | for (key in overrides) { 367 | Object.defineProperty(event, key, { 368 | value: overrides[key] 369 | }) 370 | } 371 | 372 | return event 373 | } 374 | 375 | event = extend(document.createEvent(eventType), options) 376 | 377 | // Handle differences between `initKeyboardEvent` and `initKeyEvent`. 378 | if (initEvent === 'initKeyboardEvent') { 379 | if (event[initEvent] === void 0) { 380 | initEvent = 'initKeyEvent' 381 | } else if (!('modifiersList' in options)) { 382 | var mods = [] 383 | if (options.metaKey) mods.push('Meta') 384 | if (options.altKey) mods.push('Alt') 385 | if (options.shiftKey) mods.push('Shift') 386 | if (options.ctrlKey) mods.push('Control') 387 | options['modifiersList'] = mods.join(' ') 388 | } 389 | } 390 | 391 | // Map argument names to the option values. 392 | var args = eventParameters[initEvent].map(function (parameter) { 393 | return options[parameter] 394 | }) 395 | 396 | // Initialize the event using the built-in method. 397 | event[initEvent].apply( 398 | event, [type, options.bubbles, options.cancelable].concat(args) 399 | ) 400 | 401 | // Add the override properties. 402 | for (key in overrides) { 403 | Object.defineProperty(event, key, { 404 | value: overrides[key] 405 | }) 406 | } 407 | 408 | return event 409 | } 410 | 411 | /** 412 | * Simulate an event which is dispatched on the given element. 413 | * 414 | * @param {Element} element 415 | * @param {String} type 416 | * @param {Object} options 417 | * @return {Boolean} 418 | */ 419 | exports.simulate = function (element, type, options) { 420 | var event = exports.generate(type, options) 421 | 422 | // In < IE9, the `createEvent` function is not available and we have to 423 | // resort to using `fireEvent`. 424 | if (!document.createEvent) { 425 | return element.fireEvent('on' + type, event) 426 | } 427 | return element.dispatchEvent(event) 428 | } 429 | -------------------------------------------------------------------------------- /test/events.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, simulateEvent, sinon, expect, beforeEach, afterEach */ 2 | 3 | describe('Events', function () { 4 | var eventNames = 'abort change open storage loadend popstate transitionend ' 5 | eventNames += 'pagehide' 6 | var events = eventNames.split(' ') 7 | var fixture 8 | 9 | beforeEach(function () { 10 | fixture = document.createElement('div') 11 | document.body.appendChild(fixture) 12 | }) 13 | 14 | afterEach(function () { 15 | fixture.parentNode.removeChild(fixture) 16 | fixture = null 17 | }) 18 | 19 | events.forEach(function (eventName) { 20 | it('should trigger ' + eventName, function () { 21 | var spy = sinon.spy() 22 | 23 | fixture.addEventListener(eventName, spy) 24 | simulateEvent.simulate(fixture, eventName) 25 | 26 | expect(spy).to.have.been.calledOnce 27 | 28 | var evt = simulateEvent.generate(eventName) 29 | expect(evt.type).to.equal(eventName) 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/key-events.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, simulateEvent, sinon, expect */ 2 | 3 | describe('Key Events', function () { 4 | var fixture = document.createElement('div') 5 | document.body.appendChild(fixture) 6 | 7 | describe('keydown', function () { 8 | it('should trigger with the correct options', function () { 9 | var spy = sinon.spy() 10 | 11 | fixture.addEventListener('keydown', spy) 12 | 13 | simulateEvent.simulate(fixture, 'keydown', 14 | { keyCode: 27, key: 'A', altKey: true }) 15 | 16 | expect(spy).to.have.been.calledOnce 17 | 18 | var evt = spy.getCall(0).args[0] 19 | expect(evt.keyCode).to.equal(27) 20 | expect(evt.key).to.equal('A') 21 | expect(evt.altKey).to.equal(true) 22 | expect(evt.bubbles).to.equal(true) 23 | }) 24 | 25 | it('should clone an existing event', function () { 26 | var spy = sinon.spy() 27 | 28 | fixture.addEventListener('keydown', spy) 29 | 30 | simulateEvent.simulate(fixture, 'keydown', 31 | { keyCode: 27, key: 'A', altKey: true }) 32 | 33 | expect(spy).to.have.been.calledOnce 34 | 35 | var evt = spy.getCall(0).args[0] 36 | 37 | simulateEvent.simulate(fixture, 'keydown', evt) 38 | 39 | var clone = spy.getCall(1).args[0] 40 | expect(clone.keyCode).to.equal(27) 41 | expect(clone.key).to.equal('A') 42 | expect(clone.altKey).to.equal(true) 43 | }) 44 | 45 | it('should generate an event', function () { 46 | var evt = simulateEvent.generate('keydown', 47 | { keyCode: 27, key: 'A', altKey: true }) 48 | expect(evt.keyCode).to.equal(27) 49 | expect(evt.key).to.equal('A') 50 | expect(evt.altKey).to.equal(true) 51 | expect(evt.type).to.equal('keydown') 52 | }) 53 | }) 54 | 55 | describe('keyup', function () { 56 | it('should trigger', function () { 57 | var spy = sinon.spy() 58 | 59 | fixture.addEventListener('keyup', spy) 60 | 61 | simulateEvent.simulate(fixture, 'keyup') 62 | 63 | expect(spy).to.have.been.calledOnce 64 | }) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/mouse-events.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, simulateEvent, sinon, expect */ 2 | 3 | describe('Mouse Events', function () { 4 | var fixture = document.createElement('div') 5 | document.body.appendChild(fixture) 6 | 7 | describe('click', function () { 8 | it('should trigger with the correct options', function () { 9 | var spy = sinon.spy() 10 | 11 | fixture.addEventListener('click', spy) 12 | simulateEvent.simulate(fixture, 'click', { 13 | clientX: 10, ctrlKey: true, button: 1 14 | }) 15 | 16 | expect(spy).to.have.been.calledOnce 17 | 18 | var evt = spy.getCall(0).args[0] 19 | expect(evt.clientX).to.equal(10) 20 | expect(evt.ctrlKey).to.equal(true) 21 | expect(evt.button).to.equal(1) 22 | expect(evt.bubbles).to.equal(true) 23 | expect(evt.cancelable).to.equal(true) 24 | }) 25 | 26 | it('should clone an existing event', function () { 27 | var spy = sinon.spy() 28 | 29 | fixture.addEventListener('click', spy) 30 | simulateEvent.simulate(fixture, 'click', { 31 | clientX: 10, ctrlKey: true, button: 1 32 | }) 33 | 34 | expect(spy).to.have.been.calledOnce 35 | 36 | var evt = spy.getCall(0).args[0] 37 | 38 | simulateEvent.simulate(fixture, 'click', evt) 39 | 40 | var clone = spy.getCall(1).args[0] 41 | expect(clone.clientX).to.equal(10) 42 | expect(clone.ctrlKey).to.equal(true) 43 | expect(clone.button).to.equal(1) 44 | }) 45 | 46 | it('should generate an event', function () { 47 | var evt = simulateEvent.generate('click', { 48 | clientX: 10, ctrlKey: true, button: 1 49 | }) 50 | expect(evt.clientX).to.equal(10) 51 | expect(evt.ctrlKey).to.equal(true) 52 | expect(evt.button).to.equal(1) 53 | expect(evt.type).to.equal('click') 54 | }) 55 | }) 56 | 57 | describe('mousedown', function () { 58 | it('should trigger', function () { 59 | var spy = sinon.spy() 60 | 61 | fixture.addEventListener('mousedown', spy) 62 | simulateEvent.simulate(fixture, 'mousedown') 63 | 64 | expect(spy).to.have.been.calledOnce 65 | var evt = spy.getCall(0).args[0] 66 | expect(evt.bubbles).to.equal(true) 67 | expect(evt.cancelable).to.equal(true) 68 | }) 69 | }) 70 | 71 | describe('mouseup', function () { 72 | it('should trigger', function () { 73 | var spy = sinon.spy() 74 | 75 | fixture.addEventListener('mouseup', spy) 76 | simulateEvent.simulate(fixture, 'mouseup') 77 | 78 | expect(spy).to.have.been.calledOnce 79 | 80 | var evt = spy.getCall(0).args[0] 81 | expect(evt.bubbles).to.equal(true) 82 | expect(evt.cancelable).to.equal(true) 83 | }) 84 | }) 85 | 86 | describe('mouseenter', function () { 87 | it('should trigger', function () { 88 | var spy = sinon.spy() 89 | 90 | fixture.addEventListener('mouseenter', spy) 91 | simulateEvent.simulate(fixture, 'mouseenter') 92 | 93 | expect(spy).to.have.been.calledOnce 94 | var evt = spy.getCall(0).args[0] 95 | expect(evt.bubbles).to.equal(false) 96 | expect(evt.cancelable).to.equal(false) 97 | }) 98 | }) 99 | 100 | describe('mouseleave', function () { 101 | it('should trigger', function () { 102 | var spy = sinon.spy() 103 | 104 | fixture.addEventListener('mouseleave', spy) 105 | simulateEvent.simulate(fixture, 'mouseleave') 106 | 107 | expect(spy).to.have.been.calledOnce 108 | var evt = spy.getCall(0).args[0] 109 | expect(evt.bubbles).to.equal(false) 110 | expect(evt.cancelable).to.equal(false) 111 | }) 112 | }) 113 | 114 | describe('mouseover', function () { 115 | it('should trigger', function () { 116 | var spy = sinon.spy() 117 | 118 | fixture.addEventListener('mouseover', spy) 119 | simulateEvent.simulate(fixture, 'mouseover') 120 | 121 | expect(spy).to.have.been.calledOnce 122 | var evt = spy.getCall(0).args[0] 123 | expect(evt.bubbles).to.equal(true) 124 | expect(evt.cancelable).to.equal(true) 125 | }) 126 | }) 127 | 128 | describe('mousemove', function () { 129 | it('should trigger', function () { 130 | var spy = sinon.spy() 131 | 132 | fixture.addEventListener('mousemove', spy) 133 | simulateEvent.simulate(fixture, 'mousemove') 134 | 135 | expect(spy).to.have.been.calledOnce 136 | var evt = spy.getCall(0).args[0] 137 | expect(evt.bubbles).to.equal(true) 138 | expect(evt.cancelable).to.equal(true) 139 | }) 140 | }) 141 | 142 | describe('mouseout', function () { 143 | it('should trigger', function () { 144 | var spy = sinon.spy() 145 | 146 | fixture.addEventListener('mouseout', spy) 147 | simulateEvent.simulate(fixture, 'mouseout') 148 | 149 | expect(spy).to.have.been.calledOnce 150 | var evt = spy.getCall(0).args[0] 151 | expect(evt.bubbles).to.equal(true) 152 | expect(evt.cancelable).to.equal(true) 153 | }) 154 | }) 155 | 156 | describe('contextmenu', function () { 157 | it('should trigger', function () { 158 | var spy = sinon.spy() 159 | 160 | fixture.addEventListener('contextmenu', spy) 161 | simulateEvent.simulate(fixture, 'contextmenu') 162 | 163 | expect(spy).to.have.been.calledOnce 164 | var evt = spy.getCall(0).args[0] 165 | expect(evt.bubbles).to.equal(true) 166 | expect(evt.cancelable).to.equal(true) 167 | }) 168 | }) 169 | }) 170 | -------------------------------------------------------------------------------- /test/ui-events.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, simulateEvent, sinon, expect, beforeEach, afterEach */ 2 | 3 | describe('UI Events', function () { 4 | var events = 'blur focus focusin focusout'.split(' ') 5 | var fixture 6 | 7 | beforeEach(function () { 8 | fixture = document.createElement('input') 9 | document.body.appendChild(fixture) 10 | }) 11 | 12 | afterEach(function () { 13 | fixture.parentNode.removeChild(fixture) 14 | fixture = null 15 | }) 16 | 17 | events.forEach(function (eventName) { 18 | it('should trigger', function () { 19 | var spy = sinon.spy() 20 | 21 | fixture.addEventListener(eventName, spy) 22 | simulateEvent.simulate(fixture, eventName) 23 | 24 | expect(spy).to.have.been.calledOnce 25 | 26 | var evt = simulateEvent.generate(eventName) 27 | expect(evt.type).to.equal(eventName) 28 | }) 29 | }) 30 | }) 31 | --------------------------------------------------------------------------------