|
114 | 114 | */
|
115 | 115 | function _introForElement(targetElm, group) {
|
116 | 116 | var allIntroSteps = targetElm.querySelectorAll("*[data-intro]"),
|
117 |
| - introItems = [], |
118 |
| - self = this; |
| 117 | + introItems = []; |
119 | 118 |
|
120 | 119 | if (this._options.steps) {
|
121 | 120 | //use steps passed programmatically
|
|
261 | 260 | });
|
262 | 261 |
|
263 | 262 | //set it to the introJs object
|
264 |
| - self._introItems = introItems; |
| 263 | + this._introItems = introItems; |
265 | 264 |
|
266 | 265 | //add overlay layer to the page
|
267 |
| - if(_addOverlayLayer.call(self, targetElm)) { |
| 266 | + if(_addOverlayLayer.call(this, targetElm)) { |
268 | 267 | //then, start the show
|
269 |
| - _nextStep.call(self); |
270 |
| - |
271 |
| - self._onKeyDown = function(e) { |
272 |
| - /* |
273 |
| - on keyCode: |
274 |
| - https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode |
275 |
| - This feature has been removed from the Web standards. |
276 |
| - Though some browsers may still support it, it is in |
277 |
| - the process of being dropped. |
278 |
| - Instead, you should use KeyboardEvent.code, |
279 |
| - if it's implemented. |
280 |
| -
|
281 |
| - jQuery's approach is to test for |
282 |
| - (1) e.which, then |
283 |
| - (2) e.charCode, then |
284 |
| - (3) e.keyCode |
285 |
| - https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638 |
286 |
| - */ |
287 |
| - var code = (e.code === null) ? e.which : e.code; |
288 |
| - |
289 |
| - // if code/e.which is null |
290 |
| - if (code === null) { |
291 |
| - code = (e.charCode === null) ? e.keyCode : e.charCode; |
292 |
| - } |
293 |
| - |
294 |
| - if ((code === 'Escape' || code === 27) && self._options.exitOnEsc === true) { |
295 |
| - //escape key pressed, exit the intro |
296 |
| - //check if exit callback is defined |
297 |
| - _exitIntro.call(self, targetElm); |
298 |
| - } else if (code === 'ArrowLeft' || code === 37) { |
299 |
| - //left arrow |
300 |
| - _previousStep.call(self); |
301 |
| - } else if (code === 'ArrowRight' || code === 39) { |
302 |
| - //right arrow |
303 |
| - _nextStep.call(self); |
304 |
| - } else if (code === 'Enter' || code === 13) { |
305 |
| - //srcElement === ie |
306 |
| - var target = e.target || e.srcElement; |
307 |
| - if (target && target.className.match('introjs-prevbutton')) { |
308 |
| - //user hit enter while focusing on previous button |
309 |
| - _previousStep.call(self); |
310 |
| - } else if (target && target.className.match('introjs-skipbutton')) { |
311 |
| - //user hit enter while focusing on skip button |
312 |
| - if (self._introItems.length - 1 === self._currentStep && typeof (self._introCompleteCallback) === 'function') { |
313 |
| - self._introCompleteCallback.call(self); |
314 |
| - } |
| 268 | + _nextStep.call(this); |
315 | 269 |
|
316 |
| - _exitIntro.call(self, targetElm); |
317 |
| - } else { |
318 |
| - //default behavior for responding to enter |
319 |
| - _nextStep.call(self); |
320 |
| - } |
| 270 | + if (this._options.keyboardNavigation) { |
| 271 | + DOMEvent.on(window, 'keydown', _onKeyDown, this, true); |
| 272 | + } |
| 273 | + //for window resize |
| 274 | + DOMEvent.on(window, 'resize', _onResize, this, true); |
| 275 | + } |
| 276 | + return false; |
| 277 | + } |
321 | 278 |
|
322 |
| - //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers |
323 |
| - if(e.preventDefault) { |
324 |
| - e.preventDefault(); |
325 |
| - } else { |
326 |
| - e.returnValue = false; |
327 |
| - } |
328 |
| - } |
329 |
| - }; |
| 279 | + function _onResize () { |
| 280 | + this.refresh.call(this); |
| 281 | + } |
330 | 282 |
|
331 |
| - self._onResize = function() { |
332 |
| - self.refresh.call(self); |
333 |
| - }; |
| 283 | + /** |
| 284 | + * on keyCode: |
| 285 | + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode |
| 286 | + * This feature has been removed from the Web standards. |
| 287 | + * Though some browsers may still support it, it is in |
| 288 | + * the process of being dropped. |
| 289 | + * Instead, you should use KeyboardEvent.code, |
| 290 | + * if it's implemented. |
| 291 | + * |
| 292 | + * jQuery's approach is to test for |
| 293 | + * (1) e.which, then |
| 294 | + * (2) e.charCode, then |
| 295 | + * (3) e.keyCode |
| 296 | + * https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638 |
| 297 | + * |
| 298 | + * @param type var |
| 299 | + * @return type |
| 300 | + */ |
| 301 | + function _onKeyDown (e) { |
| 302 | + var code = (e.code === null) ? e.which : e.code; |
334 | 303 |
|
335 |
| - if (window.addEventListener) { |
336 |
| - if (this._options.keyboardNavigation) { |
337 |
| - window.addEventListener('keydown', self._onKeyDown, true); |
338 |
| - } |
339 |
| - //for window resize |
340 |
| - window.addEventListener('resize', self._onResize, true); |
341 |
| - } else if (document.attachEvent) { //IE |
342 |
| - if (this._options.keyboardNavigation) { |
343 |
| - document.attachEvent('onkeydown', self._onKeyDown); |
| 304 | + // if code/e.which is null |
| 305 | + if (code === null) { |
| 306 | + code = (e.charCode === null) ? e.keyCode : e.charCode; |
| 307 | + } |
| 308 | + |
| 309 | + if ((code === 'Escape' || code === 27) && this._options.exitOnEsc === true) { |
| 310 | + //escape key pressed, exit the intro |
| 311 | + //check if exit callback is defined |
| 312 | + _exitIntro.call(this, this._targetElement); |
| 313 | + } else if (code === 'ArrowLeft' || code === 37) { |
| 314 | + //left arrow |
| 315 | + _previousStep.call(this); |
| 316 | + } else if (code === 'ArrowRight' || code === 39) { |
| 317 | + //right arrow |
| 318 | + _nextStep.call(this); |
| 319 | + } else if (code === 'Enter' || code === 13) { |
| 320 | + //srcElement === ie |
| 321 | + var target = e.target || e.srcElement; |
| 322 | + if (target && target.className.match('introjs-prevbutton')) { |
| 323 | + //user hit enter while focusing on previous button |
| 324 | + _previousStep.call(this); |
| 325 | + } else if (target && target.className.match('introjs-skipbutton')) { |
| 326 | + //user hit enter while focusing on skip button |
| 327 | + if (this._introItems.length - 1 === this._currentStep && typeof (this._introCompleteCallback) === 'function') { |
| 328 | + this._introCompleteCallback.call(this); |
344 | 329 | }
|
345 |
| - //for window resize |
346 |
| - document.attachEvent('onresize', self._onResize); |
| 330 | + |
| 331 | + _exitIntro.call(this, this._targetElement); |
| 332 | + } else { |
| 333 | + //default behavior for responding to enter |
| 334 | + _nextStep.call(this); |
| 335 | + } |
| 336 | + |
| 337 | + //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers |
| 338 | + if(e.preventDefault) { |
| 339 | + e.preventDefault(); |
| 340 | + } else { |
| 341 | + e.returnValue = false; |
347 | 342 | }
|
348 | 343 | }
|
349 |
| - return false; |
350 | 344 | }
|
351 | 345 |
|
352 | 346 | /*
|
|
567 | 561 | });
|
568 | 562 |
|
569 | 563 | //clean listeners
|
570 |
| - if (window.removeEventListener) { |
571 |
| - window.removeEventListener('keydown', this._onKeyDown, true); |
572 |
| - } else if (document.detachEvent) { //IE |
573 |
| - document.detachEvent('onkeydown', this._onKeyDown); |
574 |
| - } |
| 564 | + DOMEvent.off(window, 'keydown', _onKeyDown, this, true); |
| 565 | + DOMEvent.off(window, 'resize', _onResize, this, true); |
575 | 566 |
|
576 | 567 | //check if any callback is defined
|
577 | 568 | if (this._introExitCallback !== undefined) {
|
|
1503 | 1494 | }
|
1504 | 1495 | }
|
1505 | 1496 |
|
| 1497 | + /** |
| 1498 | + * Mark any object with an incrementing number |
| 1499 | + * used for keeping track of objects |
| 1500 | + * |
| 1501 | + * @param Object obj Any object or DOM Element |
| 1502 | + * @param String key |
| 1503 | + * @return Object |
| 1504 | + */ |
| 1505 | + var _stamp = (function () { |
| 1506 | + var keys = {}; |
| 1507 | + return function stamp (obj, key) { |
| 1508 | + |
| 1509 | + // get group key |
| 1510 | + key = key || 'introjs-stamp'; |
| 1511 | + |
| 1512 | + // each group increments from 0 |
| 1513 | + keys[key] = keys[key] || 0; |
| 1514 | + |
| 1515 | + // stamp only once per object |
| 1516 | + if (obj[key] === undefined) { |
| 1517 | + // increment key for each new object |
| 1518 | + obj[key] = keys[key]++; |
| 1519 | + } |
| 1520 | + |
| 1521 | + return obj[key]; |
| 1522 | + }; |
| 1523 | + })(); |
| 1524 | + |
| 1525 | + /** |
| 1526 | + * DOMEvent Handles all DOM events |
| 1527 | + * |
| 1528 | + * methods: |
| 1529 | + * |
| 1530 | + * on - add event handler |
| 1531 | + * off - remove event |
| 1532 | + */ |
| 1533 | + var DOMEvent = (function () { |
| 1534 | + function DOMEvent () { |
| 1535 | + var events_key = 'introjs_event'; |
| 1536 | + |
| 1537 | + /** |
| 1538 | + * Gets a unique ID for an event listener |
| 1539 | + * |
| 1540 | + * @param Object obj |
| 1541 | + * @param String type event type |
| 1542 | + * @param Function listener |
| 1543 | + * @param Object context |
| 1544 | + * @return String |
| 1545 | + */ |
| 1546 | + this._id = function (obj, type, listener, context) { |
| 1547 | + return type + _stamp(listener) + (context ? '_' + _stamp(context) : ''); |
| 1548 | + }; |
| 1549 | + |
| 1550 | + /** |
| 1551 | + * Adds event listener |
| 1552 | + * |
| 1553 | + * @param Object obj |
| 1554 | + * @param String type event type |
| 1555 | + * @param Function listener |
| 1556 | + * @param Object context |
| 1557 | + * @param Boolean useCapture |
| 1558 | + * @return null |
| 1559 | + */ |
| 1560 | + this.on = function (obj, type, listener, context, useCapture) { |
| 1561 | + var id = this._id.apply(this, arguments), |
| 1562 | + handler = function (e) { |
| 1563 | + return listener.call(context || obj, e || window.event); |
| 1564 | + }; |
| 1565 | + |
| 1566 | + if ('addEventListener' in obj) { |
| 1567 | + obj.addEventListener(type, handler, useCapture); |
| 1568 | + } else if ('attachEvent' in obj) { |
| 1569 | + obj.attachEvent('on' + type, handler); |
| 1570 | + } |
| 1571 | + |
| 1572 | + obj[events_key] = obj[events_key] || {}; |
| 1573 | + obj[events_key][id] = handler; |
| 1574 | + }; |
| 1575 | + |
| 1576 | + /** |
| 1577 | + * Removes event listener |
| 1578 | + * |
| 1579 | + * @param Object obj |
| 1580 | + * @param String type event type |
| 1581 | + * @param Function listener |
| 1582 | + * @param Object context |
| 1583 | + * @param Boolean useCapture |
| 1584 | + * @return null |
| 1585 | + */ |
| 1586 | + this.off = function (obj, type, listener, context, useCapture) { |
| 1587 | + var id = this._id.apply(this, arguments), |
| 1588 | + handler = obj[events_key] && obj[events_key][id]; |
| 1589 | + |
| 1590 | + if ('removeEventListener' in obj) { |
| 1591 | + obj.removeEventListener(type, handler, useCapture); |
| 1592 | + } else if ('detachEvent' in obj) { |
| 1593 | + obj.detachEvent('on' + type, handler); |
| 1594 | + } |
| 1595 | + |
| 1596 | + obj[events_key][id] = null; |
| 1597 | + }; |
| 1598 | + } |
| 1599 | + |
| 1600 | + return new DOMEvent(); |
| 1601 | + })(); |
| 1602 | + |
1506 | 1603 | /**
|
1507 | 1604 | * Append a class to an element
|
1508 | 1605 | *
|
|
1753 | 1850 |
|
1754 | 1851 | _addHints.call(this);
|
1755 | 1852 |
|
1756 |
| - if (document.addEventListener) { |
1757 |
| - document.addEventListener('click', _removeHintTooltip.bind(this), false); |
1758 |
| - //for window resize |
1759 |
| - window.addEventListener('resize', _reAlignHints.bind(this), true); |
1760 |
| - } else if (document.attachEvent) { //IE |
1761 |
| - //for window resize |
1762 |
| - document.attachEvent('onclick', _removeHintTooltip.bind(this)); |
1763 |
| - document.attachEvent('onresize', _reAlignHints.bind(this)); |
1764 |
| - } |
| 1853 | + /* |
| 1854 | + todo: |
| 1855 | + these events should be removed at some point |
| 1856 | + */ |
| 1857 | + DOMEvent.on(document, 'click', _removeHintTooltip, this, false); |
| 1858 | + DOMEvent.on(window, 'resize', _reAlignHints, this, true); |
1765 | 1859 | }
|
1766 | 1860 |
|
1767 | 1861 | /**
|
|
2200 | 2294 | }
|
2201 | 2295 |
|
2202 | 2296 | var introJs = function (targetElm) {
|
| 2297 | + var instance; |
| 2298 | + |
2203 | 2299 | if (typeof (targetElm) === 'object') {
|
2204 | 2300 | //Ok, create a new instance
|
2205 |
| - return new IntroJs(targetElm); |
| 2301 | + instance = new IntroJs(targetElm); |
2206 | 2302 |
|
2207 | 2303 | } else if (typeof (targetElm) === 'string') {
|
2208 | 2304 | //select the target element with query selector
|
2209 | 2305 | var targetElement = document.querySelector(targetElm);
|
2210 | 2306 |
|
2211 | 2307 | if (targetElement) {
|
2212 |
| - return new IntroJs(targetElement); |
| 2308 | + instance = new IntroJs(targetElement); |
2213 | 2309 | } else {
|
2214 | 2310 | throw new Error('There is no element with given selector.');
|
2215 | 2311 | }
|
2216 | 2312 | } else {
|
2217 |
| - return new IntroJs(document.body); |
| 2313 | + instance = new IntroJs(document.body); |
2218 | 2314 | }
|
| 2315 | + // add instance to list of _instances |
| 2316 | + // passing group to _stamp to increment |
| 2317 | + // from 0 onward somewhat reliably |
| 2318 | + introJs.instances[ _stamp(instance, 'introjs-instance') ] = instance; |
| 2319 | + |
| 2320 | + return instance; |
2219 | 2321 | };
|
2220 | 2322 |
|
2221 | 2323 | /**
|
|
2226 | 2328 | */
|
2227 | 2329 | introJs.version = VERSION;
|
2228 | 2330 |
|
| 2331 | + /** |
| 2332 | + * key-val object helper for introJs instances |
| 2333 | + * |
| 2334 | + * @property instances |
| 2335 | + * @type Object |
| 2336 | + */ |
| 2337 | + introJs.instances = {}; |
| 2338 | + |
2229 | 2339 | //Prototype
|
2230 | 2340 | introJs.fn = IntroJs.prototype = {
|
2231 | 2341 | clone: function () {
|
|
0 commit comments