├── .gitignore ├── package.json ├── bower.json ├── LICENSE ├── README.md └── ember-hammer.js /.gitignore: -------------------------------------------------------------------------------- 1 | npm_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ember-hammer", 3 | "version": "1.0.3", 4 | "description" : "ember-hammer is a neat interface for defining Hammer.js gestural behaviour in your Ember.js Views." 5 | } 6 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-hammer", 3 | "main": "ember-hammer.js", 4 | "version": "1.0.3", 5 | "homepage": "https://github.com/chriswessels/ember-hammer", 6 | "authors": [ 7 | "Chris Wessels " 8 | ], 9 | "description": "ember-hammer is a neat interface for defining Hammer.js gestural behaviour in your Ember.js Components.", 10 | "moduleType": [ 11 | "globals" 12 | ], 13 | "keywords": [ 14 | "ember.js", 15 | "hammer.js", 16 | "ember", 17 | "hammer", 18 | "gesture", 19 | "touch" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules", 25 | "bower_components", 26 | "test", 27 | "tests", 28 | "src" 29 | ], 30 | "dependencies": {} 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chris Wessels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ember-hammer 2 | ============ 3 | ember-hammer is a neat interface for defining [Hammer.js](https://github.com/EightMedia/hammer.js) gestural behaviour in your [Ember.js](http://www.emberjs.com) Components. It is easy to use and lightweight. 4 | 5 | Note: Hammer.js 2.x support is currently experimental. 6 | 7 | ##Example 8 | 9 | ```javascript 10 | /* ES6 Modules Example */ 11 | import Ember from 'ember'; 12 | 13 | export default Ember.Component.extend({ 14 | hammerOptions: { 15 | swipe_velocity: 0.5 16 | }, 17 | gestures: { 18 | swipeLeft: function (event) { 19 | // do something like send an event down the controller/route chain 20 | return false; // return `false` to stop bubbling 21 | } 22 | } 23 | }); 24 | 25 | /* Globals Example */ 26 | App.SomeComponent = Ember.Component.extend({ 27 | hammerOptions: { 28 | swipe_velocity: 0.5 29 | }, 30 | gestures: { 31 | swipeLeft: function (event) { 32 | // do something like send an event down the controller/route chain 33 | return false; // return `false` to stop bubbling 34 | } 35 | } 36 | }); 37 | ``` 38 | 39 | ##Usage 40 | 41 | ###With ember-cli 42 | 43 | In your project directory (generated by ember-cli), run the following commands in your terminal: 44 | 45 | $ bower install --save hammerjs#1.1.3 46 | $ bower install --save ember-hammer 47 | 48 | In your `Brocfile.js` (which should be in the root of your project directory), before `module.exports = app.toTree();`, add the following lines: 49 | 50 | app.import('bower_components/hammerjs/hammer.js'); 51 | app.import('bower_components/ember-hammer/ember-hammer.js'); 52 | 53 | That should be it. You'll now be able to define a `gestures` object in your components. 54 | 55 | ###With globals 56 | 57 | First, include the `ember-hammer.js` file into your asset delivery pipeline (ideally this should include minification, concatenation and gzipping facilities). ember-hammer should be included after Ember.js and Hammer.js. 58 | 59 | ###Once included 60 | 61 | Next, define a `gestures` object in any component that you'd like to enable gestural behaviour for. Inside this object, define any Hammer.js gestural event name as a key, with the callback function to be executed as the value. 62 | 63 | ```javascript 64 | gestures: { 65 | swipeLeft: function (event) { 66 | // do something like send an event down the controller/route chain 67 | return false; // return `false` to stop bubbling 68 | }, 69 | swipeRight: function (event) { 70 | /* ... */ 71 | } 72 | } 73 | ``` 74 | 75 | See the full example at the top of the README. 76 | 77 | ###Passing options to Hammer.js 78 | 79 | You can optionally define a `hammerOptions` object inside your component to specify any specific options that should be passed into Hammer.js. Options defined inside the `hammerOptions` object are specific to that component. 80 | 81 | If you'd like to set global options for all instances of Hammer.js (applicable to all components), you can use `emberHammerOptions.hammerOptions`. See the section on emberHammerOptions below. 82 | 83 | ###Event Callback Function 84 | 85 | The callback function is passed a single `event` argument, which is provided by Hammer.js. 86 | 87 | The `this` context of the callback will be set to the component object, so you can access any methods on the component that you may need to get the desired behaviour. 88 | 89 | ###Event bubbling 90 | 91 | Gestural events bubble up the DOM tree, so if you'd like to catch an event and cancel bubbling, just return `false` from your callback. 92 | 93 | ###Ember.EventDispatcher 94 | 95 | Assuming you'll be using ember-hammer (and therefore Hammer.js) to manage touch-based gestural behaviour in your application, there is no point in having Ember's EventDispatcher listen to touch events. By default, ember-hammer will prevent EventDispatcher from listening to the following touch events: 96 | 97 | 1. `touchstart` 98 | 1. `touchmove` 99 | 1. `touchstop` 100 | 1. `touchcancel` 101 | 102 | This brings a significant performance benefit. 103 | 104 | You can modify this behaviour by setting `emberHammerOptions.ignoreEvents` to an array of event names EventDispatcher shouldn't bind to. Set this option to an empty array to disable this behaviour. 105 | 106 | ####ember-fastclick 107 | 108 | If you are using ember-hammer with ember-fastclick, you will need to disable this behaviour by setting `emberHammerOptions.ignoreEvents` to an empty array. ** E.g. `window.emberHammerOptions = { ignoreEvents: [] };` 109 | 110 | ###Setting emberHammerOptions (settings / global options) 111 | 112 | You can set the global options for ember-hammer by defining an object called `emberHammerOptions` on the `window` object. It is important that the object is defined **before** `ember-hammer.js` is loaded. A suitable place might be in an inline script tag in the head section of your document. 113 | 114 | Example: 115 | 116 | ```javascript 117 | window.emberHammerOptions = { 118 | ignoreEvents: ['touchmove', 'touchstart', 'touchend', 'touchcancel'], 119 | hammerOptions: { 120 | swipe_velocity: 0.5 121 | } 122 | }; 123 | ``` 124 | 125 | `emberHammerOptions.hammerOptions` will be passed into every instance of Hammer.js. You can override these options and set additional ones for a specific `Ember.Component` by setting the `hammerOptions` key inside the component object. See the relevant section above for more information. 126 | 127 | ##License 128 | 129 | Please see the `LICENSE` file for more information. 130 | -------------------------------------------------------------------------------- /ember-hammer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ember-hammer 3 | * @module ember-hammer 4 | * @author Chris Wessels (https://github.com/chriswessels) 5 | * @url https://github.com/chriswessels/ember-hammer 6 | * @license MIT 7 | */ 8 | (function (window, Ember, Hammer, globalOptions, undefined) { 9 | var defaultOptions = { 10 | hammerOptions: null, 11 | ignoreEvents: ['touchmove', 'touchstart', 'touchend', 'touchcancel'] 12 | }, 13 | componentProperties = { 14 | /** 15 | * This property is the public interface for defining gestural behaviour for a given component. 16 | * @example 17 | * App.SomeComponent = Ember.Component.extend({ 18 | * gestures: { 19 | * swipeLeft: function (event) { 20 | * // do something like send an event down the controller/route chain 21 | * return false; // return `false` to stop bubbling 22 | * } 23 | * } 24 | * }); 25 | * @property gestures 26 | */ 27 | gestures: null, 28 | /** 29 | * This property allows you to pass component-specific options to Hammer.js. 30 | * @example 31 | * App.SomeComponent = Ember.Component.extend({ 32 | * hammerOptions: { 33 | * swipe_velocity: 0.5 34 | * } 35 | * }); 36 | * @property hammerOptions 37 | */ 38 | hammerOptions: null, 39 | /** 40 | * @property _hammerInstance 41 | * @type Hammer.Instance 42 | * @private 43 | * @default null 44 | */ 45 | _hammerInstance: null, 46 | /** 47 | * This function will ensure that `_hammerInstance` is populated with an instance of Hammer for `this.get('element')`. 48 | * The instance is cached and is only set if `this.gestures` is truthy. This means an instance of Hammer will only 49 | * be created if gesture callbacks have been specified for the component in the `gestures` object. 50 | * @method _setupHammer 51 | * @private 52 | * 53 | * @param {object} options Options with which to initialize Hammer (optional: default is an empty object) 54 | */ 55 | _setupHammer: function (options) { 56 | if (Ember.isNone(options)) { 57 | options = {}; 58 | } 59 | if (this.get('gestures') && !this.get('_hammerInstance')) { 60 | this.set('_hammerInstance', Hammer(this.get('element'), options)); 61 | } 62 | }, 63 | /** 64 | * This function will ensure that `_hammerInstance` has been populated by calling `this._setupHammer`. 65 | * It then iterates over the keys in the `gestures` object and attaches a glue function to the relevant 66 | * event using `hammer_instance.on` that changes the callback context (`this`) to the Component object 67 | * for each of the callbacks specified. 68 | * Gesture events will naturally bubble up the DOM tree, so if you want to cancel bubbling in your callback, 69 | * just return `false`. 70 | * The `event` argument passed into the callbacks you specify in the `gestures` object is provided by Hammer. 71 | * @method _setupGestures 72 | * @private 73 | */ 74 | _setupGestures: function () { 75 | var gestures, events, hammer, self, hammerOptions; 76 | 77 | self = this; 78 | gestures = this.get('gestures'); 79 | hammerOptions = Object.assign({}, 80 | Ember.get(globalOptions, 'hammerOptions') || {}, 81 | this.get('hammerOptions') || {} 82 | ); 83 | 84 | this._setupHammer(hammerOptions); 85 | 86 | if (gestures) { 87 | events = Object.keys(gestures); 88 | hammer = this.get('_hammerInstance'); 89 | 90 | events.forEach(function (value, index) { 91 | hammer.on(value.toLowerCase(), function (event) { 92 | var output = self.gestures[value].apply(self, Array.prototype.slice.call(arguments)); 93 | if (output === false) { 94 | if (typeof event.stopPropagation !== 'undefined') { 95 | event.stopPropagation(); 96 | } else { 97 | event.srcEvent.stopPropagation(); 98 | } 99 | } 100 | return output; 101 | }); 102 | }); 103 | } 104 | }, 105 | /** 106 | * This will call `dispose` on the Hammer instance for the component and then nullify it. 107 | * @method _teardownGestures 108 | * @private 109 | */ 110 | _teardownGestures: function () { 111 | var hammer = this.get('_hammerInstance'); 112 | if (hammer && typeof hammer.dispose === "function") { 113 | hammer.dispose(); 114 | } 115 | this.set('_hammerInstance', null); 116 | }, 117 | /** 118 | * This function is attached to the `didInsertElement` component event that is fired. 119 | * It will call `this._setupGestures` to initialise gestural behaviour for the component upon insertion into the DOM. 120 | * @method _onDidInsertElement 121 | * @private 122 | */ 123 | _onDidInsertElement: Ember.on('didInsertElement', function () { 124 | this._setupGestures(); 125 | return this._super(Array.prototype.slice.call(arguments)); 126 | }), 127 | /** 128 | * This function is attached to the `willDestroy` component event that is fired. 129 | * @method _onWillDestroy 130 | * @private 131 | */ 132 | _onWillDestroy: Ember.on('willDestroy', function () { 133 | this._teardownGestures(); 134 | return this._super(Array.prototype.slice.call(arguments)); 135 | }), 136 | /** 137 | * This function is observes the `gestures` property on the component. 138 | * Under normal circumstances `gestures` will never change, so this observer 139 | * is never fired. It does however ensure that if the `gestures` object is 140 | * patched, gestural behaviour is updated. 141 | * @method _observesGestures 142 | * @private 143 | */ 144 | _observesGestures: Ember.observer('gestures', function () { 145 | this._teardownGestures(); 146 | this._setupGestures(); 147 | }) 148 | }; 149 | globalOptions = Object.assign({}, defaultOptions, globalOptions || {}); 150 | Ember.EventDispatcher.reopen({ 151 | setup: function () { 152 | var events = this.get('events'), 153 | ignoreEvents = Ember.get(globalOptions, 'ignoreEvents'); 154 | 155 | ignoreEvents && ignoreEvents.length && ignoreEvents.forEach(function (value, index) { 156 | events[value] = null; 157 | delete events[value]; 158 | }); 159 | this.set('events', events); 160 | 161 | return this._super.apply(this, Array.prototype.slice.call(arguments)); 162 | } 163 | }); 164 | Ember.Component.reopen(componentProperties); 165 | })(window, Ember, Hammer, (typeof emberHammerOptions === 'undefined' ? false : emberHammerOptions)); 166 | if (typeof emberHammerOptions !== 'undefined') { 167 | emberHammerOptions = null; 168 | delete emberHammerOptions; 169 | } 170 | --------------------------------------------------------------------------------