Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50663 views
1
2
/*
3
* browser.js: Browser specific functionality for director.
4
*
5
* (C) 2011, Nodejitsu Inc.
6
* MIT LICENSE
7
*
8
*/
9
10
if (!Array.prototype.filter) {
11
Array.prototype.filter = function(filter, that) {
12
var other = [], v;
13
for (var i = 0, n = this.length; i < n; i++) {
14
if (i in this && filter.call(that, v = this[i], i, this)) {
15
other.push(v);
16
}
17
}
18
return other;
19
};
20
}
21
22
if (!Array.isArray){
23
Array.isArray = function(obj) {
24
return Object.prototype.toString.call(obj) === '[object Array]';
25
};
26
}
27
28
var dloc = document.location;
29
30
var listener = {
31
mode: 'modern',
32
hash: dloc.hash,
33
history: false,
34
35
check: function () {
36
var h = dloc.hash;
37
if (h != this.hash) {
38
this.hash = h;
39
this.onHashChanged();
40
}
41
},
42
43
fire: function () {
44
if (this.mode === 'modern') {
45
this.history === true ? window.onpopstate() : window.onhashchange();
46
}
47
else {
48
this.onHashChanged();
49
}
50
},
51
52
init: function (fn, history) {
53
var self = this;
54
this.history = history;
55
56
if (!window.Router.listeners) {
57
window.Router.listeners = [];
58
}
59
60
function onchange(onChangeEvent) {
61
for (var i = 0, l = window.Router.listeners.length; i < l; i++) {
62
window.Router.listeners[i](onChangeEvent);
63
}
64
}
65
66
//note IE8 is being counted as 'modern' because it has the hashchange event
67
if ('onhashchange' in window && (document.documentMode === undefined
68
|| document.documentMode > 7)) {
69
// At least for now HTML5 history is available for 'modern' browsers only
70
if (this.history === true) {
71
// There is an old bug in Chrome that causes onpopstate to fire even
72
// upon initial page load. Since the handler is run manually in init(),
73
// this would cause Chrome to run it twise. Currently the only
74
// workaround seems to be to set the handler after the initial page load
75
// http://code.google.com/p/chromium/issues/detail?id=63040
76
setTimeout(function() {
77
window.onpopstate = onchange;
78
}, 500);
79
}
80
else {
81
window.onhashchange = onchange;
82
}
83
this.mode = 'modern';
84
}
85
else {
86
//
87
// IE support, based on a concept by Erik Arvidson ...
88
//
89
var frame = document.createElement('iframe');
90
frame.id = 'state-frame';
91
frame.style.display = 'none';
92
document.body.appendChild(frame);
93
this.writeFrame('');
94
95
if ('onpropertychange' in document && 'attachEvent' in document) {
96
document.attachEvent('onpropertychange', function () {
97
if (event.propertyName === 'location') {
98
self.check();
99
}
100
});
101
}
102
103
window.setInterval(function () { self.check(); }, 50);
104
105
this.onHashChanged = onchange;
106
this.mode = 'legacy';
107
}
108
109
window.Router.listeners.push(fn);
110
111
return this.mode;
112
},
113
114
destroy: function (fn) {
115
if (!window.Router || !window.Router.listeners) {
116
return;
117
}
118
119
var listeners = window.Router.listeners;
120
121
for (var i = listeners.length - 1; i >= 0; i--) {
122
if (listeners[i] === fn) {
123
listeners.splice(i, 1);
124
}
125
}
126
},
127
128
setHash: function (s) {
129
// Mozilla always adds an entry to the history
130
if (this.mode === 'legacy') {
131
this.writeFrame(s);
132
}
133
134
if (this.history === true) {
135
window.history.pushState({}, document.title, s);
136
// Fire an onpopstate event manually since pushing does not obviously
137
// trigger the pop event.
138
this.fire();
139
} else {
140
dloc.hash = (s[0] === '/') ? s : '/' + s;
141
}
142
return this;
143
},
144
145
writeFrame: function (s) {
146
// IE support...
147
var f = document.getElementById('state-frame');
148
var d = f.contentDocument || f.contentWindow.document;
149
d.open();
150
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
151
d.close();
152
},
153
154
syncHash: function () {
155
// IE support...
156
var s = this._hash;
157
if (s != dloc.hash) {
158
dloc.hash = s;
159
}
160
return this;
161
},
162
163
onHashChanged: function () {}
164
};
165
166
var Router = exports.Router = function (routes) {
167
if (!(this instanceof Router)) return new Router(routes);
168
169
this.params = {};
170
this.routes = {};
171
this.methods = ['on', 'once', 'after', 'before'];
172
this._methods = {};
173
174
this._insert = this.insert;
175
this.insert = this.insertEx;
176
177
this.historySupport = (window.history != null ? window.history.pushState : null) != null
178
179
this.configure();
180
this.mount(routes || {});
181
};
182
183
Router.prototype.init = function (r) {
184
var self = this;
185
this.handler = function(onChangeEvent) {
186
var url = self.history === true ? self.getPath() : onChangeEvent.newURL.replace(/.*#/, '');
187
self.dispatch('on', url);
188
};
189
190
listener.init(this.handler, this.history);
191
192
if (this.history === false) {
193
if (dloc.hash === '' && r) {
194
dloc.hash = r;
195
} else if (dloc.hash.length > 0) {
196
self.dispatch('on', dloc.hash.replace(/^#/, ''));
197
}
198
}
199
else {
200
routeTo = dloc.hash === '' && r ? r : dloc.hash.length > 0 ? dloc.hash.replace(/^#/, '') : null;
201
if (routeTo) {
202
window.history.replaceState({}, document.title, routeTo);
203
}
204
205
// Router has been initialized, but due to the chrome bug it will not
206
// yet actually route HTML5 history state changes. Thus, decide if should route.
207
if (routeTo || this.run_in_init === true) {
208
this.handler();
209
}
210
}
211
212
return this;
213
};
214
215
Router.prototype.explode = function () {
216
var v = this.history === true ? this.getPath() : dloc.hash;
217
if (v[1] === '/') { v=v.slice(1) }
218
return v.slice(1, v.length).split("/");
219
};
220
221
Router.prototype.setRoute = function (i, v, val) {
222
var url = this.explode();
223
224
if (typeof i === 'number' && typeof v === 'string') {
225
url[i] = v;
226
}
227
else if (typeof val === 'string') {
228
url.splice(i, v, s);
229
}
230
else {
231
url = [i];
232
}
233
234
listener.setHash(url.join('/'));
235
return url;
236
};
237
238
//
239
// ### function insertEx(method, path, route, parent)
240
// #### @method {string} Method to insert the specific `route`.
241
// #### @path {Array} Parsed path to insert the `route` at.
242
// #### @route {Array|function} Route handlers to insert.
243
// #### @parent {Object} **Optional** Parent "routes" to insert into.
244
// insert a callback that will only occur once per the matched route.
245
//
246
Router.prototype.insertEx = function(method, path, route, parent) {
247
if (method === "once") {
248
method = "on";
249
route = function(route) {
250
var once = false;
251
return function() {
252
if (once) return;
253
once = true;
254
return route.apply(this, arguments);
255
};
256
}(route);
257
}
258
return this._insert(method, path, route, parent);
259
};
260
261
Router.prototype.getRoute = function (v) {
262
var ret = v;
263
264
if (typeof v === "number") {
265
ret = this.explode()[v];
266
}
267
else if (typeof v === "string"){
268
var h = this.explode();
269
ret = h.indexOf(v);
270
}
271
else {
272
ret = this.explode();
273
}
274
275
return ret;
276
};
277
278
Router.prototype.destroy = function () {
279
listener.destroy(this.handler);
280
return this;
281
};
282
283
Router.prototype.getPath = function () {
284
var path = window.location.pathname;
285
if (path.substr(0, 1) !== '/') {
286
path = '/' + path;
287
}
288
return path;
289
};
290
291