|
112 | 112 | * @returns {Boolean} Success or not?
|
113 | 113 | */
|
114 | 114 | function _introForElement(targetElm) {
|
115 |
| - var introItems = [], |
116 |
| - self = this; |
| 115 | + var introItems = []; |
117 | 116 |
|
118 | 117 | if (this._options.steps) {
|
119 | 118 | //use steps passed programmatically
|
|
247 | 246 | });
|
248 | 247 |
|
249 | 248 | //set it to the introJs object
|
250 |
| - self._introItems = introItems; |
| 249 | + this._introItems = introItems; |
251 | 250 |
|
252 | 251 | //add overlay layer to the page
|
253 |
| - if(_addOverlayLayer.call(self, targetElm)) { |
| 252 | + if(_addOverlayLayer.call(this, targetElm)) { |
254 | 253 | //then, start the show
|
255 |
| - _nextStep.call(self); |
256 |
| - |
257 |
| - self._onKeyDown = function(e) { |
258 |
| - /* |
259 |
| - on keyCode: |
260 |
| - https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode |
261 |
| - This feature has been removed from the Web standards. |
262 |
| - Though some browsers may still support it, it is in |
263 |
| - the process of being dropped. |
264 |
| - Instead, you should use KeyboardEvent.code, |
265 |
| - if it's implemented. |
266 |
| -
|
267 |
| - jQuery's approach is to test for |
268 |
| - (1) e.which, then |
269 |
| - (2) e.charCode, then |
270 |
| - (3) e.keyCode |
271 |
| - https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638 |
272 |
| - */ |
273 |
| - var code = (e.code === null) ? e.which : e.code; |
274 |
| - |
275 |
| - // if code/e.which is null |
276 |
| - if (code === null) { |
277 |
| - code = (e.charCode === null) ? e.keyCode : e.charCode; |
278 |
| - } |
279 |
| - |
280 |
| - if ((code === 'Escape' || code === 27) && self._options.exitOnEsc === true) { |
281 |
| - //escape key pressed, exit the intro |
282 |
| - //check if exit callback is defined |
283 |
| - _exitIntro.call(self, targetElm); |
284 |
| - } else if (code === 'ArrowLeft' || code === 37) { |
285 |
| - //left arrow |
286 |
| - _previousStep.call(self); |
287 |
| - } else if (code === 'ArrowRight' || code === 39) { |
288 |
| - //right arrow |
289 |
| - _nextStep.call(self); |
290 |
| - } else if (code === 'Enter' || code === 13) { |
291 |
| - //srcElement === ie |
292 |
| - var target = e.target || e.srcElement; |
293 |
| - if (target && target.className.match('introjs-prevbutton')) { |
294 |
| - //user hit enter while focusing on previous button |
295 |
| - _previousStep.call(self); |
296 |
| - } else if (target && target.className.match('introjs-skipbutton')) { |
297 |
| - //user hit enter while focusing on skip button |
298 |
| - if (self._introItems.length - 1 === self._currentStep && typeof (self._introCompleteCallback) === 'function') { |
299 |
| - self._introCompleteCallback.call(self); |
300 |
| - } |
| 254 | + _nextStep.call(this); |
301 | 255 |
|
302 |
| - _exitIntro.call(self, targetElm); |
303 |
| - } else { |
304 |
| - //default behavior for responding to enter |
305 |
| - _nextStep.call(self); |
306 |
| - } |
| 256 | + if (this._options.keyboardNavigation) { |
| 257 | + DOMEvent.on(window, 'keydown', _onKeyDown, this, true); |
| 258 | + } |
| 259 | + //for window resize |
| 260 | + DOMEvent.on(window, 'resize', _onResize, this, true); |
| 261 | + } |
| 262 | + return false; |
| 263 | + } |
307 | 264 |
|
308 |
| - //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers |
309 |
| - if(e.preventDefault) { |
310 |
| - e.preventDefault(); |
311 |
| - } else { |
312 |
| - e.returnValue = false; |
313 |
| - } |
314 |
| - } |
315 |
| - }; |
| 265 | + function _onResize () { |
| 266 | + this.refresh.call(this); |
| 267 | + } |
316 | 268 |
|
317 |
| - self._onResize = function() { |
318 |
| - self.refresh.call(self); |
319 |
| - }; |
| 269 | + /** |
| 270 | + * on keyCode: |
| 271 | + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode |
| 272 | + * This feature has been removed from the Web standards. |
| 273 | + * Though some browsers may still support it, it is in |
| 274 | + * the process of being dropped. |
| 275 | + * Instead, you should use KeyboardEvent.code, |
| 276 | + * if it's implemented. |
| 277 | + * |
| 278 | + * jQuery's approach is to test for |
| 279 | + * (1) e.which, then |
| 280 | + * (2) e.charCode, then |
| 281 | + * (3) e.keyCode |
| 282 | + * https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638 |
| 283 | + * |
| 284 | + * @param type var |
| 285 | + * @return type |
| 286 | + */ |
| 287 | + function _onKeyDown (e) { |
| 288 | + var code = (e.code === null) ? e.which : e.code; |
320 | 289 |
|
321 |
| - if (window.addEventListener) { |
322 |
| - if (this._options.keyboardNavigation) { |
323 |
| - window.addEventListener('keydown', self._onKeyDown, true); |
324 |
| - } |
325 |
| - //for window resize |
326 |
| - window.addEventListener('resize', self._onResize, true); |
327 |
| - } else if (document.attachEvent) { //IE |
328 |
| - if (this._options.keyboardNavigation) { |
329 |
| - document.attachEvent('onkeydown', self._onKeyDown); |
| 290 | + // if code/e.which is null |
| 291 | + if (code === null) { |
| 292 | + code = (e.charCode === null) ? e.keyCode : e.charCode; |
| 293 | + } |
| 294 | + |
| 295 | + if ((code === 'Escape' || code === 27) && this._options.exitOnEsc === true) { |
| 296 | + //escape key pressed, exit the intro |
| 297 | + //check if exit callback is defined |
| 298 | + _exitIntro.call(this, this._targetElement); |
| 299 | + } else if (code === 'ArrowLeft' || code === 37) { |
| 300 | + //left arrow |
| 301 | + _previousStep.call(this); |
| 302 | + } else if (code === 'ArrowRight' || code === 39) { |
| 303 | + //right arrow |
| 304 | + _nextStep.call(this); |
| 305 | + } else if (code === 'Enter' || code === 13) { |
| 306 | + //srcElement === ie |
| 307 | + var target = e.target || e.srcElement; |
| 308 | + if (target && target.className.match('introjs-prevbutton')) { |
| 309 | + //user hit enter while focusing on previous button |
| 310 | + _previousStep.call(this); |
| 311 | + } else if (target && target.className.match('introjs-skipbutton')) { |
| 312 | + //user hit enter while focusing on skip button |
| 313 | + if (this._introItems.length - 1 === this._currentStep && typeof (this._introCompleteCallback) === 'function') { |
| 314 | + this._introCompleteCallback.call(this); |
330 | 315 | }
|
331 |
| - //for window resize |
332 |
| - document.attachEvent('onresize', self._onResize); |
| 316 | + |
| 317 | + _exitIntro.call(this, this._targetElement); |
| 318 | + } else { |
| 319 | + //default behavior for responding to enter |
| 320 | + _nextStep.call(this); |
| 321 | + } |
| 322 | + |
| 323 | + //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers |
| 324 | + if(e.preventDefault) { |
| 325 | + e.preventDefault(); |
| 326 | + } else { |
| 327 | + e.returnValue = false; |
333 | 328 | }
|
334 | 329 | }
|
335 |
| - return false; |
336 | 330 | }
|
337 | 331 |
|
338 | 332 | /*
|
|
553 | 547 | });
|
554 | 548 |
|
555 | 549 | //clean listeners
|
556 |
| - if (window.removeEventListener) { |
557 |
| - window.removeEventListener('keydown', this._onKeyDown, true); |
558 |
| - } else if (document.detachEvent) { //IE |
559 |
| - document.detachEvent('onkeydown', this._onKeyDown); |
560 |
| - } |
| 550 | + DOMEvent.off(window, 'keydown', _onKeyDown, this, true); |
| 551 | + DOMEvent.off(window, 'resize', _onResize, this, true); |
561 | 552 |
|
562 | 553 | //check if any callback is defined
|
563 | 554 | if (this._introExitCallback !== undefined) {
|
|
1472 | 1463 | }
|
1473 | 1464 | }
|
1474 | 1465 |
|
| 1466 | + /** |
| 1467 | + * Mark any object with an incrementing number |
| 1468 | + * used for keeping track of objects |
| 1469 | + * |
| 1470 | + * @param Object obj Any object or DOM Element |
| 1471 | + * @param String key |
| 1472 | + * @return Object |
| 1473 | + */ |
| 1474 | + var _stamp = (function () { |
| 1475 | + var keys = {}; |
| 1476 | + return function stamp (obj, key) { |
| 1477 | + |
| 1478 | + // get group key |
| 1479 | + key = key || 'introjs-stamp'; |
| 1480 | + |
| 1481 | + // each group increments from 0 |
| 1482 | + keys[key] = keys[key] || 0; |
| 1483 | + |
| 1484 | + // stamp only once per object |
| 1485 | + if (obj[key] === undefined) { |
| 1486 | + // increment key for each new object |
| 1487 | + obj[key] = keys[key]++; |
| 1488 | + } |
| 1489 | + |
| 1490 | + return obj[key]; |
| 1491 | + }; |
| 1492 | + })(); |
| 1493 | + |
| 1494 | + /** |
| 1495 | + * DOMEvent Handles all DOM events |
| 1496 | + * |
| 1497 | + * methods: |
| 1498 | + * |
| 1499 | + * on - add event handler |
| 1500 | + * off - remove event |
| 1501 | + */ |
| 1502 | + var DOMEvent = (function () { |
| 1503 | + function DOMEvent () { |
| 1504 | + var events_key = 'introjs_event'; |
| 1505 | + |
| 1506 | + /** |
| 1507 | + * Gets a unique ID for an event listener |
| 1508 | + * |
| 1509 | + * @param Object obj |
| 1510 | + * @param String type event type |
| 1511 | + * @param Function listener |
| 1512 | + * @param Object context |
| 1513 | + * @return String |
| 1514 | + */ |
| 1515 | + this._id = function (obj, type, listener, context) { |
| 1516 | + return type + _stamp(listener) + (context ? '_' + _stamp(context) : ''); |
| 1517 | + }; |
| 1518 | + |
| 1519 | + /** |
| 1520 | + * Adds event listener |
| 1521 | + * |
| 1522 | + * @param Object obj |
| 1523 | + * @param String type event type |
| 1524 | + * @param Function listener |
| 1525 | + * @param Object context |
| 1526 | + * @param Boolean useCapture |
| 1527 | + * @return null |
| 1528 | + */ |
| 1529 | + this.on = function (obj, type, listener, context, useCapture) { |
| 1530 | + var id = this._id.apply(this, arguments), |
| 1531 | + handler = function (e) { |
| 1532 | + return listener.call(context || obj, e || window.event); |
| 1533 | + }; |
| 1534 | + |
| 1535 | + if ('addEventListener' in obj) { |
| 1536 | + obj.addEventListener(type, handler, useCapture); |
| 1537 | + } else if ('attachEvent' in obj) { |
| 1538 | + obj.attachEvent('on' + type, handler); |
| 1539 | + } |
| 1540 | + |
| 1541 | + obj[events_key] = obj[events_key] || {}; |
| 1542 | + obj[events_key][id] = handler; |
| 1543 | + }; |
| 1544 | + |
| 1545 | + /** |
| 1546 | + * Removes event listener |
| 1547 | + * |
| 1548 | + * @param Object obj |
| 1549 | + * @param String type event type |
| 1550 | + * @param Function listener |
| 1551 | + * @param Object context |
| 1552 | + * @param Boolean useCapture |
| 1553 | + * @return null |
| 1554 | + */ |
| 1555 | + this.off = function (obj, type, listener, context, useCapture) { |
| 1556 | + var id = this._id.apply(this, arguments), |
| 1557 | + handler = obj[events_key] && obj[events_key][id]; |
| 1558 | + |
| 1559 | + if ('removeEventListener' in obj) { |
| 1560 | + obj.removeEventListener(type, handler, useCapture); |
| 1561 | + } else if ('detachEvent' in obj) { |
| 1562 | + obj.detachEvent('on' + type, handler); |
| 1563 | + } |
| 1564 | + |
| 1565 | + obj[events_key][id] = null; |
| 1566 | + }; |
| 1567 | + } |
| 1568 | + |
| 1569 | + return new DOMEvent(); |
| 1570 | + })(); |
| 1571 | + |
1475 | 1572 | /**
|
1476 | 1573 | * Append a class to an element
|
1477 | 1574 | *
|
|
1722 | 1819 |
|
1723 | 1820 | _addHints.call(this);
|
1724 | 1821 |
|
1725 |
| - if (document.addEventListener) { |
1726 |
| - document.addEventListener('click', _removeHintTooltip.bind(this), false); |
1727 |
| - //for window resize |
1728 |
| - window.addEventListener('resize', _reAlignHints.bind(this), true); |
1729 |
| - } else if (document.attachEvent) { //IE |
1730 |
| - //for window resize |
1731 |
| - document.attachEvent('onclick', _removeHintTooltip.bind(this)); |
1732 |
| - document.attachEvent('onresize', _reAlignHints.bind(this)); |
1733 |
| - } |
| 1822 | + /* |
| 1823 | + todo: |
| 1824 | + these events should be removed at some point |
| 1825 | + */ |
| 1826 | + DOMEvent.on(document, 'click', _removeHintTooltip, this, false); |
| 1827 | + DOMEvent.on(window, 'resize', _reAlignHints, this, true); |
1734 | 1828 | }
|
1735 | 1829 |
|
1736 | 1830 | /**
|
|
2158 | 2252 | }
|
2159 | 2253 |
|
2160 | 2254 | var introJs = function (targetElm) {
|
| 2255 | + var instance; |
| 2256 | + |
2161 | 2257 | if (typeof (targetElm) === 'object') {
|
2162 | 2258 | //Ok, create a new instance
|
2163 |
| - return new IntroJs(targetElm); |
| 2259 | + instance = new IntroJs(targetElm); |
2164 | 2260 |
|
2165 | 2261 | } else if (typeof (targetElm) === 'string') {
|
2166 | 2262 | //select the target element with query selector
|
2167 | 2263 | var targetElement = document.querySelector(targetElm);
|
2168 | 2264 |
|
2169 | 2265 | if (targetElement) {
|
2170 |
| - return new IntroJs(targetElement); |
| 2266 | + instance = new IntroJs(targetElement); |
2171 | 2267 | } else {
|
2172 | 2268 | throw new Error('There is no element with given selector.');
|
2173 | 2269 | }
|
2174 | 2270 | } else {
|
2175 |
| - return new IntroJs(document.body); |
| 2271 | + instance = new IntroJs(document.body); |
2176 | 2272 | }
|
| 2273 | + // add instance to list of _instances |
| 2274 | + // passing group to _stamp to increment |
| 2275 | + // from 0 onward somewhat reliably |
| 2276 | + introJs[ _stamp(instance, 'introjs-instance') ] = instance; |
| 2277 | + |
| 2278 | + return instance; |
2177 | 2279 | };
|
2178 | 2280 |
|
2179 | 2281 | /**
|
|
2184 | 2286 | */
|
2185 | 2287 | introJs.version = VERSION;
|
2186 | 2288 |
|
| 2289 | + /** |
| 2290 | + * key-val object helper for introJs instances |
| 2291 | + * |
| 2292 | + * @property instances |
| 2293 | + * @type Object |
| 2294 | + */ |
| 2295 | + introJs.instances = {}; |
| 2296 | + |
2187 | 2297 | //Prototype
|
2188 | 2298 | introJs.fn = IntroJs.prototype = {
|
2189 | 2299 | clone: function () {
|
|
0 commit comments