node-forge 0.10.0 → 1.2.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.
- package/CHANGELOG.md +84 -3
- package/README.md +11 -39
- package/dist/forge.all.min.js +1 -1
- package/dist/forge.min.js +1 -1
- package/lib/http.js +16 -34
- package/lib/index.js +0 -2
- package/lib/log.js +9 -7
- package/lib/oids.js +6 -1
- package/lib/pem.js +8 -1
- package/lib/pkcs7.js +6 -3
- package/lib/pkcs7asn1.js +2 -1
- package/lib/prng.js +1 -1
- package/lib/util.js +0 -255
- package/lib/x509.js +128 -219
- package/lib/xhr.js +8 -6
- package/package.json +7 -5
- package/lib/debug.js +0 -78
- package/lib/task.js +0 -725
package/lib/http.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* Copyright (c) 2010-2014 Digital Bazaar, Inc. All rights reserved.
|
|
7
7
|
*/
|
|
8
8
|
var forge = require('./forge');
|
|
9
|
-
require('./debug');
|
|
10
9
|
require('./tls');
|
|
11
10
|
require('./util');
|
|
12
11
|
|
|
@@ -16,11 +15,6 @@ var http = module.exports = forge.http = forge.http || {};
|
|
|
16
15
|
// logging category
|
|
17
16
|
var cat = 'forge.http';
|
|
18
17
|
|
|
19
|
-
// add array of clients to debug storage
|
|
20
|
-
if(forge.debug) {
|
|
21
|
-
forge.debug.set('forge.http', 'clients', []);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
18
|
// normalizes an http header field name
|
|
25
19
|
var _normalize = function(name) {
|
|
26
20
|
return name.toLowerCase().replace(/(^.)|(-.)/g,
|
|
@@ -39,8 +33,8 @@ var _getStorageId = function(client) {
|
|
|
39
33
|
// browsers (if this is undesirable)
|
|
40
34
|
// navigator.userAgent
|
|
41
35
|
return 'forge.http.' +
|
|
42
|
-
client.url.
|
|
43
|
-
client.url.
|
|
36
|
+
client.url.protocol.slice(0, -1) + '.' +
|
|
37
|
+
client.url.hostname + '.' +
|
|
44
38
|
client.url.port;
|
|
45
39
|
};
|
|
46
40
|
|
|
@@ -127,7 +121,7 @@ var _doRequest = function(client, socket) {
|
|
|
127
121
|
// connect
|
|
128
122
|
socket.options.request.connectTime = +new Date();
|
|
129
123
|
socket.connect({
|
|
130
|
-
host: client.url.
|
|
124
|
+
host: client.url.hostname,
|
|
131
125
|
port: client.url.port,
|
|
132
126
|
policyPort: client.policyPort,
|
|
133
127
|
policyUrl: client.policyUrl
|
|
@@ -316,7 +310,7 @@ var _initSocket = function(client, socket, tlsOptions) {
|
|
|
316
310
|
// prime socket by connecting and caching TLS session, will do
|
|
317
311
|
// next request from there
|
|
318
312
|
socket.connect({
|
|
319
|
-
host: client.url.
|
|
313
|
+
host: client.url.hostname,
|
|
320
314
|
port: client.url.port,
|
|
321
315
|
policyPort: client.policyPort,
|
|
322
316
|
policyUrl: client.policyUrl
|
|
@@ -411,7 +405,7 @@ var _readCookies = function(client, response) {
|
|
|
411
405
|
*
|
|
412
406
|
* @param options:
|
|
413
407
|
* url: the url to connect to (scheme://host:port).
|
|
414
|
-
*
|
|
408
|
+
* socketPool: the flash socket pool to use.
|
|
415
409
|
* policyPort: the flash policy port to use (if other than the
|
|
416
410
|
* socket pool default), use 0 for flash default.
|
|
417
411
|
* policyUrl: the flash policy file URL to use (if provided will
|
|
@@ -447,8 +441,10 @@ http.createClient = function(options) {
|
|
|
447
441
|
// get scheme, host, and port from url
|
|
448
442
|
options.url = (options.url ||
|
|
449
443
|
window.location.protocol + '//' + window.location.host);
|
|
450
|
-
var url
|
|
451
|
-
|
|
444
|
+
var url;
|
|
445
|
+
try {
|
|
446
|
+
url = new URL(options.url);
|
|
447
|
+
} catch(e) {
|
|
452
448
|
var error = new Error('Invalid url.');
|
|
453
449
|
error.details = {url: options.url};
|
|
454
450
|
throw error;
|
|
@@ -475,7 +471,7 @@ http.createClient = function(options) {
|
|
|
475
471
|
// idle sockets
|
|
476
472
|
idle: [],
|
|
477
473
|
// whether or not the connections are secure
|
|
478
|
-
secure: (url.
|
|
474
|
+
secure: (url.protocol === 'https:'),
|
|
479
475
|
// cookie jar (key'd off of name and then path, there is only 1 domain
|
|
480
476
|
// and one setting for secure per client so name+path is unique)
|
|
481
477
|
cookies: {},
|
|
@@ -484,11 +480,6 @@ http.createClient = function(options) {
|
|
|
484
480
|
true : options.persistCookies
|
|
485
481
|
};
|
|
486
482
|
|
|
487
|
-
// add client to debug storage
|
|
488
|
-
if(forge.debug) {
|
|
489
|
-
forge.debug.get('forge.http', 'clients').push(client);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
483
|
// load cookies from disk
|
|
493
484
|
_loadCookies(client);
|
|
494
485
|
|
|
@@ -508,7 +499,7 @@ http.createClient = function(options) {
|
|
|
508
499
|
if(depth === 0 && verified === true) {
|
|
509
500
|
// compare common name to url host
|
|
510
501
|
var cn = certs[depth].subject.getField('CN');
|
|
511
|
-
if(cn === null || client.url.
|
|
502
|
+
if(cn === null || client.url.hostname !== cn.value) {
|
|
512
503
|
verified = {
|
|
513
504
|
message: 'Certificate common name does not match url host.'
|
|
514
505
|
};
|
|
@@ -523,7 +514,7 @@ http.createClient = function(options) {
|
|
|
523
514
|
tlsOptions = {
|
|
524
515
|
caStore: caStore,
|
|
525
516
|
cipherSuites: options.cipherSuites || null,
|
|
526
|
-
virtualHost: options.virtualHost || url.
|
|
517
|
+
virtualHost: options.virtualHost || url.hostname,
|
|
527
518
|
verify: options.verify || _defaultCertificateVerify,
|
|
528
519
|
getCertificate: options.getCertificate || null,
|
|
529
520
|
getPrivateKey: options.getPrivateKey || null,
|
|
@@ -563,7 +554,7 @@ http.createClient = function(options) {
|
|
|
563
554
|
client.send = function(options) {
|
|
564
555
|
// add host header if not set
|
|
565
556
|
if(options.request.getField('Host') === null) {
|
|
566
|
-
options.request.setField('Host', client.url.
|
|
557
|
+
options.request.setField('Host', client.url.origin);
|
|
567
558
|
}
|
|
568
559
|
|
|
569
560
|
// set default dummy handlers
|
|
@@ -1318,15 +1309,6 @@ http.createResponse = function() {
|
|
|
1318
1309
|
return response;
|
|
1319
1310
|
};
|
|
1320
1311
|
|
|
1321
|
-
/**
|
|
1322
|
-
* Parses the scheme, host, and port from an http(s) url.
|
|
1323
|
-
*
|
|
1324
|
-
* @param str the url string.
|
|
1325
|
-
*
|
|
1326
|
-
* @return the parsed url object or null if the url is invalid.
|
|
1327
|
-
*/
|
|
1328
|
-
http.parseUrl = forge.util.parseUrl;
|
|
1329
|
-
|
|
1330
1312
|
/**
|
|
1331
1313
|
* Returns true if the given url is within the given cookie's domain.
|
|
1332
1314
|
*
|
|
@@ -1347,11 +1329,11 @@ http.withinCookieDomain = function(url, cookie) {
|
|
|
1347
1329
|
// ensure domain starts with a '.'
|
|
1348
1330
|
// parse URL as necessary
|
|
1349
1331
|
if(typeof url === 'string') {
|
|
1350
|
-
url =
|
|
1332
|
+
url = new URL(url);
|
|
1351
1333
|
}
|
|
1352
1334
|
|
|
1353
|
-
// add '.' to front of URL
|
|
1354
|
-
var host = '.' + url.
|
|
1335
|
+
// add '.' to front of URL hostname to match against domain
|
|
1336
|
+
var host = '.' + url.hostname;
|
|
1355
1337
|
|
|
1356
1338
|
// if the host ends with domain then it falls within it
|
|
1357
1339
|
var idx = host.lastIndexOf(domain);
|
package/lib/index.js
CHANGED
|
@@ -10,7 +10,6 @@ require('./aes');
|
|
|
10
10
|
require('./aesCipherSuites');
|
|
11
11
|
require('./asn1');
|
|
12
12
|
require('./cipher');
|
|
13
|
-
require('./debug');
|
|
14
13
|
require('./des');
|
|
15
14
|
require('./ed25519');
|
|
16
15
|
require('./hmac');
|
|
@@ -30,6 +29,5 @@ require('./pss');
|
|
|
30
29
|
require('./random');
|
|
31
30
|
require('./rc2');
|
|
32
31
|
require('./ssh');
|
|
33
|
-
require('./task');
|
|
34
32
|
require('./tls');
|
|
35
33
|
require('./util');
|
package/lib/log.js
CHANGED
|
@@ -286,7 +286,7 @@ if(typeof(console) !== 'undefined' && 'log' in console) {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
/*
|
|
289
|
-
* Check for logging control query vars.
|
|
289
|
+
* Check for logging control query vars in current URL.
|
|
290
290
|
*
|
|
291
291
|
* console.level=<level-name>
|
|
292
292
|
* Set's the console log level by name. Useful to override defaults and
|
|
@@ -297,16 +297,18 @@ if(typeof(console) !== 'undefined' && 'log' in console) {
|
|
|
297
297
|
* after console.level is processed. Useful to force a level of verbosity
|
|
298
298
|
* that could otherwise be limited by a user config.
|
|
299
299
|
*/
|
|
300
|
-
if(sConsoleLogger !== null
|
|
301
|
-
|
|
302
|
-
|
|
300
|
+
if(sConsoleLogger !== null &&
|
|
301
|
+
typeof window !== 'undefined' && window.location
|
|
302
|
+
) {
|
|
303
|
+
var query = new URL(window.location.href).searchParams;
|
|
304
|
+
if(query.has('console.level')) {
|
|
303
305
|
// set with last value
|
|
304
306
|
forge.log.setLevel(
|
|
305
|
-
sConsoleLogger, query
|
|
307
|
+
sConsoleLogger, query.get('console.level').slice(-1)[0]);
|
|
306
308
|
}
|
|
307
|
-
if('console.lock'
|
|
309
|
+
if(query.has('console.lock')) {
|
|
308
310
|
// set with last value
|
|
309
|
-
var lock = query
|
|
311
|
+
var lock = query.get('console.lock').slice(-1)[0];
|
|
310
312
|
if(lock == 'true') {
|
|
311
313
|
forge.log.lock(sConsoleLogger);
|
|
312
314
|
}
|
package/lib/oids.js
CHANGED
|
@@ -42,6 +42,8 @@ _IN('1.2.840.10040.4.3', 'dsa-with-sha1');
|
|
|
42
42
|
_IN('1.3.14.3.2.7', 'desCBC');
|
|
43
43
|
|
|
44
44
|
_IN('1.3.14.3.2.26', 'sha1');
|
|
45
|
+
// Deprecated equivalent of sha1WithRSAEncryption
|
|
46
|
+
_IN('1.3.14.3.2.29', 'sha1WithRSASignature');
|
|
45
47
|
_IN('2.16.840.1.101.3.4.2.1', 'sha256');
|
|
46
48
|
_IN('2.16.840.1.101.3.4.2.2', 'sha384');
|
|
47
49
|
_IN('2.16.840.1.101.3.4.2.3', 'sha512');
|
|
@@ -104,16 +106,19 @@ _IN('2.16.840.1.101.3.4.1.42', 'aes256-CBC');
|
|
|
104
106
|
|
|
105
107
|
// certificate issuer/subject OIDs
|
|
106
108
|
_IN('2.5.4.3', 'commonName');
|
|
107
|
-
_IN('2.5.4.
|
|
109
|
+
_IN('2.5.4.4', 'surname');
|
|
110
|
+
_IN('2.5.4.5', 'serialNumber');
|
|
108
111
|
_IN('2.5.4.6', 'countryName');
|
|
109
112
|
_IN('2.5.4.7', 'localityName');
|
|
110
113
|
_IN('2.5.4.8', 'stateOrProvinceName');
|
|
111
114
|
_IN('2.5.4.9', 'streetAddress');
|
|
112
115
|
_IN('2.5.4.10', 'organizationName');
|
|
113
116
|
_IN('2.5.4.11', 'organizationalUnitName');
|
|
117
|
+
_IN('2.5.4.12', 'title');
|
|
114
118
|
_IN('2.5.4.13', 'description');
|
|
115
119
|
_IN('2.5.4.15', 'businessCategory');
|
|
116
120
|
_IN('2.5.4.17', 'postalCode');
|
|
121
|
+
_IN('2.5.4.42', 'givenName');
|
|
117
122
|
_IN('1.3.6.1.4.1.311.60.2.1.2', 'jurisdictionOfIncorporationStateOrProvinceName');
|
|
118
123
|
_IN('1.3.6.1.4.1.311.60.2.1.3', 'jurisdictionOfIncorporationCountryName');
|
|
119
124
|
|
package/lib/pem.js
CHANGED
|
@@ -106,8 +106,15 @@ pem.decode = function(str) {
|
|
|
106
106
|
break;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// accept "NEW CERTIFICATE REQUEST" as "CERTIFICATE REQUEST"
|
|
110
|
+
// https://datatracker.ietf.org/doc/html/rfc7468#section-7
|
|
111
|
+
var type = match[1];
|
|
112
|
+
if(type === 'NEW CERTIFICATE REQUEST') {
|
|
113
|
+
type = 'CERTIFICATE REQUEST';
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
var msg = {
|
|
110
|
-
type:
|
|
117
|
+
type: type,
|
|
111
118
|
procType: null,
|
|
112
119
|
contentDomain: null,
|
|
113
120
|
dekInfo: null,
|
package/lib/pkcs7.js
CHANGED
|
@@ -837,7 +837,7 @@ function _recipientFromAsn1(obj) {
|
|
|
837
837
|
serialNumber: forge.util.createBuffer(capture.serial).toHex(),
|
|
838
838
|
encryptedContent: {
|
|
839
839
|
algorithm: asn1.derToOid(capture.encAlgorithm),
|
|
840
|
-
parameter: capture.encParameter.value,
|
|
840
|
+
parameter: capture.encParameter ? capture.encParameter.value : undefined,
|
|
841
841
|
content: capture.encKey
|
|
842
842
|
}
|
|
843
843
|
};
|
|
@@ -1124,8 +1124,11 @@ function _encryptedContentToAsn1(ec) {
|
|
|
1124
1124
|
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
1125
1125
|
asn1.oidToDer(ec.algorithm).getBytes()),
|
|
1126
1126
|
// Parameters (IV)
|
|
1127
|
-
|
|
1128
|
-
|
|
1127
|
+
!ec.parameter ?
|
|
1128
|
+
undefined :
|
|
1129
|
+
asn1.create(
|
|
1130
|
+
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
|
|
1131
|
+
ec.parameter.getBytes())
|
|
1129
1132
|
]),
|
|
1130
1133
|
// [0] EncryptedContent
|
|
1131
1134
|
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
package/lib/pkcs7asn1.js
CHANGED
|
@@ -397,7 +397,8 @@ p7v.recipientInfoValidator = {
|
|
|
397
397
|
name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',
|
|
398
398
|
tagClass: asn1.Class.UNIVERSAL,
|
|
399
399
|
constructed: false,
|
|
400
|
-
captureAsn1: 'encParameter'
|
|
400
|
+
captureAsn1: 'encParameter',
|
|
401
|
+
optional: true
|
|
401
402
|
}]
|
|
402
403
|
}, {
|
|
403
404
|
name: 'RecipientInfo.encryptedKey',
|
package/lib/prng.js
CHANGED
package/lib/util.js
CHANGED
|
@@ -2258,261 +2258,6 @@ util.clearItems = function(api, id, location) {
|
|
|
2258
2258
|
_callStorageFunction(_clearItems, arguments, location);
|
|
2259
2259
|
};
|
|
2260
2260
|
|
|
2261
|
-
/**
|
|
2262
|
-
* Parses the scheme, host, and port from an http(s) url.
|
|
2263
|
-
*
|
|
2264
|
-
* @param str the url string.
|
|
2265
|
-
*
|
|
2266
|
-
* @return the parsed url object or null if the url is invalid.
|
|
2267
|
-
*/
|
|
2268
|
-
util.parseUrl = function(str) {
|
|
2269
|
-
// FIXME: this regex looks a bit broken
|
|
2270
|
-
var regex = /^(https?):\/\/([^:&^\/]*):?(\d*)(.*)$/g;
|
|
2271
|
-
regex.lastIndex = 0;
|
|
2272
|
-
var m = regex.exec(str);
|
|
2273
|
-
var url = (m === null) ? null : {
|
|
2274
|
-
full: str,
|
|
2275
|
-
scheme: m[1],
|
|
2276
|
-
host: m[2],
|
|
2277
|
-
port: m[3],
|
|
2278
|
-
path: m[4]
|
|
2279
|
-
};
|
|
2280
|
-
if(url) {
|
|
2281
|
-
url.fullHost = url.host;
|
|
2282
|
-
if(url.port) {
|
|
2283
|
-
if(url.port !== 80 && url.scheme === 'http') {
|
|
2284
|
-
url.fullHost += ':' + url.port;
|
|
2285
|
-
} else if(url.port !== 443 && url.scheme === 'https') {
|
|
2286
|
-
url.fullHost += ':' + url.port;
|
|
2287
|
-
}
|
|
2288
|
-
} else if(url.scheme === 'http') {
|
|
2289
|
-
url.port = 80;
|
|
2290
|
-
} else if(url.scheme === 'https') {
|
|
2291
|
-
url.port = 443;
|
|
2292
|
-
}
|
|
2293
|
-
url.full = url.scheme + '://' + url.fullHost;
|
|
2294
|
-
}
|
|
2295
|
-
return url;
|
|
2296
|
-
};
|
|
2297
|
-
|
|
2298
|
-
/* Storage for query variables */
|
|
2299
|
-
var _queryVariables = null;
|
|
2300
|
-
|
|
2301
|
-
/**
|
|
2302
|
-
* Returns the window location query variables. Query is parsed on the first
|
|
2303
|
-
* call and the same object is returned on subsequent calls. The mapping
|
|
2304
|
-
* is from keys to an array of values. Parameters without values will have
|
|
2305
|
-
* an object key set but no value added to the value array. Values are
|
|
2306
|
-
* unescaped.
|
|
2307
|
-
*
|
|
2308
|
-
* ...?k1=v1&k2=v2:
|
|
2309
|
-
* {
|
|
2310
|
-
* "k1": ["v1"],
|
|
2311
|
-
* "k2": ["v2"]
|
|
2312
|
-
* }
|
|
2313
|
-
*
|
|
2314
|
-
* ...?k1=v1&k1=v2:
|
|
2315
|
-
* {
|
|
2316
|
-
* "k1": ["v1", "v2"]
|
|
2317
|
-
* }
|
|
2318
|
-
*
|
|
2319
|
-
* ...?k1=v1&k2:
|
|
2320
|
-
* {
|
|
2321
|
-
* "k1": ["v1"],
|
|
2322
|
-
* "k2": []
|
|
2323
|
-
* }
|
|
2324
|
-
*
|
|
2325
|
-
* ...?k1=v1&k1:
|
|
2326
|
-
* {
|
|
2327
|
-
* "k1": ["v1"]
|
|
2328
|
-
* }
|
|
2329
|
-
*
|
|
2330
|
-
* ...?k1&k1:
|
|
2331
|
-
* {
|
|
2332
|
-
* "k1": []
|
|
2333
|
-
* }
|
|
2334
|
-
*
|
|
2335
|
-
* @param query the query string to parse (optional, default to cached
|
|
2336
|
-
* results from parsing window location search query).
|
|
2337
|
-
*
|
|
2338
|
-
* @return object mapping keys to variables.
|
|
2339
|
-
*/
|
|
2340
|
-
util.getQueryVariables = function(query) {
|
|
2341
|
-
var parse = function(q) {
|
|
2342
|
-
var rval = {};
|
|
2343
|
-
var kvpairs = q.split('&');
|
|
2344
|
-
for(var i = 0; i < kvpairs.length; i++) {
|
|
2345
|
-
var pos = kvpairs[i].indexOf('=');
|
|
2346
|
-
var key;
|
|
2347
|
-
var val;
|
|
2348
|
-
if(pos > 0) {
|
|
2349
|
-
key = kvpairs[i].substring(0, pos);
|
|
2350
|
-
val = kvpairs[i].substring(pos + 1);
|
|
2351
|
-
} else {
|
|
2352
|
-
key = kvpairs[i];
|
|
2353
|
-
val = null;
|
|
2354
|
-
}
|
|
2355
|
-
if(!(key in rval)) {
|
|
2356
|
-
rval[key] = [];
|
|
2357
|
-
}
|
|
2358
|
-
// disallow overriding object prototype keys
|
|
2359
|
-
if(!(key in Object.prototype) && val !== null) {
|
|
2360
|
-
rval[key].push(unescape(val));
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
return rval;
|
|
2364
|
-
};
|
|
2365
|
-
|
|
2366
|
-
var rval;
|
|
2367
|
-
if(typeof(query) === 'undefined') {
|
|
2368
|
-
// set cached variables if needed
|
|
2369
|
-
if(_queryVariables === null) {
|
|
2370
|
-
if(typeof(window) !== 'undefined' && window.location && window.location.search) {
|
|
2371
|
-
// parse window search query
|
|
2372
|
-
_queryVariables = parse(window.location.search.substring(1));
|
|
2373
|
-
} else {
|
|
2374
|
-
// no query variables available
|
|
2375
|
-
_queryVariables = {};
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
rval = _queryVariables;
|
|
2379
|
-
} else {
|
|
2380
|
-
// parse given query
|
|
2381
|
-
rval = parse(query);
|
|
2382
|
-
}
|
|
2383
|
-
return rval;
|
|
2384
|
-
};
|
|
2385
|
-
|
|
2386
|
-
/**
|
|
2387
|
-
* Parses a fragment into a path and query. This method will take a URI
|
|
2388
|
-
* fragment and break it up as if it were the main URI. For example:
|
|
2389
|
-
* /bar/baz?a=1&b=2
|
|
2390
|
-
* results in:
|
|
2391
|
-
* {
|
|
2392
|
-
* path: ["bar", "baz"],
|
|
2393
|
-
* query: {"k1": ["v1"], "k2": ["v2"]}
|
|
2394
|
-
* }
|
|
2395
|
-
*
|
|
2396
|
-
* @return object with a path array and query object.
|
|
2397
|
-
*/
|
|
2398
|
-
util.parseFragment = function(fragment) {
|
|
2399
|
-
// default to whole fragment
|
|
2400
|
-
var fp = fragment;
|
|
2401
|
-
var fq = '';
|
|
2402
|
-
// split into path and query if possible at the first '?'
|
|
2403
|
-
var pos = fragment.indexOf('?');
|
|
2404
|
-
if(pos > 0) {
|
|
2405
|
-
fp = fragment.substring(0, pos);
|
|
2406
|
-
fq = fragment.substring(pos + 1);
|
|
2407
|
-
}
|
|
2408
|
-
// split path based on '/' and ignore first element if empty
|
|
2409
|
-
var path = fp.split('/');
|
|
2410
|
-
if(path.length > 0 && path[0] === '') {
|
|
2411
|
-
path.shift();
|
|
2412
|
-
}
|
|
2413
|
-
// convert query into object
|
|
2414
|
-
var query = (fq === '') ? {} : util.getQueryVariables(fq);
|
|
2415
|
-
|
|
2416
|
-
return {
|
|
2417
|
-
pathString: fp,
|
|
2418
|
-
queryString: fq,
|
|
2419
|
-
path: path,
|
|
2420
|
-
query: query
|
|
2421
|
-
};
|
|
2422
|
-
};
|
|
2423
|
-
|
|
2424
|
-
/**
|
|
2425
|
-
* Makes a request out of a URI-like request string. This is intended to
|
|
2426
|
-
* be used where a fragment id (after a URI '#') is parsed as a URI with
|
|
2427
|
-
* path and query parts. The string should have a path beginning and
|
|
2428
|
-
* delimited by '/' and optional query parameters following a '?'. The
|
|
2429
|
-
* query should be a standard URL set of key value pairs delimited by
|
|
2430
|
-
* '&'. For backwards compatibility the initial '/' on the path is not
|
|
2431
|
-
* required. The request object has the following API, (fully described
|
|
2432
|
-
* in the method code):
|
|
2433
|
-
* {
|
|
2434
|
-
* path: <the path string part>.
|
|
2435
|
-
* query: <the query string part>,
|
|
2436
|
-
* getPath(i): get part or all of the split path array,
|
|
2437
|
-
* getQuery(k, i): get part or all of a query key array,
|
|
2438
|
-
* getQueryLast(k, _default): get last element of a query key array.
|
|
2439
|
-
* }
|
|
2440
|
-
*
|
|
2441
|
-
* @return object with request parameters.
|
|
2442
|
-
*/
|
|
2443
|
-
util.makeRequest = function(reqString) {
|
|
2444
|
-
var frag = util.parseFragment(reqString);
|
|
2445
|
-
var req = {
|
|
2446
|
-
// full path string
|
|
2447
|
-
path: frag.pathString,
|
|
2448
|
-
// full query string
|
|
2449
|
-
query: frag.queryString,
|
|
2450
|
-
/**
|
|
2451
|
-
* Get path or element in path.
|
|
2452
|
-
*
|
|
2453
|
-
* @param i optional path index.
|
|
2454
|
-
*
|
|
2455
|
-
* @return path or part of path if i provided.
|
|
2456
|
-
*/
|
|
2457
|
-
getPath: function(i) {
|
|
2458
|
-
return (typeof(i) === 'undefined') ? frag.path : frag.path[i];
|
|
2459
|
-
},
|
|
2460
|
-
/**
|
|
2461
|
-
* Get query, values for a key, or value for a key index.
|
|
2462
|
-
*
|
|
2463
|
-
* @param k optional query key.
|
|
2464
|
-
* @param i optional query key index.
|
|
2465
|
-
*
|
|
2466
|
-
* @return query, values for a key, or value for a key index.
|
|
2467
|
-
*/
|
|
2468
|
-
getQuery: function(k, i) {
|
|
2469
|
-
var rval;
|
|
2470
|
-
if(typeof(k) === 'undefined') {
|
|
2471
|
-
rval = frag.query;
|
|
2472
|
-
} else {
|
|
2473
|
-
rval = frag.query[k];
|
|
2474
|
-
if(rval && typeof(i) !== 'undefined') {
|
|
2475
|
-
rval = rval[i];
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
return rval;
|
|
2479
|
-
},
|
|
2480
|
-
getQueryLast: function(k, _default) {
|
|
2481
|
-
var rval;
|
|
2482
|
-
var vals = req.getQuery(k);
|
|
2483
|
-
if(vals) {
|
|
2484
|
-
rval = vals[vals.length - 1];
|
|
2485
|
-
} else {
|
|
2486
|
-
rval = _default;
|
|
2487
|
-
}
|
|
2488
|
-
return rval;
|
|
2489
|
-
}
|
|
2490
|
-
};
|
|
2491
|
-
return req;
|
|
2492
|
-
};
|
|
2493
|
-
|
|
2494
|
-
/**
|
|
2495
|
-
* Makes a URI out of a path, an object with query parameters, and a
|
|
2496
|
-
* fragment. Uses jQuery.param() internally for query string creation.
|
|
2497
|
-
* If the path is an array, it will be joined with '/'.
|
|
2498
|
-
*
|
|
2499
|
-
* @param path string path or array of strings.
|
|
2500
|
-
* @param query object with query parameters. (optional)
|
|
2501
|
-
* @param fragment fragment string. (optional)
|
|
2502
|
-
*
|
|
2503
|
-
* @return string object with request parameters.
|
|
2504
|
-
*/
|
|
2505
|
-
util.makeLink = function(path, query, fragment) {
|
|
2506
|
-
// join path parts if needed
|
|
2507
|
-
path = jQuery.isArray(path) ? path.join('/') : path;
|
|
2508
|
-
|
|
2509
|
-
var qstr = jQuery.param(query || {});
|
|
2510
|
-
fragment = fragment || '';
|
|
2511
|
-
return path +
|
|
2512
|
-
((qstr.length > 0) ? ('?' + qstr) : '') +
|
|
2513
|
-
((fragment.length > 0) ? ('#' + fragment) : '');
|
|
2514
|
-
};
|
|
2515
|
-
|
|
2516
2261
|
/**
|
|
2517
2262
|
* Check if an object is empty.
|
|
2518
2263
|
*
|