├── examples ├── use-with-AMD │ ├── assets │ │ ├── bg.jpg │ │ └── style.css │ ├── index.html │ ├── main.js │ └── require.js ├── use-with-ES6 │ ├── assets │ │ ├── bg.jpg │ │ └── style.css │ ├── package.json │ ├── index.html │ ├── main.js │ └── webpack-dev-server.config.js ├── use-with- 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/use-with-CommonJS/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Forcify.js | Demo 6 | 7 | 8 | 9 | 10 |

Forcify

11 |

Touch the sun, on every device.

12 | 13 |
14 |
15 |
16 |
Force: 0
17 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/use-with-AMD/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Forcify.js | Demo 6 | 7 | 8 | 9 | 10 |

Forcify

11 |

Touch the sun, on every device.

12 | 13 |
14 |
15 |
16 |
Force: 0
17 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/use-with- 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forcify", 3 | "version": "0.2.4", 4 | "author": "Hux ", 5 | "description": "Forcify is a JavaScript library help you polyfill 3D/Force Touch in any device.", 6 | "keywords": [ 7 | "forcify", 8 | "3d touch", 9 | "force touch", 10 | "force" 11 | ], 12 | "main": "dist/forcify.min.js", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/Huxpro/forcify" 16 | }, 17 | "bugs": "https://github.com/Huxpro/forcify/issues", 18 | "homepage": "http://huangxuan.me/forcify", 19 | "scripts": { 20 | "build" : "webpack --config webpack.config.js --progress --colors", 21 | "build-dev" : "MODE=dev webpack --config webpack.config.js --progress --colors" 22 | }, 23 | "devDependencies": { 24 | "babel-core": "^5.8.21", 25 | "babel-loader": "^5.3.2", 26 | "webpack": "^1.12.2", 27 | "webpack-dev-server": "^1.10.1" 28 | }, 29 | "dependencies": { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/homepage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Forcify.js 6 | 7 | 8 | 9 | 10 |
11 |
12 |

Forcify

13 |

Use Force Touch in any device, today.

14 |

15 | Github 16 |

17 | 20 |
21 |
22 |

Force: 0.0000

23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2015 Huang Xuan Hux 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 | -------------------------------------------------------------------------------- /examples/use-with-ES6/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Forcify Example 3 | * use with ES6 4 | * 5 | * 6 | * this example use Babel to transform ES6 into ES5 code. 7 | */ 8 | 9 | 10 | //Import Forcify 11 | import Forcify from 'forcify'; 12 | 13 | 14 | 15 | /** 16 | * Cache Element 17 | */ 18 | let element = document.getElementById('forceMe'); 19 | let element2 = document.getElementById('forceMe2'); 20 | let forceValueOutput = document.getElementById('forceValue'); 21 | let background = document.getElementById('background'); 22 | 23 | 24 | 25 | /** 26 | * Create Forcify Instances 27 | */ 28 | let qf = new Forcify(element); 29 | qf.on('force', (e) => { 30 | renderElement(e.force, element) 31 | }); 32 | 33 | // Forcify support Muti-touch! 34 | let qf2 = new Forcify(element2); 35 | qf2.on('force', (e) => { 36 | renderElement(e.force, element2) 37 | }); 38 | 39 | 40 | 41 | /** 42 | * Render! 43 | */ 44 | function renderElement(forceValue, _element) { 45 | // animate element 46 | _element.style.webkitTransform = `translateX(-50%) translateY(-50%) scale(${(1 + forceValue * 1.5)})`; 47 | 48 | // blur background, only in iOS/OSX for performance reasons. 49 | if(Forcify.detection.TOUCH3D || Forcify.detection.OSXFORCE){ 50 | background.style.webkitFilter = `blur(${forceValue * 30}px)`; 51 | } 52 | 53 | // output force value 54 | forceValueOutput.innerHTML = `Force: ${forceValue.toFixed(4)}`; 55 | } 56 | -------------------------------------------------------------------------------- /examples/use-with- 85 | ``` 86 | 87 | And you can also include Forcify in your JavaScript bundle with ES6, CommonJS or AMD syntax. 88 | 89 | ```bash 90 | $ npm install forcify --save 91 | ``` 92 | 93 | ### Usage 94 | 95 | Create a new Forcify instance, and use `on` to listen `force` event: 96 | 97 | ```javascript 98 | var ele = document.querySelector('#force') 99 | var $ele = new Forcify(ele) 100 | 101 | // add event listener 102 | $ele.on('force', (e) => { 103 | doSomething(e.force) 104 | }) 105 | ``` 106 | 107 | You can pass `options` into the `Forcify` constructor to override [default options](#forcifydefaults): 108 | 109 | ```javascript 110 | // only emit event in real supported device. 111 | var $noFallback = new Forcify(ele, { 112 | FALLBACK_TO_LONGPRESS: false 113 | }) 114 | 115 | // I am sure there would be a mess watting for u 116 | var $noShim = new Forcify(ele, { 117 | SHIM_WEIRD_BROWSWR: false 118 | }) 119 | 120 | // not easy to trigger... 121 | var $longLongPress = new Forcify(ele, { 122 | LONG_PRESS_DELAY: 10000 //ms 123 | }) 124 | ``` 125 | 126 | Also, you can use `Forcify.config` to override default options globally 127 | 128 | ```javascript 129 | // let's make duration of the force grow slower. 130 | Forcify.config({ 131 | LONG_PRESS_DURATION: 500 132 | }) 133 | 134 | ``` 135 | 136 | ## API 137 | 138 | 139 | ### Forcify.defaults 140 | 141 | Default options for Forcify instance. 142 | 143 | ##### `LONG_PRESS_DELAY: 200` 144 | 145 | - Type: `Number` 146 | - Default `200(ms)` 147 | - Delay to trigger fake Force Touch 148 | 149 | 150 | 151 | ##### `LONG_PRESS_DURATION: 100` 152 | 153 | * Type: `Number` 154 | * Default `1000(ms)` 155 | * Duration from MIN to MAX of the fake Force Touch 156 | 157 | 158 | 159 | ##### `FALLBACK_TO_LONGPRESS: true` 160 | 161 | * Type: `Boolean` 162 | * Default `true` 163 | * if Forcify fallback to long press on unsupport devices. if set false, Forcify will not fallback 'force' to 'long press' 164 | 165 | 166 | ##### `SHIM_WEIRD_BROWSER: true` 167 | 168 | * Type: `Boolean` 169 | * Default `true` 170 | * Some browser, such as Chrome, provide a very weird force value. if set false, Forcify would not try to find and ignore those weird behavior. Which means your "Force Actions" may 171 | - be triggered just by a click in some 'force: 1' devices. 172 | - be influenced in device like Nexus5 to give a force in (0,1) 173 | 174 | 175 | ### Forcify.detection 176 | 177 | Object save the results of dynamic detection. All fields is `Boolean` type and default `false`. 178 | 179 | ##### `TOUCH3D` 180 | 181 | Unfortunately there is not a feature detection for 3DTouch so far, so Forcify use a dynamic detection to detect it. 182 | If Forcify detects that force is support, all hacking stop. 183 | 184 | 185 | ##### `OSXFORCE` 186 | 187 | OSX support real webkit force touch 188 | 189 | 190 | ##### `WEIRD_CHROME` 191 | 192 | Chrome Mobile give any touchevent a 'force' property with value: 1. 193 | Forcify has to hack it. 194 | Forcify not detect Weird Chrome by UA but behaviors. 195 | 196 | ## Known Issues 197 | 198 | - When user use a old Macbook without force touch but a Magic Trackpad 2 and switch between them. 199 | 200 | ## Thanks 201 | 202 | Special thank to: 203 | 204 | - [This nice post about iOS9](http://www.mobilexweb.com/blog/ios9-safari-for-web-developers) inspired me to create Forcify. 205 | - [3D Touch Demo](https://github.com/freinbichler/3d-touch) by @freinbichler, which I used in my examples. 206 | -------------------------------------------------------------------------------- /examples/use-with-AMD/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& 19 | (f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= 20 | this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); 21 | if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", 22 | "fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, 23 | a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p, 24 | nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b, 25 | a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild= 26 | !0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!== 27 | e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)): 34 | (e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl= 35 | O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b|| 36 | (b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); -------------------------------------------------------------------------------- /dist/forcify.debug.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["Forcify"] = factory(); 8 | else 9 | root["Forcify"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | module.exports = __webpack_require__(1); 58 | 59 | 60 | /***/ }, 61 | /* 1 */ 62 | /***/ function(module, exports, __webpack_require__) { 63 | 64 | /** 65 | * forcify.js 66 | * 67 | * Core of forcify.js 68 | * created by @huxpro 69 | */ 70 | 71 | // uuid 72 | 'use strict'; 73 | 74 | Object.defineProperty(exports, '__esModule', { 75 | value: true 76 | }); 77 | 78 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 79 | 80 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 81 | 82 | var _uid = 0; 83 | 84 | // polyfill 85 | Object.extend = Object.assign ? Object.assign : function (copy, src) { 86 | for (var prop in src) { 87 | copy[prop] = src[prop]; 88 | } 89 | return copy; 90 | }; 91 | 92 | /** 93 | * @class Forcify 94 | */ 95 | 96 | var Forcify = (function () { 97 | 98 | /** 99 | * @constructor create a new Forcify Object. 100 | * @param {Node} element TODO: support Selector 101 | * @param {Object} options customize options 102 | * @return {Forcify} Instance of Forcify, you can call it 'Forcify Object' 103 | */ 104 | 105 | function Forcify(element, options) { 106 | _classCallCheck(this, Forcify); 107 | 108 | this.uid = ++_uid; // uid 109 | this.element = element; // cache element 110 | this.handlers = {}; // event handlers 111 | this.touch = null; // unique touch id 112 | this.timer = null; // unique press timer 113 | this._pressTimeStamp = 0; // unique press-time stamp 114 | 115 | // Merge Instance Options. 116 | var _temp = Object.extend({}, Forcify.defaults); 117 | this.options = Object.extend(_temp, options); 118 | 119 | // Set uid to DOM object for cache use. Decrease time complexity 120 | this.element.__fuid__ = this.uid; 121 | 122 | // Cache forcify instance (this) 123 | var caches = Forcify.cache; 124 | caches[this.uid] = this; 125 | } 126 | 127 | // Static Members 128 | 129 | /** 130 | * Bind event 131 | * @param {String} event Type of event 132 | * @param {fn} handler Event handlers, TODO: support fn[] 133 | * @return {Forcify} 134 | */ 135 | 136 | _createClass(Forcify, [{ 137 | key: 'on', 138 | value: function on(event, handler) { 139 | var handlers = this.handlers; 140 | 141 | handlers[event] = handlers[event] || []; 142 | handlers[event].push(handler); 143 | 144 | return this; 145 | } 146 | 147 | /** 148 | * Invoke event handlers 149 | * @param {Forcify} _instance 150 | * @param {Object} e Forcify custom event object 151 | * @return {null} 152 | */ 153 | }, { 154 | key: 'invokeHandlers', 155 | value: function invokeHandlers(_instance, e) { 156 | //console.log(_instance); 157 | console.log(e); 158 | _instance.handlers['force'].forEach(function (fn) { 159 | fn(e); 160 | }); 161 | } 162 | }]); 163 | 164 | return Forcify; 165 | })(); 166 | 167 | exports['default'] = Forcify; 168 | Forcify.emitEvent = function (_instance, nativeEvent) { 169 | var force = 0; 170 | var type = nativeEvent.type; 171 | 172 | console.log('type: ' + type); 173 | console.log(nativeEvent); 174 | nativeEvent.preventDefault(); // !important 175 | 176 | // Event Recognizer 177 | if (type == "touchend" || type == "touchmove" || type == "touchstart") { 178 | _instance.handleTouch(_instance, nativeEvent); 179 | } 180 | 181 | if (type == 'webkitmouseforcewillbegin' || type == 'webkitmouseforcechanged') { 182 | Forcify.detection.OSXFORCE = true; // if these event triggered, this is OSX. 183 | _instance.handleMouseForce(_instance, nativeEvent); 184 | } 185 | if (type == 'mousedown' || type == 'mouseup') { 186 | if (Forcify.detection.OSXFORCE) return; // OSX should skip this 187 | _instance.handlePress(_instance, nativeEvent); 188 | } 189 | }; 190 | 191 | /** 192 | * Forcify global coniguration 193 | * @param {object} _config 194 | */ 195 | Forcify.config = function (_config) { 196 | // Merge config.defaults into Forcify.defaults 197 | if (_config.defaults) { 198 | Forcify.defaults = Object.extend(Forcify.defaults, _config.defaults); 199 | } 200 | }; 201 | 202 | // Cache elements reference 203 | Forcify.cache = {}; 204 | 205 | // whether event bobble, globally 206 | // inner options for now. 207 | Forcify.__EVENT_BOBBLE__ = false; 208 | 209 | // Default Options 210 | Forcify.defaults = { 211 | /** 212 | * Delay to trigger fake Force Touch 213 | * @type {Number} 214 | * @default 200(ms) 215 | */ 216 | LONG_PRESS_DELAY: 200, 217 | 218 | /** 219 | * Duration from MIN to MAX of the fake Force Touch 220 | * @type {Number} 221 | * @default 1000(ms) 222 | */ 223 | LONG_PRESS_DURATION: 100, 224 | 225 | /** 226 | * if Forcify fallback to long press on unsupport devices 227 | * if set false, Forcify will not fallback 'force' to 'long press' 228 | * @type {Boolean} 229 | * @default true 230 | */ 231 | FALLBACK_TO_LONGPRESS: true, 232 | 233 | /** 234 | * Some browser, such as Chrome, provide a very weird force value. 235 | * if set false, Forcify would not try to find and ignore those weird 236 | * behavior. 237 | * Which means your "Force Actions" may 238 | * - be triggered just by a click in some 'force: 1' devices. 239 | * - be influenced in device like Nexus5 to give a force in (0,1) 240 | * @type {Boolean} 241 | * @default true 242 | */ 243 | SHIM_WEIRD_BROWSER: true 244 | }; 245 | 246 | // Detections 247 | Forcify.detection = { 248 | /** 249 | * Unfortunately there is not a feature detection for 3DTouch so far, so Forcify use a dynamic detection to detect it 250 | * If Forcify detects that force is support, all hacking stop 251 | * 252 | * @type {Boolean} 253 | * @default false 254 | */ 255 | TOUCH3D: false, 256 | 257 | /** 258 | * OSX support real webkit force touch 259 | * 260 | * @type {Boolean} 261 | * @default false 262 | */ 263 | OSXFORCE: false, 264 | 265 | /** 266 | * Chrome Mobile give any touchevent a 'force' property with value: 1. 267 | * Forcify has to hack it. 268 | * Forcify not detect Chrome by UA but behaviors. 269 | * 270 | * @type {Boolean} 271 | * @default false 272 | */ 273 | WEIRD_CHROME: false, 274 | 275 | /** 276 | * Android performs really odd, which performs different in diff devices. 277 | * Forcify has to use UA detection to handle them. 278 | * 279 | * @type {Boolean} 280 | * @default false 281 | */ 282 | ANDROID: navigator.userAgent.match(/(Android);?[\s\/]+([\d.]+)?/) 283 | }; 284 | 285 | // import modules 286 | new (__webpack_require__(2))(Forcify); 287 | new (__webpack_require__(3))(Forcify); 288 | new (__webpack_require__(4))(Forcify); 289 | new (__webpack_require__(5))(Forcify); 290 | 291 | // debugging use. 292 | // window.Forcify = Forcify; 293 | module.exports = exports['default']; 294 | 295 | /***/ }, 296 | /* 2 */ 297 | /***/ function(module, exports) { 298 | 299 | /** 300 | * touch.js 301 | * 302 | * Touch module of forcify.js 303 | * created by @huxpro 304 | */ 305 | 306 | "use strict"; 307 | 308 | Object.defineProperty(exports, "__esModule", { 309 | value: true 310 | }); 311 | 312 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 313 | 314 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 315 | 316 | var Touch = (function () { 317 | function Touch(Forcify) { 318 | _classCallCheck(this, Touch); 319 | 320 | this.init(Forcify); 321 | } 322 | 323 | _createClass(Touch, [{ 324 | key: "init", 325 | value: function init(Forcify) { 326 | var fn = Forcify.prototype; 327 | fn.handleTouch = function (_instance, nativeEvent) { 328 | var type = nativeEvent.type; 329 | 330 | // Forcify strictly limit every view respond to only one finger 331 | // So targetTouches always empty when touchend, 332 | // So we first deal with touchend 333 | if (type == "touchend") { 334 | // reset touch ref 335 | _instance.touch = null; 336 | // also send to handlePress 337 | _instance.handlePress(_instance, nativeEvent); 338 | return; 339 | } 340 | 341 | /** 342 | * It's hard to judge if Force Touch is truly supported: 343 | * 344 | * Events from supported iPhone always begin with force:0, 345 | * Events from unsupport iPhone with iOS 9 also include force:0, 346 | * We never know if a iPhone is unsupported or just not be "force-touched" 347 | * 348 | * Events from Chrome Touchable-PC include only force:0, behave well. 349 | * Events from Chrome Emulator include only force:1, not shim yet 350 | * Events from Chrome Mobile include both force:1 and webkitForce:1, 351 | * 352 | * Events from Chrome Nexus5 provide a very weird force value, 353 | * which seems depend on the AREA of the finger touched to screen! 354 | * 355 | * 356 | * Soooooooooooo sad, 357 | * Forcify has to use a Dynamic Detection to try to shim them 358 | */ 359 | 360 | /** 361 | * Shim weird browser 362 | */ 363 | if (_instance.options.SHIM_WEIRD_BROWSER) { 364 | 365 | // detect Weird Chrome 366 | if (nativeEvent.targetTouches[0].force == 1 && nativeEvent.targetTouches[0].webkitForce == 1) Forcify.detection.WEIRD_CHROME = true; 367 | 368 | // if it's Weird Chrome or Android(ensure unsupport now) 369 | if (Forcify.detection.WEIRD_CHROME || Forcify.detection.ANDROID) { 370 | console.log("CHROME, send into handlePress.."); 371 | _instance.handlePress(_instance, nativeEvent); 372 | return; 373 | }; 374 | } 375 | 376 | /** 377 | * Fallback to handlePress. 378 | */ 379 | var _force = nativeEvent.targetTouches[0].force; 380 | 381 | // if 'force' props is undefined 382 | // let handlePress to handle unsupport devices 383 | if (typeof _force == "undefined") { 384 | _instance.handlePress(_instance, nativeEvent); 385 | return; 386 | } 387 | 388 | // seeing Nexus5, we had to abandon android. 389 | // would change if there is android devices support a real 3DTouch 390 | if (_force > 0 && _force < 1 && !Forcify.detection.ANDROID) Forcify.detection.TOUCH3D = true; 391 | 392 | // If forceValue == 0, Forcify sent events to handlePress 393 | // but keep handleTouch procedure (for pre-polling) 394 | // alert(nativeEvent.targetTouches[0].force) 395 | if (_force == "0") { 396 | if (!Forcify.detection.TOUCH3D) { 397 | console.log("send into handlePress.."); 398 | _instance.handlePress(_instance, nativeEvent); 399 | } 400 | //not return 401 | } 402 | 403 | /** 404 | * All Test Passed, try to polling force value 405 | */ 406 | // Forcify support multi-touch in multi-view, 407 | // But in one view we only use the first touch. 408 | if (nativeEvent.targetTouches && nativeEvent.targetTouches.length > 1) return; 409 | if (_instance.touch) return; // prevent repeat binding 410 | _instance.touch = nativeEvent.targetTouches[0]; // get first 411 | 412 | // seeing we don't have `touchforcechange` event, 413 | // so we need to do it ourselves with polling. 414 | _instance.pollingTouchForce.bind(_instance.touch, _instance, nativeEvent)(); 415 | }; 416 | 417 | fn.pollingTouchForce = function (_instance, nativeEvent) { 418 | /** 419 | * Start Polling procedure 420 | */ 421 | console.log('polling...'); 422 | 423 | var touchEvent = this; 424 | var force = 0; 425 | var sendEvent = true; 426 | 427 | if (_instance.touch) { 428 | force = touchEvent.force; 429 | // dynamic detection real 3DTouch 430 | if (force > 0 && force < 1) Forcify.detection.TOUCH3D = true; 431 | 432 | // repeat polling 433 | setTimeout(_instance.pollingTouchForce.bind(_instance.touch, _instance, nativeEvent), 10); 434 | 435 | // a little hacking!! 436 | // if force == 0 passed to here, 437 | // this may well be a unsuppoted device provided force:0, such as iPhone5 438 | // but also probably a supported device with not "force" pressed. 439 | // 440 | // so we dont know and would NOT send force event: 441 | // for unsupport device, let handlePress fake it. 442 | // for supported device, event force:0 is useless until detected. 443 | if (force == 0 && !Forcify.detection.TOUCH3D) { 444 | sendEvent = false; 445 | } 446 | } else { 447 | // touchend trigger! sent 0; 448 | force = 0; 449 | sendEvent = true; 450 | } 451 | 452 | if (sendEvent) { 453 | var _e = { force: force, nativeEvent: nativeEvent }; 454 | _instance.invokeHandlers(_instance, _e); 455 | } 456 | }; 457 | } 458 | }]); 459 | 460 | return Touch; 461 | })(); 462 | 463 | exports["default"] = Touch; 464 | module.exports = exports["default"]; 465 | 466 | /***/ }, 467 | /* 3 */ 468 | /***/ function(module, exports) { 469 | 470 | /** 471 | * mouse.js 472 | * 473 | * Mouse module of forcify.js 474 | * created by @huxpro 475 | */ 476 | 477 | "use strict"; 478 | 479 | Object.defineProperty(exports, "__esModule", { 480 | value: true 481 | }); 482 | 483 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 484 | 485 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 486 | 487 | var Mouse = (function () { 488 | function Mouse(Forcify) { 489 | _classCallCheck(this, Mouse); 490 | 491 | this.init(Forcify); 492 | } 493 | 494 | _createClass(Mouse, [{ 495 | key: "init", 496 | value: function init(Forcify) { 497 | var fn = Forcify.prototype; 498 | 499 | // if mouse support force 500 | fn.handleMouseForce = function (_instance, nativeEvent) { 501 | // max value for trackpad is 3.0 compare to 1.0 on iOS 502 | var force = nativeEvent.webkitForce / 3; 503 | var _e = { 504 | force: force, 505 | nativeEvent: nativeEvent 506 | }; 507 | _instance.invokeHandlers(_instance, _e); 508 | }; 509 | } 510 | }]); 511 | 512 | return Mouse; 513 | })(); 514 | 515 | exports["default"] = Mouse; 516 | module.exports = exports["default"]; 517 | 518 | /***/ }, 519 | /* 4 */ 520 | /***/ function(module, exports) { 521 | 522 | /** 523 | * press.js 524 | * 525 | * Press module of forcify.js 526 | * created by @huxpro 527 | */ 528 | 529 | "use strict"; 530 | 531 | Object.defineProperty(exports, "__esModule", { 532 | value: true 533 | }); 534 | 535 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 536 | 537 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 538 | 539 | var Press = (function () { 540 | function Press(Forcify) { 541 | _classCallCheck(this, Press); 542 | 543 | this.init(Forcify); 544 | } 545 | 546 | _createClass(Press, [{ 547 | key: "init", 548 | value: function init(Forcify) { 549 | var fn = Forcify.prototype; 550 | 551 | // fallback to long press (mouse || touch) 552 | fn.handlePress = function (_instance, nativeEvent) { 553 | 554 | // if not fallback, early return everything. 555 | if (!_instance.options.FALLBACK_TO_LONGPRESS) return; 556 | 557 | /** 558 | * Start do fallback! 559 | */ 560 | var delay = _instance.options.LONG_PRESS_DELAY; 561 | var duration = _instance.options.LONG_PRESS_DURATION; 562 | var type = nativeEvent.type; 563 | 564 | // Press Down 565 | if (type === "mousedown" || type === "touchstart") { 566 | _instance._pressTimeStamp = Date.now(); 567 | _instance.timer = window.setTimeout(function () { 568 | // Long Press! 569 | // Let's trigger fake Force now. 570 | _instance.repeatPushForceValue(_instance, nativeEvent); 571 | }, delay); 572 | } 573 | 574 | // Press Up 575 | if (type === 'mouseup' || type === "touchend") { 576 | window.clearTimeout(_instance.timer); 577 | if (Date.now() - _instance._pressTimeStamp > delay) { 578 | // stop pushing 579 | _instance._pressTimeStamp = 0; 580 | 581 | // reset force. 582 | var _e = { 583 | force: 0, 584 | nativeEvent: nativeEvent 585 | }; 586 | _instance.invokeHandlers(_instance, _e); 587 | } 588 | } 589 | }; 590 | 591 | fn.repeatPushForceValue = function (_instance, nativeEvent) { 592 | // This pushing is repeating except: 593 | // 594 | // - Force support is detected to true 595 | if (Forcify.detection.TOUCH3D) return; 596 | 597 | // - touchend or mouseup trigger (_pressTimeStamp is reset) 598 | if (_instance._pressTimeStamp == 0) return; 599 | 600 | /** 601 | * Start Pushing procedure 602 | */ 603 | console.log('start pushing...'); 604 | 605 | var delay = _instance.options.LONG_PRESS_DELAY; 606 | var duration = _instance.options.LONG_PRESS_DURATION; 607 | 608 | // calculate fake force value 609 | var _ratio = (Date.now() - _instance._pressTimeStamp - delay) / duration; 610 | // the max force value is 1 611 | var _force = _ratio >= 1 ? 1 : _ratio; 612 | 613 | // invoke event handlers 614 | var _e = { 615 | force: _force, 616 | nativeEvent: nativeEvent 617 | }; 618 | _instance.invokeHandlers(_instance, _e); 619 | 620 | /** 621 | * repeat pushing... 622 | */ 623 | window.setTimeout(_instance.repeatPushForceValue.bind(null, _instance, nativeEvent, delay), 20); 624 | }; 625 | } 626 | }]); 627 | 628 | return Press; 629 | })(); 630 | 631 | exports["default"] = Press; 632 | module.exports = exports["default"]; 633 | 634 | /***/ }, 635 | /* 5 */ 636 | /***/ function(module, exports) { 637 | 638 | /** 639 | * delegate.js 640 | * 641 | * Delegate module of forcify.js 642 | * created by @huxpro 643 | */ 644 | 645 | 'use strict'; 646 | 647 | Object.defineProperty(exports, '__esModule', { 648 | value: true 649 | }); 650 | 651 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 652 | 653 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 654 | 655 | var Delegate = (function () { 656 | function Delegate(Forcify) { 657 | _classCallCheck(this, Delegate); 658 | 659 | this.init(Forcify); 660 | } 661 | 662 | _createClass(Delegate, [{ 663 | key: 'init', 664 | value: function init(Forcify) { 665 | function bindEvent() { 666 | // event need to be delegated 667 | var events = ['mousedown', 'mouseup', 'touchstart', 'touchmove', 'touchend', 'webkitmouseforcewillbegin', 'webkitmouseforcechanged']; 668 | events.forEach(function (e) { 669 | document.addEventListener(e, handleDelegate); 670 | }); 671 | } 672 | 673 | function handleDelegate(nativeEvent) { 674 | // stop event propagation. 675 | // check e.target only. 676 | if (!Forcify.__EVENT_BOBBLE__) { 677 | var _target = nativeEvent.target; 678 | var _uid = _target.__fuid__; 679 | emitByUID(_uid, nativeEvent); 680 | return; 681 | } 682 | 683 | // support event bubble 684 | // taversal e.path 685 | var _path = nativeEvent.path; 686 | _path.forEach(function (ele) { 687 | if (!ele.__fuid__) return; // fuid is ensure > 0 688 | var _uid = ele.__fuid__; 689 | emitByUID(_uid, nativeEvent); 690 | }); 691 | } 692 | 693 | function emitByUID(_uid, nativeEvent) { 694 | var caches = Forcify.cache; 695 | // if element targeted is a registered Forcify Instance. 696 | if (caches[_uid]) { 697 | // Target! Let's emit the event 698 | Forcify.emitEvent(caches[_uid], nativeEvent); 699 | } 700 | } 701 | 702 | bindEvent(); 703 | } 704 | }]); 705 | 706 | return Delegate; 707 | })(); 708 | 709 | exports['default'] = Delegate; 710 | module.exports = exports['default']; 711 | 712 | /***/ } 713 | /******/ ]) 714 | }); 715 | ; --------------------------------------------------------------------------------