Skip to content

Commit d45ef37

Browse files
author
bozdoz
committed
added DOMEvent helper for events; _stamp helper for uniquely identifying objects; added instances method for getting a specific introJs instance; resolves usablica#433 as best as anything could;
1 parent 1b9879f commit d45ef37

File tree

1 file changed

+205
-95
lines changed

1 file changed

+205
-95
lines changed

intro.js

Lines changed: 205 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@
112112
* @returns {Boolean} Success or not?
113113
*/
114114
function _introForElement(targetElm) {
115-
var introItems = [],
116-
self = this;
115+
var introItems = [];
117116

118117
if (this._options.steps) {
119118
//use steps passed programmatically
@@ -247,92 +246,87 @@
247246
});
248247

249248
//set it to the introJs object
250-
self._introItems = introItems;
249+
this._introItems = introItems;
251250

252251
//add overlay layer to the page
253-
if(_addOverlayLayer.call(self, targetElm)) {
252+
if(_addOverlayLayer.call(this, targetElm)) {
254253
//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);
301255

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+
}
307264

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+
}
316268

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;
320289

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);
330315
}
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;
333328
}
334329
}
335-
return false;
336330
}
337331

338332
/*
@@ -553,11 +547,8 @@
553547
});
554548

555549
//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);
561552

562553
//check if any callback is defined
563554
if (this._introExitCallback !== undefined) {
@@ -1472,6 +1463,112 @@
14721463
}
14731464
}
14741465

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+
14751572
/**
14761573
* Append a class to an element
14771574
*
@@ -1722,15 +1819,12 @@
17221819

17231820
_addHints.call(this);
17241821

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);
17341828
}
17351829

17361830
/**
@@ -2158,22 +2252,30 @@
21582252
}
21592253

21602254
var introJs = function (targetElm) {
2255+
var instance;
2256+
21612257
if (typeof (targetElm) === 'object') {
21622258
//Ok, create a new instance
2163-
return new IntroJs(targetElm);
2259+
instance = new IntroJs(targetElm);
21642260

21652261
} else if (typeof (targetElm) === 'string') {
21662262
//select the target element with query selector
21672263
var targetElement = document.querySelector(targetElm);
21682264

21692265
if (targetElement) {
2170-
return new IntroJs(targetElement);
2266+
instance = new IntroJs(targetElement);
21712267
} else {
21722268
throw new Error('There is no element with given selector.');
21732269
}
21742270
} else {
2175-
return new IntroJs(document.body);
2271+
instance = new IntroJs(document.body);
21762272
}
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;
21772279
};
21782280

21792281
/**
@@ -2184,6 +2286,14 @@
21842286
*/
21852287
introJs.version = VERSION;
21862288

2289+
/**
2290+
* key-val object helper for introJs instances
2291+
*
2292+
* @property instances
2293+
* @type Object
2294+
*/
2295+
introJs.instances = {};
2296+
21872297
//Prototype
21882298
introJs.fn = IntroJs.prototype = {
21892299
clone: function () {

0 commit comments

Comments
 (0)