-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
166 lines (132 loc) · 3.5 KB
/
index.js
File metadata and controls
166 lines (132 loc) · 3.5 KB
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
/**
* Expose the parse function
*/
module.exports = function(href, key, value) {
var l = arguments.length;
if (l === 1) return parse.bind(null, href);
if (l == 2) return parse('', href, key);
return parse(href, key, value);
};
/**
* Define a property
*/
var define = Object.defineProperty;
/**
* Parse a key value pair
*
* @param {String} href
* @param {String} key
* @param {Any} value
*/
function parse(base, key, value) {
var type = typeof value;
// resolve local JSON pointers
if (key === 'href' && type === 'string') return formatHref(base, value);
// TODO resolve "/" paths
if (!value || type !== 'object') return value;
var obj = copy(value);
if (key === '' || obj.href) seal(base || obj.href, obj, []);
return obj;
}
function formatHref(base, href) {
if (href.charAt(0) === '#') return (base || '') + href;
// sort the query params so they're consistent
var parts = href.split('?');
var qs = parts[1] ? '?' + parts[1].split('&').sort().join('&') : '';
if (qs === '?') qs = '';
return parts[0] + qs;
}
function copy(value) {
var obj = Array.isArray(value) ? immutableArray() : {};
var hashCode = 0;
var keys = Object.keys(value);
for (var i = 0, l = keys.length, k; i < l; i++) {
k = keys[i];
if (!value.hasOwnProperty(k)) continue;
hashCode = smi(appendHash(appendHash(hashCode, computeHash(k)), computeHash(value[k])));
define(obj, k, {
enumerable: true,
value: value[k]
});
}
define(obj, '__hash', {
value: smi(hashCode)
});
return obj;
}
function seal(base, value, path, shouldDefineHref) {
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return;
var isCollection;
for (var k in value) {
isCollection = k === 'collection' || k === 'data';
seal(base,
value[k],
(isCollection ? path : path.concat([k])),
isCollection);
}
if (shouldDefineHref && !value.href) {
if (!base) process.env.NODE_ENV !== 'production' && console.warn('collection missing base href. unexpected behavior may occur.', value);
else define(value, 'href', {
value: base + (path.length ? '#/' + path.join('/') : '')
});
}
Object.freeze(value);
}
function computeHash(item) {
var type = typeof item;
if (type === 'undefined' || item === null) return 0;
if (type === 'number') return hashNumber(item);
if (item.__hash) return item.__hash;
if (type !== 'string') item = JSON.stringify(item);
return hashString(item);
}
function hashNumber(number) {
var hash = number | 0;
while (number > 0xFFFFFFFF) {
number /= 0xFFFFFFFF;
hash ^= number;
}
return smi(hash);
}
function hashString(string) {
var hash = 0;
for (var i = 0, l = string.length; i < l; i++) {
hash = appendHash(hash, string.charCodeAt(i));
}
return smi(hash);
}
function appendHash(hash, code) {
return 31 * hash + code | 0;
}
function smi(i32) {
return ((i32 >>> 1) & 0x40000000) | (i32 & 0xBFFFFFFF);
}
/**
* Make descriptors for impure functions
*/
var impureArrayFunctions = [
'pop',
'push',
'shift',
'unshift',
'splice'
].reduce(function(acc, key) {
acc[key] = funcDescriptor(key);
return acc;
}, {});
/**
* Create an immutable array
*/
function immutableArray() {
return Object.defineProperties([], impureArrayFunctions);
}
/**
* Create a descriptor for an invalid function
*/
function funcDescriptor(prop) {
return {
value: function() {
throw new TypeError('Attempted to mutate an immutable object with Array.prototype.' + prop + '()');
}
};
}