├── .gitignore ├── logo ├── logo.png ├── text.png └── logos.sketch ├── test ├── spin.gif └── index.html ├── .travis.yml ├── LICENSE ├── dist ├── css │ ├── threeFingerTap.min.css │ └── threeFingerTap.css └── js │ ├── threeFingerTap.min.js │ └── threeFingerTap.js ├── tests └── init.test.js ├── src ├── scss │ └── threeFingerTap.scss └── js │ └── index.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sidthesloth92/three-finger-tap-js/HEAD/logo/logo.png -------------------------------------------------------------------------------- /logo/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sidthesloth92/three-finger-tap-js/HEAD/logo/text.png -------------------------------------------------------------------------------- /test/spin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sidthesloth92/three-finger-tap-js/HEAD/test/spin.gif -------------------------------------------------------------------------------- /logo/logos.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sidthesloth92/three-finger-tap-js/HEAD/logo/logos.sketch -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - '4' 10 | before_install: 11 | - npm i -g npm@^2.0.0 12 | before_script: 13 | - npm prune 14 | after_success: 15 | - npm run semantic-release 16 | branches: 17 | except: 18 | - /^v\d+\.\d+\.\d+$/ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dinesh Balaji 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 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, 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/css/threeFingerTap.min.css: -------------------------------------------------------------------------------- 1 | .tft_iframe_wrapper{position:fixed;width:70%;height:45%;box-shadow:0 0 5px 0 #4b4b4b;visibility:hidden;transition:-webkit-transform .1s linear;transition:transform .1s linear;transition:transform .1s linear,-webkit-transform .1s linear;background:center no-repeat #4b4b4b;background-size:50px;-webkit-transform:scale(0,0);transform:scale(0,0);overflow:auto;-ms-overflow-style:-ms-autohiding-scrollbar}.tft_iframe_wrapper.show{display:block;visibility:visible;-webkit-transform:scale(1,1);transform:scale(1,1)}.tft_iframe_wrapper iframe{width:100%;height:100%;border-style:none}.tft_iframe_wrapper .loader{position:absolute;width:30px;height:30px;left:50%;top:50%;border-radius:50%;border:5px solid transparent;border-top-color:rgba(255,255,255,.4);-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1;-webkit-animation:spin 2s linear infinite;animation:spin 2s linear infinite}.noscroll{overflow:hidden;-webkit-overflow-scrolling:touch!important;cursor:pointer}@-webkit-keyframes spin{from{-webkit-transform:translate(-50%,-50%) rotate(0);transform:translate(-50%,-50%) rotate(0)}to{-webkit-transform:translate(-50%,-50%) rotate(360deg);transform:translate(-50%,-50%) rotate(360deg)}}@keyframes spin{from{-webkit-transform:translate(-50%,-50%) rotate(0);transform:translate(-50%,-50%) rotate(0)}to{-webkit-transform:translate(-50%,-50%) rotate(360deg);transform:translate(-50%,-50%) rotate(360deg)}} -------------------------------------------------------------------------------- /tests/init.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const threeFingerTap = require('../dist/js/threeFingerTap.js'); 4 | 5 | let name = 'three-finger-tap'; 6 | let hoverTimeout = 1000; 7 | 8 | describe("test initialization", () => { 9 | 10 | test("test name initialization and hover default" , () => { 11 | threeFingerTap.init({ 12 | name : name, 13 | hoverTimeout : hoverTimeout 14 | }); 15 | 16 | expect(document.body.getElementsByClassName('tft_iframe_wrapper')).toHaveLength(1); 17 | expect(threeFingerTap.getName()).toBe(name); 18 | expect(threeFingerTap.getHoverTimeout()).toEqual(hoverTimeout); 19 | }); 20 | 21 | test("test custom hoverTimeout initialization" , () => { 22 | threeFingerTap.init({ 23 | name : name, 24 | hoverTimeout : 2000 25 | }); 26 | 27 | expect(document.body.getElementsByClassName('tft_iframe_wrapper')).toHaveLength(1); 28 | expect(threeFingerTap.getHoverTimeout()).toEqual(2000); 29 | }); 30 | 31 | test("test default customLoadingBackground" , () => { 32 | threeFingerTap.init({ 33 | name : name, 34 | hoverTimeout : hoverTimeout 35 | }); 36 | 37 | expect(document.body.getElementsByClassName('tft_iframe_wrapper')).toHaveLength(1); 38 | expect(document.body.querySelector('.tft_iframe_wrapper').querySelector('.loader')).toBeDefined(); 39 | }); 40 | 41 | afterEach(() => { 42 | threeFingerTap.destroy(); 43 | }); 44 | 45 | }); -------------------------------------------------------------------------------- /dist/css/threeFingerTap.css: -------------------------------------------------------------------------------- 1 | .tft_iframe_wrapper { 2 | position: fixed; 3 | width: 70%; 4 | height: 45%; 5 | box-shadow: 0px 0px 5px 0px #4b4b4b; 6 | visibility: hidden; 7 | transition: -webkit-transform .1s linear; 8 | transition: transform .1s linear; 9 | transition: transform .1s linear, -webkit-transform .1s linear; 10 | background: #4b4b4b; 11 | background-repeat: no-repeat; 12 | background-size: 50px; 13 | background-position: center; 14 | -webkit-transform: scale(0, 0); 15 | transform: scale(0, 0); 16 | overflow: auto; 17 | -ms-overflow-style: -ms-autohiding-scrollbar; } 18 | .tft_iframe_wrapper.show { 19 | display: block; 20 | visibility: visible; 21 | -webkit-transform: scale(1, 1); 22 | transform: scale(1, 1); } 23 | .tft_iframe_wrapper iframe { 24 | width: 100%; 25 | height: 100%; 26 | border-style: none; } 27 | .tft_iframe_wrapper .loader { 28 | position: absolute; 29 | width: 30px; 30 | height: 30px; 31 | left: 50%; 32 | top: 50%; 33 | border-radius: 50%; 34 | border: 5px solid transparent; 35 | border-top-color: rgba(255, 255, 255, 0.4); 36 | -webkit-transform: translate(-50%, -50%); 37 | transform: translate(-50%, -50%); 38 | z-index: -1; 39 | -webkit-animation: spin 2s linear infinite; 40 | animation: spin 2s linear infinite; } 41 | 42 | .noscroll { 43 | overflow: hidden; 44 | -webkit-overflow-scrolling: touch !important; 45 | cursor: pointer; } 46 | 47 | @-webkit-keyframes spin { 48 | from { 49 | -webkit-transform: translate(-50%, -50%) rotate(0deg); 50 | transform: translate(-50%, -50%) rotate(0deg); } 51 | to { 52 | -webkit-transform: translate(-50%, -50%) rotate(360deg); 53 | transform: translate(-50%, -50%) rotate(360deg); } } 54 | 55 | @keyframes spin { 56 | from { 57 | -webkit-transform: translate(-50%, -50%) rotate(0deg); 58 | transform: translate(-50%, -50%) rotate(0deg); } 59 | to { 60 | -webkit-transform: translate(-50%, -50%) rotate(360deg); 61 | transform: translate(-50%, -50%) rotate(360deg); } } 62 | -------------------------------------------------------------------------------- /src/scss/threeFingerTap.scss: -------------------------------------------------------------------------------- 1 | .tft_iframe_wrapper { 2 | position: fixed; 3 | width: 70%; 4 | height: 45%; 5 | box-shadow: 0px 0px 5px 0px #4b4b4b; 6 | visibility: hidden; 7 | transition: transform .1s linear; 8 | background: #4b4b4b; 9 | background-repeat: no-repeat; 10 | background-size: 50px; 11 | background-position: center; 12 | transform: scale(0, 0); 13 | overflow: auto; 14 | // To remove scrollbar from appearing in IE Edge 15 | -ms-overflow-style: -ms-autohiding-scrollbar; 16 | 17 | &.show { 18 | display: block; 19 | visibility: visible; 20 | transform: scale(1, 1); 21 | } 22 | // &:before { 23 | // content: ''; 24 | // position: absolute; 25 | // top: 0; 26 | // left: 50%; 27 | // width: 0px; 28 | // height: 0px; 29 | // border-left: 10px solid transparent; 30 | // border-right: 10px solid transparent; 31 | // border-bottom: 10px solid #4a4a4a; 32 | // transform: translate(-50%, -100%); 33 | // } 34 | iframe { 35 | width: 100%; 36 | height: 100%; 37 | border-style: none; 38 | } 39 | .loader { 40 | position: absolute; 41 | width: 30px; 42 | height: 30px; 43 | left: 50%; 44 | top: 50%; 45 | border-radius: 50%; 46 | border: 5px solid rgba(0, 0, 0, 0); 47 | border-top-color: rgba(255, 255, 255, 0.4); 48 | transform: translate(-50%, -50%); 49 | z-index: -1; 50 | animation: spin 2s linear infinite; 51 | } 52 | } 53 | 54 | .noscroll { 55 | overflow: hidden; 56 | 57 | //Fixed content not scrolling scrolls the background 58 | // https://stackoverflow.com/questions/29001977/safari-in-ios8-is-scrolling-screen-when-fixed-elements-get-focus 59 | -webkit-overflow-scrolling: touch !important; 60 | //Click not working in iOS safari 61 | // https://stackoverflow.com/questions/14795944/jquery-click-events-not-working-in-ios 62 | cursor: pointer; 63 | } 64 | 65 | @keyframes spin { 66 | from { transform: translate(-50%, -50%) rotate(0deg); } 67 | to { transform: translate(-50%, -50%) rotate(360deg); } 68 | } 69 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 |
](http://godban.github.io/browsers-support-badges/)IE / Edge | [
](http://godban.github.io/browsers-support-badges/)Firefox | [
](http://godban.github.io/browsers-support-badges/)Chrome | [
](http://godban.github.io/browsers-support-badges/)Safari | [
](http://godban.github.io/browsers-support-badges/)Opera |
146 | | --------- | --------- | --------- | --------- | --------- |
147 |
148 | # Contact
149 | If you have any issues report them at [Issues](https://github.com/sidthesloth92/three-finger-tap-js/issues)
150 |
151 | # Source
152 | [Github](https://github.com/sidthesloth92/three-finger-tap-js)
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | (() => {
2 |
3 | // Private variables
4 | let _currentNode; // specifies the current DOM element hovered or tapped
5 | let _timeout; // specifies the hover timeout or tap timeout
6 | let _name; // specifies the CSS class name
7 | let _initialized; // specifies whether the library is initialized or not
8 | let _isMobile; // indicates a mobile device or nont
9 |
10 | // API variables
11 | let _hoverTimeout; // user specified value for the hover timeout, set to 500 if mobile
12 | let _customLoadingBackground; // user specified backgroundImage CSS value
13 | let _enable = true; // user specified value indicating whether the library is currently active
14 |
15 | // Private variables
16 | let _count = 0; // number of times the user has tapped the link within the timeout
17 | let _openLink = false;
18 |
19 | // DOM nodes
20 | let _body;
21 | let _iframeWrapper;
22 | let _iframe;
23 |
24 | // API Methods
25 | function init({ name, hoverTimeout, customLoadingBackground}) {
26 | if(!_initialized) {
27 | _initialized = true;
28 | }
29 | else {
30 | throw new Error("Library already initialized");
31 | }
32 | _isMobile = _isTouchDevice();
33 | setName(name);
34 | setHoverTimeout(hoverTimeout);
35 | setCustomLoadingBackground(customLoadingBackground);
36 | _constructDOM();
37 | _addEventListeners();
38 | _enable = true;
39 | }
40 |
41 | function destroy() {
42 | _body.removeChild(_iframeWrapper);
43 |
44 | window.removeEventListener('mousemove', _browserFunctionality);
45 | window.removeEventListener('click', _mobileFunctionality);
46 |
47 | _body.removeEventListener('click', _hidePreviewWindow);
48 |
49 | _body = _iframeWrapper = _iframe = _name = _isMobile = _customLoadingBackground = _hoverTimeout = undefined;
50 | _reset();
51 |
52 | _initialized = false;
53 | _enable = false;
54 | }
55 |
56 | function enable() {
57 | _enable = true;
58 | }
59 |
60 | function disable() {
61 | _enable = false;
62 | }
63 |
64 | function setName(name) {
65 | if (!name) {
66 | throw new Error("Name not specified");
67 | }
68 | _name = name;
69 | }
70 |
71 | function getName() {
72 | return _name;
73 | }
74 |
75 | function setHoverTimeout(hoverTimeout) {
76 | if(!_isMobile) {
77 | if (isNaN(hoverTimeout)) {
78 | throw new Error("hoverTimeout should have a numerical value");
79 | }
80 | _hoverTimeout = hoverTimeout;
81 | }
82 | else {
83 | _hoverTimeout = 500;
84 | console.log("Touch device. hoverTimeout value ignored.");
85 | }
86 | }
87 |
88 | function getHoverTimeout() {
89 | return _hoverTimeout;
90 | }
91 |
92 | function setCustomLoadingBackground(customLoadingBackground) {
93 | let tempDiv = document.createElement('div');
94 | tempDiv.style.backgroundImage = customLoadingBackground;
95 |
96 | if (tempDiv.style.backgroundImage && !customLoadingBackground) {
97 | throw new Error("Invalid value for customLoadingBackground. Must be a possible value for CSS backgroundImage property");
98 | }
99 | _customLoadingBackground = customLoadingBackground;
100 | _updateCustomLoadingBackground();
101 | }
102 |
103 | function getCustomLoadingBackground() {
104 | return _customLoadingBackground;
105 | }
106 |
107 | function getIsMobileDevice() {
108 | return _isMobile;
109 | }
110 |
111 | // Private Methods
112 | function _isTouchDevice() {
113 | return window.hasOwnProperty('ontouchstart');
114 | }
115 |
116 | function _reset() {
117 | _currentNode = undefined;
118 | _openLink = false;
119 | _count = 0;
120 | clearTimeout(_timeout);
121 | }
122 |
123 | function _constructDOM() {
124 | let fragment = document.createDocumentFragment();
125 |
126 | _body = document.querySelector('body');
127 | _iframeWrapper = document.createElement('div');
128 | _iframeWrapper.classList.add('tft_iframe_wrapper');
129 |
130 | _iframe = document.createElement('iframe');
131 | _iframeWrapper.appendChild(_iframe);
132 |
133 | fragment.appendChild(_iframeWrapper);
134 | _body.appendChild(fragment);
135 |
136 | _updateCustomLoadingBackground();
137 | }
138 |
139 | function _updateCustomLoadingBackground() {
140 | if (_iframeWrapper) {
141 | if (!_customLoadingBackground) {
142 | _iframeWrapper.style.backgroundImage = "";
143 |
144 | let loader = document.createElement('div');
145 | loader.classList.add('loader');
146 | _iframeWrapper.appendChild(loader);
147 | }
148 | else {
149 | let loader = _iframeWrapper.querySelector('.loader');
150 | if(loader) {
151 | loader.remove();
152 | }
153 |
154 | _iframeWrapper.style.backgroundImage = _customLoadingBackground;
155 | }
156 | }
157 | }
158 |
159 | function _addEventListeners() {
160 | if (!_isMobile) {
161 | window.addEventListener('mousemove', _browserFunctionality);
162 | } else {
163 | _openLink = false;
164 | window.addEventListener('click', _mobileFunctionality);
165 | }
166 | _body.addEventListener('click', _hidePreviewWindow);
167 | }
168 | function _browserFunctionality(event) {
169 | if (_enable && event.target.classList.contains(_name)) {
170 | if (!_currentNode) {
171 | _currentNode = event.target;
172 | _timeout = setTimeout(function () {
173 | if (_currentNode && _currentNode.classList.contains(_name)) {
174 | _showPreviewWindow();
175 | }
176 | }, _hoverTimeout);
177 | }
178 | } else {
179 | _reset();
180 | }
181 | }
182 | function _mobileFunctionality(event) {
183 | if (event.target.classList.contains(_name)) {
184 | if (event.target !== _currentNode) {
185 | _count = 0;
186 | _currentNode = event.target;
187 | clearTimeout(_timeout);
188 | }
189 | if (!_openLink) {
190 | _count++;
191 |
192 | if (_count == 1) {
193 | _timeout = setTimeout(function () {
194 | if (_currentNode) {
195 | if (_count >= 3 && _enable) {
196 | _showPreviewWindow();
197 | _reset();
198 | } else {
199 | _openLink = true;
200 | _currentNode.click();
201 | }
202 | }
203 | }, _hoverTimeout);
204 | }
205 | event.preventDefault();
206 | } else {
207 | _reset();
208 | }
209 | } else {
210 | _reset();
211 | }
212 | }
213 |
214 | function _showPreviewWindow() {
215 | let positionBox = _currentNode.getBoundingClientRect();
216 | let { xQuadrant, yQuadrant } = _findQuadrant(positionBox);
217 |
218 | let previewWindowData = _findPreviewWindowPosition({ xQuadrant, yQuadrant, positionBox });
219 | previewWindowData.src = _currentNode.getAttribute('href');
220 | _updatePreviewWindow(previewWindowData);
221 | }
222 |
223 | function _hidePreviewWindow(event) {
224 | _iframeWrapper.classList.remove('show');
225 | _iframeWrapper.style.left = "";
226 | _iframeWrapper.style.top = "";
227 | _iframe.setAttribute('src', '');
228 | _body.classList.remove('noscroll');
229 | }
230 |
231 | function _findQuadrant(positionBox) {
232 | let { left: x, top: y } = positionBox;
233 | console.log(x, y);
234 |
235 | // var xQuadrant = (x < (window.innerWidth / 3)) ? 0 : (x < (window.innerWidth / 3 * 2)) ? 1 : 2;
236 | // var yQuadrant = (y < (window.innerHeight / 3)) ? 0 : (y < (window.innerHeight / 3 * 2)) ? 1 : 2;
237 | let xPointOne = (window.innerWidth / 2) - (0.1 * window.innerWidth);
238 | let xPointTwo = (window.innerWidth / 2) + (0.1 * window.innerWidth);
239 | let xQuadrant = (x < xPointOne) ? 0 : (x < xPointTwo) ? 1 : 2;
240 | let yQuadrant = (y < (window.innerHeight / 2)) ? 0 : 1;
241 | console.log(`xQuadrant: ${xQuadrant}, yQuadrant : ${yQuadrant}`);
242 |
243 | return {
244 | xQuadrant,
245 | yQuadrant
246 | };
247 | }
248 |
249 | function _findPreviewWindowPosition({ xQuadrant, yQuadrant, positionBox }) {
250 |
251 | let top, bottom, left, right;
252 |
253 | if (xQuadrant === 0) {
254 | left = positionBox.left + 'px';
255 | right = "";
256 | }
257 | else if (xQuadrant === 1) {
258 | left = (positionBox.left - ((window.innerWidth * 0.7) / 2) + (positionBox.width / 2)) + 'px';
259 | right = "";
260 | }
261 | else if (xQuadrant === 2) {
262 | left = "";
263 | right = (window.innerWidth - positionBox.right) + 'px';
264 | }
265 |
266 | if (!yQuadrant) {
267 | top = positionBox.top + positionBox.height + 10 + 'px';
268 | bottom = "";
269 | }
270 | else {
271 | top = "";
272 | bottom = (window.innerHeight - positionBox.top + 10) + 'px';
273 | }
274 | // var left = (0.3 * window.innerWidth) - (((0.3 * window.innerWidth) / 2) * xQuadrant);
275 | // var top = (0.3 * window.innerHeight) - (((0.3 * window.innerHeight) / 2) * yQuadrant);
276 |
277 | console.log(`top: ${top} bottom : ${bottom}`);
278 | console.log(`left : ${left} right : ${right}`);
279 |
280 | return {
281 | top,
282 | right,
283 | bottom,
284 | left
285 | };
286 | }
287 |
288 | function _updatePreviewWindow({ top, bottom, left, right, src }) {
289 | _body.classList.add('noscroll');
290 | _iframeWrapper.classList.add('show');
291 |
292 | _iframeWrapper.style.top = top;
293 | _iframeWrapper.style.bottom = bottom;
294 |
295 | _iframeWrapper.style.left = left;
296 | _iframeWrapper.style.right = right;
297 |
298 | _iframe.setAttribute('src', src);
299 | }
300 |
301 | let threeFingerTap = {
302 | init,
303 | destroy,
304 | enable,
305 | disable,
306 | setName,
307 | getName,
308 | getHoverTimeout,
309 | setHoverTimeout,
310 | getCustomLoadingBackground,
311 | setCustomLoadingBackground,
312 | getIsMobileDevice
313 | };
314 |
315 | if(typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
316 | module.exports = threeFingerTap;
317 | }
318 | else {
319 | window.threeFingerTap = threeFingerTap;
320 | }
321 | })();
322 |
323 |
324 |
--------------------------------------------------------------------------------
/dist/js/threeFingerTap.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | (function () {
4 |
5 | // Private variables
6 | var _currentNode = void 0; // specifies the current DOM element hovered or tapped
7 | var _timeout = void 0; // specifies the hover timeout or tap timeout
8 | var _name = void 0; // specifies the CSS class name
9 | var _initialized = void 0; // specifies whether the library is initialized or not
10 | var _isMobile = void 0; // indicates a mobile device or nont
11 |
12 | // API variables
13 | var _hoverTimeout = void 0; // user specified value for the hover timeout, set to 500 if mobile
14 | var _customLoadingBackground = void 0; // user specified backgroundImage CSS value
15 | var _enable = true; // user specified value indicating whether the library is currently active
16 |
17 | // Private variables
18 | var _count = 0; // number of times the user has tapped the link within the timeout
19 | var _openLink = false;
20 |
21 | // DOM nodes
22 | var _body = void 0;
23 | var _iframeWrapper = void 0;
24 | var _iframe = void 0;
25 |
26 | // API Methods
27 | function init(_ref) {
28 | var name = _ref.name,
29 | hoverTimeout = _ref.hoverTimeout,
30 | customLoadingBackground = _ref.customLoadingBackground;
31 |
32 | if (!_initialized) {
33 | _initialized = true;
34 | } else {
35 | throw new Error("Library already initialized");
36 | }
37 | _isMobile = _isTouchDevice();
38 | setName(name);
39 | setHoverTimeout(hoverTimeout);
40 | setCustomLoadingBackground(customLoadingBackground);
41 | _constructDOM();
42 | _addEventListeners();
43 | _enable = true;
44 | }
45 |
46 | function destroy() {
47 | _body.removeChild(_iframeWrapper);
48 |
49 | window.removeEventListener('mousemove', _browserFunctionality);
50 | window.removeEventListener('click', _mobileFunctionality);
51 |
52 | _body.removeEventListener('click', _hidePreviewWindow);
53 |
54 | _body = _iframeWrapper = _iframe = _name = _isMobile = _customLoadingBackground = _hoverTimeout = undefined;
55 | _reset();
56 |
57 | _initialized = false;
58 | _enable = false;
59 | }
60 |
61 | function enable() {
62 | _enable = true;
63 | }
64 |
65 | function disable() {
66 | _enable = false;
67 | }
68 |
69 | function setName(name) {
70 | if (!name) {
71 | throw new Error("Name not specified");
72 | }
73 | _name = name;
74 | }
75 |
76 | function getName() {
77 | return _name;
78 | }
79 |
80 | function setHoverTimeout(hoverTimeout) {
81 | if (!_isMobile) {
82 | if (isNaN(hoverTimeout)) {
83 | throw new Error("hoverTimeout should have a numerical value");
84 | }
85 | _hoverTimeout = hoverTimeout;
86 | } else {
87 | _hoverTimeout = 500;
88 | console.log("Touch device. hoverTimeout value ignored.");
89 | }
90 | }
91 |
92 | function getHoverTimeout() {
93 | return _hoverTimeout;
94 | }
95 |
96 | function setCustomLoadingBackground(customLoadingBackground) {
97 | var tempDiv = document.createElement('div');
98 | tempDiv.style.backgroundImage = customLoadingBackground;
99 |
100 | if (tempDiv.style.backgroundImage && !customLoadingBackground) {
101 | throw new Error("Invalid value for customLoadingBackground. Must be a possible value for CSS backgroundImage property");
102 | }
103 | _customLoadingBackground = customLoadingBackground;
104 | _updateCustomLoadingBackground();
105 | }
106 |
107 | function getCustomLoadingBackground() {
108 | return _customLoadingBackground;
109 | }
110 |
111 | function getIsMobileDevice() {
112 | return _isMobile;
113 | }
114 |
115 | // Private Methods
116 | function _isTouchDevice() {
117 | return window.hasOwnProperty('ontouchstart');
118 | }
119 |
120 | function _reset() {
121 | _currentNode = undefined;
122 | _openLink = false;
123 | _count = 0;
124 | clearTimeout(_timeout);
125 | }
126 |
127 | function _constructDOM() {
128 | var fragment = document.createDocumentFragment();
129 |
130 | _body = document.querySelector('body');
131 | _iframeWrapper = document.createElement('div');
132 | _iframeWrapper.classList.add('tft_iframe_wrapper');
133 |
134 | _iframe = document.createElement('iframe');
135 | _iframeWrapper.appendChild(_iframe);
136 |
137 | fragment.appendChild(_iframeWrapper);
138 | _body.appendChild(fragment);
139 |
140 | _updateCustomLoadingBackground();
141 | }
142 |
143 | function _updateCustomLoadingBackground() {
144 | if (_iframeWrapper) {
145 | if (!_customLoadingBackground) {
146 | _iframeWrapper.style.backgroundImage = "";
147 |
148 | var loader = document.createElement('div');
149 | loader.classList.add('loader');
150 | _iframeWrapper.appendChild(loader);
151 | } else {
152 | var _loader = _iframeWrapper.querySelector('.loader');
153 | if (_loader) {
154 | _loader.remove();
155 | }
156 |
157 | _iframeWrapper.style.backgroundImage = _customLoadingBackground;
158 | }
159 | }
160 | }
161 |
162 | function _addEventListeners() {
163 | if (!_isMobile) {
164 | window.addEventListener('mousemove', _browserFunctionality);
165 | } else {
166 | _openLink = false;
167 | window.addEventListener('click', _mobileFunctionality);
168 | }
169 | _body.addEventListener('click', _hidePreviewWindow);
170 | }
171 | function _browserFunctionality(event) {
172 | if (_enable && event.target.classList.contains(_name)) {
173 | if (!_currentNode) {
174 | _currentNode = event.target;
175 | _timeout = setTimeout(function () {
176 | if (_currentNode && _currentNode.classList.contains(_name)) {
177 | _showPreviewWindow();
178 | }
179 | }, _hoverTimeout);
180 | }
181 | } else {
182 | _reset();
183 | }
184 | }
185 | function _mobileFunctionality(event) {
186 | if (event.target.classList.contains(_name)) {
187 | if (event.target !== _currentNode) {
188 | _count = 0;
189 | _currentNode = event.target;
190 | clearTimeout(_timeout);
191 | }
192 | if (!_openLink) {
193 | _count++;
194 |
195 | if (_count == 1) {
196 | _timeout = setTimeout(function () {
197 | if (_currentNode) {
198 | if (_count >= 3 && _enable) {
199 | _showPreviewWindow();
200 | _reset();
201 | } else {
202 | _openLink = true;
203 | _currentNode.click();
204 | }
205 | }
206 | }, _hoverTimeout);
207 | }
208 | event.preventDefault();
209 | } else {
210 | _reset();
211 | }
212 | } else {
213 | _reset();
214 | }
215 | }
216 |
217 | function _showPreviewWindow() {
218 | var positionBox = _currentNode.getBoundingClientRect();
219 |
220 | var _findQuadrant2 = _findQuadrant(positionBox),
221 | xQuadrant = _findQuadrant2.xQuadrant,
222 | yQuadrant = _findQuadrant2.yQuadrant;
223 |
224 | var previewWindowData = _findPreviewWindowPosition({ xQuadrant: xQuadrant, yQuadrant: yQuadrant, positionBox: positionBox });
225 | previewWindowData.src = _currentNode.getAttribute('href');
226 | _updatePreviewWindow(previewWindowData);
227 | }
228 |
229 | function _hidePreviewWindow(event) {
230 | _iframeWrapper.classList.remove('show');
231 | _iframeWrapper.style.left = "";
232 | _iframeWrapper.style.top = "";
233 | _iframe.setAttribute('src', '');
234 | _body.classList.remove('noscroll');
235 | }
236 |
237 | function _findQuadrant(positionBox) {
238 | var x = positionBox.left,
239 | y = positionBox.top;
240 |
241 | console.log(x, y);
242 |
243 | // var xQuadrant = (x < (window.innerWidth / 3)) ? 0 : (x < (window.innerWidth / 3 * 2)) ? 1 : 2;
244 | // var yQuadrant = (y < (window.innerHeight / 3)) ? 0 : (y < (window.innerHeight / 3 * 2)) ? 1 : 2;
245 | var xPointOne = window.innerWidth / 2 - 0.1 * window.innerWidth;
246 | var xPointTwo = window.innerWidth / 2 + 0.1 * window.innerWidth;
247 | var xQuadrant = x < xPointOne ? 0 : x < xPointTwo ? 1 : 2;
248 | var yQuadrant = y < window.innerHeight / 2 ? 0 : 1;
249 | console.log('xQuadrant: ' + xQuadrant + ', yQuadrant : ' + yQuadrant);
250 |
251 | return {
252 | xQuadrant: xQuadrant,
253 | yQuadrant: yQuadrant
254 | };
255 | }
256 |
257 | function _findPreviewWindowPosition(_ref2) {
258 | var xQuadrant = _ref2.xQuadrant,
259 | yQuadrant = _ref2.yQuadrant,
260 | positionBox = _ref2.positionBox;
261 |
262 |
263 | var top = void 0,
264 | bottom = void 0,
265 | left = void 0,
266 | right = void 0;
267 |
268 | if (xQuadrant === 0) {
269 | left = positionBox.left + 'px';
270 | right = "";
271 | } else if (xQuadrant === 1) {
272 | left = positionBox.left - window.innerWidth * 0.7 / 2 + positionBox.width / 2 + 'px';
273 | right = "";
274 | } else if (xQuadrant === 2) {
275 | left = "";
276 | right = window.innerWidth - positionBox.right + 'px';
277 | }
278 |
279 | if (!yQuadrant) {
280 | top = positionBox.top + positionBox.height + 10 + 'px';
281 | bottom = "";
282 | } else {
283 | top = "";
284 | bottom = window.innerHeight - positionBox.top + 10 + 'px';
285 | }
286 | // var left = (0.3 * window.innerWidth) - (((0.3 * window.innerWidth) / 2) * xQuadrant);
287 | // var top = (0.3 * window.innerHeight) - (((0.3 * window.innerHeight) / 2) * yQuadrant);
288 |
289 | console.log('top: ' + top + ' bottom : ' + bottom);
290 | console.log('left : ' + left + ' right : ' + right);
291 |
292 | return {
293 | top: top,
294 | right: right,
295 | bottom: bottom,
296 | left: left
297 | };
298 | }
299 |
300 | function _updatePreviewWindow(_ref3) {
301 | var top = _ref3.top,
302 | bottom = _ref3.bottom,
303 | left = _ref3.left,
304 | right = _ref3.right,
305 | src = _ref3.src;
306 |
307 | _body.classList.add('noscroll');
308 | _iframeWrapper.classList.add('show');
309 |
310 | _iframeWrapper.style.top = top;
311 | _iframeWrapper.style.bottom = bottom;
312 |
313 | _iframeWrapper.style.left = left;
314 | _iframeWrapper.style.right = right;
315 |
316 | _iframe.setAttribute('src', src);
317 | }
318 |
319 | var threeFingerTap = {
320 | init: init,
321 | destroy: destroy,
322 | enable: enable,
323 | disable: disable,
324 | setName: setName,
325 | getName: getName,
326 | getHoverTimeout: getHoverTimeout,
327 | setHoverTimeout: setHoverTimeout,
328 | getCustomLoadingBackground: getCustomLoadingBackground,
329 | setCustomLoadingBackground: setCustomLoadingBackground,
330 | getIsMobileDevice: getIsMobileDevice
331 | };
332 |
333 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
334 | module.exports = threeFingerTap;
335 | } else {
336 | window.threeFingerTap = threeFingerTap;
337 | }
338 | })();
339 |
--------------------------------------------------------------------------------