This repository has been archived by the owner on Feb 17, 2021. It is now read-only.
/
utils.js
190 lines (174 loc) · 5.38 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
let validIdRegEx = /^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
/**
* Tests if a given callout ID is valid. Specifically, callout IDs
* require the first character to be a letter and all following
* characters to be alphanumeric, underscores, or dashes.
*
* @param {String} id - The ID to test.
* @returns {Boolean} True if defined and valid, false otherwise.
*/
export function isIdValid(id) {
return typeof id === 'string' && validIdRegEx.test(id);
}
/**
* Get a numeric value from a number or string. Used for sanitizing
* provided pixel values that could be expressed as a number
* or as a string such as "10px".
*
* @param {Number|String} val - The value to parse.
* @returns {Number} The parsed numeric value. If a value can't be
* determined, this will return 0.
*/
export function getPixelValue(val) {
let valType = typeof val;
if (valType === 'number') {
return val;
}
if (valType === 'string') {
let result = parseInt(val, 10);
return isNaN(result) ? 0 : result;
}
return 0;
}
/**
* A function to get a single DOM element. Relies on document.querySelector,
* but handles exceptions gracefully and returns null if element is not found
* or exception is thrown
* @private
*/
function getElement(element) {
try {
return document.querySelector(element);
} catch (err) { }
return null;
}
/**
* Given a value of "target" config option, returns the target DOM element.
* It is recommended to only assign one target per step. However, there are
* some use cases which require multiple step targets to be supplied. In
* this event, we will use the first target in the array that we can
* locate on the page.
*
* Param target is expected to be one of the following:
* 1) String - a query selector string
* 2) String [] - an array of query selector strings
* 3) DOM Element - a target DOM element itself
*
* @param {String|String[]|Element} target - The element to target.
* @returns {?Element} The matched element, or null if not found.
*/
export function getTargetEl(target) {
if (!target) {
return null;
}
if (typeof target === 'string') {
//Just one target to test. Check and return its results.
return getElement(target);
} else if (Array.isArray(target)) {
let queriedTarget;
// Multiple items to check. Check each and return the first success.
// Assuming they are all strings.
for (let i = 0, len = target.length; i < len; i++) {
if (typeof target[i] === 'string') {
queriedTarget = getElement(target[i]);
if (queriedTarget) {
return queriedTarget;
}
}
}
return null;
}
// Assume that the target is a DOM element
return target;
}
/**
* Determine whether given element is assigned the given class
*
* @param {Element} domEl - The element that should to be check for class presence
* @param {String} className - The class name to search for
*/
export function hasClass(domEl, className) {
if (!domEl.className) {
return false;
}
else {
let domClasses = ' ' + domEl.className + ' ';
let arrClassNames = domClasses.split(/\s+/);
return arrClassNames.indexOf(className) >= 0;
}
}
/**
* Adds one or more classes to a DOM element.
*
* @param {Element} domEl - The element to add the classes to.
* @param {String} strClassNames - A comma-separated list of classes to add.
*/
export function addClass(domEl, strClassNames) {
if (!domEl.className) {
domEl.className = strClassNames;
}
else {
let domClasses = ' ' + domEl.className + ' ';
let arrClassNames = strClassNames.split(/\s+/);
arrClassNames.forEach((className) => {
if (domClasses.indexOf(' ' + className + ' ') < 0) {
domClasses += className + ' ';
}
});
domEl.className = domClasses.replace(/^\s+|\s+$/g, '');
}
}
/**
* Remove one or more classes from a DOM element.
*
* @param {Element} domEl - The element to remove the classes from.
* @param {String} strClassNamesToRemove - A list of classes to remove. Class names
* should be separated by space.
*/
export function removeClass(domEl, strClassNamesToRemove) {
let domClasses = ' ' + domEl.className + ' ';
let arrClassesToRemove = strClassNamesToRemove.split(/\s+/);
arrClassesToRemove.forEach((className) => {
domClasses = domClasses.replace(' ' + className + ' ', ' ');
});
domEl.className = domClasses.replace(/^\s+|\s+$/g, '');
}
/**
* Log error to the console
*
* @param {String} message - The message to log.
*/
export function logError(message) {
if (typeof console !== 'undefined' && typeof console.error !== 'undefined') {
console.error(message);
}
}
/**
* Shallow copy of obj2 into obj1
* @private
*/
export function extend(obj1, obj2) {
for (let prop in obj2) {
if (obj2.hasOwnProperty(prop)) {
obj1[prop] = obj2[prop];
}
}
return obj1;
}
/**
* Determines if el parameter of this function conforms for the DOM
* API required by hopscotch
*/
export function isDOMElement(el) {
if (!el || typeof el.getBoundingClientRect !== 'function') {
return false;
}
let elBox = el.getBoundingClientRect();
return typeof el.style === 'object' &&
typeof elBox.right === 'number' &&
typeof elBox.left === 'number' &&
typeof elBox.top === 'number' &&
typeof elBox.bottom === 'number' &&
typeof elBox.height === 'number' &&
typeof elBox.width === 'number';
}