backbrace 0.0.17
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.
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/backbrace.gemspec +17 -0
- data/lib/backbrace.rb +50 -0
- data/lib/backbrace/resource_helpers.rb +55 -0
- data/lib/backbrace/version.rb +3 -0
- data/lib/templates/app.components.js +5 -0
- data/lib/templates/app.helpers.js +33 -0
- data/lib/templates/app.js +9 -0
- data/lib/templates/app.models.js +7 -0
- data/lib/templates/app.router.js +43 -0
- data/lib/templates/app.views.js +9 -0
- data/lib/templates/init.js +5 -0
- data/vendor/assets/javascripts/backbone.js +1431 -0
- data/vendor/assets/javascripts/crossroads.js +602 -0
- data/vendor/assets/javascripts/signals.js +422 -0
- data/vendor/assets/javascripts/underscore.js +1059 -0
- metadata +65 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
/** @license
|
|
2
|
+
* crossroads <http://millermedeiros.github.com/crossroads.js/>
|
|
3
|
+
* License: MIT
|
|
4
|
+
* Author: Miller Medeiros
|
|
5
|
+
* Version: 0.9.0 (2012/5/28 23:9)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
(function (define) {
|
|
9
|
+
define(['signals'], function (signals) {
|
|
10
|
+
|
|
11
|
+
var crossroads,
|
|
12
|
+
UNDEF;
|
|
13
|
+
|
|
14
|
+
// Helpers -----------
|
|
15
|
+
//====================
|
|
16
|
+
|
|
17
|
+
function arrayIndexOf(arr, val) {
|
|
18
|
+
if (arr.indexOf) {
|
|
19
|
+
return arr.indexOf(val);
|
|
20
|
+
} else {
|
|
21
|
+
//Array.indexOf doesn't work on IE 6-7
|
|
22
|
+
var n = arr.length;
|
|
23
|
+
while (n--) {
|
|
24
|
+
if (arr[n] === val) {
|
|
25
|
+
return n;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isKind(val, kind) {
|
|
33
|
+
return '[object ' + kind + ']' === Object.prototype.toString.call(val);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isRegExp(val) {
|
|
37
|
+
return isKind(val, 'RegExp');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isArray(val) {
|
|
41
|
+
return isKind(val, 'Array');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isFunction(val) {
|
|
45
|
+
return typeof val === 'function';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//borrowed from AMD-utils
|
|
49
|
+
function typecastValue(val) {
|
|
50
|
+
var r;
|
|
51
|
+
if (val === null || val === 'null') {
|
|
52
|
+
r = null;
|
|
53
|
+
} else if (val === 'true') {
|
|
54
|
+
r = true;
|
|
55
|
+
} else if (val === 'false') {
|
|
56
|
+
r = false;
|
|
57
|
+
} else if (val === UNDEF || val === 'undefined') {
|
|
58
|
+
r = UNDEF;
|
|
59
|
+
} else if (val === '' || isNaN(val)) {
|
|
60
|
+
//isNaN('') returns false
|
|
61
|
+
r = val;
|
|
62
|
+
} else {
|
|
63
|
+
//parseFloat(null || '') returns NaN
|
|
64
|
+
r = parseFloat(val);
|
|
65
|
+
}
|
|
66
|
+
return r;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function typecastArrayValues(values) {
|
|
70
|
+
var n = values.length,
|
|
71
|
+
result = [];
|
|
72
|
+
while (n--) {
|
|
73
|
+
result[n] = typecastValue(values[n]);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//borrowed from AMD-Utils
|
|
79
|
+
function decodeQueryString(str) {
|
|
80
|
+
var queryArr = (str || '').replace('?', '').split('&'),
|
|
81
|
+
n = queryArr.length,
|
|
82
|
+
obj = {},
|
|
83
|
+
item, val;
|
|
84
|
+
while (n--) {
|
|
85
|
+
item = queryArr[n].split('=');
|
|
86
|
+
val = typecastValue(item[1]);
|
|
87
|
+
obj[item[0]] = (typeof val === 'string') ? decodeURIComponent(val) : val;
|
|
88
|
+
}
|
|
89
|
+
return obj;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// Crossroads --------
|
|
94
|
+
//====================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @constructor
|
|
98
|
+
*/
|
|
99
|
+
function Crossroads() {
|
|
100
|
+
this._routes = [];
|
|
101
|
+
this._prevRoutes = [];
|
|
102
|
+
this.bypassed = new signals.Signal();
|
|
103
|
+
this.routed = new signals.Signal();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
Crossroads.prototype = {
|
|
107
|
+
|
|
108
|
+
greedy:false,
|
|
109
|
+
|
|
110
|
+
greedyEnabled:true,
|
|
111
|
+
|
|
112
|
+
normalizeFn:null,
|
|
113
|
+
|
|
114
|
+
create:function () {
|
|
115
|
+
return new Crossroads();
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
shouldTypecast:false,
|
|
119
|
+
|
|
120
|
+
addRoute:function (pattern, callback, priority) {
|
|
121
|
+
var route = new Route(pattern, callback, priority, this);
|
|
122
|
+
this._sortedInsert(route);
|
|
123
|
+
return route;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
removeRoute:function (route) {
|
|
127
|
+
var i = arrayIndexOf(this._routes, route);
|
|
128
|
+
if (i !== -1) {
|
|
129
|
+
this._routes.splice(i, 1);
|
|
130
|
+
}
|
|
131
|
+
route._destroy();
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
removeAllRoutes:function () {
|
|
135
|
+
var n = this.getNumRoutes();
|
|
136
|
+
while (n--) {
|
|
137
|
+
this._routes[n]._destroy();
|
|
138
|
+
}
|
|
139
|
+
this._routes.length = 0;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
parse:function (request, defaultArgs) {
|
|
143
|
+
request = request || '';
|
|
144
|
+
defaultArgs = defaultArgs || [];
|
|
145
|
+
|
|
146
|
+
var routes = this._getMatchedRoutes(request),
|
|
147
|
+
i = 0,
|
|
148
|
+
n = routes.length,
|
|
149
|
+
cur;
|
|
150
|
+
|
|
151
|
+
if (n) {
|
|
152
|
+
this._notifyPrevRoutes(routes, request);
|
|
153
|
+
this._prevRoutes = routes;
|
|
154
|
+
//shold be incremental loop, execute routes in order
|
|
155
|
+
while (i < n) {
|
|
156
|
+
cur = routes[i];
|
|
157
|
+
cur.route.matched.dispatch.apply(cur.route.matched, defaultArgs.concat(cur.params));
|
|
158
|
+
cur.isFirst = !i;
|
|
159
|
+
this.routed.dispatch.apply(this.routed, defaultArgs.concat([request, cur]));
|
|
160
|
+
i += 1;
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
this.bypassed.dispatch.apply(this.bypassed, defaultArgs.concat([request]));
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
_notifyPrevRoutes:function (matchedRoutes, request) {
|
|
168
|
+
var i = 0, prev;
|
|
169
|
+
while (prev = this._prevRoutes[i++]) {
|
|
170
|
+
//check if switched exist since route may be disposed
|
|
171
|
+
if (prev.route.switched && this._didSwitch(prev.route, matchedRoutes)) {
|
|
172
|
+
prev.route.switched.dispatch(request);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
_didSwitch:function (route, matchedRoutes) {
|
|
178
|
+
var matched,
|
|
179
|
+
i = 0;
|
|
180
|
+
while (matched = matchedRoutes[i++]) {
|
|
181
|
+
// only dispatch switched if it is going to a different route
|
|
182
|
+
if (matched.route === route) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
getNumRoutes:function () {
|
|
190
|
+
return this._routes.length;
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
_sortedInsert:function (route) {
|
|
194
|
+
//simplified insertion sort
|
|
195
|
+
var routes = this._routes,
|
|
196
|
+
n = routes.length;
|
|
197
|
+
do {
|
|
198
|
+
--n;
|
|
199
|
+
} while (routes[n] && route._priority <= routes[n]._priority);
|
|
200
|
+
routes.splice(n + 1, 0, route);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
_getMatchedRoutes:function (request) {
|
|
204
|
+
var res = [],
|
|
205
|
+
routes = this._routes,
|
|
206
|
+
n = routes.length,
|
|
207
|
+
route;
|
|
208
|
+
//should be decrement loop since higher priorities are added at the end of array
|
|
209
|
+
while (route = routes[--n]) {
|
|
210
|
+
if ((!res.length || this.greedy || route.greedy) && route.match(request)) {
|
|
211
|
+
res.push({
|
|
212
|
+
route:route,
|
|
213
|
+
params:route._getParamsArray(request)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (!this.greedyEnabled && res.length) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return res;
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
toString:function () {
|
|
224
|
+
return '[crossroads numRoutes:' + this.getNumRoutes() + ']';
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
//"static" instance
|
|
229
|
+
crossroads = new Crossroads();
|
|
230
|
+
crossroads.VERSION = '0.9.0';
|
|
231
|
+
|
|
232
|
+
crossroads.NORM_AS_ARRAY = function (req, vals) {
|
|
233
|
+
return [vals.vals_];
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
crossroads.NORM_AS_OBJECT = function (req, vals) {
|
|
237
|
+
return [vals];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
// Route --------------
|
|
242
|
+
//=====================
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @constructor
|
|
246
|
+
*/
|
|
247
|
+
function Route(pattern, callback, priority, router) {
|
|
248
|
+
var isRegexPattern = isRegExp(pattern),
|
|
249
|
+
patternLexer = crossroads.patternLexer;
|
|
250
|
+
this._router = router;
|
|
251
|
+
this._pattern = pattern;
|
|
252
|
+
this._paramsIds = isRegexPattern ? null : patternLexer.getParamIds(this._pattern);
|
|
253
|
+
this._optionalParamsIds = isRegexPattern ? null : patternLexer.getOptionalParamsIds(this._pattern);
|
|
254
|
+
this._matchRegexp = isRegexPattern ? pattern : patternLexer.compilePattern(pattern);
|
|
255
|
+
this.matched = new signals.Signal();
|
|
256
|
+
this.switched = new signals.Signal();
|
|
257
|
+
if (callback) {
|
|
258
|
+
this.matched.add(callback);
|
|
259
|
+
}
|
|
260
|
+
this._priority = priority || 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
Route.prototype = {
|
|
264
|
+
|
|
265
|
+
greedy:false,
|
|
266
|
+
|
|
267
|
+
rules:void(0),
|
|
268
|
+
|
|
269
|
+
match:function (request) {
|
|
270
|
+
request = request || '';
|
|
271
|
+
return this._matchRegexp.test(request) && this._validateParams(request); //validate params even if regexp because of `request_` rule.
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
_validateParams:function (request) {
|
|
275
|
+
var rules = this.rules,
|
|
276
|
+
values = this._getParamsObject(request),
|
|
277
|
+
key;
|
|
278
|
+
for (key in rules) {
|
|
279
|
+
// normalize_ isn't a validation rule... (#39)
|
|
280
|
+
if (key !== 'normalize_' && rules.hasOwnProperty(key) && !this._isValidParam(request, key, values)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
_isValidParam:function (request, prop, values) {
|
|
288
|
+
var validationRule = this.rules[prop],
|
|
289
|
+
val = values[prop],
|
|
290
|
+
isValid = false,
|
|
291
|
+
isQuery = (prop.indexOf('?') === 0);
|
|
292
|
+
|
|
293
|
+
if (val == null && this._optionalParamsIds && arrayIndexOf(this._optionalParamsIds, prop) !== -1) {
|
|
294
|
+
isValid = true;
|
|
295
|
+
}
|
|
296
|
+
else if (isRegExp(validationRule)) {
|
|
297
|
+
if (isQuery) {
|
|
298
|
+
val = values[prop + '_']; //use raw string
|
|
299
|
+
}
|
|
300
|
+
isValid = validationRule.test(val);
|
|
301
|
+
}
|
|
302
|
+
else if (isArray(validationRule)) {
|
|
303
|
+
if (isQuery) {
|
|
304
|
+
val = values[prop + '_']; //use raw string
|
|
305
|
+
}
|
|
306
|
+
isValid = arrayIndexOf(validationRule, val) !== -1;
|
|
307
|
+
}
|
|
308
|
+
else if (isFunction(validationRule)) {
|
|
309
|
+
isValid = validationRule(val, request, values);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return isValid; //fail silently if validationRule is from an unsupported type
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
_getParamsObject:function (request) {
|
|
316
|
+
var shouldTypecast = this._router.shouldTypecast,
|
|
317
|
+
values = crossroads.patternLexer.getParamValues(request, this._matchRegexp, shouldTypecast),
|
|
318
|
+
o = {},
|
|
319
|
+
n = values.length,
|
|
320
|
+
param, val;
|
|
321
|
+
while (n--) {
|
|
322
|
+
val = values[n];
|
|
323
|
+
if (this._paramsIds) {
|
|
324
|
+
param = this._paramsIds[n];
|
|
325
|
+
if (param.indexOf('?') === 0 && val) {
|
|
326
|
+
//make a copy of the original string so array and
|
|
327
|
+
//RegExp validation can be applied properly
|
|
328
|
+
o[param + '_'] = val;
|
|
329
|
+
//update vals_ array as well since it will be used
|
|
330
|
+
//during dispatch
|
|
331
|
+
val = decodeQueryString(val);
|
|
332
|
+
values[n] = val;
|
|
333
|
+
}
|
|
334
|
+
o[param] = val;
|
|
335
|
+
}
|
|
336
|
+
//alias to paths and for RegExp pattern
|
|
337
|
+
o[n] = val;
|
|
338
|
+
}
|
|
339
|
+
o.request_ = shouldTypecast ? typecastValue(request) : request;
|
|
340
|
+
o.vals_ = values;
|
|
341
|
+
return o;
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
_getParamsArray:function (request) {
|
|
345
|
+
var norm = this.rules ? this.rules.normalize_ : null,
|
|
346
|
+
params;
|
|
347
|
+
norm = norm || this._router.normalizeFn; // default normalize
|
|
348
|
+
if (norm && isFunction(norm)) {
|
|
349
|
+
params = norm(request, this._getParamsObject(request));
|
|
350
|
+
} else {
|
|
351
|
+
params = this._getParamsObject(request).vals_;
|
|
352
|
+
}
|
|
353
|
+
return params;
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
interpolate:function (replacements) {
|
|
357
|
+
var str = crossroads.patternLexer.interpolate(this._pattern, replacements);
|
|
358
|
+
if (!this._validateParams(str)) {
|
|
359
|
+
throw new Error('Generated string doesn\'t validate against `Route.rules`.');
|
|
360
|
+
}
|
|
361
|
+
return str;
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
dispose:function () {
|
|
365
|
+
this._router.removeRoute(this);
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
_destroy:function () {
|
|
369
|
+
this.matched.dispose();
|
|
370
|
+
this.switched.dispose();
|
|
371
|
+
this.matched = this.switched = this._pattern = this._matchRegexp = null;
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
toString:function () {
|
|
375
|
+
return '[Route pattern:"' + this._pattern + '", numListeners:' + this.matched.getNumListeners() + ']';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
// Pattern Lexer ------
|
|
382
|
+
//=====================
|
|
383
|
+
|
|
384
|
+
crossroads.patternLexer = (function () {
|
|
385
|
+
|
|
386
|
+
var
|
|
387
|
+
//match chars that should be escaped on string regexp
|
|
388
|
+
ESCAPE_CHARS_REGEXP = /[\\.+*?\^$\[\](){}\/'#]/g,
|
|
389
|
+
|
|
390
|
+
//trailing slashes (begin/end of string)
|
|
391
|
+
LOOSE_SLASHES_REGEXP = /^\/|\/$/g,
|
|
392
|
+
LEGACY_SLASHES_REGEXP = /\/$/g,
|
|
393
|
+
|
|
394
|
+
//params - everything between `{ }` or `: :`
|
|
395
|
+
PARAMS_REGEXP = /(?:\{|:)([^}:]+)(?:\}|:)/g,
|
|
396
|
+
|
|
397
|
+
//used to save params during compile (avoid escaping things that
|
|
398
|
+
//shouldn't be escaped).
|
|
399
|
+
TOKENS = {
|
|
400
|
+
'OS':{
|
|
401
|
+
//optional slashes
|
|
402
|
+
//slash between `::` or `}:` or `\w:` or `:{?` or `}{?` or `\w{?`
|
|
403
|
+
rgx:/([:}]|\w(?=\/))\/?(:|(?:\{\?))/g,
|
|
404
|
+
save:'$1{{id}}$2',
|
|
405
|
+
res:'\\/?'
|
|
406
|
+
},
|
|
407
|
+
'RS':{
|
|
408
|
+
//required slashes
|
|
409
|
+
//used to insert slash between `:{` and `}{`
|
|
410
|
+
rgx:/([:}])\/?(\{)/g,
|
|
411
|
+
save:'$1{{id}}$2',
|
|
412
|
+
res:'\\/'
|
|
413
|
+
},
|
|
414
|
+
'RQ':{
|
|
415
|
+
//required query string - everything in between `{? }`
|
|
416
|
+
rgx:/\{\?([^}]+)\}/g,
|
|
417
|
+
//everything from `?` till `#` or end of string
|
|
418
|
+
res:'\\?([^#]+)'
|
|
419
|
+
},
|
|
420
|
+
'OQ':{
|
|
421
|
+
//optional query string - everything in between `:? :`
|
|
422
|
+
rgx:/:\?([^:]+):/g,
|
|
423
|
+
//everything from `?` till `#` or end of string
|
|
424
|
+
res:'(?:\\?([^#]*))?'
|
|
425
|
+
},
|
|
426
|
+
'OR':{
|
|
427
|
+
//optional rest - everything in between `: *:`
|
|
428
|
+
rgx:/:([^:]+)\*:/g,
|
|
429
|
+
res:'(.*)?' // optional group to avoid passing empty string as captured
|
|
430
|
+
},
|
|
431
|
+
'RR':{
|
|
432
|
+
//rest param - everything in between `{ *}`
|
|
433
|
+
rgx:/\{([^}]+)\*\}/g,
|
|
434
|
+
res:'(.+)'
|
|
435
|
+
},
|
|
436
|
+
// required/optional params should come after rest segments
|
|
437
|
+
'RP':{
|
|
438
|
+
//required params - everything between `{ }`
|
|
439
|
+
rgx:/\{([^}]+)\}/g,
|
|
440
|
+
res:'([^\\/?]+)'
|
|
441
|
+
},
|
|
442
|
+
'OP':{
|
|
443
|
+
//optional params - everything between `: :`
|
|
444
|
+
rgx:/:([^:]+):/g,
|
|
445
|
+
res:'([^\\/?]+)?\/?'
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
LOOSE_SLASH = 1,
|
|
450
|
+
STRICT_SLASH = 2,
|
|
451
|
+
LEGACY_SLASH = 3,
|
|
452
|
+
|
|
453
|
+
_slashMode = LOOSE_SLASH;
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
function precompileTokens() {
|
|
457
|
+
var key, cur;
|
|
458
|
+
for (key in TOKENS) {
|
|
459
|
+
if (TOKENS.hasOwnProperty(key)) {
|
|
460
|
+
cur = TOKENS[key];
|
|
461
|
+
cur.id = '__CR_' + key + '__';
|
|
462
|
+
cur.save = ('save' in cur) ? cur.save.replace('{{id}}', cur.id) : cur.id;
|
|
463
|
+
cur.rRestore = new RegExp(cur.id, 'g');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
precompileTokens();
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
function captureVals(regex, pattern) {
|
|
472
|
+
var vals = [], match;
|
|
473
|
+
while (match = regex.exec(pattern)) {
|
|
474
|
+
vals.push(match[1]);
|
|
475
|
+
}
|
|
476
|
+
return vals;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function getParamIds(pattern) {
|
|
480
|
+
return captureVals(PARAMS_REGEXP, pattern);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function getOptionalParamsIds(pattern) {
|
|
484
|
+
return captureVals(TOKENS.OP.rgx, pattern);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function compilePattern(pattern) {
|
|
488
|
+
pattern = pattern || '';
|
|
489
|
+
|
|
490
|
+
if (pattern) {
|
|
491
|
+
if (_slashMode === LOOSE_SLASH) {
|
|
492
|
+
pattern = pattern.replace(LOOSE_SLASHES_REGEXP, '');
|
|
493
|
+
}
|
|
494
|
+
else if (_slashMode === LEGACY_SLASH) {
|
|
495
|
+
pattern = pattern.replace(LEGACY_SLASHES_REGEXP, '');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
//save tokens
|
|
499
|
+
pattern = replaceTokens(pattern, 'rgx', 'save');
|
|
500
|
+
//regexp escape
|
|
501
|
+
pattern = pattern.replace(ESCAPE_CHARS_REGEXP, '\\$&');
|
|
502
|
+
//restore tokens
|
|
503
|
+
pattern = replaceTokens(pattern, 'rRestore', 'res');
|
|
504
|
+
|
|
505
|
+
if (_slashMode === LOOSE_SLASH) {
|
|
506
|
+
pattern = '\\/?' + pattern;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (_slashMode !== STRICT_SLASH) {
|
|
511
|
+
//single slash is treated as empty and end slash is optional
|
|
512
|
+
pattern += '\\/?';
|
|
513
|
+
}
|
|
514
|
+
return new RegExp('^' + pattern + '$');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function replaceTokens(pattern, regexpName, replaceName) {
|
|
518
|
+
var cur, key;
|
|
519
|
+
for (key in TOKENS) {
|
|
520
|
+
if (TOKENS.hasOwnProperty(key)) {
|
|
521
|
+
cur = TOKENS[key];
|
|
522
|
+
pattern = pattern.replace(cur[regexpName], cur[replaceName]);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return pattern;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function getParamValues(request, regexp, shouldTypecast) {
|
|
529
|
+
var vals = regexp.exec(request);
|
|
530
|
+
if (vals) {
|
|
531
|
+
vals.shift();
|
|
532
|
+
if (shouldTypecast) {
|
|
533
|
+
vals = typecastArrayValues(vals);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return vals;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function interpolate(pattern, replacements) {
|
|
540
|
+
if (typeof pattern !== 'string') {
|
|
541
|
+
throw new Error('Route pattern should be a string.');
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
var replaceFn = function (match, prop) {
|
|
545
|
+
var val;
|
|
546
|
+
if (prop in replacements) {
|
|
547
|
+
val = replacements[prop];
|
|
548
|
+
if (match.indexOf('*') === -1 && val.indexOf('/') !== -1) {
|
|
549
|
+
throw new Error('Invalid value "' + val + '" for segment "' + match + '".');
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (match.indexOf('{') !== -1) {
|
|
553
|
+
throw new Error('The segment ' + match + ' is required.');
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
val = '';
|
|
557
|
+
}
|
|
558
|
+
return val;
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
if (!TOKENS.OS.trail) {
|
|
562
|
+
TOKENS.OS.trail = new RegExp('(?:' + TOKENS.OS.id + ')+$');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return pattern
|
|
566
|
+
.replace(TOKENS.OS.rgx, TOKENS.OS.save)
|
|
567
|
+
.replace(PARAMS_REGEXP, replaceFn)
|
|
568
|
+
.replace(TOKENS.OS.trail, '')// remove trailing
|
|
569
|
+
.replace(TOKENS.OS.rRestore, '/'); // add slash between segments
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
//API
|
|
573
|
+
return {
|
|
574
|
+
strict:function () {
|
|
575
|
+
_slashMode = STRICT_SLASH;
|
|
576
|
+
},
|
|
577
|
+
loose:function () {
|
|
578
|
+
_slashMode = LOOSE_SLASH;
|
|
579
|
+
},
|
|
580
|
+
legacy:function () {
|
|
581
|
+
_slashMode = LEGACY_SLASH;
|
|
582
|
+
},
|
|
583
|
+
getParamIds:getParamIds,
|
|
584
|
+
getOptionalParamsIds:getOptionalParamsIds,
|
|
585
|
+
getParamValues:getParamValues,
|
|
586
|
+
compilePattern:compilePattern,
|
|
587
|
+
interpolate:interpolate
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
}());
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
return crossroads;
|
|
594
|
+
});
|
|
595
|
+
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
|
|
596
|
+
if (typeof module !== 'undefined' && module.exports) { //Node
|
|
597
|
+
module.exports = factory(require(deps[0]));
|
|
598
|
+
} else {
|
|
599
|
+
/*jshint sub:true */
|
|
600
|
+
window['crossroads'] = factory(window[deps[0]]);
|
|
601
|
+
}
|
|
602
|
+
}));
|