diff --git a/static/rison.js b/static/rison.js index 29026c930dd..7945668c806 100644 --- a/static/rison.js +++ b/static/rison.js @@ -1,6 +1,5 @@ /* eslint-disable */ -// prettier-ignore -// Taken from https://github.com/Nanonid/rison at 917679fb6cafa15e2a186cd5a47163792899b321 +// Taken from https://github.com/Nanonid/rison at e64af6c096fd30950ec32cfd48526ca6ee21649d (Jun 9, 2017) // Uses CommonJS, AMD or browser globals to create a module. // Based on: https://github.com/umdjs/umd/blob/master/commonjsStrict.js (function (root, factory) { @@ -14,19 +13,18 @@ // Browser globals factory((root.rison = {})); } -}(this, function (exports) { +})(this, function (exports) { var rison = exports; -////////////////////////////////////////////////// -// -// the stringifier is based on -// http://json.org/json.js as of 2006-04-28 from json.org -// the parser is based on -// http://osteele.com/sources/openlaszlo/json -// + ////////////////////////////////////////////////// + // + // the stringifier is based on + // http://json.org/json.js as of 2006-04-28 from json.org + // the parser is based on + // http://osteele.com/sources/openlaszlo/json + // - if (typeof rison == 'undefined') - window.rison = {}; + if (typeof rison == 'undefined') window.rison = {}; /** * rules for an uri encoder that is more tolerant than encodeURIComponent @@ -36,11 +34,22 @@ * we also allow ,:@$/ * */ - rison.uri_ok = { // ok in url paths and in form query args - '~': true, '!': true, '*': true, '(': true, ')': true, - '-': true, '_': true, '.': true, ',': true, - ':': true, '@': true, '$': true, - "'": true, '/': true + rison.uri_ok = { + // ok in url paths and in form query args + '~': true, + '!': true, + '*': true, + '(': true, + ')': true, + '-': true, + _: true, + '.': true, + ',': true, + ':': true, + '@': true, + $: true, + "'": true, + '/': true, }; /* @@ -54,17 +63,16 @@ /** * punctuation characters that are legal inside ids. */ -// this var isn't actually used -//rison.idchar_punctuation = "_-./~"; + // this var isn't actually used + //rison.idchar_punctuation = "_-./~"; (function () { var l = []; for (var hi = 0; hi < 16; hi++) { for (var lo = 0; lo < 16; lo++) { - if (hi + lo == 0) continue; + if (hi + lo === 0) continue; var c = String.fromCharCode(hi * 16 + lo); - if (!/\w|[-_.\/~]/.test(c)) - l.push('\\u00' + hi.toString(16) + lo.toString(16)); + if (!/\w|[-_.\/~]/.test(c)) l.push('\\u00' + hi.toString(16) + lo.toString(16)); } } /** @@ -72,24 +80,21 @@ * and classes are illegal in ids. * */ - rison.not_idchar = l.join('') + rison.not_idchar = l.join(''); //idcrx = new RegExp('[' + rison.not_idchar + ']'); //console.log('NOT', (idcrx.test(' ')) ); })(); -//rison.not_idchar = " \t\r\n\"<>[]{}'!=:(),*@$;&"; + //rison.not_idchar = " \t\r\n\"<>[]{}'!=:(),*@$;&"; rison.not_idchar = " '!:(),*@$"; - /** * characters that are illegal as the start of an id * this is so ids can't look like numbers. */ - rison.not_idstart = "-0123456789"; - + rison.not_idstart = '-0123456789'; (function () { - var idrx = '[^' + rison.not_idstart + rison.not_idchar + - '][^' + rison.not_idchar + ']*'; + var idrx = '[^' + rison.not_idstart + rison.not_idchar + '][^' + rison.not_idchar + ']*'; rison.id_ok = new RegExp('^' + idrx + '$'); @@ -108,8 +113,7 @@ * and quotes " " as "+" instead of "%20" */ rison.quote = function (x) { - if (/^[-A-Za-z0-9~!*()_.',:@$\/]*$/.test(x)) - return x; + if (/^[-A-Za-z0-9~!*()_.',:@$\/]*$/.test(x)) return x; return encodeURIComponent(x) .replace(/%2C/g, ',') @@ -120,17 +124,18 @@ .replace(/%20/g, '+'); }; - -// -// based on json.js 2006-04-28 from json.org -// license: http://www.json.org/license.html -// -// hacked by nix for use in uris. -// + // + // based on json.js 2006-04-28 from json.org + // license: http://www.json.org/license.html + // + // hacked by nix for use in uris. + // (function () { - var sq = { // url-ok but quoted in strings - "'": true, '!': true + var sq = { + // url-ok but quoted in strings + "'": true, + '!': true, }, enc = function (v) { if (v && typeof v.toJSON === 'function') v = v.toJSON(); @@ -139,7 +144,12 @@ }, s = { array: function (x) { - var a = ['!('], b, f, i, l = x.length, v; + var a = ['!('], + b, + f, + i, + l = x.length, + v; for (i = 0; i < l; i += 1) { v = enc(x[i]); if (typeof v == 'string') { @@ -153,17 +163,15 @@ a[a.length] = ')'; return a.join(''); }, - 'boolean': function (x) { - if (x) - return '!t'; - return '!f' + boolean: function (x) { + if (x) return '!t'; + return '!f'; }, - 'null': function (x) { - return "!n"; + null: function () { + return '!n'; }, number: function (x) { - if (!isFinite(x)) - return '!n'; + if (!isFinite(x)) return '!n'; // strip '+' out of exponent, '-' is ok though return String(x).replace(/\+/, ''); }, @@ -176,9 +184,14 @@ if (typeof x.__prototype__ === 'object' && typeof x.__prototype__.encode_rison !== 'undefined') return x.encode_rison(); - var a = ['('], b, f, i, v, ki, ks = []; - for (i in x) - ks[ks.length] = i; + var a = ['('], + b, + i, + v, + k, + ki, + ks = []; + for (i in x) ks[ks.length] = i; ks.sort(); for (ki = 0; ki < ks.length; ki++) { i = ks[ki]; @@ -187,7 +200,8 @@ if (b) { a[a.length] = ','; } - a.push(s.string(i), ':', v); + k = isNaN(parseInt(i)) ? s.string(i) : s.number(i); + a.push(k, ':', v); b = true; } } @@ -197,11 +211,9 @@ return '!n'; }, string: function (x) { - if (x == '') - return "''"; + if (x === '') return "''"; - if (rison.id_ok.test(x)) - return x; + if (rison.id_ok.test(x)) return x; x = x.replace(/(['!])/g, function (a, b) { if (sq[b]) return '!' + b; @@ -209,12 +221,12 @@ }); return "'" + x + "'"; }, - undefined: function (x) { + undefined: function () { // ignore undefined just like JSON - } + return; + }, }; - /** * rison-encode a javascript structure * @@ -232,7 +244,7 @@ */ rison.encode_object = function (v) { if (typeof v != 'object' || v === null || v instanceof Array) - throw new Error("rison.encode_object expects an object argument"); + throw new Error('rison.encode_object expects an object argument'); var r = s[typeof v](v); return r.substring(1, r.length - 1); }; @@ -242,8 +254,7 @@ * */ rison.encode_array = function (v) { - if (!(v instanceof Array)) - throw new Error("rison.encode_array expects an array argument"); + if (!(v instanceof Array)) throw new Error('rison.encode_array expects an array argument'); var r = s[typeof v](v); return r.substring(2, r.length - 1); }; @@ -255,19 +266,16 @@ rison.encode_uri = function (v) { return rison.quote(s[typeof v](v)); }; - })(); - -// -// based on openlaszlo-json and hacked by nix for use in uris. -// -// Author: Oliver Steele -// Copyright: Copyright 2006 Oliver Steele. All rights reserved. -// Homepage: http://osteele.com/sources/openlaszlo/json -// License: MIT License. -// Version: 1.0 - + // + // based on openlaszlo-json and hacked by nix for use in uris. + // + // Author: Oliver Steele + // Copyright: Copyright 2006 Oliver Steele. All rights reserved. + // Homepage: http://osteele.com/sources/openlaszlo/json + // License: MIT License. + // Version: 1.0 /** * parse a rison string into a javascript structure. @@ -303,7 +311,6 @@ return rison.decode('!(' + r + ')'); }; - /** * construct a new parser object for reuse. * @@ -321,12 +328,11 @@ * by default the rison decoder tolerates no whitespace. * to accept whitespace set rison.parser.WHITESPACE = " \t\n\r\f"; */ - rison.parser.WHITESPACE = ""; + rison.parser.WHITESPACE = ''; -// expose this as-is? + // expose this as-is? rison.parser.prototype.setOptions = function (options) { - if (options['errorHandler']) - this.errorHandler = options.errorHandler; + if (options['errorHandler']) this.errorHandler = options.errorHandler; }; /** @@ -339,14 +345,12 @@ var value = this.readValue(); if (!this.message && this.next()) value = this.error("unable to parse string as rison: '" + rison.encode(str) + "'"); - if (this.message && this.errorHandler) - this.errorHandler(this.message, this.index); + if (this.message && this.errorHandler) this.errorHandler(this.message, this.index); return value; }; rison.parser.prototype.error = function (message) { - if (typeof(console) != 'undefined') - console.log('rison parser error: ', message); + if (typeof console != 'undefined') console.log('rison parser error: ', message); this.message = message; return undefined; }; @@ -355,8 +359,7 @@ var c = this.next(); var fn = c && this.table[c]; - if (fn) - return fn.apply(this); + if (fn) return fn.apply(this); // fell through table, parse as an id @@ -373,27 +376,25 @@ if (m.length > 0) { var id = m[0]; this.index = i + id.length; - return id; // a string + return id; // a string } if (c) return this.error("invalid character: '" + c + "'"); - return this.error("empty expression"); + return this.error('empty expression'); }; rison.parser.parse_array = function (parser) { var ar = []; var c; - while ((c = parser.next()) != ')') { + while ((c = parser.next()) !== ')') { if (!c) return parser.error("unmatched '!('"); if (ar.length) { - if (c != ',') - parser.error("missing ','"); - } else if (c == ',') { + if (c !== ',') parser.error("missing ','"); + } else if (c === ',') { return parser.error("extra ','"); - } else - --parser.index; + } else --parser.index; var n = parser.readValue(); - if (typeof n == "undefined") return undefined; + if (typeof n == 'undefined') return undefined; ar.push(n); } return ar; @@ -403,7 +404,7 @@ t: true, f: false, n: null, - '(': rison.parser.parse_array + '(': rison.parser.parse_array, }; rison.parser.prototype.table = { @@ -412,9 +413,9 @@ var c = s.charAt(this.index++); if (!c) return this.error('"!" at end of input'); var x = rison.parser.bangs[c]; - if (typeof(x) == 'function') { + if (typeof x == 'function') { return x.call(null, this); - } else if (typeof(x) == 'undefined') { + } else if (typeof x == 'undefined') { return this.error('unknown literal: "!' + c + '"'); } return x; @@ -423,19 +424,17 @@ var o = {}; var c; var count = 0; - while ((c = this.next()) != ')') { + while ((c = this.next()) !== ')') { if (count) { - if (c != ',') - this.error("missing ','"); - } else if (c == ',') { + if (c !== ',') this.error("missing ','"); + } else if (c === ',') { return this.error("extra ','"); - } else - --this.index; + } else --this.index; var k = this.readValue(); - if (typeof k == "undefined") return undefined; - if (this.next() != ':') return this.error("missing ':'"); + if (typeof k == 'undefined') return undefined; + if (this.next() !== ':') return this.error("missing ':'"); var v = this.readValue(); - if (typeof v == "undefined") return undefined; + if (typeof v == 'undefined') return undefined; o[k] = v; count++; } @@ -447,12 +446,11 @@ var start = i; var segments = []; var c; - while ((c = s.charAt(i++)) != "'") { + while ((c = s.charAt(i++)) !== "'") { //if (i == s.length) return this.error('unmatched "\'"'); if (!c) return this.error('unmatched "\'"'); - if (c == '!') { - if (start < i - 1) - segments.push(s.slice(start, i - 1)); + if (c === '!') { + if (start < i - 1) segments.push(s.slice(start, i - 1)); c = s.charAt(i++); if ("!'".indexOf(c) >= 0) { segments.push(c); @@ -462,10 +460,9 @@ start = i; } } - if (start < i - 1) - segments.push(s.slice(start, i - 1)); + if (start < i - 1) segments.push(s.slice(start, i - 1)); this.index = i; - return segments.length == 1 ? segments[0] : segments.join(''); + return segments.length === 1 ? segments[0] : segments.join(''); }, // Also any digit. The statement that follows this table // definition fills in the digits. @@ -478,7 +475,7 @@ var transitions = { 'int+.': 'frac', 'int+e': 'exp', - 'frac+e': 'exp' + 'frac+e': 'exp', }; do { var c = s.charAt(i++); @@ -489,32 +486,31 @@ continue; } state = transitions[state + '+' + c.toLowerCase()]; - if (state == 'exp') permittedSigns = '-'; + if (state === 'exp') permittedSigns = '-'; } while (state); this.index = --i; s = s.slice(start, i); - if (s == '-') return this.error("invalid number"); + if (s === '-') return this.error('invalid number'); return Number(s); - } + }, }; -// copy table['-'] to each of table[i] | i <- '0'..'9': + // copy table['-'] to each of table[i] | i <- '0'..'9': (function (table) { - for (var i = 0; i <= 9; i++) - table[String(i)] = table['-']; + for (var i = 0; i <= 9; i++) table[String(i)] = table['-']; })(rison.parser.prototype.table); -// return the next non-whitespace character, or undefined + // return the next non-whitespace character, or undefined rison.parser.prototype.next = function () { var c; var s = this.string; var i = this.index; do { - if (i == s.length) return undefined; + if (i === s.length) return undefined; c = s.charAt(i++); } while (rison.parser.WHITESPACE.indexOf(c) >= 0); this.index = i; return c; }; -// End of UMD module wrapper -})); + // End of UMD module wrapper +}); diff --git a/test/rison.js b/test/rison.js new file mode 100644 index 00000000000..26af421a06f --- /dev/null +++ b/test/rison.js @@ -0,0 +1,44 @@ +import * as rison from '../static/rison'; + +// Copied from https://github.com/Nanonid/rison/blob/master/python/rison/tests.py +const py_testcases = { + '(a:0,b:1)': {a: 0, b: 1}, + "(a:0,b:foo,c:'23skidoo')": {a: 0, c: '23skidoo', b: 'foo'}, + '!t': true, + '!f': false, + '!n': null, + "''": '', + 0: 0, + 1.5: 1.5, + '-3': -3, + '1e30': 1e30, + '1e-30': 1.0000000000000001e-30, // eslint-disable-line quote-props + 'G.': 'G.', + a: 'a', + "'0a'": '0a', + "'abc def'": 'abc def', + '()': {}, + '(a:0)': {a: 0}, + '(id:!n,type:/common/document)': {type: '/common/document', id: null}, + '!()': [], + "!(!t,!f,!n,'')": [true, false, null, ''], + "'-h'": '-h', + 'a-z': 'a-z', + "'wow!!'": 'wow!', + 'domain.com': 'domain.com', + "'user@domain.com'": 'user@domain.com', + "'US $10'": 'US $10', + "'can!'t'": "can't", +}; + +describe('Rison test cases', () => { + for (const [r, obj] of Object.entries(py_testcases)) { + it(`Should decode "${r}"`, () => { + // hack to get around "TypeError: Cannot read properties of null (reading 'should')" + ({x: rison.decode(r)}.should.deep.equal({x: obj})); + }); + it(`Should encode ${JSON.stringify(obj)}`, () => { + rison.encode(obj).should.deep.equal(r); + }); + } +});