-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
172 lines (133 loc) · 3.93 KB
/
index.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
/**
* Allows application to access and update current app state via query string
*/
module.exports = queryState;
var eventify = require('ngraph.events');
var windowHistory = require('./lib/windowHistory.js');
/**
* Just a convenience function that returns singleton instance of a query state
*/
queryState.instance = instance;
// this variable holds singleton instance of the query state
var singletonQS;
/**
* Creates new instance of the query state.
*/
function queryState(defaults, options) {
options = options || {};
var history = options.history || windowHistory(defaults, options);
validateHistoryAPI(history);
history.onChanged(updateQuery)
var query = history.get() || Object.create(null);
var api = {
/**
* Gets current state.
*
* @param {string?} keyName if present then value for this key is returned.
* Otherwise the entire app state is returned.
*/
get: getValue,
/**
* Merges current app state with new key/value.
*
* @param {string} key name
* @param {string|number|date} value
*/
set: setValue,
/**
* Removes value from the query string
*/
unset: unsetValue,
/**
* Similar to `set()`, but only sets value if it was not set before.
*
* @param {string} key name
* @param {string|number|date} value
*/
setIfEmpty: setIfEmpty,
/**
* Releases all resources acquired by query state. After calling this method
* no hash monitoring will happen and no more events will be fired.
*/
dispose: dispose,
onChange: onChange,
offChange: offChange,
getHistoryObject: getHistoryObject,
}
var eventBus = eventify({});
return api;
function onChange(callback, ctx) {
eventBus.on('change', callback, ctx);
}
function offChange(callback, ctx) {
eventBus.off('change', callback, ctx)
}
function getHistoryObject() {
return history;
}
function dispose() {
// dispose all history listeners
history.dispose();
// And remove our own listeners
eventBus.off();
}
function getValue(keyName) {
if (keyName === undefined) return query;
return query[keyName];
}
function setValue(keyName, value) {
var keyNameType = typeof keyName;
if (keyNameType === 'object') {
Object.keys(keyName).forEach(function(key) {
query[key] = keyName[key];
});
} else if (keyNameType === 'string') {
query[keyName] = value;
}
history.set(query);
return api;
}
function unsetValue(keyName) {
if (!(keyName in query)) return; // nothing to do
delete query[keyName];
history.set(query);
return api;
}
function updateQuery(newAppState) {
query = newAppState;
eventBus.fire('change', query);
}
function setIfEmpty(keyName, value) {
if (typeof keyName === 'object') {
Object.keys(keyName).forEach(function(key) {
// TODO: Can I remove code duplication? The main reason why I don't
// want recursion here is to avoid spamming `history.set()`
if (key in query) return; // key name is not empty
query[key] = keyName[key];
});
}
if (keyName in query) return; // key name is not empty
query[keyName] = value;
history.set(query);
return api;
}
}
/**
* Returns singleton instance of the query state.
*
* @param {Object} defaults - if present, then it is passed to the current instance
* of the query state. Defaults are applied only if they were not present before.
*/
function instance(defaults, options) {
if (!singletonQS) {
singletonQS = queryState(defaults, options);
} else if (defaults) {
singletonQS.setIfEmpty(defaults);
}
return singletonQS;
}
function validateHistoryAPI(history) {
if (!history) throw new Error('history is required');
if (typeof history.dispose !== 'function') throw new Error('dispose is required');
if (typeof history.onChanged !== 'function') throw new Error('onChanged is required');
}