Initial commit

This commit is contained in:
2026-04-15 17:08:39 +02:00
parent ae164c47a8
commit 47fd1c2b7a
1819 changed files with 685388 additions and 0 deletions

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,17 @@
all: build test site
build:
./node_modules/jison/lib/cli.js src/jsonlint.y src/jsonlint.l
mv jsonlint.js lib/jsonlint.js
node scripts/bundle.js | ./node_modules/uglify-js/bin/uglifyjs > web/jsonlint.js
site:
cp web/jsonlint.js ../jsonlint-pages/jsonlint.js
deploy: site
cd ../jsonlint-pages && git commit -a -m 'deploy site updates' && git push origin gh-pages
test: lib/jsonlint.js test/all-tests.js
node test/all-tests.js

View File

@@ -0,0 +1,7 @@
JSON Lint
=========
A fork of the `lines-primitive` branch of [tmcw/jsonlint](https://github.com/tmcw/jsonlint), which is a fork of [zaach/jsonlint](https://github.com/zaach/jsonlint) that adds line number annotations to the parsed JSON.
This fork is used by Mapbox GL JS, specifically for providing helpful error messages when validating Mapbox GL style JSON documents.

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env node
/**
* Manual formatter taken straight from https://github.com/umbrae/jsonlintdotcom
**/
/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
/**
* jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability).
*
**/
var formatter = (function () {
function repeat(s, count) {
return new Array(count + 1).join(s);
}
function formatJson(json, indentChars) {
var i = 0,
il = 0,
tab = (typeof indentChars !== "undefined") ? indentChars : " ",
newJson = "",
indentLevel = 0,
inString = false,
currentChar = null;
for (i = 0, il = json.length; i < il; i += 1) {
currentChar = json.charAt(i);
switch (currentChar) {
case '{':
case '[':
if (!inString) {
newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '}':
case ']':
if (!inString) {
indentLevel -= 1;
newJson += "\n" + repeat(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ',':
if (!inString) {
newJson += ",\n" + repeat(tab, indentLevel);
} else {
newJson += currentChar;
}
break;
case ':':
if (!inString) {
newJson += ": ";
} else {
newJson += currentChar;
}
break;
case ' ':
case "\n":
case "\t":
if (inString) {
newJson += currentChar;
}
break;
case '"':
if (i > 0 && json.charAt(i - 1) !== '\\') {
inString = !inString;
}
newJson += currentChar;
break;
default:
newJson += currentChar;
break;
}
}
return newJson;
}
return { "formatJson": formatJson };
}());
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.formatter = formatter;
}

View File

@@ -0,0 +1,681 @@
/* parser generated by jison 0.4.15 */
/*
Returns a Parser object of the following structure:
Parser: {
yy: {}
}
Parser.prototype: {
yy: {},
trace: function(),
symbols_: {associative list: name ==> number},
terminals_: {associative list: number ==> name},
productions_: [...],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
table: [...],
defaultActions: {...},
parseError: function(str, hash),
parse: function(input),
lexer: {
EOF: 1,
parseError: function(str, hash),
setInput: function(input),
input: function(),
unput: function(str),
more: function(),
less: function(n),
pastInput: function(),
upcomingInput: function(),
showPosition: function(),
test_match: function(regex_match_array, rule_index),
next: function(),
lex: function(),
begin: function(condition),
popState: function(),
_currentRules: function(),
topState: function(),
pushState: function(condition),
options: {
ranges: boolean (optional: true ==> token location info will include a .range[] member)
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
},
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
rules: [...],
conditions: {associative list: name ==> set},
}
}
token location info (@$, _$, etc.): {
first_line: n,
last_line: n,
first_column: n,
last_column: n,
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
}
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
text: (matched text)
token: (the produced terminal token, if any)
line: (yylineno)
}
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
loc: (yylloc)
expected: (string describing the set of expected tokens)
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
}
*/
var parser = (function(){
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,12],$V1=[1,13],$V2=[1,9],$V3=[1,10],$V4=[1,11],$V5=[1,14],$V6=[1,15],$V7=[14,18,22,24],$V8=[18,22],$V9=[22,24];
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1},
terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},
productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */
var $0 = $$.length - 1;
switch (yystate) {
case 1:
// replace escaped characters with actual character
this.$ = new String(yytext.replace(/\\(\\|")/g, "$"+"1")
.replace(/\\n/g,'\n')
.replace(/\\r/g,'\r')
.replace(/\\t/g,'\t')
.replace(/\\v/g,'\v')
.replace(/\\f/g,'\f')
.replace(/\\b/g,'\b'));
this.$.__line__ = this._$.first_line;
break;
case 2:
this.$ = new Number(yytext);
this.$.__line__ = this._$.first_line;
break;
case 3:
this.$ = null;
break;
case 4:
this.$ = new Boolean(true);
this.$.__line__ = this._$.first_line;
break;
case 5:
this.$ = new Boolean(false);
this.$.__line__ = this._$.first_line;
break;
case 6:
return this.$ = $$[$0-1];
break;
case 13:
this.$ = {}; Object.defineProperty(this.$, '__line__', {
value: this._$.first_line,
enumerable: false
})
break;
case 14: case 19:
this.$ = $$[$0-1]; Object.defineProperty(this.$, '__line__', {
value: this._$.first_line,
enumerable: false
})
break;
case 15:
this.$ = [$$[$0-2], $$[$0]];
break;
case 16:
this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
break;
case 17:
this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1];
break;
case 18:
this.$ = []; Object.defineProperty(this.$, '__line__', {
value: this._$.first_line,
enumerable: false
})
break;
case 20:
this.$ = [$$[$0]];
break;
case 21:
this.$ = $$[$0-2]; $$[$0-2].push($$[$0]);
break;
}
},
table: [{3:5,4:$V0,5:6,6:$V1,7:3,8:$V2,9:4,10:$V3,11:$V4,12:1,13:2,15:7,16:8,17:$V5,23:$V6},{1:[3]},{14:[1,16]},o($V7,[2,7]),o($V7,[2,8]),o($V7,[2,9]),o($V7,[2,10]),o($V7,[2,11]),o($V7,[2,12]),o($V7,[2,3]),o($V7,[2,4]),o($V7,[2,5]),o([14,18,21,22,24],[2,1]),o($V7,[2,2]),{3:20,4:$V0,18:[1,17],19:18,20:19},{3:5,4:$V0,5:6,6:$V1,7:3,8:$V2,9:4,10:$V3,11:$V4,13:23,15:7,16:8,17:$V5,23:$V6,24:[1,21],25:22},{1:[2,6]},o($V7,[2,13]),{18:[1,24],22:[1,25]},o($V8,[2,16]),{21:[1,26]},o($V7,[2,18]),{22:[1,28],24:[1,27]},o($V9,[2,20]),o($V7,[2,14]),{3:20,4:$V0,20:29},{3:5,4:$V0,5:6,6:$V1,7:3,8:$V2,9:4,10:$V3,11:$V4,13:30,15:7,16:8,17:$V5,23:$V6},o($V7,[2,19]),{3:5,4:$V0,5:6,6:$V1,7:3,8:$V2,9:4,10:$V3,11:$V4,13:31,15:7,16:8,17:$V5,23:$V6},o($V8,[2,17]),o($V8,[2,15]),o($V9,[2,21])],
defaultActions: {16:[2,6]},
parseError: function parseError(str, hash) {
if (hash.recoverable) {
this.trace(str);
} else {
throw new Error(str);
}
},
parse: function parse(input) {
var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
var args = lstack.slice.call(arguments, 1);
var lexer = Object.create(this.lexer);
var sharedState = { yy: {} };
for (var k in this.yy) {
if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
sharedState.yy[k] = this.yy[k];
}
}
lexer.setInput(input, sharedState.yy);
sharedState.yy.lexer = lexer;
sharedState.yy.parser = this;
if (typeof lexer.yylloc == 'undefined') {
lexer.yylloc = {};
}
var yyloc = lexer.yylloc;
lstack.push(yyloc);
var ranges = lexer.options && lexer.options.ranges;
if (typeof sharedState.yy.parseError === 'function') {
this.parseError = sharedState.yy.parseError;
} else {
this.parseError = Object.getPrototypeOf(this).parseError;
}
function popStack(n) {
stack.length = stack.length - 2 * n;
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}
_token_stack:
function lex() {
var token;
token = lexer.lex() || EOF;
if (typeof token !== 'number') {
token = self.symbols_[token] || token;
}
return token;
}
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
while (true) {
state = stack[stack.length - 1];
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol === null || typeof symbol == 'undefined') {
symbol = lex();
}
action = table[state] && table[state][symbol];
}
if (typeof action === 'undefined' || !action.length || !action[0]) {
var errStr = '';
expected = [];
for (p in table[state]) {
if (this.terminals_[p] && p > TERROR) {
expected.push('\'' + this.terminals_[p] + '\'');
}
}
if (lexer.showPosition) {
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
} else {
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
}
this.parseError(errStr, {
text: lexer.match,
token: this.terminals_[symbol] || symbol,
line: lexer.yylineno,
loc: yyloc,
expected: expected
});
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(lexer.yytext);
lstack.push(lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = lexer.yyleng;
yytext = lexer.yytext;
yylineno = lexer.yylineno;
yyloc = lexer.yylloc;
if (recovering > 0) {
recovering--;
}
} else {
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {
first_line: lstack[lstack.length - (len || 1)].first_line,
last_line: lstack[lstack.length - 1].last_line,
first_column: lstack[lstack.length - (len || 1)].first_column,
last_column: lstack[lstack.length - 1].last_column
};
if (ranges) {
yyval._$.range = [
lstack[lstack.length - (len || 1)].range[0],
lstack[lstack.length - 1].range[1]
];
}
r = this.performAction.apply(yyval, [
yytext,
yyleng,
yylineno,
sharedState.yy,
action[1],
vstack,
lstack
].concat(args));
if (typeof r !== 'undefined') {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}};
/* generated by jison-lex 0.3.4 */
var lexer = (function(){
var lexer = ({
EOF:1,
parseError:function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
// resets the lexer, sets new input
setInput:function (input, yy) {
this.yy = yy || this.yy || {};
this._input = input;
this._more = this._backtrack = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {
first_line: 1,
first_column: 0,
last_line: 1,
last_column: 0
};
if (this.options.ranges) {
this.yylloc.range = [0,0];
}
this.offset = 0;
return this;
},
// consumes and returns one char from the input
input:function () {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) {
this.yylloc.range[1]++;
}
this._input = this._input.slice(1);
return ch;
},
// unshifts one char (or a string) into the input
unput:function (ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length - len);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length - 1);
this.matched = this.matched.substr(0, this.matched.length - 1);
if (lines.length - 1) {
this.yylineno -= lines.length - 1;
}
var r = this.yylloc.range;
this.yylloc = {
first_line: this.yylloc.first_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0)
+ oldLines[oldLines.length - lines.length].length - lines[0].length :
this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
this.yyleng = this.yytext.length;
return this;
},
// When called from action, caches matched text and appends it on next action
more:function () {
this._more = true;
return this;
},
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
reject:function () {
if (this.options.backtrack_lexer) {
this._backtrack = true;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
return this;
},
// retain first n characters of the match
less:function (n) {
this.unput(this.match.slice(n));
},
// displays already matched input, i.e. for error messages
pastInput:function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
},
// displays upcoming input, i.e. for error messages
upcomingInput:function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20-next.length);
}
return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
},
// displays the character position where the lexing error occurred, i.e. for error messages
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c + "^";
},
// test the lexed token: return FALSE when not a match, otherwise return token
test_match:function (match, indexed_rule) {
var token,
lines,
backup;
if (this.options.backtrack_lexer) {
// save context
backup = {
yylineno: this.yylineno,
yylloc: {
first_line: this.yylloc.first_line,
last_line: this.last_line,
first_column: this.yylloc.first_column,
last_column: this.yylloc.last_column
},
yytext: this.yytext,
match: this.match,
matches: this.matches,
matched: this.matched,
yyleng: this.yyleng,
offset: this.offset,
_more: this._more,
_input: this._input,
yy: this.yy,
conditionStack: this.conditionStack.slice(0),
done: this.done
};
if (this.options.ranges) {
backup.yylloc.range = this.yylloc.range.slice(0);
}
}
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno += lines.length;
}
this.yylloc = {
first_line: this.yylloc.last_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.last_column,
last_column: lines ?
lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
this.yylloc.last_column + match[0].length
};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._backtrack = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
if (this.done && this._input) {
this.done = false;
}
if (token) {
return token;
} else if (this._backtrack) {
// recover context
for (var k in backup) {
this[k] = backup[k];
}
return false; // rule action called reject() implying the next rule should be tested instead.
}
return false;
},
// return next match in input
next:function () {
if (this.done) {
return this.EOF;
}
if (!this._input) {
this.done = true;
}
var token,
match,
tempMatch,
index;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i = 0; i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (this.options.backtrack_lexer) {
token = this.test_match(tempMatch, rules[i]);
if (token !== false) {
return token;
} else if (this._backtrack) {
match = false;
continue; // rule action called reject() implying a rule MISmatch.
} else {
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
} else if (!this.options.flex) {
break;
}
}
}
if (match) {
token = this.test_match(match, rules[index]);
if (token !== false) {
return token;
}
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
},
// return next match that has a token
lex:function lex() {
var r = this.next();
if (r) {
return r;
} else {
return this.lex();
}
},
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
begin:function begin(condition) {
this.conditionStack.push(condition);
},
// pop the previously active lexer condition state off the condition stack
popState:function popState() {
var n = this.conditionStack.length - 1;
if (n > 0) {
return this.conditionStack.pop();
} else {
return this.conditionStack[0];
}
},
// produce the lexer rule set which is active for the currently active lexer condition state
_currentRules:function _currentRules() {
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
} else {
return this.conditions["INITIAL"].rules;
}
},
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
topState:function topState(n) {
n = this.conditionStack.length - 1 - Math.abs(n || 0);
if (n >= 0) {
return this.conditionStack[n];
} else {
return "INITIAL";
}
},
// alias for begin(condition)
pushState:function pushState(condition) {
this.begin(condition);
},
// return the number of states currently on the stack
stateStackSize:function stateStackSize() {
return this.conditionStack.length;
},
options: {},
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
var YYSTATE=YY_START;
switch($avoiding_name_collisions) {
case 0:/* skip whitespace */
break;
case 1:return 6
break;
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
break;
case 3:return 17
break;
case 4:return 18
break;
case 5:return 23
break;
case 6:return 24
break;
case 7:return 22
break;
case 8:return 21
break;
case 9:return 10
break;
case 10:return 11
break;
case 11:return 8
break;
case 12:return 14
break;
case 13:return 'INVALID'
break;
}
},
rules: [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}
});
return lexer;
})();
parser.lexer = lexer;
function Parser () {
this.yy = {};
}
Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = parser;
exports.Parser = parser.Parser;
exports.parse = function () { return parser.parse.apply(parser, arguments); };
}

View File

@@ -0,0 +1,34 @@
{
"author": "Zach Carter <zach@carter.name> (http://zaa.ch)",
"name": "@mapbox/jsonlint-lines-primitives",
"description": "Validate JSON",
"keywords": [
"json",
"validation",
"lint",
"jsonlint"
],
"version": "2.0.2",
"preferGlobal": true,
"repository": {
"type": "git",
"url": "git://github.com/mapbox/jsonlint.git"
},
"bugs": {
"url": "http://github.com/mapbox/jsonlint/issues"
},
"main": "lib/jsonlint.js",
"engines": {
"node": ">= 0.6"
},
"dependencies": {},
"devDependencies": {
"test": "*",
"jison": "*",
"uglify-js": "*"
},
"scripts": {
"test": "node test/all-tests.js"
},
"optionalDependencies": {}
}

View File

@@ -0,0 +1,8 @@
var fs = require('fs');
var source = "var jsonlint = (function(){var require=true,module=false;var exports={};" +
fs.readFileSync(__dirname+'/../lib/jsonlint.js', 'utf8') +
"return exports;})()";
console.log(source);

View File

@@ -0,0 +1,24 @@
int "-"?([0-9]|[1-9][0-9]+)
exp [eE][-+]?[0-9]+
frac "."[0-9]+
%%
\s+ /* skip whitespace */
{int}{frac}?{exp}?\b return 'NUMBER'
\"(?:'\\'[\\"bfnrt/]|'\\u'[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*\" yytext = yytext.substr(1,yyleng-2); return 'STRING'
"{" return '{'
"}" return '}'
"[" return '['
"]" return ']'
"," return ','
":" return ':'
"true" return 'TRUE'
"false" return 'FALSE'
"null" return 'NULL'
<<EOF>> return 'EOF'
. return 'INVALID'
%%

View File

@@ -0,0 +1,110 @@
%start JSONText
/*
ECMA-262 5th Edition, 15.12.1 The JSON Grammar.
*/
%%
JSONString
: STRING
{ // replace escaped characters with actual character
$$ = new String(yytext.replace(/\\(\\|")/g, "$"+"1")
.replace(/\\n/g,'\n')
.replace(/\\r/g,'\r')
.replace(/\\t/g,'\t')
.replace(/\\v/g,'\v')
.replace(/\\f/g,'\f')
.replace(/\\b/g,'\b'));
$$.__line__ = @$.first_line;
}
;
JSONNumber
: NUMBER
{
$$ = new Number(yytext);
$$.__line__ = @$.first_line;
}
;
JSONNullLiteral
: NULL
{
$$ = null;
}
;
JSONBooleanLiteral
: TRUE
{
$$ = new Boolean(true);
$$.__line__ = @$.first_line;
}
| FALSE
{
$$ = new Boolean(false);
$$.__line__ = @$.first_line;
}
;
JSONText
: JSONValue EOF
{return $$ = $1;}
;
JSONValue
: JSONNullLiteral
| JSONBooleanLiteral
| JSONString
| JSONNumber
| JSONObject
| JSONArray
;
JSONObject
: '{' '}'
{$$ = {}; Object.defineProperty($$, '__line__', {
value: @$.first_line,
enumerable: false
})}
| '{' JSONMemberList '}'
{$$ = $2; Object.defineProperty($$, '__line__', {
value: @$.first_line,
enumerable: false
})}
;
JSONMember
: JSONString ':' JSONValue
{$$ = [$1, $3];}
;
JSONMemberList
: JSONMember
{{$$ = {}; $$[$1[0]] = $1[1];}}
| JSONMemberList ',' JSONMember
{$$ = $1; $1[$3[0]] = $3[1];}
;
JSONArray
: '[' ']'
{$$ = []; Object.defineProperty($$, '__line__', {
value: @$.first_line,
enumerable: false
})}
| '[' JSONElementList ']'
{$$ = $2; Object.defineProperty($$, '__line__', {
value: @$.first_line,
enumerable: false
})}
;
JSONElementList
: JSONValue
{$$ = [$1];}
| JSONElementList ',' JSONValue
{$$ = $1; $1.push($3);}
;

View File

@@ -0,0 +1,210 @@
var fs = require("fs"),
assert = require("assert"),
parser = require("../lib/jsonlint").parser;
exports["test string with line break"] = function () {
var json = '{"foo": "bar\nbar"}';
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test string literal"] = function () {
var json = '"foo"';
assert.equal(parser.parse(json), "foo");
assert.equal(parser.parse(json).__line__, 1);
};
exports["test number literal"] = function () {
var json = '1234';
assert.equal(parser.parse(json), 1234);
assert.equal(parser.parse(json).__line__, 1);
};
exports["test null literal"] = function () {
var json = 'null';
assert.equal(parser.parse(json), null);
};
exports["test boolean literal"] = function () {
var json = 'true';
assert.equal(parser.parse(json), true);
assert.equal(parser.parse(json).__line__, 1);
};
exports["test unclosed array"] = function () {
var json = fs.readFileSync(__dirname + "/fails/2.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test unquotedkey keys must be quoted"] = function () {
var json = fs.readFileSync(__dirname + "/fails/3.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test extra comma"] = function () {
var json = fs.readFileSync(__dirname + "/fails/4.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test double extra comma"] = function () {
var json = fs.readFileSync(__dirname + "/fails/5.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test missing value"] = function () {
var json = fs.readFileSync(__dirname + "/fails/6.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test comma after the close"] = function () {
var json = fs.readFileSync(__dirname + "/fails/7.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test extra close"] = function () {
var json = fs.readFileSync(__dirname + "/fails/8.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test extra comma after value"] = function () {
var json = fs.readFileSync(__dirname + "/fails/9.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test extra value after close with misplaced quotes"] = function () {
var json = fs.readFileSync(__dirname + "/fails/10.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test illegal expression addition"] = function () {
var json = fs.readFileSync(__dirname + "/fails/11.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test illegal invocation of alert"] = function () {
var json = fs.readFileSync(__dirname + "/fails/12.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test numbers cannot have leading zeroes"] = function () {
var json = fs.readFileSync(__dirname + "/fails/13.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test numbers cannot be hex"] = function () {
var json = fs.readFileSync(__dirname + "/fails/14.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test illegal backslash escape \\0"] = function () {
var json = fs.readFileSync(__dirname + "/fails/15.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test unquoted text"] = function () {
var json = fs.readFileSync(__dirname + "/fails/16.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test illegal backslash escape \\x"] = function () {
var json = fs.readFileSync(__dirname + "/fails/17.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test missing colon"] = function () {
var json = fs.readFileSync(__dirname + "/fails/19.json")
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test double colon"] = function () {
var json = fs.readFileSync(__dirname + "/fails/20.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test comma instead of colon"] = function () {
var json = fs.readFileSync(__dirname + "/fails/21.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test colon instead of comma"] = function () {
var json = fs.readFileSync(__dirname + "/fails/22.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test bad raw value"] = function () {
var json = fs.readFileSync(__dirname + "/fails/23.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test single quotes"] = function () {
var json = fs.readFileSync(__dirname + "/fails/24.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test tab character in string"] = function () {
var json = fs.readFileSync(__dirname + "/fails/25.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test tab character in string 2"] = function () {
var json = fs.readFileSync(__dirname + "/fails/26.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test line break in string"] = function () {
var json = fs.readFileSync(__dirname + "/fails/27.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test line break in string in array"] = function () {
var json = fs.readFileSync(__dirname + "/fails/28.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test 0e"] = function () {
var json = fs.readFileSync(__dirname + "/fails/29.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test 0e+"] = function () {
var json = fs.readFileSync(__dirname + "/fails/30.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test 0e+ 1"] = function () {
var json = fs.readFileSync(__dirname + "/fails/31.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test comma instead of closing brace"] = function () {
var json = fs.readFileSync(__dirname + "/fails/32.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
};
exports["test bracket mismatch"] = function () {
var json = fs.readFileSync(__dirname + "/fails/33.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
}
exports["test extra brace"] = function () {
var json = fs.readFileSync(__dirname + "/fails/34.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
}
exports["test pass-1"] = function () {
var json = fs.readFileSync(__dirname + "/passes/1.json").toString();
assert.doesNotThrow(function () {parser.parse(json)}, "should pass");
}
exports["test pass-2"] = function () {
var json = fs.readFileSync(__dirname + "/passes/2.json").toString();
assert.doesNotThrow(function () {parser.parse(json)}, "should pass");
}
exports["test pass-3"] = function () {
var json = fs.readFileSync(__dirname + "/passes/3.json").toString();
assert.doesNotThrow(function () {parser.parse(json)}, "should pass");
}
if (require.main === module) {
require("test").run(exports);
}

View File

@@ -0,0 +1 @@
{"Extra value after close": true} "misplaced quoted value"

View File

@@ -0,0 +1 @@
{"Illegal expression": 1 + 2}

View File

@@ -0,0 +1 @@
{"Illegal invocation": alert()}

View File

@@ -0,0 +1 @@
{"Numbers cannot have leading zeroes": 013}

View File

@@ -0,0 +1 @@
{"Numbers cannot be hex": 0x14}

View File

@@ -0,0 +1 @@
["Illegal backslash escape: \x15"]

View File

@@ -0,0 +1 @@
[\naked]

View File

@@ -0,0 +1 @@
["Illegal backslash escape: \017"]

View File

@@ -0,0 +1 @@
{"Missing colon" null}

View File

@@ -0,0 +1 @@
["Unclosed array"

View File

@@ -0,0 +1 @@
{"Double colon":: null}

View File

@@ -0,0 +1 @@
{"Comma instead of colon", null}

View File

@@ -0,0 +1 @@
["Colon instead of comma": false]

View File

@@ -0,0 +1 @@
["Bad value", truth]

View File

@@ -0,0 +1 @@
['single quote']

View File

@@ -0,0 +1 @@
[" tab character in string "]

View File

@@ -0,0 +1 @@
["tab\ character\ in\ string\ "]

View File

@@ -0,0 +1,2 @@
["line
break"]

View File

@@ -0,0 +1,2 @@
["line\
break"]

View File

@@ -0,0 +1 @@
[0e]

View File

@@ -0,0 +1 @@
{unquoted_key: "keys must be quoted"}

View File

@@ -0,0 +1 @@
[0e+]

View File

@@ -0,0 +1 @@
[0e+-1]

View File

@@ -0,0 +1 @@
{"Comma instead if closing brace": true,

View File

@@ -0,0 +1 @@
["mismatch"}

View File

@@ -0,0 +1 @@
{"extra brace": 1}}

View File

@@ -0,0 +1 @@
["extra comma",]

View File

@@ -0,0 +1 @@
["double extra comma",,]

View File

@@ -0,0 +1 @@
[ , "<-- missing value"]

View File

@@ -0,0 +1 @@
["Comma after the close"],

View File

@@ -0,0 +1 @@
["Extra close"]]

View File

@@ -0,0 +1 @@
{"Extra comma": true,}

View File

@@ -0,0 +1,58 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]

View File

@@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]

View File

@@ -0,0 +1,6 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

View File

@@ -0,0 +1,334 @@
/*jslint evil: true, strict: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
this.JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());

View File

@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JSON Lint</title>
<script src="json2.js"></script>
<script src="jsonlint.js"></script>
<script>
window.onload = function () {
document.getElementById("button").onclick = function () {
try {
var result = jsonlint.parse(document.getElementById("source").value);
if (result) {
document.getElementById("result").innerHTML = "JSON is valid!";
document.getElementById("result").className = "pass";
if (document.getElementById("reformat").checked) {
document.getElementById("source").value = JSON.stringify(result, null, " ");
}
}
} catch(e) {
document.getElementById("result").innerHTML = e;
document.getElementById("result").className = "fail";
}
};
}
</script>
<style>
body {font-family: sans-serif;}
#result {
padding: 1em;
}
.pass {
background-color: #efe;
color: #393;
border: 2px solid #393;
}
.fail {
background-color: #fee;
color: #933;
border: 2px solid #933;
}
textarea { width: 100%; }
</style>
</head>
<body>
<h1>JSON Lint</h1>
<p>A pure JavaScript version of the service provided at <a href="http://jsonlint.com/">jsonlint.com</a>.</p>
<textarea id="source" rows="20" cols="50">
</textarea>
<p>
<button id="button">Validate</button>
<input type="checkbox" value="yes" id="reformat" /><label for="reformat">reformat JSON</label>
</p>
<h2>Results</h2>
<pre id="result"></pre>
<p><a href="http://github.com/zaach/jsonlint">project on github</a></p>
</body>
</html>

File diff suppressed because one or more lines are too long

13
node_modules/@mapbox/point-geometry/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,13 @@
Copyright (c) 2024, Mapbox
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

259
node_modules/@mapbox/point-geometry/README.md generated vendored Normal file
View File

@@ -0,0 +1,259 @@
# @mapbox/point-geometry
A `Point` class for representing point geometry with useful utility methods.
## Installation
```sh
$ npm install @mapbox/point-geometry
```
## API
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Point
A standalone point geometry with useful accessor, comparison, and
modification methods.
#### Parameters
* `x` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the x-coordinate. This could be longitude or screen pixels, or any other sort of unit.
* `y` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the y-coordinate. This could be latitude or screen pixels, or any other sort of unit.
#### Examples
```javascript
const point = new Point(-77, 38);
```
#### clone
Clone this point, returning a new point that can be modified
without affecting the old one.
Returns **[Point](#point)** the clone
#### add
Add this point's x & y coordinates to another point,
yielding a new point.
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[Point](#point)** output point
#### sub
Subtract this point's x & y coordinates to from point,
yielding a new point.
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[Point](#point)** output point
#### multByPoint
Multiply this point's x & y coordinates by point,
yielding a new point.
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[Point](#point)** output point
#### divByPoint
Divide this point's x & y coordinates by point,
yielding a new point.
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[Point](#point)** output point
#### mult
Multiply this point's x & y coordinates by a factor,
yielding a new point.
##### Parameters
* `k` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** factor
Returns **[Point](#point)** output point
#### div
Divide this point's x & y coordinates by a factor,
yielding a new point.
##### Parameters
* `k` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** factor
Returns **[Point](#point)** output point
#### rotate
Rotate this point around the 0, 0 origin by an angle a,
given in radians
##### Parameters
* `a` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle to rotate around, in radians
Returns **[Point](#point)** output point
#### rotateAround
Rotate this point around p point by an angle a,
given in radians
##### Parameters
* `a` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle to rotate around, in radians
* `p` **[Point](#point)** Point to rotate around
Returns **[Point](#point)** output point
#### matMult
Multiply this point by a 4x1 transformation matrix
##### Parameters
* `m` **\[[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)]** transformation matrix
Returns **[Point](#point)** output point
#### unit
Calculate this point but as a unit vector from 0, 0, meaning
that the distance from the resulting point to the 0, 0
coordinate will be equal to 1 and the angle from the resulting
point to the 0, 0 coordinate will be the same as before.
Returns **[Point](#point)** unit vector point
#### perp
Compute a perpendicular point, where the new y coordinate
is the old x coordinate and the new x coordinate is the old y
coordinate multiplied by -1
Returns **[Point](#point)** perpendicular point
#### round
Return a version of this point with the x & y coordinates
rounded to integers.
Returns **[Point](#point)** rounded point
#### mag
Return the magnitude of this point: this is the Euclidean
distance from the 0, 0 coordinate to this point's x and y
coordinates.
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** magnitude
#### equals
Judge whether this point is equal to another point, returning
true or false.
##### Parameters
* `other` **[Point](#point)** the other point
Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** whether the points are equal
#### dist
Calculate the distance from this point to another point
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** distance
#### distSqr
Calculate the distance from this point to another point,
without the square root step. Useful if you're comparing
relative distances.
##### Parameters
* `p` **[Point](#point)** the other point
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** distance
#### angle
Get the angle from the 0, 0 coordinate to this point, in radians
coordinates.
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle
#### angleTo
Get the angle from this point to another point, in radians
##### Parameters
* `b` **[Point](#point)** the other point
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle
#### angleWith
Get the angle between this point and another point, in radians
##### Parameters
* `b` **[Point](#point)** the other point
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle
#### angleWithSep
Find the angle of the two vectors, solving the formula for
the cross product a x b = |a||b|sin(θ) for θ.
##### Parameters
* `x` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the x-coordinate
* `y` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the y-coordinate
Returns **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** the angle in radians
#### convert
Construct a point from an array if necessary, otherwise if the input
is already a Point, or an unknown type, return it unchanged
##### Parameters
* `a` **(\[[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)] | [Point](#point))** any kind of input value
##### Examples
```javascript
// this
var point = Point.convert([0, 1]);
// is equivalent to
var point = new Point(0, 1);
```
Returns **[Point](#point)** constructed point, or passed-through value.

214
node_modules/@mapbox/point-geometry/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,214 @@
/**
* A standalone point geometry with useful accessor, comparison, and
* modification methods.
*
* @class
* @param {number} x the x-coordinate. This could be longitude or screen pixels, or any other sort of unit.
* @param {number} y the y-coordinate. This could be latitude or screen pixels, or any other sort of unit.
*
* @example
* const point = new Point(-77, 38);
*/
declare function Point(x: number, y: number): void;
declare class Point {
/**
* A standalone point geometry with useful accessor, comparison, and
* modification methods.
*
* @class
* @param {number} x the x-coordinate. This could be longitude or screen pixels, or any other sort of unit.
* @param {number} y the y-coordinate. This could be latitude or screen pixels, or any other sort of unit.
*
* @example
* const point = new Point(-77, 38);
*/
constructor(x: number, y: number);
x: number;
y: number;
/**
* Clone this point, returning a new point that can be modified
* without affecting the old one.
* @return {Point} the clone
*/
clone(): Point;
/**
* Add this point's x & y coordinates to another point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
add(p: Point): Point;
/**
* Subtract this point's x & y coordinates to from point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
sub(p: Point): Point;
/**
* Multiply this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
multByPoint(p: Point): Point;
/**
* Divide this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
divByPoint(p: Point): Point;
/**
* Multiply this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
mult(k: number): Point;
/**
* Divide this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
div(k: number): Point;
/**
* Rotate this point around the 0, 0 origin by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @return {Point} output point
*/
rotate(a: number): Point;
/**
* Rotate this point around p point by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @param {Point} p Point to rotate around
* @return {Point} output point
*/
rotateAround(a: number, p: Point): Point;
/**
* Multiply this point by a 4x1 transformation matrix
* @param {[number, number, number, number]} m transformation matrix
* @return {Point} output point
*/
matMult(m: [number, number, number, number]): Point;
/**
* Calculate this point but as a unit vector from 0, 0, meaning
* that the distance from the resulting point to the 0, 0
* coordinate will be equal to 1 and the angle from the resulting
* point to the 0, 0 coordinate will be the same as before.
* @return {Point} unit vector point
*/
unit(): Point;
/**
* Compute a perpendicular point, where the new y coordinate
* is the old x coordinate and the new x coordinate is the old y
* coordinate multiplied by -1
* @return {Point} perpendicular point
*/
perp(): Point;
/**
* Return a version of this point with the x & y coordinates
* rounded to integers.
* @return {Point} rounded point
*/
round(): Point;
/**
* Return the magnitude of this point: this is the Euclidean
* distance from the 0, 0 coordinate to this point's x and y
* coordinates.
* @return {number} magnitude
*/
mag(): number;
/**
* Judge whether this point is equal to another point, returning
* true or false.
* @param {Point} other the other point
* @return {boolean} whether the points are equal
*/
equals(other: Point): boolean;
/**
* Calculate the distance from this point to another point
* @param {Point} p the other point
* @return {number} distance
*/
dist(p: Point): number;
/**
* Calculate the distance from this point to another point,
* without the square root step. Useful if you're comparing
* relative distances.
* @param {Point} p the other point
* @return {number} distance
*/
distSqr(p: Point): number;
/**
* Get the angle from the 0, 0 coordinate to this point, in radians
* coordinates.
* @return {number} angle
*/
angle(): number;
/**
* Get the angle from this point to another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleTo(b: Point): number;
/**
* Get the angle between this point and another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleWith(b: Point): number;
/**
* Find the angle of the two vectors, solving the formula for
* the cross product a x b = |a||b|sin(θ) for θ.
* @param {number} x the x-coordinate
* @param {number} y the y-coordinate
* @return {number} the angle in radians
*/
angleWithSep(x: number, y: number): number;
/** @param {[number, number, number, number]} m */
_matMult(m: [number, number, number, number]): this;
/** @param {Point} p */
_add(p: Point): this;
/** @param {Point} p */
_sub(p: Point): this;
/** @param {number} k */
_mult(k: number): this;
/** @param {number} k */
_div(k: number): this;
/** @param {Point} p */
_multByPoint(p: Point): this;
/** @param {Point} p */
_divByPoint(p: Point): this;
_unit(): this;
_perp(): this;
/** @param {number} angle */
_rotate(angle: number): this;
/**
* @param {number} angle
* @param {Point} p
*/
_rotateAround(angle: number, p: Point): this;
_round(): this;
}
declare namespace Point {
/**
* Construct a point from an array if necessary, otherwise if the input
* is already a Point, return it unchanged.
* @param {Point | [number, number] | {x: number, y: number}} p input value
* @return {Point} constructed point.
* @example
* // this
* var point = Point.convert([0, 1]);
* // is equivalent to
* var point = new Point(0, 1);
*/
function convert(p: Point | [number, number] | {
x: number;
y: number;
}): Point;
}
export default Point;

323
node_modules/@mapbox/point-geometry/index.js generated vendored Normal file
View File

@@ -0,0 +1,323 @@
/**
* A standalone point geometry with useful accessor, comparison, and
* modification methods.
*
* @class
* @param {number} x the x-coordinate. This could be longitude or screen pixels, or any other sort of unit.
* @param {number} y the y-coordinate. This could be latitude or screen pixels, or any other sort of unit.
*
* @example
* const point = new Point(-77, 38);
*/
export default function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
/**
* Clone this point, returning a new point that can be modified
* without affecting the old one.
* @return {Point} the clone
*/
clone() { return new Point(this.x, this.y); },
/**
* Add this point's x & y coordinates to another point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
add(p) { return this.clone()._add(p); },
/**
* Subtract this point's x & y coordinates to from point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
sub(p) { return this.clone()._sub(p); },
/**
* Multiply this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
multByPoint(p) { return this.clone()._multByPoint(p); },
/**
* Divide this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
divByPoint(p) { return this.clone()._divByPoint(p); },
/**
* Multiply this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
mult(k) { return this.clone()._mult(k); },
/**
* Divide this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
div(k) { return this.clone()._div(k); },
/**
* Rotate this point around the 0, 0 origin by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @return {Point} output point
*/
rotate(a) { return this.clone()._rotate(a); },
/**
* Rotate this point around p point by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @param {Point} p Point to rotate around
* @return {Point} output point
*/
rotateAround(a, p) { return this.clone()._rotateAround(a, p); },
/**
* Multiply this point by a 4x1 transformation matrix
* @param {[number, number, number, number]} m transformation matrix
* @return {Point} output point
*/
matMult(m) { return this.clone()._matMult(m); },
/**
* Calculate this point but as a unit vector from 0, 0, meaning
* that the distance from the resulting point to the 0, 0
* coordinate will be equal to 1 and the angle from the resulting
* point to the 0, 0 coordinate will be the same as before.
* @return {Point} unit vector point
*/
unit() { return this.clone()._unit(); },
/**
* Compute a perpendicular point, where the new y coordinate
* is the old x coordinate and the new x coordinate is the old y
* coordinate multiplied by -1
* @return {Point} perpendicular point
*/
perp() { return this.clone()._perp(); },
/**
* Return a version of this point with the x & y coordinates
* rounded to integers.
* @return {Point} rounded point
*/
round() { return this.clone()._round(); },
/**
* Return the magnitude of this point: this is the Euclidean
* distance from the 0, 0 coordinate to this point's x and y
* coordinates.
* @return {number} magnitude
*/
mag() {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
/**
* Judge whether this point is equal to another point, returning
* true or false.
* @param {Point} other the other point
* @return {boolean} whether the points are equal
*/
equals(other) {
return this.x === other.x &&
this.y === other.y;
},
/**
* Calculate the distance from this point to another point
* @param {Point} p the other point
* @return {number} distance
*/
dist(p) {
return Math.sqrt(this.distSqr(p));
},
/**
* Calculate the distance from this point to another point,
* without the square root step. Useful if you're comparing
* relative distances.
* @param {Point} p the other point
* @return {number} distance
*/
distSqr(p) {
const dx = p.x - this.x,
dy = p.y - this.y;
return dx * dx + dy * dy;
},
/**
* Get the angle from the 0, 0 coordinate to this point, in radians
* coordinates.
* @return {number} angle
*/
angle() {
return Math.atan2(this.y, this.x);
},
/**
* Get the angle from this point to another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleTo(b) {
return Math.atan2(this.y - b.y, this.x - b.x);
},
/**
* Get the angle between this point and another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleWith(b) {
return this.angleWithSep(b.x, b.y);
},
/**
* Find the angle of the two vectors, solving the formula for
* the cross product a x b = |a||b|sin(θ) for θ.
* @param {number} x the x-coordinate
* @param {number} y the y-coordinate
* @return {number} the angle in radians
*/
angleWithSep(x, y) {
return Math.atan2(
this.x * y - this.y * x,
this.x * x + this.y * y);
},
/** @param {[number, number, number, number]} m */
_matMult(m) {
const x = m[0] * this.x + m[1] * this.y,
y = m[2] * this.x + m[3] * this.y;
this.x = x;
this.y = y;
return this;
},
/** @param {Point} p */
_add(p) {
this.x += p.x;
this.y += p.y;
return this;
},
/** @param {Point} p */
_sub(p) {
this.x -= p.x;
this.y -= p.y;
return this;
},
/** @param {number} k */
_mult(k) {
this.x *= k;
this.y *= k;
return this;
},
/** @param {number} k */
_div(k) {
this.x /= k;
this.y /= k;
return this;
},
/** @param {Point} p */
_multByPoint(p) {
this.x *= p.x;
this.y *= p.y;
return this;
},
/** @param {Point} p */
_divByPoint(p) {
this.x /= p.x;
this.y /= p.y;
return this;
},
_unit() {
this._div(this.mag());
return this;
},
_perp() {
const y = this.y;
this.y = this.x;
this.x = -y;
return this;
},
/** @param {number} angle */
_rotate(angle) {
const cos = Math.cos(angle),
sin = Math.sin(angle),
x = cos * this.x - sin * this.y,
y = sin * this.x + cos * this.y;
this.x = x;
this.y = y;
return this;
},
/**
* @param {number} angle
* @param {Point} p
*/
_rotateAround(angle, p) {
const cos = Math.cos(angle),
sin = Math.sin(angle),
x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
this.x = x;
this.y = y;
return this;
},
_round() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
},
constructor: Point
};
/**
* Construct a point from an array if necessary, otherwise if the input
* is already a Point, return it unchanged.
* @param {Point | [number, number] | {x: number, y: number}} p input value
* @return {Point} constructed point.
* @example
* // this
* var point = Point.convert([0, 1]);
* // is equivalent to
* var point = new Point(0, 1);
*/
Point.convert = function (p) {
if (p instanceof Point) {
return /** @type {Point} */ (p);
}
if (Array.isArray(p)) {
return new Point(+p[0], +p[1]);
}
if (p.x !== undefined && p.y !== undefined) {
return new Point(+p.x, +p.y);
}
throw new Error('Expected [x, y] or {x, y} point format');
};

40
node_modules/@mapbox/point-geometry/package.json generated vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "@mapbox/point-geometry",
"version": "1.1.0",
"description": "a point geometry with transforms",
"main": "index.js",
"type": "module",
"exports": "./index.js",
"types": "index.d.ts",
"scripts": {
"lint": "eslint --fix index.js test.js",
"pretest": "npm run lint",
"test": "tsc && node test.js",
"doc": "documentation readme index.js --section=API --markdown-toc=false",
"cov": "node --test --experimental-test-coverage"
},
"repository": {
"type": "git",
"url": "git@github.com:mapbox/point-geometry.git"
},
"keywords": [
"point",
"geometry",
"primitive"
],
"author": "Tom MacWright",
"license": "ISC",
"bugs": {
"url": "https://github.com/mapbox/point-geometry/issues"
},
"homepage": "https://github.com/mapbox/point-geometry",
"devDependencies": {
"documentation": "^14.0.3",
"eslint": "^9.6.0",
"eslint-config-mourner": "^4.0.2",
"typescript": "^5.5.3"
},
"files": [
"index.d.ts"
]
}

9
node_modules/@mapbox/tiny-sdf/LICENSE.txt generated vendored Normal file
View File

@@ -0,0 +1,9 @@
BSD-2-Clause
Copyright (c) 2016-2024 Mapbox, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

63
node_modules/@mapbox/tiny-sdf/README.md generated vendored Normal file
View File

@@ -0,0 +1,63 @@
# TinySDF [![Volodymyr Agafonkin's projects](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects) [![Node](https://github.com/mapbox/tiny-sdf/actions/workflows/node.yml/badge.svg)](https://github.com/mapbox/tiny-sdf/actions/workflows/node.yml)
TinySDF is a tiny and fast JavaScript library for generating SDF (signed distance field)
from system fonts on the browser using Canvas 2D and
[Felzenszwalb/Huttenlocher distance transform](https://cs.brown.edu/~pff/papers/dt-final.pdf).
This is very useful for [rendering text with WebGL](https://www.mapbox.com/blog/text-signed-distance-fields/).
## [Demo](http://mapbox.github.io/tiny-sdf)
## Usage
Create a TinySDF for drawing glyph SDFs based on font parameters:
```js
const tinySdf = new TinySDF({
fontSize: 24, // Font size in pixels
fontFamily: 'sans-serif', // CSS font-family
fontWeight: 'normal', // CSS font-weight
fontStyle: 'normal', // CSS font-style
buffer: 3, // Whitespace buffer around a glyph in pixels
radius: 8, // How many pixels around the glyph shape to use for encoding distance
cutoff: 0.25 // How much of the radius (relative) is used for the inside part of the glyph
});
const glyph = tinySdf.draw('泽'); // draw a single character
```
Returns an object with the following properties:
- `data` is a `Uint8ClampedArray` array of alpha values (0255) for a `width` x `height` grid.
- `width`: Width of the returned bitmap.
- `height`: Height of the returned bitmap.
- `glyphTop`: Maximum ascent of the glyph from alphabetic baseline.
- `glyphLeft`: Currently hardwired to 0 (actual glyph differences are encoded in the rasterization).
- `glyphWidth`: Width of the rasterized portion of the glyph.
- `glyphHeight` Height of the rasterized portion of the glyph.
- `glyphAdvance`: Layout advance.
TinySDF is provided as a ES module, so it's only supported on modern browsers, excluding IE.
```html
<script type="module">
import TinySDF from 'https://cdn.skypack.dev/@mapbox/tiny-sdf';
...
</script>
```
In Node, you can't use `require` — only `import` in ESM-capable versions (v12.15+):
```js
import TinySDF from '@mapbox/tiny-sdf';
```
## Development
```bash
npm test # run tests
npm start # start server for the demo page
```
## License
This implementation is licensed under the [BSD 2-Clause license](https://opensource.org/licenses/BSD-2-Clause). It's based directly on the algorithm published in the Felzenszwalb/Huttenlocher paper, and is not a port of the existing C++ implementation provided by the paper's authors.

24
node_modules/@mapbox/tiny-sdf/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
export declare type TinySDFOptions = {
fontSize?: number;
buffer?: number;
radius?: number;
cutoff?: number;
fontFamily?: string;
fontWeight?: string;
fontStyle?: string;
lang?: string;
};
export default class TinySDF {
constructor(options: TinySDFOptions);
draw(char: string): {
data: Uint8ClampedArray;
width: number;
height: number;
glyphWidth: number;
glyphHeight: number;
glyphTop: number;
glyphLeft: number;
glyphAdvance: number;
};
}

158
node_modules/@mapbox/tiny-sdf/index.js generated vendored Normal file
View File

@@ -0,0 +1,158 @@
const INF = 1e20;
// lookup table for gamma-corrected, signed squared alpha distance values
const alphaTable = new Float64Array(256);
for (let i = 0; i < 256; i++) {
const d = 0.5 - Math.pow(i / 255, 1 / 2.2);
alphaTable[i] = d * Math.abs(d);
}
alphaTable[255] = -INF;
export default class TinySDF {
constructor({
fontSize = 24,
buffer = 3,
radius = 8,
cutoff = 0.25,
fontFamily = 'sans-serif',
fontWeight = 'normal',
fontStyle = 'normal',
lang = null
} = {}) {
this.buffer = buffer; // padding around a glyph's bounding box
this.radius = radius; // how many pixels around the glyph edge are encoded as signed distances
this.cutoff = cutoff; // how much of the SDF byte range represents inside vs outside the edge
this.lang = lang; // language of the Canvas drawing context
// make the canvas size big enough to both have the specified buffer around the glyph
// for "halo", and account for some glyphs possibly being larger than their font size
const size = this.size = fontSize + buffer * 4;
const canvas = this._createCanvas(size);
const ctx = this.ctx = canvas.getContext('2d', {willReadFrequently: true});
ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
ctx.textBaseline = 'alphabetic';
ctx.textAlign = 'left'; // Necessary so that RTL text doesn't have different alignment
ctx.fillStyle = 'black';
// two grids of squared distances: one for the outside of the glyph shape, one for the inside;
// the signed distance is derived as sqrt(outer) - sqrt(inner)
this.gridOuter = new Float64Array(size * size);
this.gridInner = new Float64Array(size * size);
this.f = new Float64Array(size);
this.z = new Float64Array(size + 1);
this.v = new Uint16Array(size);
}
_createCanvas(size) {
if (typeof OffscreenCanvas !== 'undefined') {
return new OffscreenCanvas(size, size);
}
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
return canvas;
}
draw(char) {
const {
width: glyphAdvance,
actualBoundingBoxAscent,
actualBoundingBoxDescent,
actualBoundingBoxLeft,
actualBoundingBoxRight
} = this.ctx.measureText(char);
// The integer/pixel part of the alignment is encoded in metrics.glyphTop/glyphLeft
// The remainder is implicitly encoded in the rasterization
const glyphTop = Math.ceil(actualBoundingBoxAscent);
const glyphLeft = Math.floor(actualBoundingBoxLeft);
// If the glyph overflows the canvas size, it will be clipped at the bottom/right
const glyphWidth = Math.max(0, Math.min(this.size - this.buffer, Math.ceil(actualBoundingBoxRight) - glyphLeft));
const glyphHeight = Math.max(0, Math.min(this.size - this.buffer, glyphTop + Math.ceil(actualBoundingBoxDescent)));
const width = glyphWidth + 2 * this.buffer;
const height = glyphHeight + 2 * this.buffer;
const len = Math.max(width * height, 0);
const data = new Uint8ClampedArray(len);
const glyph = {data, width, height, glyphWidth, glyphHeight, glyphTop, glyphLeft, glyphAdvance};
if (glyphWidth === 0 || glyphHeight === 0) return glyph;
const {ctx, buffer, gridInner, gridOuter} = this;
if (this.lang) ctx.lang = this.lang;
ctx.clearRect(buffer, buffer, glyphWidth, glyphHeight);
ctx.fillText(char, buffer - glyphLeft, buffer + glyphTop);
const imgData = ctx.getImageData(buffer, buffer, glyphWidth, glyphHeight);
// default: outside the glyph (INF distance) for outer, inside (0 distance) for inner
gridOuter.fill(INF, 0, len);
gridInner.fill(0, 0, len);
// for anti-aliased pixels, treat partial coverage as a distance approximation:
// a fully covered pixel gets 0 outer / INF inner; a partial pixel gets a small
// non-zero outer or inner distance based on how far its coverage deviates from 0.5
let imgIdx = 3; // start at the alpha channel of the first pixel
for (let y = 0; y < glyphHeight; y++) {
let j = (y + buffer) * width + buffer;
for (let x = 0; x < glyphWidth; x++, imgIdx += 4, j++) {
const a = imgData.data[imgIdx]; // alpha value
if (a === 0) continue; // empty pixels
const t = alphaTable[a];
gridOuter[j] = Math.max(0, t);
gridInner[j] = Math.max(0, -t);
}
}
edt(gridOuter, 0, 0, width, height, width, this.f, this.v, this.z);
edt(gridInner, buffer, buffer, glyphWidth, glyphHeight, width, this.f, this.v, this.z);
// encode signed distance as a byte: inside the glyph maps to high values, outside to low,
// with the edge gradient spanning [-radius * cutoff, radius * (1 - cutoff)] pixels around the edge;
// Uint8ClampedArray clamps beyond that
const scale = 255 / this.radius;
const base = 255 * (1 - this.cutoff);
for (let i = 0; i < len; i++) {
const d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
data[i] = Math.round(base - scale * d);
}
return glyph;
}
}
// 2D Euclidean squared distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/papers/dt-final.pdf
function edt(data, x0, y0, width, height, gridSize, f, v, z) {
for (let x = x0; x < x0 + width; x++) edt1d(data, y0 * gridSize + x, gridSize, height, f, v, z);
for (let y = y0; y < y0 + height; y++) edt1d(data, y * gridSize + x0, 1, width, f, v, z);
}
// 1D squared distance transform
function edt1d(grid, offset, stride, length, f, v, z) {
v[0] = 0;
z[0] = -INF;
z[1] = INF;
f[0] = grid[offset];
for (let q = 1, k = 0, s = 0; q < length; q++) {
f[q] = grid[offset + q * stride];
const q2 = q * q;
do {
const r = v[k];
s = (f[q] - f[r] + q2 - r * r) / (q - r) / 2;
} while (s <= z[k] && --k > -1);
k++;
v[k] = q;
z[k] = s;
z[k + 1] = INF;
}
for (let q = 0, k = 0; q < length; q++) {
while (z[k + 1] < q) k++;
const r = v[k];
const qr = q - r;
grid[offset + q * stride] = f[r] + qr * qr;
}
}

45
node_modules/@mapbox/tiny-sdf/package.json generated vendored Normal file
View File

@@ -0,0 +1,45 @@
{
"name": "@mapbox/tiny-sdf",
"version": "2.1.0",
"description": "Browser-side SDF font generator",
"type": "module",
"main": "index.js",
"exports": "./index.js",
"typings": "./index.d.ts",
"scripts": {
"pretest": "eslint index.js index.html test",
"test": "node --test",
"bench": "node ./test/bench.js",
"start": "st --no-cache --localhost --index index.html ."
},
"repository": {
"type": "git",
"url": "git+https://github.com/mapbox/tiny-sdf.git"
},
"keywords": [
"sdf",
"signed distance fields",
"font",
"canvas",
"text",
"distance transform"
],
"author": "Vladimir Agafonkin",
"license": "BSD-2-Clause",
"bugs": {
"url": "https://github.com/mapbox/tiny-sdf/issues"
},
"homepage": "https://github.com/mapbox/tiny-sdf#readme",
"files": [
"index.d.ts"
],
"devDependencies": {
"canvas": "^3.2.3",
"eslint": "^10.2.0",
"eslint-config-mourner": "^4.1.0",
"eslint-plugin-html": "^8.1.4",
"pixelmatch": "^7.1.0",
"pngjs": "^7.0.0",
"st": "^3.0.3"
}
}

28
node_modules/@mapbox/unitbezier/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,28 @@
BSD-2-Clause
Copyright (C) 2008 Apple Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ported from Webkit
http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h

17
node_modules/@mapbox/unitbezier/README.md generated vendored Normal file
View File

@@ -0,0 +1,17 @@
[![Build Status](https://travis-ci.org/mapbox/unitbezier.svg)](https://travis-ci.org/mapbox/unitbezier)
# unitbezier
Unit bezier interpolation function: a port to JavaScript from Webkit:
http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h
## api
### new UnitBezier(p1x, p1y, p2x, p2y)
Initialize a new bezier curve given the points
### bezier.solve(x, epsilon)
Evaluate bezier for value `x` (ranging from 0 to 1) with `epsilon` precision (1e-6 by default).

8
node_modules/@mapbox/unitbezier/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
export default class UnitBezier {
constructor(p1x: number, p1y: number, p2x: number, p2y: number);
sampleCurveX(t: number): number;
sampleCurveY(t: number): number;
sampleCurveDerivativeX(t: number): number;
solveCurveX(x: number, epsilon?: number): number;
solve(x: number, epsilon?: number): number;
}

78
node_modules/@mapbox/unitbezier/index.js generated vendored Normal file
View File

@@ -0,0 +1,78 @@
'use strict';
module.exports = UnitBezier;
function UnitBezier(p1x, p1y, p2x, p2y) {
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
this.cx = 3.0 * p1x;
this.bx = 3.0 * (p2x - p1x) - this.cx;
this.ax = 1.0 - this.cx - this.bx;
this.cy = 3.0 * p1y;
this.by = 3.0 * (p2y - p1y) - this.cy;
this.ay = 1.0 - this.cy - this.by;
this.p1x = p1x;
this.p1y = p1y;
this.p2x = p2x;
this.p2y = p2y;
}
UnitBezier.prototype = {
sampleCurveX: function (t) {
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
return ((this.ax * t + this.bx) * t + this.cx) * t;
},
sampleCurveY: function (t) {
return ((this.ay * t + this.by) * t + this.cy) * t;
},
sampleCurveDerivativeX: function (t) {
return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;
},
solveCurveX: function (x, epsilon) {
if (epsilon === undefined) epsilon = 1e-6;
if (x < 0.0) return 0.0;
if (x > 1.0) return 1.0;
var t = x;
// First try a few iterations of Newton's method - normally very fast.
for (var i = 0; i < 8; i++) {
var x2 = this.sampleCurveX(t) - x;
if (Math.abs(x2) < epsilon) return t;
var d2 = this.sampleCurveDerivativeX(t);
if (Math.abs(d2) < 1e-6) break;
t = t - x2 / d2;
}
// Fall back to the bisection method for reliability.
var t0 = 0.0;
var t1 = 1.0;
t = x;
for (i = 0; i < 20; i++) {
x2 = this.sampleCurveX(t);
if (Math.abs(x2 - x) < epsilon) break;
if (x > x2) {
t0 = t;
} else {
t1 = t;
}
t = (t1 - t0) * 0.5 + t0;
}
return t;
},
solve: function (x, epsilon) {
return this.sampleCurveY(this.solveCurveX(x, epsilon));
}
};

39
node_modules/@mapbox/unitbezier/package.json generated vendored Normal file
View File

@@ -0,0 +1,39 @@
{
"name": "@mapbox/unitbezier",
"version": "0.0.1",
"description": "unit bezier curve interpolation",
"main": "index.js",
"typings": "index.d.ts",
"scripts": {
"pretest": "eslint index.js test/*.js",
"test": "node test/unitbezier.js"
},
"files": [
"index.js",
"index.d.ts"
],
"repository": {
"type": "git",
"url": "git@github.com:mapbox/unitbezier.git"
},
"keywords": [
"unit",
"bezier",
"interpolation",
"webkit"
],
"author": "",
"license": "BSD-2-Clause",
"bugs": {
"url": "https://github.com/mapbox/unitbezier/issues"
},
"homepage": "https://github.com/mapbox/unitbezier",
"devDependencies": {
"eslint": "^8.0.1",
"eslint-config-mourner": "^2.0.3",
"tape": "^5.3.1"
},
"eslintConfig": {
"extends": "mourner"
}
}

27
node_modules/@mapbox/vector-tile/LICENSE.txt generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2024, Mapbox
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Mapbox nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

95
node_modules/@mapbox/vector-tile/README.md generated vendored Normal file
View File

@@ -0,0 +1,95 @@
# vector-tile
This library reads [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec) and allows access to the layers and features.
## Example
```js
import {VectorTile} from '@mapbox/vector-tile';
import Protobuf from 'pbf';
const tile = new VectorTile(new Protobuf(data));
// Contains a map of all layers
tile.layers;
const landuse = tile.layers.landuse;
// Amount of features in this layer
landuse.length;
// Returns the first feature
landuse.feature(0);
```
Vector tiles contained in [serialtiles-spec](https://github.com/mapbox/serialtiles-spec)
are gzip-encoded, so a complete example of parsing them with the native
zlib module would be:
```js
import {VectorTile} from '@mapbox/vector-tile';
import Protobuf from 'pbf';
import {gunzipSync} from 'zlib';
const buffer = gunzipSync(data);
const tile = new VectorTile(new Protobuf(buffer));
```
## Install
To install:
npm install @mapbox/vector-tile
## API Reference
### VectorTile
An object that parses vector tile data and makes it readable.
#### Constructor
- **new VectorTile(protobuf[, end])** &mdash;
parses the vector tile data contained in the given [Protobuf](https://github.com/mapbox/pbf) object,
saving resulting layers in the created object as a `layers` property. Optionally accepts end index.
#### Properties
- **layers** (Object) &mdash; an object containing parsed layers in the form of `{<name>: <layer>, ...}`,
where each layer is a `VectorTileLayer` object.
### VectorTileLayer
An object that contains the data for a single vector tile layer.
#### Properties
- **version** (`Number`, default: `1`)
- **name** (`String`) &mdash; layer name
- **extent** (`Number`, default: `4096`) &mdash; tile extent size
- **length** (`Number`) &mdash; number of features in the layer
#### Methods
- **feature(i)** &mdash; get a feature (`VectorTileFeature`) by the given index from the layer.
### VectorTileFeature
An object that contains the data for a single feature.
#### Properties
- **type** (`Number`) &mdash; type of the feature (also see `VectorTileFeature.types`)
- **extent** (`Number`) &mdash; feature extent size
- **id** (`Number`) &mdash; feature identifier, if present
- **properties** (`Object`) &mdash; object literal with feature properties
#### Methods
- **loadGeometry()** &mdash; parses feature geometry and returns an array of
[Point](https://github.com/mapbox/point-geometry) arrays (with each point having `x` and `y` properties)
- **bbox()** &mdash; calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2]`
- **toGeoJSON(x, y, z)** &mdash; returns a GeoJSON representation of the feature. (`x`, `y`, and `z` refer to the containing tile's index.)

81
node_modules/@mapbox/vector-tile/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,81 @@
/** classifies an array of rings into polygons with outer rings and holes
* @param {Point[][]} rings
*/
export function classifyRings(rings: Point[][]): Point[][][];
/** @import Pbf from 'pbf' */
/** @import {Feature} from 'geojson' */
export class VectorTileFeature {
/**
* @param {Pbf} pbf
* @param {number} end
* @param {number} extent
* @param {string[]} keys
* @param {(number | string | boolean)[]} values
*/
constructor(pbf: Pbf, end: number, extent: number, keys: string[], values: (number | string | boolean)[]);
/** @type {Record<string, number | string | boolean>} */
properties: Record<string, number | string | boolean>;
extent: number;
/** @type {0 | 1 | 2 | 3} */
type: 0 | 1 | 2 | 3;
/** @type {number | undefined} */
id: number | undefined;
/** @private */
private _pbf;
/** @private */
private _geometry;
/** @private */
private _keys;
/** @private */
private _values;
loadGeometry(): Point[][];
bbox(): number[];
/**
* @param {number} x
* @param {number} y
* @param {number} z
* @return {Feature}
*/
toGeoJSON(x: number, y: number, z: number): Feature;
}
export namespace VectorTileFeature {
let types: ["Unknown", "Point", "LineString", "Polygon"];
}
export class VectorTileLayer {
/**
* @param {Pbf} pbf
* @param {number} [end]
*/
constructor(pbf: Pbf, end?: number);
version: number;
name: string;
extent: number;
length: number;
/** @private */
private _pbf;
/** @private
* @type {string[]} */
private _keys;
/** @private
* @type {(number | string | boolean)[]} */
private _values;
/** @private
* @type {number[]} */
private _features;
/** return feature `i` from this layer as a `VectorTileFeature`
* @param {number} i
*/
feature(i: number): VectorTileFeature;
}
export class VectorTile {
/**
* @param {Pbf} pbf
* @param {number} [end]
*/
constructor(pbf: Pbf, end?: number);
/** @type {Record<string, VectorTileLayer>} */
layers: Record<string, VectorTileLayer>;
}
import Point from '@mapbox/point-geometry';
import type { Feature } from 'geojson';
import type Pbf from 'pbf';

388
node_modules/@mapbox/vector-tile/index.js generated vendored Normal file
View File

@@ -0,0 +1,388 @@
import Point from '@mapbox/point-geometry';
/** @import Pbf from 'pbf' */
/** @import {Feature} from 'geojson' */
export class VectorTileFeature {
/**
* @param {Pbf} pbf
* @param {number} end
* @param {number} extent
* @param {string[]} keys
* @param {(number | string | boolean)[]} values
*/
constructor(pbf, end, extent, keys, values) {
// Public
/** @type {Record<string, number | string | boolean>} */
this.properties = {};
this.extent = extent;
/** @type {0 | 1 | 2 | 3} */
this.type = 0;
/** @type {number | undefined} */
this.id = undefined;
/** @private */
this._pbf = pbf;
/** @private */
this._geometry = -1;
/** @private */
this._keys = keys;
/** @private */
this._values = values;
pbf.readFields(readFeature, this, end);
}
loadGeometry() {
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
/** @type Point[][] */
const lines = [];
/** @type Point[] | undefined */
let line;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (cmd === 1) { // moveTo
if (line) lines.push(line);
line = [];
}
if (line) line.push(new Point(x, y));
} else if (cmd === 7) {
// Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
if (line) {
line.push(line[0].clone()); // closePolygon
}
} else {
throw new Error(`unknown command ${cmd}`);
}
}
if (line) lines.push(line);
return lines;
}
bbox() {
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1,
length = 0,
x = 0,
y = 0,
x1 = Infinity,
x2 = -Infinity,
y1 = Infinity,
y2 = -Infinity;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (x < x1) x1 = x;
if (x > x2) x2 = x;
if (y < y1) y1 = y;
if (y > y2) y2 = y;
} else if (cmd !== 7) {
throw new Error(`unknown command ${cmd}`);
}
}
return [x1, y1, x2, y2];
}
/**
* @param {number} x
* @param {number} y
* @param {number} z
* @return {Feature}
*/
toGeoJSON(x, y, z) {
const size = this.extent * Math.pow(2, z),
x0 = this.extent * x,
y0 = this.extent * y,
vtCoords = this.loadGeometry();
/** @param {Point} p */
function projectPoint(p) {
return [
(p.x + x0) * 360 / size - 180,
360 / Math.PI * Math.atan(Math.exp((1 - (p.y + y0) * 2 / size) * Math.PI)) - 90
];
}
/** @param {Point[]} line */
function projectLine(line) {
return line.map(projectPoint);
}
/** @type {Feature["geometry"]} */
let geometry;
if (this.type === 1) {
const points = [];
for (const line of vtCoords) {
points.push(line[0]);
}
const coordinates = projectLine(points);
geometry = points.length === 1 ?
{type: 'Point', coordinates: coordinates[0]} :
{type: 'MultiPoint', coordinates};
} else if (this.type === 2) {
const coordinates = vtCoords.map(projectLine);
geometry = coordinates.length === 1 ?
{type: 'LineString', coordinates: coordinates[0]} :
{type: 'MultiLineString', coordinates};
} else if (this.type === 3) {
const polygons = classifyRings(vtCoords);
const coordinates = [];
for (const polygon of polygons) {
coordinates.push(polygon.map(projectLine));
}
geometry = coordinates.length === 1 ?
{type: 'Polygon', coordinates: coordinates[0]} :
{type: 'MultiPolygon', coordinates};
} else {
throw new Error('unknown feature type');
}
/** @type {Feature} */
const result = {
type: 'Feature',
geometry,
properties: this.properties
};
if (this.id != null) {
result.id = this.id;
}
return result;
}
}
/** @type {['Unknown', 'Point', 'LineString', 'Polygon']} */
VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
/**
* @param {number} tag
* @param {VectorTileFeature} feature
* @param {Pbf} pbf
*/
function readFeature(tag, feature, pbf) {
if (tag === 1) feature.id = pbf.readVarint();
else if (tag === 2) readTag(pbf, feature);
else if (tag === 3) feature.type = /** @type {0 | 1 | 2 | 3} */ (pbf.readVarint());
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 4) feature._geometry = pbf.pos;
}
/**
* @param {Pbf} pbf
* @param {VectorTileFeature} feature
*/
function readTag(pbf, feature) {
const end = pbf.readVarint() + pbf.pos;
while (pbf.pos < end) {
// @ts-expect-error TS2341 deliberately accessing a private property
const key = feature._keys[pbf.readVarint()];
// @ts-expect-error TS2341 deliberately accessing a private property
const value = feature._values[pbf.readVarint()];
feature.properties[key] = value;
}
}
/** classifies an array of rings into polygons with outer rings and holes
* @param {Point[][]} rings
*/
export function classifyRings(rings) {
const len = rings.length;
if (len <= 1) return [rings];
const polygons = [];
let polygon, ccw;
for (let i = 0; i < len; i++) {
const area = signedArea(rings[i]);
if (area === 0) continue;
if (ccw === undefined) ccw = area < 0;
if (ccw === area < 0) {
if (polygon) polygons.push(polygon);
polygon = [rings[i]];
} else if (polygon) {
polygon.push(rings[i]);
}
}
if (polygon) polygons.push(polygon);
return polygons;
}
/** @param {Point[]} ring */
function signedArea(ring) {
let sum = 0;
for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
p1 = ring[i];
p2 = ring[j];
sum += (p2.x - p1.x) * (p1.y + p2.y);
}
return sum;
}
export class VectorTileLayer {
/**
* @param {Pbf} pbf
* @param {number} [end]
*/
constructor(pbf, end) {
// Public
this.version = 1;
this.name = '';
this.extent = 4096;
this.length = 0;
/** @private */
this._pbf = pbf;
/** @private
* @type {string[]} */
this._keys = [];
/** @private
* @type {(number | string | boolean)[]} */
this._values = [];
/** @private
* @type {number[]} */
this._features = [];
pbf.readFields(readLayer, this, end);
this.length = this._features.length;
}
/** return feature `i` from this layer as a `VectorTileFeature`
* @param {number} i
*/
feature(i) {
if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
this._pbf.pos = this._features[i];
const end = this._pbf.readVarint() + this._pbf.pos;
return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
}
}
/**
* @param {number} tag
* @param {VectorTileLayer} layer
* @param {Pbf} pbf
*/
function readLayer(tag, layer, pbf) {
if (tag === 15) layer.version = pbf.readVarint();
else if (tag === 1) layer.name = pbf.readString();
else if (tag === 5) layer.extent = pbf.readVarint();
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 2) layer._features.push(pbf.pos);
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 3) layer._keys.push(pbf.readString());
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 4) layer._values.push(readValueMessage(pbf));
}
/**
* @param {Pbf} pbf
*/
function readValueMessage(pbf) {
let value = null;
const end = pbf.readVarint() + pbf.pos;
while (pbf.pos < end) {
const tag = pbf.readVarint() >> 3;
value = tag === 1 ? pbf.readString() :
tag === 2 ? pbf.readFloat() :
tag === 3 ? pbf.readDouble() :
tag === 4 ? pbf.readVarint64() :
tag === 5 ? pbf.readVarint() :
tag === 6 ? pbf.readSVarint() :
tag === 7 ? pbf.readBoolean() : null;
}
if (value == null) {
throw new Error('unknown feature value');
}
return value;
}
export class VectorTile {
/**
* @param {Pbf} pbf
* @param {number} [end]
*/
constructor(pbf, end) {
/** @type {Record<string, VectorTileLayer>} */
this.layers = pbf.readFields(readTile, {}, end);
}
}
/**
* @param {number} tag
* @param {Record<string, VectorTileLayer>} layers
* @param {Pbf} pbf
*/
function readTile(tag, layers, pbf) {
if (tag === 3) {
const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
if (layer.length) layers[layer.name] = layer;
}
}

34
node_modules/@mapbox/vector-tile/package.json generated vendored Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "@mapbox/vector-tile",
"description": "Parses vector tiles",
"repository": {
"url": "git+https://github.com/mapbox/vector-tile-js.git"
},
"version": "2.0.4",
"type": "module",
"exports": "./index.js",
"license": "BSD-3-Clause",
"main": "index.js",
"types": "index.d.ts",
"dependencies": {
"@mapbox/point-geometry": "~1.1.0",
"@types/geojson": "^7946.0.16",
"pbf": "^4.0.1"
},
"devDependencies": {
"benchmark": "^2.1.4",
"eslint": "^9.29.0",
"eslint-config-mourner": "^4.0.2",
"typescript": "^5.8.3"
},
"scripts": {
"lint": "eslint index.js test/*.js",
"pretest": "npm run lint",
"test": "tsc && node --test",
"cov": "node --test --experimental-test-coverage",
"prepublishOnly": "npm test"
},
"files": [
"index.d.ts"
]
}

28
node_modules/@mapbox/whoots-js/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,28 @@
_Breaking changes, which may affect downstream projects are marked with a_ :warning:
## 3.1.0
##### 2018-Jul-12
* Replace legacy Rollup `jsnext:main` with now standard `module` ([#74])
* :warning: Drop support for Node 4
[#74]: https://github.com/mapbox/whoots-js/issues/74
## 3.0.0
##### 2017-Feb-13
* :warning: whoots-js is now a scoped package under the @mapbox namespace
## 2.1.0
##### Jul 15, 2016
* Release as ES6 module alongside UMD build, add `jsnext:main` to `package.json`
## 2.0.0
##### Jun 1, 2016
* :warning: Refactor for a classless API
## 1.1.0
##### May 23, 2016
* Make getTileBbox and getMercCoords public
## 1.0.0
##### May 23, 2016
* Initial release

15
node_modules/@mapbox/whoots-js/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2017, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

53
node_modules/@mapbox/whoots-js/README.md generated vendored Normal file
View File

@@ -0,0 +1,53 @@
[![npm version](https://badge.fury.io/js/%40mapbox%2Fwhoots-js.svg)](https://badge.fury.io/js/%40mapbox%2Fwhoots-js)
[![Build Status](https://secure.travis-ci.org/mapbox/whoots-js.svg)](http://travis-ci.org/mapbox/whoots-js)
[![Coverage Status](https://coveralls.io/repos/github/mapbox/whoots-js/badge.svg?branch=master)](https://coveralls.io/github/mapbox/whoots-js?branch=master)
## whoots-js
Request tiles from WMS servers that support EPSG:3857.
This project is a JavaScript port of https://github.com/timwaters/whoots by Tim Waters.
### What is it?
Given a `z/x/y` tile coordinate like `19/154308/197167`, `whoots-js` can request imagery from an EPSG:3857 supporting WMS server like this:
```
http://geodata.state.nj.us/imagerywms/Natural2015?
bbox=-8242663.382160267,4966572.349857613,-8242586.945131982,4966648.786885899
&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857
&width=256&height=256&layers=Natural2015
```
### Usage
```js
var WhooTS = require('@mapbox/whoots-js');
// Get an image url for a given tile coordinate
var baseUrl = 'http://geodata.state.nj.us/imagerywms/Natural2015';
var layer = 'Natural2015';
var url = WhooTS.getURL(baseUrl, layer, 154308, 197167, 19);
```
### Server
This project includes a sample redirecting wms proxy server in `server.js`.
`npm run server` will start a local server on port 8080 that redirects tile requests.
Valid tile requests look like:
```
http://localhost:8080/tms/{z}/{x}/{y}/{layer}/{endpoint}
http://localhost:8080/tms/19/154308/197167/Natural2015/http://geodata.state.nj.us/imagerywms/Natural2015
```
### Documentation
Complete API documentation is here: http://mapbox.github.io/whoots-js/

88
node_modules/@mapbox/whoots-js/index.js generated vendored Normal file
View File

@@ -0,0 +1,88 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.WhooTS = {})));
}(this, (function (exports) {
/**
* getURL
*
* @param {String} baseUrl Base url of the WMS server
* @param {String} layer Layer name
* @param {Number} x Tile coordinate x
* @param {Number} y Tile coordinate y
* @param {Number} z Tile zoom
* @param {Object} [options]
* @param {String} [options.format='image/png']
* @param {String} [options.service='WMS']
* @param {String} [options.version='1.1.1']
* @param {String} [options.request='GetMap']
* @param {String} [options.srs='EPSG:3857']
* @param {Number} [options.width='256']
* @param {Number} [options.height='256']
* @returns {String} url
* @example
* var baseUrl = 'http://geodata.state.nj.us/imagerywms/Natural2015';
* var layer = 'Natural2015';
* var url = whoots.getURL(baseUrl, layer, 154308, 197167, 19);
*/
function getURL(baseUrl, layer, x, y, z, options) {
options = options || {};
var url = baseUrl + '?' + [
'bbox=' + getTileBBox(x, y, z),
'format=' + (options.format || 'image/png'),
'service=' + (options.service || 'WMS'),
'version=' + (options.version || '1.1.1'),
'request=' + (options.request || 'GetMap'),
'srs=' + (options.srs || 'EPSG:3857'),
'width=' + (options.width || 256),
'height=' + (options.height || 256),
'layers=' + layer
].join('&');
return url;
}
/**
* getTileBBox
*
* @param {Number} x Tile coordinate x
* @param {Number} y Tile coordinate y
* @param {Number} z Tile zoom
* @returns {String} String of the bounding box
*/
function getTileBBox(x, y, z) {
// for Google/OSM tile scheme we need to alter the y
y = (Math.pow(2, z) - y - 1);
var min = getMercCoords(x * 256, y * 256, z),
max = getMercCoords((x + 1) * 256, (y + 1) * 256, z);
return min[0] + ',' + min[1] + ',' + max[0] + ',' + max[1];
}
/**
* getMercCoords
*
* @param {Number} x Pixel coordinate x
* @param {Number} y Pixel coordinate y
* @param {Number} z Tile zoom
* @returns {Array} [x, y]
*/
function getMercCoords(x, y, z) {
var resolution = (2 * Math.PI * 6378137 / 256) / Math.pow(2, z),
merc_x = (x * resolution - 2 * Math.PI * 6378137 / 2.0),
merc_y = (y * resolution - 2 * Math.PI * 6378137 / 2.0);
return [merc_x, merc_y];
}
exports.getURL = getURL;
exports.getTileBBox = getTileBBox;
exports.getMercCoords = getMercCoords;
Object.defineProperty(exports, '__esModule', { value: true });
})));

78
node_modules/@mapbox/whoots-js/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,78 @@
export { getURL, getTileBBox, getMercCoords };
/**
* getURL
*
* @param {String} baseUrl Base url of the WMS server
* @param {String} layer Layer name
* @param {Number} x Tile coordinate x
* @param {Number} y Tile coordinate y
* @param {Number} z Tile zoom
* @param {Object} [options]
* @param {String} [options.format='image/png']
* @param {String} [options.service='WMS']
* @param {String} [options.version='1.1.1']
* @param {String} [options.request='GetMap']
* @param {String} [options.srs='EPSG:3857']
* @param {Number} [options.width='256']
* @param {Number} [options.height='256']
* @returns {String} url
* @example
* var baseUrl = 'http://geodata.state.nj.us/imagerywms/Natural2015';
* var layer = 'Natural2015';
* var url = whoots.getURL(baseUrl, layer, 154308, 197167, 19);
*/
function getURL(baseUrl, layer, x, y, z, options) {
options = options || {};
var url = baseUrl + '?' + [
'bbox=' + getTileBBox(x, y, z),
'format=' + (options.format || 'image/png'),
'service=' + (options.service || 'WMS'),
'version=' + (options.version || '1.1.1'),
'request=' + (options.request || 'GetMap'),
'srs=' + (options.srs || 'EPSG:3857'),
'width=' + (options.width || 256),
'height=' + (options.height || 256),
'layers=' + layer
].join('&');
return url;
}
/**
* getTileBBox
*
* @param {Number} x Tile coordinate x
* @param {Number} y Tile coordinate y
* @param {Number} z Tile zoom
* @returns {String} String of the bounding box
*/
function getTileBBox(x, y, z) {
// for Google/OSM tile scheme we need to alter the y
y = (Math.pow(2, z) - y - 1);
var min = getMercCoords(x * 256, y * 256, z),
max = getMercCoords((x + 1) * 256, (y + 1) * 256, z);
return min[0] + ',' + min[1] + ',' + max[0] + ',' + max[1];
}
/**
* getMercCoords
*
* @param {Number} x Pixel coordinate x
* @param {Number} y Pixel coordinate y
* @param {Number} z Tile zoom
* @returns {Array} [x, y]
*/
function getMercCoords(x, y, z) {
var resolution = (2 * Math.PI * 6378137 / 256) / Math.pow(2, z),
merc_x = (x * resolution - 2 * Math.PI * 6378137 / 2.0),
merc_y = (y * resolution - 2 * Math.PI * 6378137 / 2.0);
return [merc_x, merc_y];
}

33
node_modules/@mapbox/whoots-js/package.json generated vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "@mapbox/whoots-js",
"description": "Request tiles from WMS servers that support EPSG:3857",
"version": "3.1.0",
"main": "index.js",
"module": "index.mjs",
"license": "ISC",
"author": "Bryan Housel <bryan@mapbox.com>",
"repository": "mapbox/whoots-js",
"keywords": [
"WMS",
"tiles",
"EPSG:3857"
],
"dependencies": {},
"devDependencies": {
"coveralls": "^3.0.0",
"documentation": "4.0.0-beta5",
"eslint": "^5.0.0",
"rollup": "0.60.0",
"tap": "^12.0.0"
},
"engines": {
"node": ">=6.0.0"
},
"scripts": {
"build": "rollup -f umd -n WhooTS index.mjs --no-indent --no-strict -o index.js",
"docs": "documentation build index.mjs --lint --github --format html --output docs/",
"lint": "eslint index.mjs server.js test",
"start": "node server.js",
"test": "npm run lint && npm run build && tap --cov test/*.js"
}
}

40
node_modules/@mapbox/whoots-js/server.js generated vendored Normal file
View File

@@ -0,0 +1,40 @@
'use strict';
var WhooTS = require('./');
var http = require('http');
var url = require('url');
var PORT = 8080;
// valid requests look like:
// http://localhost:8080/tms/{z}/{x}/{y}/{layer}/{baseUrl}
// http://localhost:8080/tms/19/154308/197167/Natural2015/http://geodata.state.nj.us/imagerywms/Natural2015
function handleRequest(request, response) {
var pathname = url.parse(request.url, true).pathname;
var params = pathname.split('/');
if (params.length > 6 && params[1].toLowerCase() === 'tms') {
var z = +params[2],
x = +params[3],
y = +params[4],
layer = params[5],
baseUrl = pathname.replace(params.slice(0,6).join('/') + '/', '');
if (!isNaN(z) && !isNaN(x) && !isNaN(y) && layer && url.parse(baseUrl).protocol) {
response.writeHead(302, { 'Location': WhooTS.getURL(baseUrl, layer, x, y, z) });
response.end('Redirect');
return;
}
}
response.statusCode = '404';
response.end('Not Found');
}
var server = http.createServer(handleRequest);
server.listen(PORT, function() {
/* eslint-disable no-console */
console.log('Server listening on: http://localhost:%s', PORT);
/* eslint-enable no-console */
});