@ouroboros/browser 0.2.0 → 0.3.1

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.
Files changed (4) hide show
  1. package/hash.d.ts +4 -16
  2. package/hash.js +74 -69
  3. package/hash.ts +98 -83
  4. package/package.json +1 -1
package/hash.d.ts CHANGED
@@ -7,18 +7,7 @@
7
7
  * @copyright Ouroboros Coding Inc.
8
8
  * @created 2018-12-09
9
9
  */
10
- type Callback = (value: string | null) => void;
11
- /**
12
- * Init
13
- *
14
- * Initialises the internal hash by fetching and parsing the current
15
- * location hash
16
- *
17
- * @name init
18
- * @access public
19
- * @return {void}
20
- */
21
- declare function init(): void;
10
+ import { SubscribeCallback, SubscribeReturn } from '@ouroboros/subscribe';
22
11
  /**
23
12
  * Get
24
13
  *
@@ -55,11 +44,11 @@ declare function set(name: string | object, value?: string): void;
55
44
  * @param {function} callback The function to call when the value changes
56
45
  * @return {void}
57
46
  */
58
- declare function subscribe(name: string, callback: Callback): void;
47
+ declare function subscribe(name: string, callback: SubscribeCallback): SubscribeReturn;
59
48
  /**
60
49
  * Unsubscribe
61
50
  *
62
- * Removes a callback from the dCallbacks
51
+ * Removes a callback from the oSubscribe
63
52
  *
64
53
  * @name unsubscribe
65
54
  * @access public
@@ -67,9 +56,8 @@ declare function subscribe(name: string, callback: Callback): void;
67
56
  * @param {function} callback The callback to remove
68
57
  * @return {void}
69
58
  */
70
- declare function unsubscribe(name: string, callback: Callback): void;
59
+ declare function unsubscribe(name: string, callback: SubscribeCallback): void;
71
60
  declare const hash: {
72
- init: typeof init;
73
61
  get: typeof get;
74
62
  set: typeof set;
75
63
  subscribe: typeof subscribe;
package/hash.js CHANGED
@@ -7,63 +7,56 @@
7
7
  * @copyright Ouroboros Coding Inc.
8
8
  * @created 2018-12-09
9
9
  */
10
- // Generic modules
11
- import clone from '@ouroboros/clone';
10
+ // Ouroboros modules
11
+ import Subscribe from '@ouroboros/subscribe';
12
12
  import { empty, isObject, parseQuery } from '@ouroboros/tools';
13
13
  // name regex
14
14
  const nameRE = /^[a-zA-Z_]+$/;
15
- // Callbacks
16
- const dCallbacks = {};
17
- // Values
18
- let dHash = {};
15
+ // Subscribers by name
16
+ const oSubscribe = {};
17
+ // Hash values by name (only null for init)
18
+ let dHash = null;
19
+ /**
20
+ * Init
21
+ *
22
+ * Only called once
23
+ */
24
+ if (dHash === null) {
25
+ // Track changes
26
+ window.addEventListener("hashchange", hashChanged);
27
+ // Parse the current location hash
28
+ dHash = parseQuery(window.location.hash.substring(1));
29
+ }
19
30
  /**
20
31
  * Hash Changed
21
32
  *
22
33
  * Called when the location hash has been altered, notifies any watchers of
23
34
  * hash values changing
24
35
  *
25
- * @name _hashChanged
36
+ * @name hashChanged
26
37
  * @access private
27
38
  * @return {void}
28
39
  */
29
- function _hashChanged() {
40
+ function hashChanged() {
30
41
  // Store the current hash
31
- const old = clone(dHash);
42
+ const old = { ...dHash };
32
43
  // Re-parse the current location hash
33
44
  dHash = parseQuery(window.location.hash.substring(1));
34
- // If there are dCallbacks
35
- if (!empty(dCallbacks)) {
36
- // Check each watch
37
- for (const name in dCallbacks) {
38
- // If the value didn't exist and now it does, or it did exist
39
- // and now it doesn't, or the values don't match
40
- if ((!(name in old) && name in dHash) ||
41
- (name in old && !(name in dHash)) ||
42
- old[name] !== dHash[name]) {
43
- // Go through each callback and call it
44
- for (const f of dCallbacks[name]) {
45
- f(dHash[name] || null);
46
- }
45
+ // If there are oSubscribe
46
+ if (!empty(oSubscribe)) {
47
+ // Check each subscribe instance
48
+ for (const name of Object.keys(oSubscribe)) {
49
+ // If the value no longer exists, set it to null
50
+ if (name in old && !(name in dHash)) {
51
+ oSubscribe[name].set(null);
52
+ }
53
+ // Else, the value is new, or altered, so use the new value
54
+ else {
55
+ oSubscribe[name].set(dHash[name]);
47
56
  }
48
57
  }
49
58
  }
50
59
  }
51
- /**
52
- * Init
53
- *
54
- * Initialises the internal hash by fetching and parsing the current
55
- * location hash
56
- *
57
- * @name init
58
- * @access public
59
- * @return {void}
60
- */
61
- function init() {
62
- // Track changes
63
- window.addEventListener("hashchange", _hashChanged);
64
- // Parse the current location hash
65
- dHash = parseQuery(window.location.hash.substring(1));
66
- }
67
60
  /**
68
61
  * Get
69
62
  *
@@ -90,6 +83,20 @@ function get(name, defaultReturn) {
90
83
  return defaultReturn === undefined ? null : defaultReturn;
91
84
  }
92
85
  }
86
+ /**
87
+ * Reset
88
+ *
89
+ * Notifies all existing subscriptions that all data went away
90
+ *
91
+ * @name reset
92
+ * @access private
93
+ */
94
+ function reset() {
95
+ // Go through each subscribe instance
96
+ for (const name of Object.keys(oSubscribe)) {
97
+ oSubscribe[name].set(null);
98
+ }
99
+ }
93
100
  /**
94
101
  * Set
95
102
  *
@@ -111,7 +118,7 @@ function set(name, value) {
111
118
  name = { [name]: value };
112
119
  }
113
120
  // Make a copy of the current hash
114
- const copy = clone(dHash);
121
+ const copy = { ...dHash };
115
122
  // Go through each name
116
123
  for (const n of Object.keys(name)) {
117
124
  // If the name is invalid
@@ -131,10 +138,24 @@ function set(name, value) {
131
138
  const temp = [];
132
139
  // Go through each name
133
140
  for (const k of Object.keys(copy)) {
134
- temp.push(k + '=' + copy[k]);
141
+ temp.push(copy[k] === '' ? k : (k + '=' + copy[k]));
142
+ }
143
+ // If we have none
144
+ if (temp.length === 0) {
145
+ // If we can use pushState
146
+ if ('pushState' in history) {
147
+ history.pushState('', document.title, window.location.pathname + window.location.search);
148
+ reset();
149
+ }
150
+ // Else, we can't remove the '#', but clear the rest regardless
151
+ else {
152
+ window.location.hash = '';
153
+ }
154
+ }
155
+ // Else, reset the window location hash
156
+ else {
157
+ window.location.hash = temp.join('&');
135
158
  }
136
- // Reset the window location hash
137
- window.location.hash = temp.join('&');
138
159
  }
139
160
  /**
140
161
  * Subscribe
@@ -149,27 +170,18 @@ function set(name, value) {
149
170
  * @return {void}
150
171
  */
151
172
  function subscribe(name, callback) {
152
- // If we already have the name
153
- if (name in dCallbacks) {
154
- // Go through the callbacks associated with the name
155
- for (const f of dCallbacks[name]) {
156
- // If we already have the callback there's no need to store it
157
- if (f === callback) {
158
- return;
159
- }
160
- }
173
+ // If we don't already have the name
174
+ if (!(name in oSubscribe)) {
175
+ // Create a new subscribe instance
176
+ oSubscribe[name] = new Subscribe(name in dHash ? dHash[name] : null);
161
177
  }
162
- // Else if we don't have any callbacks for the name
163
- else {
164
- dCallbacks[name] = [];
165
- }
166
- // Add the callback to the dCallbacks
167
- dCallbacks[name].push(callback);
178
+ // Add the callback and return
179
+ return oSubscribe[name].subscribe(callback);
168
180
  }
169
181
  /**
170
182
  * Unsubscribe
171
183
  *
172
- * Removes a callback from the dCallbacks
184
+ * Removes a callback from the oSubscribe
173
185
  *
174
186
  * @name unsubscribe
175
187
  * @access public
@@ -179,18 +191,11 @@ function subscribe(name, callback) {
179
191
  */
180
192
  function unsubscribe(name, callback) {
181
193
  // If we have the name
182
- if (name in dCallbacks) {
183
- // Go through the callbacks associated with the name
184
- for (let i = 0; i < dCallbacks[name].length; ++i) {
185
- // If we find the callback
186
- if (dCallbacks[name][i] === callback) {
187
- // Remove it
188
- dCallbacks[name].splice(i, 1);
189
- return;
190
- }
191
- }
194
+ if (name in oSubscribe) {
195
+ // Attempt to unsubscribe
196
+ oSubscribe[name].unsubscribe(callback);
192
197
  }
193
198
  }
194
199
  // Default export
195
- const hash = { init, get, set, subscribe, unsubscribe };
200
+ const hash = { get, set, subscribe, unsubscribe };
196
201
  export default hash;
package/hash.ts CHANGED
@@ -8,21 +8,38 @@
8
8
  * @created 2018-12-09
9
9
  */
10
10
 
11
- // Generic modules
12
- import clone from '@ouroboros/clone';
11
+ // Ouroboros modules
12
+ import Subscribe, {
13
+ SubscribeCallback,
14
+ SubscribeReturn
15
+ } from '@ouroboros/subscribe';
13
16
  import { empty, isObject, parseQuery } from '@ouroboros/tools';
14
17
 
15
- // Callback type
16
- type Callback = (value: string | null) => void;
18
+ // Type
19
+ type HashType = Record<string, string>;
17
20
 
18
21
  // name regex
19
22
  const nameRE = /^[a-zA-Z_]+$/;
20
23
 
21
- // Callbacks
22
- const dCallbacks: Record<string, Callback[]> = {};
24
+ // Subscribers by name
25
+ const oSubscribe: Record<string, Subscribe> = {};
23
26
 
24
- // Values
25
- let dHash: Record<string, string> = {};
27
+ // Hash values by name (only null for init)
28
+ let dHash: HashType | null = null;
29
+
30
+ /**
31
+ * Init
32
+ *
33
+ * Only called once
34
+ */
35
+ if(dHash === null) {
36
+
37
+ // Track changes
38
+ window.addEventListener("hashchange", hashChanged);
39
+
40
+ // Parse the current location hash
41
+ dHash = parseQuery(window.location.hash.substring(1));
42
+ }
26
43
 
27
44
  /**
28
45
  * Hash Changed
@@ -30,58 +47,37 @@ let dHash: Record<string, string> = {};
30
47
  * Called when the location hash has been altered, notifies any watchers of
31
48
  * hash values changing
32
49
  *
33
- * @name _hashChanged
50
+ * @name hashChanged
34
51
  * @access private
35
52
  * @return {void}
36
53
  */
37
- function _hashChanged(): void {
54
+ function hashChanged(): void {
38
55
 
39
56
  // Store the current hash
40
- const old = clone(dHash);
57
+ const old = { ...dHash };
41
58
 
42
59
  // Re-parse the current location hash
43
60
  dHash = parseQuery(window.location.hash.substring(1));
44
61
 
45
- // If there are dCallbacks
46
- if(!empty(dCallbacks)) {
62
+ // If there are oSubscribe
63
+ if(!empty(oSubscribe)) {
47
64
 
48
- // Check each watch
49
- for(const name in dCallbacks) {
65
+ // Check each subscribe instance
66
+ for(const name of Object.keys(oSubscribe)) {
50
67
 
51
- // If the value didn't exist and now it does, or it did exist
52
- // and now it doesn't, or the values don't match
53
- if((!(name in old) && name in dHash) ||
54
- (name in old && !(name in dHash)) ||
55
- old[name] !== dHash[name]) {
68
+ // If the value no longer exists, set it to null
69
+ if(name in old && !(name in dHash)) {
70
+ oSubscribe[name].set(null);
71
+ }
56
72
 
57
- // Go through each callback and call it
58
- for(const f of dCallbacks[name]) {
59
- f(dHash[name] || null);
60
- }
73
+ // Else, the value is new, or altered, so use the new value
74
+ else {
75
+ oSubscribe[name].set(dHash[name]);
61
76
  }
62
77
  }
63
78
  }
64
79
  }
65
80
 
66
- /**
67
- * Init
68
- *
69
- * Initialises the internal hash by fetching and parsing the current
70
- * location hash
71
- *
72
- * @name init
73
- * @access public
74
- * @return {void}
75
- */
76
- function init(): void {
77
-
78
- // Track changes
79
- window.addEventListener("hashchange", _hashChanged);
80
-
81
- // Parse the current location hash
82
- dHash = parseQuery(window.location.hash.substring(1));
83
- }
84
-
85
81
  /**
86
82
  * Get
87
83
  *
@@ -102,8 +98,8 @@ function get(name: string, defaultReturn?: string): string | null {
102
98
  }
103
99
 
104
100
  // If there is a value for the name
105
- if(typeof dHash[name] !== 'undefined') {
106
- return dHash[name];
101
+ if(typeof (dHash as HashType)[name] !== 'undefined') {
102
+ return (dHash as HashType)[name];
107
103
  }
108
104
 
109
105
  // Else, return the default
@@ -112,6 +108,22 @@ function get(name: string, defaultReturn?: string): string | null {
112
108
  }
113
109
  }
114
110
 
111
+ /**
112
+ * Reset
113
+ *
114
+ * Notifies all existing subscriptions that all data went away
115
+ *
116
+ * @name reset
117
+ * @access private
118
+ */
119
+ function reset(): void {
120
+
121
+ // Go through each subscribe instance
122
+ for(const name of Object.keys(oSubscribe)) {
123
+ oSubscribe[name].set(null);
124
+ }
125
+ }
126
+
115
127
  /**
116
128
  * Set
117
129
  *
@@ -137,7 +149,7 @@ function set(name: string | object, value?: string) {
137
149
  }
138
150
 
139
151
  // Make a copy of the current hash
140
- const copy = clone(dHash);
152
+ const copy = { ...dHash };
141
153
 
142
154
  // Go through each name
143
155
  for(const n of Object.keys(name)) {
@@ -162,11 +174,32 @@ function set(name: string | object, value?: string) {
162
174
 
163
175
  // Go through each name
164
176
  for(const k of Object.keys(copy)) {
165
- temp.push(k + '=' + copy[k]);
177
+ temp.push(copy[k] === '' ? k : (k + '=' + copy[k]));
178
+ }
179
+
180
+ // If we have none
181
+ if(temp.length === 0) {
182
+
183
+ // If we can use pushState
184
+ if('pushState' in history) {
185
+ history.pushState(
186
+ '',
187
+ document.title,
188
+ window.location.pathname + window.location.search
189
+ );
190
+ reset();
191
+ }
192
+
193
+ // Else, we can't remove the '#', but clear the rest regardless
194
+ else {
195
+ window.location.hash = '';
196
+ }
166
197
  }
167
198
 
168
- // Reset the window location hash
169
- window.location.hash = temp.join('&');
199
+ // Else, reset the window location hash
200
+ else {
201
+ window.location.hash = temp.join('&');
202
+ }
170
203
  }
171
204
 
172
205
  /**
@@ -181,34 +214,25 @@ function set(name: string | object, value?: string) {
181
214
  * @param {function} callback The function to call when the value changes
182
215
  * @return {void}
183
216
  */
184
- function subscribe(name: string, callback: Callback): void {
185
-
186
- // If we already have the name
187
- if(name in dCallbacks) {
217
+ function subscribe(name: string, callback: SubscribeCallback): SubscribeReturn {
188
218
 
189
- // Go through the callbacks associated with the name
190
- for(const f of dCallbacks[name]) {
219
+ // If we don't already have the name
220
+ if(!(name in oSubscribe)) {
191
221
 
192
- // If we already have the callback there's no need to store it
193
- if(f === callback) {
194
- return;
195
- }
196
- }
222
+ // Create a new subscribe instance
223
+ oSubscribe[name] = new Subscribe(
224
+ name in (dHash as HashType) ? (dHash as HashType)[name] : null
225
+ );
197
226
  }
198
227
 
199
- // Else if we don't have any callbacks for the name
200
- else {
201
- dCallbacks[name] = [];
202
- }
203
-
204
- // Add the callback to the dCallbacks
205
- dCallbacks[name].push(callback);
228
+ // Add the callback and return
229
+ return oSubscribe[name].subscribe(callback);
206
230
  }
207
231
 
208
232
  /**
209
233
  * Unsubscribe
210
234
  *
211
- * Removes a callback from the dCallbacks
235
+ * Removes a callback from the oSubscribe
212
236
  *
213
237
  * @name unsubscribe
214
238
  * @access public
@@ -216,25 +240,16 @@ function subscribe(name: string, callback: Callback): void {
216
240
  * @param {function} callback The callback to remove
217
241
  * @return {void}
218
242
  */
219
- function unsubscribe(name: string, callback: Callback): void {
243
+ function unsubscribe(name: string, callback: SubscribeCallback): void {
220
244
 
221
245
  // If we have the name
222
- if(name in dCallbacks) {
223
-
224
- // Go through the callbacks associated with the name
225
- for(let i = 0; i < dCallbacks[name].length; ++i) {
246
+ if(name in oSubscribe) {
226
247
 
227
- // If we find the callback
228
- if(dCallbacks[name][i] === callback) {
229
-
230
- // Remove it
231
- dCallbacks[name].splice(i, 1);
232
- return;
233
- }
234
- }
248
+ // Attempt to unsubscribe
249
+ oSubscribe[name].unsubscribe(callback);
235
250
  }
236
251
  }
237
252
 
238
253
  // Default export
239
- const hash = { init, get, set, subscribe, unsubscribe };
240
- export default hash;
254
+ const hash = { get, set, subscribe, unsubscribe };
255
+ export default hash;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouroboros/browser",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Package to handle common browser functionality.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",