@ouroboros/browser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/clipboard.d.ts +24 -0
- package/clipboard.js +60 -0
- package/clipboard.ts +66 -0
- package/cookies.d.ts +55 -0
- package/cookies.js +87 -0
- package/cookies.ts +98 -0
- package/hash.d.ts +78 -0
- package/hash.js +195 -0
- package/hash.ts +239 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/index.ts +4 -0
- package/package.json +38 -0
- package/pageVisibility.d.ts +48 -0
- package/pageVisibility.js +132 -0
- package/pageVisibility.ts +156 -0
- package/tsconfig.json +16 -0
- package/tslint.json +10 -0
package/hash.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash
|
|
3
|
+
*
|
|
4
|
+
* JS Library to manage hash values
|
|
5
|
+
*
|
|
6
|
+
* @author Chris Nasr <chris@ouroboroscoding.com>
|
|
7
|
+
* @copyright Ouroboros Coding Inc.
|
|
8
|
+
* @created 2018-12-09
|
|
9
|
+
*/
|
|
10
|
+
// Generic modules
|
|
11
|
+
import { clone, empty, isObject, parseQuery } from '@ouroboros/tools';
|
|
12
|
+
// name regex
|
|
13
|
+
const nameRE = /^[a-zA-Z_]+$/;
|
|
14
|
+
// Callbacks
|
|
15
|
+
const dCallbacks = {};
|
|
16
|
+
// Values
|
|
17
|
+
let dHash = {};
|
|
18
|
+
/**
|
|
19
|
+
* Hash Changed
|
|
20
|
+
*
|
|
21
|
+
* Called when the location hash has been altered, notifies any watchers of
|
|
22
|
+
* hash values changing
|
|
23
|
+
*
|
|
24
|
+
* @name _hashChanged
|
|
25
|
+
* @access private
|
|
26
|
+
* @return {void}
|
|
27
|
+
*/
|
|
28
|
+
function _hashChanged() {
|
|
29
|
+
// Store the current hash
|
|
30
|
+
const old = clone(dHash);
|
|
31
|
+
// Re-parse the current location hash
|
|
32
|
+
dHash = parseQuery(window.location.hash.substring(1));
|
|
33
|
+
// If there are dCallbacks
|
|
34
|
+
if (!empty(dCallbacks)) {
|
|
35
|
+
// Check each watch
|
|
36
|
+
for (const name in dCallbacks) {
|
|
37
|
+
// If the value didn't exist and now it does, or it did exist
|
|
38
|
+
// and now it doesn't, or the values don't match
|
|
39
|
+
if ((!(name in old) && name in dHash) ||
|
|
40
|
+
(name in old && !(name in dHash)) ||
|
|
41
|
+
old[name] !== dHash[name]) {
|
|
42
|
+
// Go through each callback and call it
|
|
43
|
+
for (const f of dCallbacks[name]) {
|
|
44
|
+
f(dHash[name] || null);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Init
|
|
52
|
+
*
|
|
53
|
+
* Initialises the internal hash by fetching and parsing the current
|
|
54
|
+
* location hash
|
|
55
|
+
*
|
|
56
|
+
* @name init
|
|
57
|
+
* @access public
|
|
58
|
+
* @return {void}
|
|
59
|
+
*/
|
|
60
|
+
function init() {
|
|
61
|
+
// Track changes
|
|
62
|
+
window.addEventListener("hashchange", _hashChanged);
|
|
63
|
+
// Parse the current location hash
|
|
64
|
+
dHash = parseQuery(window.location.hash.substring(1));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get
|
|
68
|
+
*
|
|
69
|
+
* Returns a hash name
|
|
70
|
+
*
|
|
71
|
+
* @name get
|
|
72
|
+
* @access public
|
|
73
|
+
* @static
|
|
74
|
+
* @param {string} name The name to look for
|
|
75
|
+
* @param {string?} defaultReturn The value to return if the name isn't found
|
|
76
|
+
* @return {string | null}
|
|
77
|
+
*/
|
|
78
|
+
function get(name, defaultReturn) {
|
|
79
|
+
// If the name is invalid
|
|
80
|
+
if (!nameRE.test(name)) {
|
|
81
|
+
throw new Error('Invalid Hash name');
|
|
82
|
+
}
|
|
83
|
+
// If there is a value for the name
|
|
84
|
+
if (typeof dHash[name] !== 'undefined') {
|
|
85
|
+
return dHash[name];
|
|
86
|
+
}
|
|
87
|
+
// Else, return the default
|
|
88
|
+
else {
|
|
89
|
+
return defaultReturn === undefined ? null : defaultReturn;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Set
|
|
94
|
+
*
|
|
95
|
+
* Sets a specific name
|
|
96
|
+
*
|
|
97
|
+
* @name set
|
|
98
|
+
* @access public
|
|
99
|
+
* @param {string | object} name The name to set, or an object of name/value pairs
|
|
100
|
+
* @param {string?} value The value to set the name to
|
|
101
|
+
*/
|
|
102
|
+
function set(name, value) {
|
|
103
|
+
// If the name is not an object
|
|
104
|
+
if (!isObject(name)) {
|
|
105
|
+
// If the value is not defined
|
|
106
|
+
if (typeof value === 'undefined') {
|
|
107
|
+
value = '';
|
|
108
|
+
}
|
|
109
|
+
// Set the value
|
|
110
|
+
name = { [name]: value };
|
|
111
|
+
}
|
|
112
|
+
// Make a copy of the current hash
|
|
113
|
+
const copy = clone(dHash);
|
|
114
|
+
// Go through each name
|
|
115
|
+
for (const n of Object.keys(name)) {
|
|
116
|
+
// If the name is invalid
|
|
117
|
+
if (!nameRE.test(n)) {
|
|
118
|
+
throw new Error('Invalid Hash name: ' + n);
|
|
119
|
+
}
|
|
120
|
+
// If we got null, delete the name
|
|
121
|
+
if (name[n] === null) {
|
|
122
|
+
delete copy[n];
|
|
123
|
+
}
|
|
124
|
+
// Else, set the new value
|
|
125
|
+
else {
|
|
126
|
+
copy[n] = name[n];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Init an array to store the parts
|
|
130
|
+
const temp = [];
|
|
131
|
+
// Go through each name
|
|
132
|
+
for (const k of Object.keys(copy)) {
|
|
133
|
+
temp.push(k + '=' + copy[k]);
|
|
134
|
+
}
|
|
135
|
+
// Reset the window location hash
|
|
136
|
+
window.location.hash = temp.join('&');
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe
|
|
140
|
+
*
|
|
141
|
+
* Adds a callback for a specific name which will be called if the name is
|
|
142
|
+
* added, deleted, or changed
|
|
143
|
+
*
|
|
144
|
+
* @name subscribe
|
|
145
|
+
* @access public
|
|
146
|
+
* @param {string} name The name of the value to watch
|
|
147
|
+
* @param {function} callback The function to call when the value changes
|
|
148
|
+
* @return {void}
|
|
149
|
+
*/
|
|
150
|
+
function subscribe(name, callback) {
|
|
151
|
+
// If we already have the name
|
|
152
|
+
if (name in dCallbacks) {
|
|
153
|
+
// Go through the callbacks associated with the name
|
|
154
|
+
for (const f of dCallbacks[name]) {
|
|
155
|
+
// If we already have the callback there's no need to store it
|
|
156
|
+
if (f === callback) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Else if we don't have any callbacks for the name
|
|
162
|
+
else {
|
|
163
|
+
dCallbacks[name] = [];
|
|
164
|
+
}
|
|
165
|
+
// Add the callback to the dCallbacks
|
|
166
|
+
dCallbacks[name].push(callback);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Unsubscribe
|
|
170
|
+
*
|
|
171
|
+
* Removes a callback from the dCallbacks
|
|
172
|
+
*
|
|
173
|
+
* @name unsubscribe
|
|
174
|
+
* @access public
|
|
175
|
+
* @param {string} name The name of the value to stop watching
|
|
176
|
+
* @param {function} callback The callback to remove
|
|
177
|
+
* @return {void}
|
|
178
|
+
*/
|
|
179
|
+
function unsubscribe(name, callback) {
|
|
180
|
+
// If we have the name
|
|
181
|
+
if (name in dCallbacks) {
|
|
182
|
+
// Go through the callbacks associated with the name
|
|
183
|
+
for (let i = 0; i < dCallbacks[name].length; ++i) {
|
|
184
|
+
// If we find the callback
|
|
185
|
+
if (dCallbacks[name][i] === callback) {
|
|
186
|
+
// Remove it
|
|
187
|
+
dCallbacks[name].splice(i, 1);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Default export
|
|
194
|
+
const hash = { init, get, set, subscribe, unsubscribe };
|
|
195
|
+
export default hash;
|
package/hash.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash
|
|
3
|
+
*
|
|
4
|
+
* JS Library to manage hash values
|
|
5
|
+
*
|
|
6
|
+
* @author Chris Nasr <chris@ouroboroscoding.com>
|
|
7
|
+
* @copyright Ouroboros Coding Inc.
|
|
8
|
+
* @created 2018-12-09
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Generic modules
|
|
12
|
+
import { clone, empty, isObject, parseQuery } from '@ouroboros/tools';
|
|
13
|
+
|
|
14
|
+
// Callback type
|
|
15
|
+
type Callback = (value: string | null) => {};
|
|
16
|
+
|
|
17
|
+
// name regex
|
|
18
|
+
const nameRE = /^[a-zA-Z_]+$/;
|
|
19
|
+
|
|
20
|
+
// Callbacks
|
|
21
|
+
const dCallbacks: Record<string, Callback[]> = {};
|
|
22
|
+
|
|
23
|
+
// Values
|
|
24
|
+
let dHash: Record<string, string> = {};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Hash Changed
|
|
28
|
+
*
|
|
29
|
+
* Called when the location hash has been altered, notifies any watchers of
|
|
30
|
+
* hash values changing
|
|
31
|
+
*
|
|
32
|
+
* @name _hashChanged
|
|
33
|
+
* @access private
|
|
34
|
+
* @return {void}
|
|
35
|
+
*/
|
|
36
|
+
function _hashChanged(): void {
|
|
37
|
+
|
|
38
|
+
// Store the current hash
|
|
39
|
+
const old = clone(dHash);
|
|
40
|
+
|
|
41
|
+
// Re-parse the current location hash
|
|
42
|
+
dHash = parseQuery(window.location.hash.substring(1));
|
|
43
|
+
|
|
44
|
+
// If there are dCallbacks
|
|
45
|
+
if(!empty(dCallbacks)) {
|
|
46
|
+
|
|
47
|
+
// Check each watch
|
|
48
|
+
for(const name in dCallbacks) {
|
|
49
|
+
|
|
50
|
+
// If the value didn't exist and now it does, or it did exist
|
|
51
|
+
// and now it doesn't, or the values don't match
|
|
52
|
+
if((!(name in old) && name in dHash) ||
|
|
53
|
+
(name in old && !(name in dHash)) ||
|
|
54
|
+
old[name] !== dHash[name]) {
|
|
55
|
+
|
|
56
|
+
// Go through each callback and call it
|
|
57
|
+
for(const f of dCallbacks[name]) {
|
|
58
|
+
f(dHash[name] || null);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Init
|
|
67
|
+
*
|
|
68
|
+
* Initialises the internal hash by fetching and parsing the current
|
|
69
|
+
* location hash
|
|
70
|
+
*
|
|
71
|
+
* @name init
|
|
72
|
+
* @access public
|
|
73
|
+
* @return {void}
|
|
74
|
+
*/
|
|
75
|
+
function init(): void {
|
|
76
|
+
|
|
77
|
+
// Track changes
|
|
78
|
+
window.addEventListener("hashchange", _hashChanged);
|
|
79
|
+
|
|
80
|
+
// Parse the current location hash
|
|
81
|
+
dHash = parseQuery(window.location.hash.substring(1));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get
|
|
86
|
+
*
|
|
87
|
+
* Returns a hash name
|
|
88
|
+
*
|
|
89
|
+
* @name get
|
|
90
|
+
* @access public
|
|
91
|
+
* @static
|
|
92
|
+
* @param {string} name The name to look for
|
|
93
|
+
* @param {string?} defaultReturn The value to return if the name isn't found
|
|
94
|
+
* @return {string | null}
|
|
95
|
+
*/
|
|
96
|
+
function get(name: string, defaultReturn?: string): string | null {
|
|
97
|
+
|
|
98
|
+
// If the name is invalid
|
|
99
|
+
if(!nameRE.test(name)) {
|
|
100
|
+
throw new Error('Invalid Hash name');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If there is a value for the name
|
|
104
|
+
if(typeof dHash[name] !== 'undefined') {
|
|
105
|
+
return dHash[name];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Else, return the default
|
|
109
|
+
else {
|
|
110
|
+
return defaultReturn === undefined ? null : defaultReturn;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Set
|
|
116
|
+
*
|
|
117
|
+
* Sets a specific name
|
|
118
|
+
*
|
|
119
|
+
* @name set
|
|
120
|
+
* @access public
|
|
121
|
+
* @param {string | object} name The name to set, or an object of name/value pairs
|
|
122
|
+
* @param {string?} value The value to set the name to
|
|
123
|
+
*/
|
|
124
|
+
function set(name: string | object, value?: string) {
|
|
125
|
+
|
|
126
|
+
// If the name is not an object
|
|
127
|
+
if(!isObject(name)) {
|
|
128
|
+
|
|
129
|
+
// If the value is not defined
|
|
130
|
+
if(typeof value === 'undefined') {
|
|
131
|
+
value = '';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Set the value
|
|
135
|
+
name = {[name as string]: value}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Make a copy of the current hash
|
|
139
|
+
const copy = clone(dHash);
|
|
140
|
+
|
|
141
|
+
// Go through each name
|
|
142
|
+
for(const n of Object.keys(name)) {
|
|
143
|
+
|
|
144
|
+
// If the name is invalid
|
|
145
|
+
if(!nameRE.test(n)) {
|
|
146
|
+
throw new Error('Invalid Hash name: ' + n);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// If we got null, delete the name
|
|
150
|
+
if(name[n as keyof object] === null) {
|
|
151
|
+
delete copy[n];
|
|
152
|
+
}
|
|
153
|
+
// Else, set the new value
|
|
154
|
+
else {
|
|
155
|
+
copy[n] = name[n as keyof object];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Init an array to store the parts
|
|
160
|
+
const temp: string[] = [];
|
|
161
|
+
|
|
162
|
+
// Go through each name
|
|
163
|
+
for(const k of Object.keys(copy)) {
|
|
164
|
+
temp.push(k + '=' + copy[k]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Reset the window location hash
|
|
168
|
+
window.location.hash = temp.join('&');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Subscribe
|
|
173
|
+
*
|
|
174
|
+
* Adds a callback for a specific name which will be called if the name is
|
|
175
|
+
* added, deleted, or changed
|
|
176
|
+
*
|
|
177
|
+
* @name subscribe
|
|
178
|
+
* @access public
|
|
179
|
+
* @param {string} name The name of the value to watch
|
|
180
|
+
* @param {function} callback The function to call when the value changes
|
|
181
|
+
* @return {void}
|
|
182
|
+
*/
|
|
183
|
+
function subscribe(name: string, callback: Callback): void {
|
|
184
|
+
|
|
185
|
+
// If we already have the name
|
|
186
|
+
if(name in dCallbacks) {
|
|
187
|
+
|
|
188
|
+
// Go through the callbacks associated with the name
|
|
189
|
+
for(const f of dCallbacks[name]) {
|
|
190
|
+
|
|
191
|
+
// If we already have the callback there's no need to store it
|
|
192
|
+
if(f === callback) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Else if we don't have any callbacks for the name
|
|
199
|
+
else {
|
|
200
|
+
dCallbacks[name] = [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Add the callback to the dCallbacks
|
|
204
|
+
dCallbacks[name].push(callback);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Unsubscribe
|
|
209
|
+
*
|
|
210
|
+
* Removes a callback from the dCallbacks
|
|
211
|
+
*
|
|
212
|
+
* @name unsubscribe
|
|
213
|
+
* @access public
|
|
214
|
+
* @param {string} name The name of the value to stop watching
|
|
215
|
+
* @param {function} callback The callback to remove
|
|
216
|
+
* @return {void}
|
|
217
|
+
*/
|
|
218
|
+
function unsubscribe(name: string, callback: Callback): void {
|
|
219
|
+
|
|
220
|
+
// If we have the name
|
|
221
|
+
if(name in dCallbacks) {
|
|
222
|
+
|
|
223
|
+
// Go through the callbacks associated with the name
|
|
224
|
+
for(let i = 0; i < dCallbacks[name].length; ++i) {
|
|
225
|
+
|
|
226
|
+
// If we find the callback
|
|
227
|
+
if(dCallbacks[name][i] === callback) {
|
|
228
|
+
|
|
229
|
+
// Remove it
|
|
230
|
+
dCallbacks[name].splice(i, 1);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Default export
|
|
238
|
+
const hash = { init, get, set, subscribe, unsubscribe };
|
|
239
|
+
export default hash;
|
package/index.d.ts
ADDED
package/index.js
ADDED
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ouroboros/browser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Package to handle common browser functionality.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tslint -p tsconfig.json && tsc",
|
|
9
|
+
"clean": "rm *.js *.d.ts",
|
|
10
|
+
"test": "echo \"Error: no test specified.\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/ouroboroscoding/browser-js.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"javascript",
|
|
18
|
+
"browser",
|
|
19
|
+
"clipboard",
|
|
20
|
+
"cookies",
|
|
21
|
+
"hash",
|
|
22
|
+
"page visibility"
|
|
23
|
+
],
|
|
24
|
+
"author": "Chris Nasr <chris@ouroboroscoding.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/ouroboroscoding/browser-js/issues"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"tslint": "^6.1.3",
|
|
31
|
+
"tslint-config-prettier": "^1.18.0",
|
|
32
|
+
"typescript": "^4.8.3"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@ouroboros/tools": "^0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {}
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Visibility
|
|
3
|
+
*
|
|
4
|
+
* Library/Namespace containing various functions to track page visibility
|
|
5
|
+
*
|
|
6
|
+
* @author Chris Nasr <chris@ouroboroscoding.com>
|
|
7
|
+
* @copyright Ouroboros Coding Inc.
|
|
8
|
+
* @created 2018-08-01
|
|
9
|
+
*/
|
|
10
|
+
type Callback = (...args: any[]) => {};
|
|
11
|
+
/**
|
|
12
|
+
* Get
|
|
13
|
+
*
|
|
14
|
+
* Returns the current state of visibility
|
|
15
|
+
*
|
|
16
|
+
* @name get
|
|
17
|
+
* @access public
|
|
18
|
+
* @return {object | false}
|
|
19
|
+
*/
|
|
20
|
+
export declare function get(): object | false;
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe
|
|
23
|
+
*
|
|
24
|
+
* Track changes on page visibility
|
|
25
|
+
*
|
|
26
|
+
* @name subscribe
|
|
27
|
+
* @access public
|
|
28
|
+
* @param {function} callback Function to call on visibility changes
|
|
29
|
+
* @return {boolean}
|
|
30
|
+
*/
|
|
31
|
+
export declare function subscribe(callback: Callback): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Ubsubscribe
|
|
34
|
+
*
|
|
35
|
+
* Stop tracking changes in page visibility
|
|
36
|
+
*
|
|
37
|
+
* @name unsubscribe
|
|
38
|
+
* @access public
|
|
39
|
+
* @param {function} callback
|
|
40
|
+
* @return {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export declare function unsubscribe(callback: Callback): boolean;
|
|
43
|
+
declare const pageVisibility: {
|
|
44
|
+
get: typeof get;
|
|
45
|
+
subscribe: typeof subscribe;
|
|
46
|
+
unsubscribe: typeof unsubscribe;
|
|
47
|
+
};
|
|
48
|
+
export default pageVisibility;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Visibility
|
|
3
|
+
*
|
|
4
|
+
* Library/Namespace containing various functions to track page visibility
|
|
5
|
+
*
|
|
6
|
+
* @author Chris Nasr <chris@ouroboroscoding.com>
|
|
7
|
+
* @copyright Ouroboros Coding Inc.
|
|
8
|
+
* @created 2018-08-01
|
|
9
|
+
*/
|
|
10
|
+
// Init the visibility property name and event values
|
|
11
|
+
let bVis = false;
|
|
12
|
+
const dVis = {
|
|
13
|
+
property: 'hidden',
|
|
14
|
+
event: 'visibilitychange',
|
|
15
|
+
state: 'visibilityState'
|
|
16
|
+
};
|
|
17
|
+
const lCallbacks = [];
|
|
18
|
+
// Figure out the name of the visibility property and event
|
|
19
|
+
// Valid browser prefixes
|
|
20
|
+
const lPrefixes = ['moz', 'ms', 'o', 'webkit'];
|
|
21
|
+
// If a prefix is required
|
|
22
|
+
if ('hidden' in document) {
|
|
23
|
+
bVis = true;
|
|
24
|
+
}
|
|
25
|
+
// Else, loop through the prefixes
|
|
26
|
+
else {
|
|
27
|
+
for (const prefix of lPrefixes) {
|
|
28
|
+
// If the prefixes version exists
|
|
29
|
+
if ((prefix + 'Hidden') in document) {
|
|
30
|
+
dVis.property = prefix + 'Hidden';
|
|
31
|
+
dVis.event = prefix + 'visibilitychange';
|
|
32
|
+
dVis.state = prefix + 'VisibilityState';
|
|
33
|
+
bVis = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Track
|
|
40
|
+
*
|
|
41
|
+
* The actual function passed to the event so that we only have one event listener
|
|
42
|
+
*
|
|
43
|
+
* @name track
|
|
44
|
+
* @access private
|
|
45
|
+
* @return {void}
|
|
46
|
+
*/
|
|
47
|
+
function track() {
|
|
48
|
+
// Call the callbacks and pass them bool and state values
|
|
49
|
+
for (const f of lCallbacks) {
|
|
50
|
+
f(document[dVis.property], document[dVis.state]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get
|
|
55
|
+
*
|
|
56
|
+
* Returns the current state of visibility
|
|
57
|
+
*
|
|
58
|
+
* @name get
|
|
59
|
+
* @access public
|
|
60
|
+
* @return {object | false}
|
|
61
|
+
*/
|
|
62
|
+
export function get() {
|
|
63
|
+
// If the library is available
|
|
64
|
+
if (bVis) {
|
|
65
|
+
return {
|
|
66
|
+
property: document[dVis.property],
|
|
67
|
+
state: document[dVis.state]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.error('Page Visibility API not available');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Subscribe
|
|
77
|
+
*
|
|
78
|
+
* Track changes on page visibility
|
|
79
|
+
*
|
|
80
|
+
* @name subscribe
|
|
81
|
+
* @access public
|
|
82
|
+
* @param {function} callback Function to call on visibility changes
|
|
83
|
+
* @return {boolean}
|
|
84
|
+
*/
|
|
85
|
+
export function subscribe(callback) {
|
|
86
|
+
// If the Page Visibility API is not available
|
|
87
|
+
if (bVis === false) {
|
|
88
|
+
console.error('Page Visibility API not available');
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
// Save the callback for later
|
|
92
|
+
lCallbacks.push(callback);
|
|
93
|
+
// Attach an event listener if this is our first event
|
|
94
|
+
if (lCallbacks.length === 1) {
|
|
95
|
+
document.addEventListener(dVis.event, track);
|
|
96
|
+
}
|
|
97
|
+
// Return ok
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Ubsubscribe
|
|
102
|
+
*
|
|
103
|
+
* Stop tracking changes in page visibility
|
|
104
|
+
*
|
|
105
|
+
* @name unsubscribe
|
|
106
|
+
* @access public
|
|
107
|
+
* @param {function} callback
|
|
108
|
+
* @return {boolean}
|
|
109
|
+
*/
|
|
110
|
+
export function unsubscribe(callback) {
|
|
111
|
+
// If the Page Visibility API is not available
|
|
112
|
+
if (bVis === false) {
|
|
113
|
+
console.error('Page Visibility API not available');
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
// Find the index of the callback
|
|
117
|
+
const iIndex = lCallbacks.indexOf(callback);
|
|
118
|
+
// If it exists
|
|
119
|
+
if (iIndex > -1) {
|
|
120
|
+
// Remove the callback
|
|
121
|
+
lCallbacks.splice(iIndex, 1);
|
|
122
|
+
// Remove event listener if we have no more callbacks
|
|
123
|
+
if (lCallbacks.length === 0) {
|
|
124
|
+
document.removeEventListener(dVis.event, track);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Return ok
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// Default export
|
|
131
|
+
const pageVisibility = { get, subscribe, unsubscribe };
|
|
132
|
+
export default pageVisibility;
|