├── .travis.yml ├── .mocha ├── .babelrc ├── .mocha.md ├── .mocha.json ├── index.js ├── webpack.config.dist.js ├── .jscsrc ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── src ├── module.js ├── lib │ ├── canonical-title-map.js │ ├── resolve.js │ ├── traverse.spec.js │ ├── sf-path.spec.js │ ├── validate.spec.js │ ├── sf-path.js │ ├── traverse.js │ ├── schema-defaults.spec.js │ ├── validate.js │ ├── canonical-title-map.spec.js │ ├── select.js │ ├── resolve.spec.js │ ├── merge.js │ ├── merge.spec.js │ ├── select.spec.js │ └── schema-defaults.js └── module.spec.js ├── bower.json ├── tsconfig.json ├── dist └── json-schema-form-core.min.js ├── .eslintrc.json ├── LICENSE ├── webpack.config.js ├── tslint.json ├── package.json ├── README.md ├── CONTRIBUTING.md ├── docs └── test.md └── lib └── json-refs-standalone-min.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | 5 | sudo: false 6 | -------------------------------------------------------------------------------- /.mocha: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | --require babel-polyfill 3 | --reporter spec 4 | --watch-extensions js 5 | src/**/*.spec.js 6 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "es2015", "stage-0" ] 4 | ], 5 | "plugins": [ "transform-flow-strip-types", "transform-async-to-generator" ] 6 | } 7 | -------------------------------------------------------------------------------- /.mocha.md: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | --require babel-polyfill 3 | --recursive 4 | --reporter markdown 5 | --watch-extensions js 6 | src/**/*.spec.js 7 | -------------------------------------------------------------------------------- /.mocha.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporterEnabled": "spec, markdown", 3 | "markdownReporterOptions": { 4 | "output": "test.md", 5 | "mochaFile": "test.md" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export { schemaDefaults } from './src/schemaDefaults'; 2 | export { canonicalTitleMap } from './src/canonicalTitleMap'; 3 | export { sfPath } from './src/sfPath'; 4 | export { merge } from './src/merge'; 5 | export { select } from './src/select'; 6 | export { traverse } from './src/traverse'; 7 | export { validate } from './src/validate'; 8 | -------------------------------------------------------------------------------- /webpack.config.dist.js: -------------------------------------------------------------------------------- 1 | const config = require('./webpack.config.js'); 2 | const path = require('path'); 3 | const includes = [ 4 | path.join(__dirname, 'src', 'lib', 'index') 5 | ]; 6 | 7 | config.entry = { 8 | "json-schema-form-core": includes, 9 | "json-schema-form-core.min": includes 10 | } 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "google", 3 | "disallowSpacesInsideObjectBrackets": null, 4 | "requireSpacesInsideObjectBrackets": { 5 | "allExcept": [ "[", "]", "{", "}" ] 6 | }, 7 | "disallowSpacesInsideArrayBrackets": null, 8 | "requireSpacesInsideArrayBrackets": { 9 | "allExcept": [ "[", "]", "{", "}" ] 10 | }, 11 | "disallowKeywordsOnNewLine": [ ], 12 | "disallowMultipleVarDecl": null, 13 | "requireSemicolons": true 14 | } 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Description 2 | 3 | Add your description here 4 | 5 | #### Fixes Related issues 6 | - add related 7 | - issues here 8 | 9 | #### Checklist 10 | - [ ] I have read and understand the CONTRIBUTIONS.md file 11 | - [ ] I have searched for and linked related issues 12 | - [ ] I have created test cases to ensure quick resolution of the PR is easier 13 | - [ ] I am NOT targeting main branch 14 | - [ ] I did NOT include the dist folder in my PR 15 | 16 | @json-schema-form/angular-schema-form-lead 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | # /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # IDEs and editors 12 | /.idea 13 | /.vscode 14 | .editorconfig 15 | .project 16 | .classpath 17 | *.launch 18 | .settings/ 19 | 20 | # misc 21 | /.sass-cache 22 | /connect.lock 23 | /coverage/* 24 | /libpeerconnection.log 25 | npm-debug.log 26 | testem.log 27 | /typings 28 | 29 | # e2e 30 | /e2e/*.js 31 | /e2e/*.map 32 | 33 | #System Files 34 | .DS_Store 35 | Thumbs.db 36 | -------------------------------------------------------------------------------- /src/module.js: -------------------------------------------------------------------------------- 1 | import * as schemaDefaultsImp from './lib/schema-defaults'; 2 | import * as sfPathImp from './lib/sf-path'; 3 | import canonicalTitleMapImp from './lib/canonical-title-map'; 4 | 5 | export { merge } from './lib/merge'; 6 | export { select } from './lib/select'; 7 | export { jsonref } from './lib/resolve'; 8 | export { traverseSchema, traverseForm } from './lib/traverse'; 9 | export { validate } from './lib/validate'; 10 | 11 | export const sfPath = sfPathImp; 12 | export const schemaDefaults = schemaDefaultsImp; 13 | export const canonicalTitleMap = canonicalTitleMapImp; 14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-schema-form-core", 3 | "main": "dist/json-schema-form-core.js", 4 | "maintainers": [ 5 | "Marcel J Bennett " 6 | ], 7 | "description": "json-schema-form-core", 8 | "keywords": [ 9 | "json", 10 | "json-schema", 11 | "schema", 12 | "json-schema-form" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "index.js", 20 | "package.json", 21 | "docs" 22 | ], 23 | "devDependencies": { 24 | "json-refs": "2.1.6" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | #### Enhancement 9 | As a user/developer, when I ... I should be able to ... 10 | 11 | #### Expected behaviour 12 | I expected ... 13 | 14 | #### Actual behaviour 15 | It actually ... 16 | 17 | #### Gist/Plunker/Demo 18 | [Description](url) 19 | 20 | #### Related issues 21 | This is/maybe related to ... 22 | 23 | @json-schema-form/angular-schema-form-lead 24 | -------------------------------------------------------------------------------- /src/lib/canonical-title-map.js: -------------------------------------------------------------------------------- 1 | // Takes a titleMap in either object or list format and returns one 2 | // in the list format. 3 | export default function(titleMap: Array, originalEnum?: any) { 4 | if (!Array.isArray(titleMap)) { 5 | const canonical = []; 6 | if (originalEnum) { 7 | originalEnum.forEach((value) => { 8 | canonical.push({ name: titleMap[value], value }); 9 | }); 10 | } 11 | else { 12 | Object.keys(titleMap).forEach((value) => { 13 | canonical.push({ name: titleMap[value], value }); 14 | }); 15 | } 16 | return canonical; 17 | } 18 | return titleMap; 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": [ "es6" ], 8 | "mapRoot": "./", 9 | "module": "commonjs", 10 | "moduleResolution": "classic", 11 | "noImplicitAny": false, 12 | "outDir": "./dist/", 13 | "removeComments": false, 14 | "sourceMap": true, 15 | "target": "es6", 16 | "typeRoots": [ 17 | "../node_modules/@types" 18 | ] 19 | }, 20 | "files": [ 21 | "src/lib/index.ts" 22 | ], 23 | "compileOnSave": false, 24 | "buildOnSave": false 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/resolve.js: -------------------------------------------------------------------------------- 1 | import * as JsonRefs from './../../lib/json-refs-standalone'; 2 | 3 | export function jsonref(schema, callBack) { 4 | let promise = new Promise( 5 | function(resolve, reject) { 6 | JsonRefs.resolveRefs(schema, { 7 | "filter": [ 'relative', 'local', 'remote' ] 8 | }) 9 | .then((res) => { resolve(res.resolved); }) 10 | .catch((err) => { reject(new Error(err)); }); 11 | } 12 | ); 13 | 14 | if(typeof(callBack) === 'function') { 15 | promise 16 | .then((resolved) => { callBack(null, resolved); }) 17 | .catch((error) => { callBack(error); }); 18 | } 19 | else { 20 | return promise; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/lib/traverse.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { traverseSchema, traverseForm } from './traverse'; 4 | 5 | chai.should(); 6 | 7 | describe('traverse.js', () => { 8 | it('should hold functions for applying functions on branches of a json-schema or ui-schema', () => { 9 | traverseSchema.should.be.an('function'); 10 | traverseForm.should.be.an('function'); 11 | }); 12 | 13 | // describe('traverseSchema', () => { 14 | // it('should ', () => { 15 | // should.be.eq(); 16 | // }); 17 | // }); 18 | // 19 | // describe('traverseForm', () => { 20 | // it('should ', () => { 21 | // should.be.eq(); 22 | // }); 23 | // }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/module.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { 4 | merge, 5 | select, 6 | traverseSchema, traverseForm, 7 | validate, 8 | sfPath, 9 | schemaDefaults, 10 | canonicalTitleMap, 11 | jsonref 12 | } from './module'; 13 | 14 | chai.should(); 15 | 16 | describe('module.js', () => { 17 | it('should hold all the public functions of the API', () => { 18 | merge.should.be.an('function'); 19 | select.should.be.an('function'); 20 | traverseSchema.should.be.an('function'); 21 | traverseForm.should.be.an('function'); 22 | validate.should.be.an('function'); 23 | sfPath.should.be.an('object'); 24 | schemaDefaults.should.be.an('object'); 25 | canonicalTitleMap.should.be.an('function'); 26 | jsonref.should.be.an('function'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /dist/json-schema-form-core.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * json-schema-form-core 3 | * @version 1.0.0-alpha.2 4 | * @date Sun, 19 Feb 2017 12:19:19 GMT 5 | * @link https://github.com/json-schema-form/json-schema-form-core 6 | * @license MIT 7 | * Copyright (c) 2014-2017 JSON Schema Form 8 | */ 9 | module.exports=function(r){function n(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return r[t].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var e={};return n.m=r,n.c=e,n.i=function(r){return r},n.d=function(r,e,t){n.o(r,e)||Object.defineProperty(r,e,{configurable:!1,enumerable:!0,get:t})},n.n=function(r){var e=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(e,"a",e),e},n.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},n.p="",n(n.s=0)}([function(r,n,e){!function(){throw new Error('Cannot find module "C:\\Users\\Marcel\\Documents\\SERIOUS\\git\\json-schema-form-core\\src\\lib\\index"')}()}]); -------------------------------------------------------------------------------- /src/lib/sf-path.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { parse, stringify, normalize, name } from './sf-path'; 4 | 5 | chai.should(); 6 | 7 | describe('sf-path.js', () => { 8 | it('should hold functions for working with object paths and keys', () => { 9 | parse.should.be.an('function'); 10 | stringify.should.be.an('function'); 11 | normalize.should.be.an('function'); 12 | name.should.be.an('function'); 13 | }); 14 | 15 | // describe('parse', () => { 16 | // it('should ', () => { 17 | // should.be.eq(); 18 | // }); 19 | // }); 20 | // 21 | // describe('stringify', () => { 22 | // it('should ', () => { 23 | // should.be.eq(); 24 | // }); 25 | // }); 26 | // 27 | // describe('normalize', () => { 28 | // it('should ', () => { 29 | // should.be.eq(); 30 | // }); 31 | // }); 32 | // 33 | // describe('name', () => { 34 | // it('should ', () => { 35 | // should.be.eq(); 36 | // }); 37 | // }); 38 | }); 39 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 6, 8 | "sourceType": "module" 9 | }, 10 | "extends": "google", 11 | "rules": { 12 | "array-bracket-spacing": [ 13 | 2, 14 | "always", 15 | { 16 | "objectsInArrays": false, 17 | "arraysInArrays": false 18 | } 19 | ], 20 | "object-curly-spacing": [ 21 | 2, 22 | "always", 23 | { 24 | "objectsInObjects": false, 25 | "arraysInObjects": false 26 | } 27 | ], 28 | "brace-style": [ 29 | 2, 30 | "stroustrup", 31 | { 32 | "allowSingleLine": true 33 | } 34 | ], 35 | "max-len": [ 36 | 2, 37 | 120, 38 | { "ignoreStrings": true } 39 | ], 40 | "semi": [ 41 | 2, 42 | "always" 43 | ], 44 | "linebreak-style": "off", 45 | "space-before-function-paren": "off", 46 | "valid-jsdoc": "off", 47 | "no-invalid-this": "off" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 JSON Schema Form 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib/validate.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { validate } from './validate'; 4 | 5 | let should = chai.should(); 6 | 7 | describe('validate.js', () => { 8 | it('should hold a validation function for testing against tv4 until an option to pass in a validator is created', () => { 9 | validate.should.be.an('function'); 10 | }); 11 | 12 | describe('validate', () => { 13 | const form = { 'key': [ 'hero' ], 'schema': { 'type': 'string' }}; 14 | it('should return a result object {"error":null, "missing":[], "valid":true}, with valid set to true when the data is valid for the schema', () => { 15 | let value = 'Batman'; 16 | let result = validate(form, value); 17 | should.not.exist(result.error); 18 | result.missing.should.be.a('array'); 19 | result.missing.length.should.equal(0); 20 | result.valid.should.equal(true); 21 | }); 22 | 23 | it('should return an error object with a message "Invalid type: array (expected string)" when the data is not valid', () => { 24 | let value = [0]; 25 | let result = validate(form, value); 26 | result.error.message.should.eq("Invalid type: array (expected string)"); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/lib/sf-path.js: -------------------------------------------------------------------------------- 1 | export { parse } from 'objectpath'; 2 | export { stringify } from 'objectpath'; 3 | export { normalize } from 'objectpath'; 4 | 5 | /** 6 | * I am a name formatter function for processing keys into names for classes or Id. 7 | * 8 | * @param {Array} key I am the key array of a processed schema key 9 | * @param {string} separator I am the separator between the key items and optional form name 10 | * @param {string} formName I am an optional form name 11 | * @param {boolean} omitNumbers I determine if numeric values should be included in the output or withheld 12 | * 13 | * @return {string} I am the formatted key 14 | */ 15 | export function name (key: Array, separator?: string, formName = '', omitNumbers = false) { 16 | if (key) { 17 | let fieldKey = key.slice(); 18 | let fieldSeparator = separator || '-'; 19 | 20 | if (omitNumbers) { 21 | fieldKey = fieldKey.filter(function(currentKey: any) { 22 | return typeof currentKey !== 'number'; 23 | }); 24 | }; 25 | 26 | return ((formName.length !== 0) 27 | ? formName + fieldSeparator 28 | : '' 29 | ) + fieldKey.join(fieldSeparator); 30 | }; 31 | 32 | return ''; 33 | }; 34 | -------------------------------------------------------------------------------- /src/lib/traverse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Traverse a schema, applying a function(schema,path) on every sub schema 3 | * i.e. every property of an object. 4 | */ 5 | export function traverseSchema(schema, fn, path, ignoreArrays) { 6 | ignoreArrays = ignoreArrays === undefined ? true : ignoreArrays; 7 | 8 | path = path || []; 9 | 10 | const traverse = function(schemaObject: any, processorFunction: Function, pathArray: Array) { 11 | processorFunction(schemaObject, pathArray); 12 | if (schemaObject.properties) { 13 | Object.keys(schemaObject.properties).forEach((name) => { 14 | const currentPath = pathArray.slice(); 15 | currentPath.push(name); 16 | traverse(schemaObject.properties[name], processorFunction, currentPath); 17 | }); 18 | } 19 | 20 | // Only support type "array" which have a schemaObject as "items". 21 | if (!ignoreArrays && schemaObject.items) { 22 | const arrPath = pathArray.slice(); arrPath.push(''); 23 | traverse(schemaObject.items, processorFunction, arrPath); 24 | } 25 | }; 26 | 27 | traverse(schema, fn, path || []); 28 | } 29 | 30 | export function traverseForm(form, fn) { 31 | fn(form); 32 | if (form.items) { 33 | form.items.forEach((f) => { 34 | traverseForm(f, fn); 35 | }); 36 | } 37 | 38 | if (form.tabs) { 39 | form.tabs.forEach((tab) => { 40 | if (tab.items) { 41 | tab.items.forEach((f) => { 42 | traverseForm(f, fn); 43 | }); 44 | } 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/lib/schema-defaults.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { select } from './select'; 4 | 5 | chai.should(); 6 | 7 | describe('select.js', () => { 8 | const data = { 9 | 'name': 'Freddy', 10 | 'weapon': { 11 | 'glove': { 12 | 'method': [ 'stab' ] 13 | } 14 | }, 15 | 'frienemies': [ 16 | { 17 | 'name': 'Jason', 18 | 'weapon': { 19 | 'machette': { 20 | 'method': [ 'slash' ] 21 | } 22 | } 23 | }, 24 | { 25 | 'name': 'Ash Williams', 26 | 'weapon': { 27 | 'boomstick': { 28 | 'method': [ 'boom' ] 29 | }, 30 | 'chainsaw': { 31 | 'method': [ 'hack', 'slash', 'lop' ] 32 | } 33 | } 34 | } 35 | ], 36 | }; 37 | 38 | it('should provide a function for getting and setting an object value', () => { 39 | select.should.be.an('function'); 40 | }); 41 | 42 | describe('select', () => { 43 | it('should get a value from an object', () => { 44 | let value = select('frienemies[1].weapon.boomstick.method[0]', data); 45 | value.should.eq('boom'); 46 | }); 47 | 48 | it('should set a value on an object', () => { 49 | let value = select('weapon.glove.method[1]', data, 'slice'); 50 | data.weapon.glove.method.should.be.deep.equal([ 'stab', 'slice' ]); 51 | }); 52 | 53 | it('should create any undefined objects or arrays in the path when setting a value', () => { 54 | let data = {}; 55 | let value = select('property.array[1].value', data, 'something'); 56 | data.should.be.deep.equal({ 57 | 'property': { 58 | 'array': [ 59 | , 60 | { 'value': 'something' } 61 | ] 62 | } 63 | }); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/lib/validate.js: -------------------------------------------------------------------------------- 1 | /* Common code for validating a value against its form and schema definition */ 2 | import tv4 from 'tv4'; 3 | 4 | /** 5 | * Validate a value against its form definition and schema. 6 | * The value should either be of proper type or a string, some type 7 | * coercion is applied. 8 | * 9 | * @param {Object} form A merged form definition, i.e. one with a schema. 10 | * @param {Any} value the value to validate. 11 | * @return {Object} a tv4js result object. 12 | */ 13 | export function validate(form, value) { 14 | if (!form) { 15 | return { valid: true }; 16 | }; 17 | 18 | let schema = form.schema; 19 | if (!schema) { 20 | return { valid: true }; 21 | }; 22 | 23 | // Input of type text and textareas will give us a viewValue of '' 24 | // when empty, this is a valid value in a schema and does not count as something 25 | // that breaks validation of 'required'. But for our own sanity an empty field should 26 | // not validate if it's required. 27 | if (value === '') { 28 | value = undefined; 29 | }; 30 | 31 | // Numbers fields will give a null value, which also means empty field 32 | if (form.type === 'number' && value === null) { 33 | value = undefined; 34 | }; 35 | 36 | // Version 4 of JSON Schema has the required property not on the 37 | // property itself but on the wrapping object. Since we like to test 38 | // only this property we wrap it in a fake object. 39 | let wrap = { type: 'object', 'properties': {}, required: undefined}; 40 | let propName = form.key[form.key.length - 1]; 41 | wrap.properties[propName] = schema; 42 | 43 | if (form.required) { 44 | wrap.required = [ propName ]; 45 | }; 46 | 47 | let valueWrap = {}; 48 | if (typeof value !== 'undefined') { 49 | valueWrap[propName] = value; 50 | }; 51 | 52 | return tv4.validateResult(valueWrap, wrap); 53 | }; 54 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global __dirname */ 2 | const webpack = require('webpack'); 3 | const path = require('path'); 4 | const package = require('./package.json'); 5 | const buildDate = new Date(); 6 | console.log('JSON Schema Form Core v' + package.version); 7 | const plugins = [ 8 | new webpack.BannerPlugin( 9 | 'json-schema-form-core\n' + 10 | '@version ' + package.version + '\n' + 11 | '@date ' + buildDate.toUTCString() + '\n' + 12 | '@link https://github.com/json-schema-form/json-schema-form-core\n' + 13 | '@license MIT\n' + 14 | 'Copyright (c) 2014-' + buildDate.getFullYear() + ' JSON Schema Form'), 15 | /* Minification only occurs if the output is named .min */ 16 | new webpack.optimize.UglifyJsPlugin( 17 | { 18 | include: /\.min\.js$/, 19 | minimize: true 20 | }) 21 | ]; 22 | const library = 'JSONSchemaFormCore'; 23 | 24 | module.exports = { 25 | entry: { 26 | "json-schema-form-core": [ path.join(__dirname, 'src', 'module') ] 27 | }, 28 | output: { 29 | path: path.join(__dirname, 'dist'), 30 | filename: '[name].js', 31 | library: library, 32 | libraryTarget: 'commonjs2' 33 | }, 34 | resolve: { 35 | modules: [ 36 | path.join(__dirname, "src"), 37 | path.join(__dirname, "src/lib"), 38 | "node_modules" 39 | ], 40 | extensions: [ '.ts', '.js' ] 41 | }, 42 | target: 'node', 43 | module: { 44 | rules: [ 45 | { 46 | test: /\.js$/, 47 | use: [{ 48 | loader: 'babel-loader', 49 | options: { 50 | babelrc: false, 51 | presets: [ 52 | ["es2015", { "modules": false }] 53 | ], 54 | plugins: [ "transform-flow-strip-types" ] 55 | } 56 | }], 57 | exclude: /node_modules/ 58 | }, 59 | { 60 | test: /\.ts/, 61 | use: [ 'babel-loader', 'ts-loader' ], 62 | exclude: /node_modules/ 63 | } 64 | ] 65 | }, 66 | devtool: 'source-map', 67 | plugins: plugins 68 | }; 69 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "max-line-length": [ 17 | true, 18 | 140 19 | ], 20 | "member-access": false, 21 | "member-ordering": [ 22 | true, 23 | "static-before-instance", 24 | "variables-before-functions" 25 | ], 26 | "no-arg": true, 27 | "no-bitwise": true, 28 | "no-console": [ 29 | true, 30 | "debug", 31 | "info", 32 | "time", 33 | "timeEnd", 34 | "trace" 35 | ], 36 | "no-construct": true, 37 | "no-debugger": true, 38 | "no-duplicate-variable": true, 39 | "no-empty": false, 40 | "no-eval": true, 41 | "no-inferrable-types": true, 42 | "no-shadowed-variable": true, 43 | "no-string-literal": false, 44 | "no-switch-case-fall-through": true, 45 | "no-trailing-whitespace": true, 46 | "no-unused-expression": true, 47 | "no-use-before-declare": true, 48 | "no-var-keyword": true, 49 | "object-literal-sort-keys": false, 50 | "one-line": [ 51 | true, 52 | "check-open-brace", 53 | "check-catch", 54 | "check-whitespace" 55 | ], 56 | "quotemark": [ 57 | true, 58 | "single" 59 | ], 60 | "radix": true, 61 | "semicolon": [ 62 | "always" 63 | ], 64 | "triple-equals": [ 65 | true, 66 | "allow-null-check" 67 | ], 68 | "typedef-whitespace": [ 69 | true, 70 | { 71 | "call-signature": "nospace", 72 | "index-signature": "nospace", 73 | "parameter": "nospace", 74 | "property-declaration": "nospace", 75 | "variable-declaration": "nospace" 76 | } 77 | ], 78 | "variable-name": false, 79 | "whitespace": [ 80 | true, 81 | "check-branch", 82 | "check-decl", 83 | "check-operator", 84 | "check-separator", 85 | "check-type" 86 | ] 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/lib/canonical-title-map.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import canonicalTitleMap from './canonical-title-map'; 4 | 5 | chai.should(); 6 | 7 | describe('canonical-title-map.js', () => { 8 | it('should hold a normalisation function for enums and titleMaps to generate titleMaps', () => { 9 | canonicalTitleMap.should.be.an('function'); 10 | }); 11 | 12 | describe('canonicalTitleMap', () => { 13 | const enumeration = [ 'Joker', 'Riddler', 'Bane', 'Penguin', 'Cat Woman' ]; 14 | const titlemap = [ 15 | { 'name': 'Joker', 'value': 'Joker' }, 16 | { 'name': 'Riddler', 'value': 'Riddler' }, 17 | { 'name': 'Bane', 'value': 'Bane' }, 18 | { 'name': 'Penguin', 'value': 'Penguin' }, 19 | { 'name': 'Cat Woman', 'value': 'Cat Woman' } 20 | ]; 21 | const titlemapObj = { 22 | 'Joker': 'Joker', 23 | 'Riddler': 'Riddler', 24 | 'Bane': 'Bane', 25 | 'Penguin': 'Penguin', 26 | 'Cat Woman': 'Cat Woman' 27 | }; 28 | 29 | it('should return a titleMap for a titleMap object with original enum', () => { 30 | let result = canonicalTitleMap(titlemapObj, enumeration); 31 | result.should.be.deep.equal(titlemap); 32 | }); 33 | 34 | it('should return a titleMap for a titleMap list with original enum', () => { 35 | let result = canonicalTitleMap(titlemap, enumeration); 36 | result.should.be.deep.equal(titlemap); 37 | }); 38 | 39 | it('should return a titleMap for a titleMap object without enum', () => { 40 | let result = canonicalTitleMap(titlemapObj); 41 | result.should.be.deep.equal(titlemap); 42 | }); 43 | 44 | it('should return a titleMap for a titleMap list without enum', () => { 45 | let result = canonicalTitleMap(titlemap); 46 | result.should.be.deep.equal(titlemap); 47 | }); 48 | 49 | it('should return a titleMap for a titleMap object with original enum, returning "undefined" name if the enum value is not found', () => { 50 | enumeration.push('Mr Freeze'); 51 | let result = canonicalTitleMap(titlemapObj, enumeration); 52 | titlemap.push({ 53 | "name": undefined, 54 | "value": "Mr Freeze" 55 | }) 56 | result.should.be.deep.equal(titlemap); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/lib/select.js: -------------------------------------------------------------------------------- 1 | import * as sfPath from './sf-path'; 2 | 3 | const numRe = /^\d+$/; 4 | 5 | /** 6 | * @description 7 | * Utility method to access deep properties without 8 | * throwing errors when things are not defined. 9 | * Can also set a value in a deep structure, creating objects when missing 10 | * ex. 11 | * var foo = Select('address.contact.name',obj) 12 | * Select('address.contact.name',obj,'Leeroy') 13 | * 14 | * @param {string} projection A dot path to the property you want to get/set 15 | * @param {object} obj (optional) The object to project on, defaults to 'this' 16 | * @param {Any} valueToSet (opional) The value to set, if parts of the path of 17 | * the projection is missing empty objects will be created. 18 | * @returns {Any|undefined} returns the value at the end of the projection path 19 | * or undefined if there is none. 20 | */ 21 | export function select(projection, obj, valueToSet) { 22 | if (!obj) { 23 | obj = this; 24 | }; 25 | 26 | // Support [] array syntax 27 | let parts = typeof projection === 'string' ? sfPath.parse(projection) : projection; 28 | 29 | if (typeof valueToSet !== 'undefined' && parts.length === 1) { 30 | // special case, just setting one variable 31 | obj[parts[0]] = valueToSet; 32 | 33 | return obj; 34 | }; 35 | 36 | if (typeof valueToSet !== 'undefined' && 37 | typeof obj[parts[0]] === 'undefined') { 38 | // We need to look ahead to check if array is appropriate 39 | obj[parts[0]] = parts.length > 2 && numRe.test(parts[1]) ? [] : {}; 40 | }; 41 | 42 | let value = obj[parts[0]]; 43 | 44 | for (let i = 1; i < parts.length; i++) { 45 | // Special case: We allow JSON Form syntax for arrays using empty brackets 46 | // These will of course not work here so we exit if they are found. 47 | if (parts[i] === '') { 48 | return undefined; 49 | }; 50 | 51 | if (typeof valueToSet !== 'undefined') { 52 | if (i === parts.length - 1) { 53 | // last step. Let's set the value 54 | value[parts[i]] = valueToSet; 55 | return valueToSet; 56 | } 57 | else { 58 | // Make sure to create new objects on the way if they are not there. 59 | // We need to look ahead to check if array is appropriate 60 | let tmp = value[parts[i]]; 61 | 62 | if (typeof tmp === 'undefined' || tmp === null) { 63 | tmp = numRe.test(parts[i + 1]) ? [] : {}; 64 | value[parts[i]] = tmp; 65 | }; 66 | 67 | value = tmp; 68 | }; 69 | } 70 | else if (value) { 71 | // Just get nex value. 72 | value = value[parts[i]]; 73 | }; 74 | }; 75 | 76 | return value; 77 | }; 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-schema-form-core", 3 | "version": "1.0.0-alpha.5", 4 | "description": "JSON-Schema and JSON-UI-Schema utilities for form generation.", 5 | "main": "dist/json-schema-form-core.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "watch": "webpack --watch", 9 | "dist-untested": "webpack --config webpack.config.dist.js", 10 | "lint": "eslint src/**/*.js", 11 | "tslint": "tslint \"src/**/*.ts\"", 12 | "test": "mocha --opts .mocha", 13 | "testdoc": "mocha --opts .mocha && mocha --opts .mocha.md > docs/test.md" 14 | }, 15 | "author": "json-schema-form", 16 | "contributors": [ 17 | "David Jensen (https://github.com/davidlgj)", 18 | "Marcel J Bennett (https://github.com/Anthropic)" 19 | ], 20 | "license": "MIT", 21 | "licenses": [ 22 | { 23 | "type": "MIT", 24 | "url": "https://raw.githubusercontent.com/json-schema-form/json-schema-form-core/master/LICENSE" 25 | } 26 | ], 27 | "homepage": "https://github.com/json-schema-form/json-schema-form-core#readme", 28 | "bugs": { 29 | "url": "https://github.com/json-schema-form/json-schema-form-core/issues" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "http://github.com/json-schema-form/json-schema-form-core.git" 34 | }, 35 | "keywords": [ 36 | "angular-schema-form", 37 | "json-schema-form-core", 38 | "json-schema-form", 39 | "json-ui-schema", 40 | "json-schema" 41 | ], 42 | "dependencies": { 43 | "tv4": "^1.2.7" 44 | }, 45 | "devDependencies": { 46 | "@types/chai": "^3.4.34", 47 | "@types/mocha": "^2.2.33", 48 | "@types/node": "^6.0.42", 49 | "babel-core": "^6.18.2", 50 | "babel-loader": "^6.2.9", 51 | "babel-plugin-syntax-async-functions": "^6.13.0", 52 | "babel-plugin-transform-async-to-generator": "^6.22.0", 53 | "babel-plugin-transform-flow-strip-types": "^6.21.0", 54 | "babel-polyfill": "^6.20.0", 55 | "babel-preset-es2015": "^6.18.0", 56 | "babel-preset-latest": "^6.16.0", 57 | "babel-preset-stage-0": "^6.22.0", 58 | "babel-register": "^6.18.0", 59 | "chai": "^3.5.0", 60 | "eslint": "^3.19.0", 61 | "eslint-config-google": "^0.7.1", 62 | "json-refs": "^2.1.6", 63 | "karma": "^1.3.0", 64 | "karma-babel-preprocessor": "^6.0.1", 65 | "karma-chai-sinon": "^0.1.5", 66 | "karma-coverage": "^1.1.1", 67 | "karma-growler-reporter": "0.0.2", 68 | "karma-mocha": "^1.3.0", 69 | "karma-phantomjs-launcher": "^1.0.2", 70 | "karma-webpack": "^1.8.1", 71 | "mocha": "^3.2.0", 72 | "objectpath": "^1.2.1", 73 | "sinon": "^1.17.7", 74 | "sinon-chai": "^2.8.0", 75 | "ts-loader": "^1.2.2", 76 | "ts-node": "^1.2.1", 77 | "tslint": "^4.0.2", 78 | "tslint-loader": "^3.2.1", 79 | "typescript": "~2.0.3", 80 | "tv4": "^1.2.7", 81 | "webpack": "^2.2.1" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Core 2 | [![Gitter](https://img.shields.io/badge/GITTER-JOIN%20CHAT%20%E2%86%92-ff69b4.svg?style=flat-square)](https://gitter.im/json-schema-form/angular-schema-form?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | [![Build Status](https://travis-ci.org/json-schema-form/json-schema-form-core.svg?branch=development)](https://travis-ci.org/json-schema-form/json-schema-form-core) 4 | 5 | Core library for working with JSON-Schema with a UI-Schema (Form) definition that doesn't depend on a framework. 6 | 7 | This library, through the use of its merge module, combines the schema and ui-schema 8 | into a canonical schema for use by its services and external libraries. 9 | 10 | You **DO NOT** use this file in addition to Angular Schema Form, it is embedded at 11 | build into any frameworks using it. 12 | 13 | ## Work-In-Progress! 14 | There is [test output](docs/test.md) that forms some super basic documentation 15 | and I intend to expand them much further to the point of almost being 16 | useful before I create a proper API and document that. 17 | 18 | ## Keeping Track 19 | After changing to Webpack 2, this library now includes a detailed version 20 | header which is passed through into `Angular Schema Form` and also the `Bootstrap` decorator bundle 21 | 22 | ```javascript 23 | /*! 24 | * json-schema-form-core 25 | * @version 1.0.0-alpha.5 26 | * @date Sat, 14 Jan 2017 08:08:15 GMT 27 | * @link https://github.com/json-schema-form/json-schema-form-core 28 | * @license MIT 29 | * Copyright (c) 2014-2017 JSON Schema Form 30 | */ 31 | ``` 32 | 33 | ## Contributing / Plans 34 | The main contributions we need to the core at the moment are related to both the migration 35 | of `Angular Schema Form` features to the core (like templates/builders) and the addition 36 | of an API for use by ASF (Angular) and RSF (React) libraries. 37 | 38 | Please contact @Anthropic via our [Gitter](https://gitter.im/json-schema-form/angular-schema-form) if you wish to get involved. 39 | 40 | ## Testing it 41 | 42 | ### With Angular Schema Form 43 | There is a branch in angular-schema-form called `feature/webpack-babel` that integrates the core. 44 | To use it roughly follow these steps: 45 | 46 | * Clone angular-schema-form to a **sibling** directory and switch to branch `feature/webpack-babel` 47 | * `npm install` to install dependencies 48 | * `npm run build` to build with the core. 49 | * Use dist/angular-schema-form.js, now with the core from this folder. *No need to also load ObjectPath since it is already included* 50 | 51 | ### With Mocha tests 52 | Tests are written in mocha + chai and run trough `npm test`. 53 | 54 | When the command `npm run testdoc` is run instead, the tests will also generate a readable 55 | `markdown` file [test.md](docs/test.md) to document elements of the library. 56 | 57 | ## Notes 58 | * ObjectPath is bundled with json-schema-form-core 59 | * angular-schema-form bundles json-schema-form-core so the user doesn't have to include it as an dependency. 60 | * The code for not using ObjectPath on Angular 1.2 is removed. Could maybe be fixed but I (davidlgj) strongly believe its time to drop Angular 1.2 support since it complicates validation code as well. 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | - [How to setup to develop](#setup) 4 | - [How to build the package](#build) 5 | - [Working on the libraries](#work) 6 | - [PR requirements](#requirements) 7 | 8 | 9 | ## How to setup to develop 10 | To get started clone all json-schema-form library repos into sibling folders. 11 | json-schema-form-core 12 | angular-schema-form 13 | angular-schema-form-bootstrap 14 | angular-schema-form-material 15 | 16 | Once cloned each repo has npm commands for assisting development 17 | ```bash 18 | # Run unit tests 19 | npm run test 20 | 21 | # Run the build 22 | npm run build 23 | 24 | # Run the build and minify 25 | npm run dist # not in json-schema-form-core 26 | 27 | # Run the build and watch for changes 28 | npm run watch 29 | ``` 30 | 31 | 32 | ## How to build the package 33 | When working on `angular-schema-form` running the `npm build` will look for a sibling 34 | folder when importing `json-schema-form-core`. This allows you to work on bugs or 35 | issues that require work on both libraries simultaneously. 36 | 37 | This is set up for bootstrap and material design decorators also. 38 | 39 | The bootstrap repo is required to build `angular-schema-form` with the bootstrap 40 | decorator bundle distribution. 41 | 42 | 43 | ## Working on the libraries 44 | When I work on the libraries I use a multi-tab console tool like 45 | Terminator (Linux) or ConEmu (Windows) 46 | 47 | Run each of the following in a separate tab: 48 | ```bash 49 | json-schema-form-core> npm run watch 50 | json-schema-form-core> npm run test 51 | angular-schema-form> npm run watch 52 | angular-schema-form> npm run test 53 | ``` 54 | This will re-compile all the libraries after changes that affect them which 55 | then runs the related tests. 56 | 57 | A static file web server is required to run the examples, but the example 58 | can be used to run saved gist of the example app. It can help to add a model 59 | to one of the example app json files to test with. 60 | 61 | 62 | ## PR requirements 63 | We love contributions! 64 | 65 | **Please base any merge request on the _development_ branch instead of _master_.** 66 | 67 | The reason for this is that we're trying to use 68 | [git flow](http://danielkummer.github.io/git-flow-cheatsheet/), and it makes merging your pull 69 | request a heck of a lot easier for us. 70 | 71 | Please **avoid including anything from the `dist/`** directory as that can make merging harder, and we 72 | always generate these files when we make a new release. 73 | 74 | **If you are using this library with another repo other than *ASF* it must still maintain compatibility 75 | with *ASF* which is why the instructions for *ASF* are above**, eventually there will be enough test 76 | cases that you wont need to test both, but we aren't there yet. 77 | 78 | **The bootstrap decorator, has been moved to it's own repo. It's here [github.com/json-schema-form/angular-schema-form-bootstrap](https://github.com/json-schema-form/angular-schema-form-bootstrap)** 79 | 80 | Feel free to submit issues on the main repo anyway though. 81 | 82 | If its a new field type consider making it an add-on instead, 83 | especially if it has external dependencies. See [extending Schema Form documentation.](docs/extending.md) 84 | 85 | With new features we love to see updates to the docs as well as tests, that makes it super 86 | easy and fast for us to merge it! 87 | 88 | Also consider running any code through the code style checker [jscs](https://github.com/mdevils/node-jscs) 89 | (or even better use it in your editor) with preset set to `google`. You can also 90 | use `gulp jscs` to check your code. I hope to set up ESLint in the not too distant future. 91 | -------------------------------------------------------------------------------- /src/lib/resolve.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { jsonref } from './resolve'; 4 | 5 | chai.should(); 6 | 7 | describe('resolve.js', () => { 8 | const schema = { 9 | "id": "http://some.site.somewhere/entry-schema#", 10 | "$schema": "http://json-schema.org/draft-04/schema#", 11 | "description": "schema for an fstab entry", 12 | "type": "object", 13 | "required": [ "storage" ], 14 | "properties": { 15 | "storage": { 16 | "type": "object", 17 | "oneOf": [ 18 | { "$ref": "#/definitions/diskDevice" }, 19 | { "$ref": "#/definitions/diskUUID" }, 20 | { "$ref": "#/definitions/nfs" }, 21 | { "$ref": "#/definitions/tmpfs" } 22 | ] 23 | } 24 | }, 25 | "definitions": { 26 | "diskDevice": { 27 | "properties": { 28 | "type": { "enum": [ "disk" ] }, 29 | "device": { 30 | "type": "string", 31 | "pattern": "^/dev/[^/]+(/[^/]+)*$" 32 | } 33 | }, 34 | "required": [ "type", "device" ], 35 | "additionalProperties": false 36 | }, 37 | "diskUUID": { 38 | "properties": { 39 | "type": { "enum": [ "disk" ] }, 40 | "label": { 41 | "type": "string", 42 | "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" 43 | } 44 | }, 45 | "required": [ "type", "label" ], 46 | "additionalProperties": false 47 | }, 48 | "nfs": { 49 | "properties": { 50 | "type": { "enum": [ "nfs" ] }, 51 | "remotePath": { 52 | "type": "string", 53 | "pattern": "^(/[^/]+)+$" 54 | }, 55 | "server": { 56 | "type": "string", 57 | "oneOf": [ 58 | { "format": "host-name" }, 59 | { "format": "ipv4" }, 60 | { "format": "ipv6" } 61 | ] 62 | } 63 | }, 64 | "required": [ "type", "server", "remotePath" ], 65 | "additionalProperties": false 66 | }, 67 | "tmpfs": { 68 | "properties": { 69 | "type": { "enum": [ "tmpfs" ] }, 70 | "sizeInMB": { 71 | "type": "integer", 72 | "minimum": 16, 73 | "maximum": 512 74 | } 75 | }, 76 | "required": [ "type", "sizeInMB" ], 77 | "additionalProperties": false 78 | } 79 | } 80 | }; 81 | 82 | const remote = { 83 | "id": "http://some.site.somewhere/entry-schema#", 84 | "$schema": "http://json-schema.org/draft-04/schema#", 85 | "description": "test remote schema resolution", 86 | "type": "object", 87 | "properties": { 88 | "relative": { "$ref": "https://raw.githubusercontent.com/json-schema-org/json-schema-org.github.io/master/geo" } 89 | } 90 | }; 91 | 92 | it('should contain a function for resolving relative & local references', () => { 93 | jsonref.should.be.an('function'); 94 | }) 95 | 96 | describe('jsonref', () => { 97 | 98 | it('should resolve relative json-ref via promise', (done) => { 99 | jsonref(schema) 100 | .then((resolved) => { 101 | resolved.properties.storage.oneOf[0].properties.should.have.property('device'); 102 | resolved.properties.storage.oneOf[0].properties.should.equal( 103 | resolved.definitions.diskDevice.properties 104 | ); 105 | resolved.properties.storage.oneOf[3].properties.should.equal( 106 | resolved.definitions.tmpfs.properties 107 | ); 108 | done(); 109 | }) 110 | .catch((error) => { 111 | done(error); 112 | }); 113 | }); 114 | 115 | it('should resolve relative json-ref via callback', (done) => { 116 | jsonref(schema, function(error, resolved) { 117 | if (error) done(error); 118 | resolved.properties.storage.oneOf[0].properties.should.have.property('device'); 119 | done(); 120 | }); 121 | }); 122 | 123 | //I believe this only fails in phantomjs due to https://github.com/ariya/phantomjs/issues/11195 124 | it('should resolve remote json-ref via callback', (done) => { 125 | jsonref(remote, function(error, resolved) { 126 | if (error) done(error); 127 | //resolved.properties.relative.latitude.type.should.equal('number'); 128 | done(); 129 | }); 130 | }); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /src/lib/merge.js: -------------------------------------------------------------------------------- 1 | import {stringify, parse} from './sf-path'; 2 | import { defaultForm, createDefaults } from './schema-defaults'; 3 | import canonicalTitleMap from './canonical-title-map'; 4 | 5 | // export function merge(schema, form, schemaDefaultTypes, ignore, options, readonly, asyncTemplates) { 6 | export function merge(lookup, form, typeDefaults=createDefaults(), ignore, options, readonly, asyncTemplates) { 7 | let formItems = []; 8 | let formItemRest = []; 9 | form = form || []; 10 | let idx = form.indexOf('*'); 11 | options = options || {}; 12 | let stdForm = {}; 13 | 14 | let idxRest = form.indexOf('...'); 15 | if(typeof lookup === 'object' && lookup.hasOwnProperty('properties')) { 16 | readonly = readonly || lookup.readonly || lookup.readOnly; 17 | stdForm = defaultForm(lookup, typeDefaults, ignore, options); 18 | 19 | let defaultFormLookup = stdForm.lookup; 20 | 21 | lookup = defaultFormLookup || lookup; 22 | formItems = formItems.concat(stdForm.form); 23 | }; 24 | 25 | if (idx !== -1) { 26 | form = form.slice(0, idx).concat(formItems).concat(form.slice(idx + 1)); 27 | } 28 | 29 | //simple case, we have a "...", just put the formItemRest there 30 | if (stdForm.form && idxRest !== -1) { 31 | let formKeys = form.map(function(obj) { 32 | if (typeof obj === 'string'){ 33 | return obj; 34 | } 35 | else if (obj.key) { 36 | return obj.key; 37 | }; 38 | }).filter(function(element) { 39 | return element !== undefined; 40 | }); 41 | 42 | formItemRest = formItemRest.concat( 43 | stdForm.form.map(function(obj) { 44 | let isInside = formKeys.indexOf(obj.key[0]) !== -1; 45 | if (!isInside) { 46 | return obj; 47 | }; 48 | }) 49 | .filter(function(element) { 50 | return element !== undefined; 51 | }) 52 | ); 53 | }; 54 | 55 | if (idxRest !== -1) { 56 | form = form.slice(0, idxRest).concat(formItemRest).concat(form.slice(idxRest + 1)); 57 | }; 58 | 59 | // ok let's merge! 60 | // We look at the supplied form and extend it with schema standards 61 | return form.map((obj) => { 62 | // handle the shortcut with just a name 63 | if (typeof obj === 'string') { 64 | obj = { key: obj }; 65 | } 66 | 67 | if (obj.key) { 68 | if (typeof obj.key === 'string') { 69 | obj.key = parse(obj.key); 70 | } 71 | } 72 | 73 | // If it has a titleMap make sure it's a list 74 | if (obj.titleMap) { 75 | obj.titleMap = canonicalTitleMap(obj.titleMap); 76 | } 77 | 78 | // extend with std form from schema. 79 | if (obj.key) { 80 | const strid = stringify(obj.key); 81 | if (lookup[strid]) { 82 | const schemaDefaults = lookup[strid]; 83 | if (schemaDefaults) { 84 | Object.keys(schemaDefaults).forEach((attr) => { 85 | if (obj[attr] === undefined) { 86 | obj[attr] = schemaDefaults[attr]; 87 | } 88 | }); 89 | } 90 | } 91 | } 92 | 93 | // Are we inheriting readonly? 94 | if (readonly === true) { // Inheriting false is not cool. 95 | obj.readonly = true; 96 | } 97 | 98 | // if it's a type with items, merge 'em! 99 | if (obj.items) { 100 | obj.items = merge(lookup, obj.items, typeDefaults, ignore, options, obj.readonly, asyncTemplates); 101 | } 102 | 103 | // if its has tabs, merge them also! 104 | if (obj.tabs) { 105 | obj.tabs.forEach((tab) => { 106 | if (tab.items) { 107 | tab.items = merge(lookup, tab.items, typeDefaults, ignore, options, obj.readonly, asyncTemplates); 108 | } 109 | }); 110 | } 111 | 112 | // Special case: checkbox 113 | // Since have to ternary state we need a default 114 | if (obj.type === 'checkbox') { 115 | // Check for schema property, as the checkbox may be part of the explicitly defined form 116 | if (obj.schema === undefined) { 117 | obj.schema = { default: false }; 118 | } 119 | else if (obj.schema['default'] === undefined) { 120 | obj.schema['default'] = false; 121 | }; 122 | }; 123 | 124 | // Special case: template type with tempplateUrl that's needs to be loaded before rendering 125 | // TODO: this is not a clean solution. Maybe something cleaner can be made when $ref support 126 | // is introduced since we need to go async then anyway 127 | if (asyncTemplates && obj.type === 'template' && !obj.template && obj.templateUrl) { 128 | asyncTemplates.push(obj); 129 | }; 130 | 131 | return obj; 132 | }); 133 | } 134 | -------------------------------------------------------------------------------- /src/lib/merge.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { defaultForm, createDefaults } from './schema-defaults'; 4 | import { merge } from './merge'; 5 | 6 | chai.should(); 7 | 8 | // describe('merge.js', () => { 9 | // it('should hold functions for generating a default form schema from defaults it creates', () => { 10 | // defaultForm.should.be.an('function'); 11 | // createDefaults.should.be.an('function'); 12 | // }); 13 | // 14 | // describe('createDefaults', () => { 15 | // it('should create default rules', () => { 16 | // const rules = createDefaults(); 17 | // rules.should.be.an('object'); 18 | // }); 19 | // }); 20 | // }); 21 | 22 | describe('merge.js', () => { 23 | const schema = { 24 | "type": "object", 25 | "properties": { 26 | "name": { 27 | "title": "Name", 28 | "description": "Gimme yea name lad", 29 | "type": "string" 30 | }, 31 | "shoe": { 32 | "title": "Shoes", 33 | "description": "Shoe details", 34 | "type": "array", 35 | "items": { 36 | "type": "object", 37 | "properties": { 38 | "brand": {"type":"string"} 39 | } 40 | } 41 | }, 42 | "gender": { 43 | "readOnly": true, 44 | "title": "Choose", 45 | "type": "string", 46 | "enum": [ 47 | "undefined", 48 | "null", 49 | "NaN", 50 | ] 51 | } 52 | } 53 | }; 54 | 55 | const stdForm = defaultForm(schema, createDefaults()); 56 | 57 | it('should contain a function for merging schema and form definitions', () => { 58 | merge.should.be.an('function'); 59 | }) 60 | 61 | describe('merge', () => { 62 | it('should handle a schema lookup or schema for first argument', () => { 63 | merge(stdForm.lookup, [ 'name', 'shoe', 'gender' ]).should.be.deep.equal(stdForm.form); 64 | merge(schema, [ '*' ]).should.be.deep.equal(stdForm.form); 65 | }); 66 | 67 | it('should handle a wildcard * in the form definition', () => { 68 | merge(schema, [ '*' ]).should.be.deep.equal(stdForm.form); 69 | }); 70 | 71 | it('should not handle a wildcard * if the schema is a lookup and cannot be inserted', () => { 72 | merge(stdForm.lookup, [ '*' ]).should.not.be.deep.equal(stdForm.form); 73 | }); 74 | 75 | it('should handle a rest "..." key in the form definition', () => { 76 | merge(schema, [ '...', 'gender' ]).should.be.deep.equal(stdForm.form); 77 | }); 78 | 79 | it('should not handle a rest "..." key in the form definition when the schema is a lookup and cannot be inserted', () => { 80 | merge(stdForm.lookup, [ '...', 'gender' ]).should.not.be.deep.equal(stdForm.form); 81 | }); 82 | 83 | it('should combine a schema and form definition, regardless of order', () => { 84 | merge(schema, [ 'name', 'shoe', 'gender' ]).should.be.deep.equal(stdForm.form); 85 | merge(schema, [ 'gender' ]).should.be.deep.equal([stdForm.form[2]]); 86 | merge(schema, [ 'gender', 'name' ]).should.be.deep.equal([stdForm.form[2],stdForm.form[0]]); 87 | }); 88 | 89 | 90 | it('should allow items that are not in the schema', () => { 91 | merge(schema, [ '*', { type:'fieldset' }]).should.be.deep.equal(stdForm.form.concat([{ type:'fieldset' }])); 92 | }); 93 | 94 | it('should translate "readOnly" in schema to "readonly" on the merged form defintion', () => { 95 | var merged = merge(schema, [ 'gender' ]); 96 | merged[0].should.have.property('readonly'); 97 | merged[0].readonly.should.eq(true) 98 | }); 99 | 100 | it('should push readOnly in schema down into objects and arrays', () => { 101 | let subschema = { 102 | 'type': 'object', 103 | 'readOnly': true, 104 | 'properties': { 105 | 'sub': { 106 | 'type': 'object', 107 | 'properties': { 108 | 'array': { 109 | 'type': 'array', 110 | 'items': { 111 | 'type': 'object', 112 | 'properties': { 113 | 'foo': { 114 | 'type': 'string' 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | }; 123 | 124 | var merged = merge(subschema, [ '*' ]); 125 | 126 | //sub 127 | merged[0].should.have.property('readonly'); 128 | merged[0].readonly.should.eq(true); 129 | 130 | //array 131 | merged[0].items[0].should.have.property('readonly'); 132 | merged[0].items[0].readonly.should.eq(true); 133 | 134 | //array items 135 | merged[0].items[0].items[0].should.have.property('readonly'); 136 | merged[0].items[0].items[0].readonly.should.eq(true); 137 | }); 138 | 139 | it('should push readonly in form def down into objects and arrays', () => { 140 | let subschema = { 141 | 'type': 'object', 142 | 'properties': { 143 | 'sub': { 144 | 'type': 'object', 145 | 'properties': { 146 | 'array': { 147 | 'type': 'array', 148 | 'items': { 149 | 'type': 'object', 150 | 'properties': { 151 | 'foo': { 152 | 'type': 'string' 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | }; 161 | 162 | var merged = merge(subschema, [{key: 'sub', readonly: true}]); 163 | 164 | // sub 165 | merged[0].should.have.property('readonly'); 166 | merged[0].readonly.should.eq(true); 167 | 168 | // array 169 | merged[0].items[0].should.have.property('readonly'); 170 | merged[0].items[0].readonly.should.eq(true); 171 | 172 | // array items 173 | merged[0].items[0].items[0].should.have.property('readonly'); 174 | merged[0].items[0].items[0].readonly.should.eq(true); 175 | }); 176 | 177 | it('should add x-schema-form properties', () => { 178 | let subschema = { 179 | "type": "object", 180 | "properties": { 181 | "comment": { 182 | "type": "string", 183 | "title": "Comment", 184 | "x-schema-form": { 185 | "type": "textarea", 186 | "placeholder": "Don't hold back" 187 | } 188 | } 189 | } 190 | }; 191 | 192 | var merged = merge(subschema, [ '*' ]); 193 | 194 | merged.should.deep.eq( 195 | [ 196 | { 197 | "title": "Comment", 198 | "schema": { 199 | "type": "string", 200 | "title":"Comment", 201 | "x-schema-form": { 202 | "type": "textarea", 203 | "placeholder": "Don't hold back" 204 | } 205 | }, 206 | "ngModelOptions": {}, 207 | "key": [ "comment" ], 208 | "type": "textarea", 209 | "placeholder": "Don't hold back" 210 | } 211 | ] 212 | ) 213 | }); 214 | }); 215 | }); 216 | -------------------------------------------------------------------------------- /src/lib/select.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { describe, it} from 'mocha'; 3 | import { defaultForm, createDefaults } from './schema-defaults'; 4 | 5 | chai.should(); 6 | 7 | describe('schema-defaults.js', () => { 8 | it('should hold functions for generating a default form schema from defaults it creates', () => { 9 | defaultForm.should.be.an('function'); 10 | createDefaults.should.be.an('function'); 11 | }); 12 | 13 | describe('createDefaults', () => { 14 | it('should create default rules', () => { 15 | const rules = createDefaults(); 16 | rules.should.be.an('object'); 17 | }); 18 | }); 19 | 20 | describe('defaultForm', () => { 21 | it('should generate default form def from a schema', () => { 22 | const schema = { 23 | 'type': 'object', 24 | 'properties': { 25 | 'name': { 26 | 'title': 'Name', 27 | 'description': 'Gimme yea name lad', 28 | 'type': 'string' 29 | }, 30 | 'gender': { 31 | 'title': 'Choose', 32 | 'type': 'string', 33 | 'enum': [ 34 | 'undefined', 35 | 'null', 36 | 'NaN', 37 | ] 38 | }, 39 | 'overEighteen': { 40 | 'title': 'Are you over 18 years old?', 41 | 'type': 'boolean', 42 | 'default': false 43 | }, 44 | 'attributes': { 45 | 'type': 'object', 46 | 'required': [ 'eyecolor' ], 47 | 'properties': { 48 | 'eyecolor': { 'type': 'string', 'title': 'Eye color' }, 49 | 'haircolor': { 'type': 'string', 'title': 'Hair color' }, 50 | 'shoulders': { 51 | 'type': 'object', 52 | 'title': 'Shoulders', 53 | 'properties': { 54 | 'left': { 'type': 'string' }, 55 | 'right': { 'type': 'string' }, 56 | } 57 | } 58 | } 59 | } 60 | } 61 | }; 62 | 63 | const form = [ 64 | { 65 | 'title': 'Name', 66 | 'description': 'Gimme yea name lad', 67 | 'schema': { 68 | 'title': 'Name', 69 | 'description': 'Gimme yea name lad', 70 | 'type': 'string' 71 | }, 72 | 'ngModelOptions': {}, 73 | 'key': [ 74 | 'name' 75 | ], 76 | 'type': 'text' 77 | }, 78 | { 79 | 'title': 'Choose', 80 | 'schema': { 81 | 'title': 'Choose', 82 | 'type': 'string', 83 | 'enum': [ 84 | 'undefined', 85 | 'null', 86 | 'NaN' 87 | ] 88 | }, 89 | 'ngModelOptions': {}, 90 | 'key': [ 91 | 'gender' 92 | ], 93 | 'type': 'select', 94 | 'titleMap': [ 95 | { 96 | 'name': 'undefined', 97 | 'value': 'undefined' 98 | }, 99 | { 100 | 'name': 'null', 101 | 'value': 'null' 102 | }, 103 | { 104 | 'name': 'NaN', 105 | 'value': 'NaN' 106 | } 107 | ] 108 | }, 109 | { 110 | 'title': 'Are you over 18 years old?', 111 | 'schema': { 112 | 'title': 'Are you over 18 years old?', 113 | 'type': 'boolean', 114 | 'default': false 115 | }, 116 | 'ngModelOptions': {}, 117 | 'key': [ 118 | 'overEighteen' 119 | ], 120 | 'type': 'checkbox' 121 | }, 122 | { 123 | 'title': 'attributes', 124 | 'schema': { 125 | 'type': 'object', 126 | 'required': [ 127 | 'eyecolor' 128 | ], 129 | 'properties': { 130 | 'eyecolor': { 131 | 'type': 'string', 132 | 'title': 'Eye color' 133 | }, 134 | 'haircolor': { 135 | 'type': 'string', 136 | 'title': 'Hair color' 137 | }, 138 | 'shoulders': { 139 | 'type': 'object', 140 | 'title': 'Shoulders', 141 | 'properties': { 142 | 'left': { 143 | 'type': 'string' 144 | }, 145 | 'right': { 146 | 'type': 'string' 147 | } 148 | } 149 | } 150 | } 151 | }, 152 | 'ngModelOptions': {}, 153 | 'key': [ 154 | 'attributes' 155 | ], 156 | 'type': 'fieldset', 157 | 'items': [ 158 | { 159 | 'title': 'Eye color', 160 | 'required': true, 161 | 'schema': { 162 | 'type': 'string', 163 | 'title': 'Eye color' 164 | }, 165 | 'ngModelOptions': {}, 166 | 'key': [ 167 | 'attributes', 168 | 'eyecolor' 169 | ], 170 | 'type': 'text' 171 | }, 172 | { 173 | 'title': 'Hair color', 174 | 'schema': { 175 | 'type': 'string', 176 | 'title': 'Hair color' 177 | }, 178 | 'ngModelOptions': {}, 179 | 'key': [ 180 | 'attributes', 181 | 'haircolor' 182 | ], 183 | 'type': 'text' 184 | }, 185 | { 186 | 'title': 'Shoulders', 187 | 'schema': { 188 | 'type': 'object', 189 | 'title': 'Shoulders', 190 | 'properties': { 191 | 'left': { 192 | 'type': 'string' 193 | }, 194 | 'right': { 195 | 'type': 'string' 196 | } 197 | } 198 | }, 199 | 'ngModelOptions': {}, 200 | 'key': [ 201 | 'attributes', 202 | 'shoulders' 203 | ], 204 | 'type': 'fieldset', 205 | 'items': [ 206 | { 207 | 'title': 'left', 208 | 'schema': { 209 | 'type': 'string' 210 | }, 211 | 'ngModelOptions': {}, 212 | 'key': [ 213 | 'attributes', 214 | 'shoulders', 215 | 'left' 216 | ], 217 | 'type': 'text' 218 | }, 219 | { 220 | 'title': 'right', 221 | 'schema': { 222 | 'type': 'string' 223 | }, 224 | 'ngModelOptions': {}, 225 | 'key': [ 226 | 'attributes', 227 | 'shoulders', 228 | 'right' 229 | ], 230 | 'type': 'text' 231 | } 232 | ] 233 | } 234 | ] 235 | } 236 | ]; 237 | 238 | const f = defaultForm(schema, createDefaults()); 239 | f.form.should.be.deep.equal(form); 240 | }); 241 | }); 242 | }); 243 | -------------------------------------------------------------------------------- /src/lib/schema-defaults.js: -------------------------------------------------------------------------------- 1 | import { stringify } from './sf-path'; 2 | import canonicalTitleMap from './canonical-title-map'; 3 | 4 | /* Utils */ 5 | const stripNullType = (type) => { 6 | if (Array.isArray(type) && type.length === 2) { 7 | if (type[0] === 'null') { 8 | return type[1]; 9 | }; 10 | if (type[1] === 'null') { 11 | return type[0]; 12 | }; 13 | }; 14 | return type; 15 | }; 16 | 17 | // Creates an default titleMap list from an enum, i.e. a list of strings. 18 | const enumToTitleMap = (enm) => { 19 | const titleMap = []; // canonical titleMap format is a list. 20 | enm.forEach((name) => { 21 | titleMap.push({ name, value: name }); 22 | }); 23 | return titleMap; 24 | }; 25 | 26 | /** 27 | * Creates a default form definition from a schema. 28 | */ 29 | export function defaultFormDefinition(schemaTypes, name, schema, options) { 30 | const rules = schemaTypes[stripNullType(schema.type)]; 31 | if (rules) { 32 | let def; 33 | // We give each rule a possibility to recurse it's children. 34 | const innerDefaultFormDefinition = (childName, childSchema , childOptions) => 35 | defaultFormDefinition(schemaTypes, childName, childSchema, childOptions); 36 | for (let i = 0; i < rules.length; i++) { 37 | def = rules[i](name, schema, options, innerDefaultFormDefinition); 38 | 39 | // first handler in list that actually returns something is our handler! 40 | if (def) { 41 | 42 | // Do we have form defaults in the schema under the x-schema-form-attribute? 43 | if (def.schema['x-schema-form']) { 44 | Object.assign(def, def.schema['x-schema-form']); 45 | } 46 | 47 | return def; 48 | } 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * Creates a form object with all common properties 55 | */ 56 | export function stdFormObj(name, schema, options) { 57 | options = options || {}; 58 | 59 | // The Object.assign used to be a angular.copy. Should work though. 60 | const f = options.global && options.global.formDefaults ? 61 | Object.assign({}, options.global.formDefaults) : {}; 62 | if (options.global && options.global.supressPropertyTitles === true) { 63 | f.title = schema.title; 64 | } 65 | else { 66 | f.title = schema.title || name; 67 | } 68 | 69 | if (schema.description) { f.description = schema.description; } 70 | if (options.required === true || schema.required === true) { f.required = true; } 71 | if (schema.maxLength) { f.maxlength = schema.maxLength; } 72 | if (schema.minLength) { f.minlength = schema.minLength; } 73 | if (schema.readOnly || schema.readonly) { f.readonly = true; } 74 | if (schema.minimum) { f.minimum = schema.minimum + (schema.exclusiveMinimum ? 1 : 0); } 75 | if (schema.maximum) { f.maximum = schema.maximum - (schema.exclusiveMaximum ? 1 : 0); } 76 | 77 | // Non standard attributes (DONT USE DEPRECATED) 78 | // If you must set stuff like this in the schema use the x-schema-form attribute 79 | if (schema.validationMessage) { f.validationMessage = schema.validationMessage; } 80 | if (schema.enumNames) { f.titleMap = canonicalTitleMap(schema.enumNames, schema['enum']); } 81 | f.schema = schema; 82 | 83 | // Ng model options doesn't play nice with undefined, might be defined 84 | // globally though 85 | f.ngModelOptions = f.ngModelOptions || {}; 86 | 87 | return f; 88 | }; 89 | 90 | /*** Schema types to form type mappings, with defaults ***/ 91 | export function text(name, schema, options) { 92 | if (stripNullType(schema.type) === 'string' && !schema['enum']) { 93 | const f = stdFormObj(name, schema, options); 94 | f.key = options.path; 95 | f.type = 'text'; 96 | options.lookup[stringify(options.path)] = f; 97 | return f; 98 | } 99 | } 100 | 101 | // default in json form for number and integer is a text field 102 | // input type="number" would be more suitable don't ya think? 103 | export function number(name, schema, options) { 104 | if (stripNullType(schema.type) === 'number') { 105 | const f = stdFormObj(name, schema, options); 106 | f.key = options.path; 107 | f.type = 'number'; 108 | options.lookup[stringify(options.path)] = f; 109 | return f; 110 | } 111 | } 112 | 113 | export function integer(name, schema, options) { 114 | if (stripNullType(schema.type) === 'integer') { 115 | const f = stdFormObj(name, schema, options); 116 | f.key = options.path; 117 | f.type = 'number'; 118 | options.lookup[stringify(options.path)] = f; 119 | return f; 120 | } 121 | } 122 | 123 | export function checkbox(name, schema, options) { 124 | if (stripNullType(schema.type) === 'boolean') { 125 | const f = stdFormObj(name, schema, options); 126 | f.key = options.path; 127 | f.type = 'checkbox'; 128 | options.lookup[stringify(options.path)] = f; 129 | return f; 130 | } 131 | } 132 | 133 | export function select(name, schema, options) { 134 | if (stripNullType(schema.type) === 'string' && schema['enum']) { 135 | const f = stdFormObj(name, schema, options); 136 | f.key = options.path; 137 | f.type = 'select'; 138 | if (!f.titleMap) { 139 | f.titleMap = enumToTitleMap(schema['enum']); 140 | } 141 | options.lookup[stringify(options.path)] = f; 142 | return f; 143 | } 144 | } 145 | 146 | export function checkboxes(name, schema, options) { 147 | if (stripNullType(schema.type) === 'array' && schema.items && schema.items['enum']) { 148 | const f = stdFormObj(name, schema, options); 149 | f.key = options.path; 150 | f.type = 'checkboxes'; 151 | if (!f.titleMap) { 152 | f.titleMap = enumToTitleMap(schema.items['enum']); 153 | } 154 | options.lookup[stringify(options.path)] = f; 155 | return f; 156 | } 157 | } 158 | 159 | export function fieldset(name, schema, options, defaultFormDef) { 160 | if (stripNullType(schema.type) === 'object') { 161 | const f = stdFormObj(name, schema, options); 162 | f.type = 'fieldset'; 163 | f.key = options.path; 164 | f.items = []; 165 | options.lookup[stringify(options.path)] = f; 166 | 167 | // recurse down into properties 168 | if (schema.properties) { 169 | Object.keys(schema.properties).forEach((key) => { 170 | const value = schema.properties[key]; 171 | const path = options.path.slice(); 172 | path.push(key); 173 | if (options.ignore[stringify(path)] !== true) { 174 | const required = schema.required && schema.required.indexOf(key) !== -1; 175 | 176 | const def = defaultFormDef(key, value, { 177 | path, 178 | required: required || false, 179 | lookup: options.lookup, 180 | ignore: options.ignore, 181 | global: options.global 182 | }); 183 | if (def) { 184 | f.items.push(def); 185 | } 186 | } 187 | }); 188 | } 189 | return f; 190 | } 191 | } 192 | 193 | export function array(name, schema, options, defaultFormDef) { 194 | if (stripNullType(schema.type) === 'array') { 195 | const f = stdFormObj(name, schema, options); 196 | f.type = 'array'; 197 | f.key = options.path; 198 | options.lookup[stringify(options.path)] = f; 199 | 200 | const required = schema.required && 201 | schema.required.indexOf(options.path[options.path.length - 1]) !== -1; 202 | 203 | // The default is to always just create one child. This works since if the 204 | // schemas items declaration is of type: "object" then we get a fieldset. 205 | // We also follow json form notatation, adding empty brackets "[]" to 206 | // signify arrays. 207 | 208 | const arrPath = options.path.slice(); 209 | arrPath.push(''); 210 | 211 | f.items = [ 212 | defaultFormDef(name, schema.items, { 213 | path: arrPath, 214 | required: required || false, 215 | lookup: options.lookup, 216 | ignore: options.ignore, 217 | global: options.global 218 | }) 219 | ]; 220 | 221 | return f; 222 | } 223 | } 224 | 225 | export function createDefaults() { 226 | // First sorted by schema type then a list. 227 | // Order has importance. First handler returning an form snippet will be used. 228 | return { 229 | string: [ select, text ], 230 | object: [ fieldset ], 231 | number: [ number ], 232 | integer: [ integer ], 233 | boolean: [ checkbox ], 234 | array: [ checkboxes, array ] 235 | }; 236 | }; 237 | 238 | /** 239 | * Create form defaults from schema 240 | */ 241 | export function defaultForm(schema: any, defaultSchemaTypes: any, ignore?: any, globalOptions?: any) { 242 | const form = []; 243 | const lookup = {}; // Map path => form obj for fast lookup in merging 244 | ignore = ignore || {}; 245 | globalOptions = globalOptions || {}; 246 | defaultSchemaTypes = defaultSchemaTypes || createDefaults(); 247 | 248 | if (schema.properties) { 249 | Object.keys(schema.properties).forEach((key) => { 250 | if (ignore[key] !== true) { 251 | const required = schema.required && schema.required.indexOf(key) !== -1; 252 | const def = defaultFormDefinition(defaultSchemaTypes, key, schema.properties[key], { 253 | path: [ key ], // Path to this property in bracket notation. 254 | lookup: lookup, // Extra map to register with. Optimization for merger. 255 | ignore: ignore, // The ignore list of paths (sans root level name) 256 | required: required, // Is it required? (v4 json schema style) 257 | global: globalOptions // Global options, including form defaults 258 | }); 259 | if (def) { 260 | form.push(def); 261 | } 262 | } 263 | }); 264 | } 265 | else { 266 | throw new Error('Not implemented. Only type "object" allowed at root level of schema.'); 267 | } 268 | return { form: form, lookup: lookup }; 269 | } 270 | -------------------------------------------------------------------------------- /docs/test.md: -------------------------------------------------------------------------------- 1 | # TOC 2 | - [canonical-title-map.js](#canonical-title-mapjs) 3 | - [canonicalTitleMap](#canonical-title-mapjs-canonicaltitlemap) 4 | - [merge.js](#mergejs) 5 | - [merge](#mergejs-merge) 6 | - [select.js](#selectjs) 7 | - [select](#selectjs-select) 8 | - [schema-defaults.js](#schema-defaultsjs) 9 | - [createDefaults](#schema-defaultsjs-createdefaults) 10 | - [defaultForm](#schema-defaultsjs-defaultform) 11 | - [sf-path.js](#sf-pathjs) 12 | - [traverse.js](#traversejs) 13 | - [validate.js](#validatejs) 14 | - [validate](#validatejs-validate) 15 | - [module.js](#modulejs) 16 | 17 | 18 | 19 | # canonical-title-map.js 20 | should hold a normalisation function for enums and titleMaps to generate titleMaps. 21 | 22 | ```js 23 | _canonicalTitleMap2.default.should.be.an('function'); 24 | ``` 25 | 26 | 27 | ## canonicalTitleMap 28 | should return a titleMap for a titleMap object with original enum. 29 | 30 | ```js 31 | var result = (0, _canonicalTitleMap2.default)(titlemapObj, enumeration); 32 | result.should.be.deep.equal(titlemap); 33 | ``` 34 | 35 | should return a titleMap for a titleMap list with original enum. 36 | 37 | ```js 38 | var result = (0, _canonicalTitleMap2.default)(titlemap, enumeration); 39 | result.should.be.deep.equal(titlemap); 40 | ``` 41 | 42 | should return a titleMap for a titleMap object without enum. 43 | 44 | ```js 45 | var result = (0, _canonicalTitleMap2.default)(titlemapObj); 46 | result.should.be.deep.equal(titlemap); 47 | ``` 48 | 49 | should return a titleMap for a titleMap list without enum. 50 | 51 | ```js 52 | var result = (0, _canonicalTitleMap2.default)(titlemap); 53 | result.should.be.deep.equal(titlemap); 54 | ``` 55 | 56 | should return a titleMap for a titleMap object with original enum, returning "undefined" name if the enum value is not found. 57 | 58 | ```js 59 | enumeration.push('Mr Freeze'); 60 | var result = (0, _canonicalTitleMap2.default)(titlemapObj, enumeration); 61 | titlemap.push({ 62 | "name": undefined, 63 | "value": "Mr Freeze" 64 | }); 65 | result.should.be.deep.equal(titlemap); 66 | ``` 67 | 68 | 69 | # merge.js 70 | should contain a function for merging schema and form definitions. 71 | 72 | ```js 73 | _merge.merge.should.be.an('function'); 74 | ``` 75 | 76 | 77 | ## merge 78 | should handle a schema lookup or schema for first argument. 79 | 80 | ```js 81 | (0, _merge.merge)(stdForm.lookup, ['name', 'shoe', 'gender']).should.be.deep.equal(stdForm.form); 82 | (0, _merge.merge)(schema, ['*']).should.be.deep.equal(stdForm.form); 83 | ``` 84 | 85 | should handle a wildcard * in the form definition. 86 | 87 | ```js 88 | (0, _merge.merge)(schema, ['*']).should.be.deep.equal(stdForm.form); 89 | ``` 90 | 91 | should not handle a wildcard * if the schema is a lookup and cannot be inserted. 92 | 93 | ```js 94 | (0, _merge.merge)(stdForm.lookup, ['*']).should.not.be.deep.equal(stdForm.form); 95 | ``` 96 | 97 | should handle a rest "..." key in the form definition. 98 | 99 | ```js 100 | (0, _merge.merge)(schema, ['...', 'gender']).should.be.deep.equal(stdForm.form); 101 | ``` 102 | 103 | should not handle a rest "..." key in the form definition when the schema is a lookup and cannot be inserted. 104 | 105 | ```js 106 | (0, _merge.merge)(stdForm.lookup, ['...', 'gender']).should.not.be.deep.equal(stdForm.form); 107 | ``` 108 | 109 | should combine a schema and form definition, regardless of order. 110 | 111 | ```js 112 | (0, _merge.merge)(schema, ['name', 'shoe', 'gender']).should.be.deep.equal(stdForm.form); 113 | (0, _merge.merge)(schema, ['gender']).should.be.deep.equal([stdForm.form[2]]); 114 | (0, _merge.merge)(schema, ['gender', 'name']).should.be.deep.equal([stdForm.form[2], stdForm.form[0]]); 115 | ``` 116 | 117 | should allow items that are not in the schema. 118 | 119 | ```js 120 | (0, _merge.merge)(schema, ['*', { type: 'fieldset' }]).should.be.deep.equal(stdForm.form.concat([{ type: 'fieldset' }])); 121 | ``` 122 | 123 | should translate "readOnly" in schema to "readonly" on the merged form defintion. 124 | 125 | ```js 126 | var merged = (0, _merge.merge)(schema, ['gender']); 127 | merged[0].should.have.property('readonly'); 128 | merged[0].readonly.should.eq(true); 129 | ``` 130 | 131 | should push readOnly in schema down into objects and arrays. 132 | 133 | ```js 134 | var subschema = { 135 | 'type': 'object', 136 | 'readOnly': true, 137 | 'properties': { 138 | 'sub': { 139 | 'type': 'object', 140 | 'properties': { 141 | 'array': { 142 | 'type': 'array', 143 | 'items': { 144 | 'type': 'object', 145 | 'properties': { 146 | 'foo': { 147 | 'type': 'string' 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } 155 | }; 156 | var merged = (0, _merge.merge)(subschema, ['*']); 157 | //sub 158 | merged[0].should.have.property('readonly'); 159 | merged[0].readonly.should.eq(true); 160 | //array 161 | merged[0].items[0].should.have.property('readonly'); 162 | merged[0].items[0].readonly.should.eq(true); 163 | //array items 164 | merged[0].items[0].items[0].should.have.property('readonly'); 165 | merged[0].items[0].items[0].readonly.should.eq(true); 166 | ``` 167 | 168 | should push readonly in form def down into objects and arrays. 169 | 170 | ```js 171 | var subschema = { 172 | 'type': 'object', 173 | 'properties': { 174 | 'sub': { 175 | 'type': 'object', 176 | 'properties': { 177 | 'array': { 178 | 'type': 'array', 179 | 'items': { 180 | 'type': 'object', 181 | 'properties': { 182 | 'foo': { 183 | 'type': 'string' 184 | } 185 | } 186 | } 187 | } 188 | } 189 | } 190 | } 191 | }; 192 | var merged = (0, _merge.merge)(subschema, [{ key: 'sub', readonly: true }]); 193 | //sub 194 | merged[0].should.have.property('readonly'); 195 | merged[0].readonly.should.eq(true); 196 | //array 197 | merged[0].items[0].should.have.property('readonly'); 198 | merged[0].items[0].readonly.should.eq(true); 199 | //array items 200 | merged[0].items[0].items[0].should.have.property('readonly'); 201 | merged[0].items[0].items[0].readonly.should.eq(true); 202 | ``` 203 | 204 | 205 | # select.js 206 | should provide a function for getting and setting an object value. 207 | 208 | ```js 209 | _select.select.should.be.an('function'); 210 | ``` 211 | 212 | 213 | ## select 214 | should get a value from an object. 215 | 216 | ```js 217 | var value = (0, _select.select)('frienemies[1].weapon.boomstick.method[0]', data); 218 | value.should.eq('boom'); 219 | ``` 220 | 221 | should set a value on an object. 222 | 223 | ```js 224 | var value = (0, _select.select)('weapon.glove.method[1]', data, 'slice'); 225 | data.weapon.glove.method.should.be.deep.equal(['stab', 'slice']); 226 | ``` 227 | 228 | should create any undefined objects or arrays in the path when setting a value. 229 | 230 | ```js 231 | var data = {}; 232 | var value = (0, _select.select)('property.array[1].value', data, 'something'); 233 | data.should.be.deep.equal({ 234 | 'property': { 235 | 'array': [, { 'value': 'something' }] 236 | } 237 | }); 238 | ``` 239 | 240 | 241 | # schema-defaults.js 242 | should hold functions for generating a default form schema from defaults it creates. 243 | 244 | ```js 245 | _schemaDefaults.defaultForm.should.be.an('function'); 246 | _schemaDefaults.createDefaults.should.be.an('function'); 247 | ``` 248 | 249 | 250 | ## createDefaults 251 | should create default rules. 252 | 253 | ```js 254 | var rules = (0, _schemaDefaults.createDefaults)(); 255 | rules.should.be.an('object'); 256 | ``` 257 | 258 | 259 | ## defaultForm 260 | should generate default form def from a schema. 261 | 262 | ```js 263 | var schema = { 264 | 'type': 'object', 265 | 'properties': { 266 | 'name': { 267 | 'title': 'Name', 268 | 'description': 'Gimme yea name lad', 269 | 'type': 'string' 270 | }, 271 | 'gender': { 272 | 'title': 'Choose', 273 | 'type': 'string', 274 | 'enum': ['undefined', 'null', 'NaN'] 275 | }, 276 | 'overEighteen': { 277 | 'title': 'Are you over 18 years old?', 278 | 'type': 'boolean', 279 | 'default': false 280 | }, 281 | 'attributes': { 282 | 'type': 'object', 283 | 'required': ['eyecolor'], 284 | 'properties': { 285 | 'eyecolor': { 'type': 'string', 'title': 'Eye color' }, 286 | 'haircolor': { 'type': 'string', 'title': 'Hair color' }, 287 | 'shoulders': { 288 | 'type': 'object', 289 | 'title': 'Shoulders', 290 | 'properties': { 291 | 'left': { 'type': 'string' }, 292 | 'right': { 'type': 'string' } 293 | } 294 | } 295 | } 296 | } 297 | } 298 | }; 299 | var form = [{ 300 | 'title': 'Name', 301 | 'description': 'Gimme yea name lad', 302 | 'schema': { 303 | 'title': 'Name', 304 | 'description': 'Gimme yea name lad', 305 | 'type': 'string' 306 | }, 307 | 'ngModelOptions': {}, 308 | 'key': ['name'], 309 | 'type': 'text' 310 | }, { 311 | 'title': 'Choose', 312 | 'schema': { 313 | 'title': 'Choose', 314 | 'type': 'string', 315 | 'enum': ['undefined', 'null', 'NaN'] 316 | }, 317 | 'ngModelOptions': {}, 318 | 'key': ['gender'], 319 | 'type': 'select', 320 | 'titleMap': [{ 321 | 'name': 'undefined', 322 | 'value': 'undefined' 323 | }, { 324 | 'name': 'null', 325 | 'value': 'null' 326 | }, { 327 | 'name': 'NaN', 328 | 'value': 'NaN' 329 | }] 330 | }, { 331 | 'title': 'Are you over 18 years old?', 332 | 'schema': { 333 | 'title': 'Are you over 18 years old?', 334 | 'type': 'boolean', 335 | 'default': false 336 | }, 337 | 'ngModelOptions': {}, 338 | 'key': ['overEighteen'], 339 | 'type': 'checkbox' 340 | }, { 341 | 'title': 'attributes', 342 | 'schema': { 343 | 'type': 'object', 344 | 'required': ['eyecolor'], 345 | 'properties': { 346 | 'eyecolor': { 347 | 'type': 'string', 348 | 'title': 'Eye color' 349 | }, 350 | 'haircolor': { 351 | 'type': 'string', 352 | 'title': 'Hair color' 353 | }, 354 | 'shoulders': { 355 | 'type': 'object', 356 | 'title': 'Shoulders', 357 | 'properties': { 358 | 'left': { 359 | 'type': 'string' 360 | }, 361 | 'right': { 362 | 'type': 'string' 363 | } 364 | } 365 | } 366 | } 367 | }, 368 | 'ngModelOptions': {}, 369 | 'key': ['attributes'], 370 | 'type': 'fieldset', 371 | 'items': [{ 372 | 'title': 'Eye color', 373 | 'required': true, 374 | 'schema': { 375 | 'type': 'string', 376 | 'title': 'Eye color' 377 | }, 378 | 'ngModelOptions': {}, 379 | 'key': ['attributes', 'eyecolor'], 380 | 'type': 'text' 381 | }, { 382 | 'title': 'Hair color', 383 | 'schema': { 384 | 'type': 'string', 385 | 'title': 'Hair color' 386 | }, 387 | 'ngModelOptions': {}, 388 | 'key': ['attributes', 'haircolor'], 389 | 'type': 'text' 390 | }, { 391 | 'title': 'Shoulders', 392 | 'schema': { 393 | 'type': 'object', 394 | 'title': 'Shoulders', 395 | 'properties': { 396 | 'left': { 397 | 'type': 'string' 398 | }, 399 | 'right': { 400 | 'type': 'string' 401 | } 402 | } 403 | }, 404 | 'ngModelOptions': {}, 405 | 'key': ['attributes', 'shoulders'], 406 | 'type': 'fieldset', 407 | 'items': [{ 408 | 'title': 'left', 409 | 'schema': { 410 | 'type': 'string' 411 | }, 412 | 'ngModelOptions': {}, 413 | 'key': ['attributes', 'shoulders', 'left'], 414 | 'type': 'text' 415 | }, { 416 | 'title': 'right', 417 | 'schema': { 418 | 'type': 'string' 419 | }, 420 | 'ngModelOptions': {}, 421 | 'key': ['attributes', 'shoulders', 'right'], 422 | 'type': 'text' 423 | }] 424 | }] 425 | }]; 426 | var f = (0, _schemaDefaults.defaultForm)(schema, (0, _schemaDefaults.createDefaults)()); 427 | f.form.should.be.deep.equal(form); 428 | ``` 429 | 430 | 431 | # sf-path.js 432 | should hold functions for working with object paths and keys. 433 | 434 | ```js 435 | _sfPath.parse.should.be.an('function'); 436 | _sfPath.stringify.should.be.an('function'); 437 | _sfPath.normalize.should.be.an('function'); 438 | _sfPath.name.should.be.an('function'); 439 | ``` 440 | 441 | 442 | # traverse.js 443 | should hold functions for applying functions on branches of a json-schema or ui-schema. 444 | 445 | ```js 446 | _traverse.traverseSchema.should.be.an('function'); 447 | _traverse.traverseForm.should.be.an('function'); 448 | ``` 449 | 450 | 451 | # validate.js 452 | should hold a validation function for testing against tv4 until an option to pass in a validator is created. 453 | 454 | ```js 455 | _validate.validate.should.be.an('function'); 456 | ``` 457 | 458 | 459 | ## validate 460 | should return a result object {"error":null, "missing":[], "valid":true}, with valid set to true when the data is valid for the schema. 461 | 462 | ```js 463 | var value = 'Batman'; 464 | var result = (0, _validate.validate)(form, value); 465 | result.should.be.deep.equal({ "error": null, "missing": [], "valid": true }); 466 | ``` 467 | 468 | should return an error object with a message "Invalid type: array (expected string)" when the data is not valid. 469 | 470 | ```js 471 | var value = [0]; 472 | var result = (0, _validate.validate)(form, value); 473 | result.error.message.should.eq("Invalid type: array (expected string)"); 474 | ``` 475 | 476 | 477 | # module.js 478 | should hold all the public functions of the API. 479 | 480 | ```js 481 | _module.merge.should.be.an('function'); 482 | _module.select.should.be.an('function'); 483 | _module.traverseSchema.should.be.an('function'); 484 | _module.traverseForm.should.be.an('function'); 485 | _module.validate.should.be.an('function'); 486 | _module.sfPath.should.be.an('object'); 487 | _module.schemaDefaults.should.be.an('object'); 488 | _module.canonicalTitleMap.should.be.an('function'); 489 | ``` 490 | 491 | -------------------------------------------------------------------------------- /lib/json-refs-standalone-min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.JsonRefs=e()}}(function(){var e;return function t(e,r,n){function o(s,a){if(!r[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var f=r[s]={exports:{}};e[s][0].call(f.exports,function(t){var r=e[s][1][t];return o(r?r:t)},f,f.exports,t,e,r,n)}return r[s].exports}for(var i="function"==typeof require&&require,s=0;s-1?n=i:(r=d(e,"Undefined")?void 0:m(e),d(r,"Undefined")?n=i:(n=r,n.path=F(U.join(r.path,i.path)),n.query=o(r.query,i.query))),n.fragment=void 0,(-1===M.indexOf(n.reference)&&0===n.path.indexOf("../")?"../":"")+q.serialize(n)}function s(e,t){var r,n=[];return t.length>0&&(r=e,t.slice(0,t.length-1).forEach(function(e){e in r&&(r=r[e],n.push(r))})),n}function a(e,t,r,o,i,s,a,f,p){var h,l;if(r.length>0)try{h=c(t,r)}catch(y){"remote"===e&&(o.error=y.message,o.missing=!0)}else h=t;return d(h,"Undefined")||(o.value=h),d(h,"Array")||d(h,"Object")?(l=n(i),"local"===e?(delete l.subDocPath,t=h):(l.relativeBase=U.dirname(s[s.length-1]),0===r.length?delete l.subDocPath:l.subDocPath=r),u(t,l,s,a,f,p)):void 0}function u(e,t,r,n,o,s){var u=Promise.resolve(),c=n.length?j(n[n.length-1]):[],f=O(e,t),p=t.subDocPath||[],l=S(p),y=["#"];return r.forEach(function(e,t){"#"!==e.charAt(0)&&y.push(n[t])}),y.reverse(),"#"!==(r[r.length-1]||"").charAt(0)&&(o.documents[S(c)]=e),Object.keys(f).forEach(function(v){var m,g,b,E,w=f[v];b=0===r.length?c.concat(j(v)):c.concat(j(v).slice(0===r.length?0:p.length)),E=S(b),d(o[E],"Undefined")&&(o.refs[E]=f[v],d(w.error,"Undefined")&&"invalid"!==w.type&&(L.indexOf(w.type)>-1?(m=i(t.relativeBase,w.uri),g=r.indexOf(m)):(m=w.uri,g=n.indexOf(m)),w.ancestorPtrs=y,w.indirect=s,-1===g?L.indexOf(w.type)>-1?u=u.then(function(){return h(m,t).then(function(e){return a("remote",e,d(w.uriDetails.fragment,"Undefined")?[]:j(decodeURI(w.uriDetails.fragment)),w,t,r.concat(m),n.concat(E),o,s)})["catch"](function(e){w.error=e.message,w.missing=!0})}):0!==E.indexOf(m+"/")&&E!==m&&0!==l.indexOf(m+"/")&&l!==m?0!==m.indexOf(l+"/")&&(u=u.then(function(){return a("local",e,j(m),w,t,r.concat(m),n.concat(E),o,s||-1===m.indexOf(l+"/")&&m!==l)})):w.circular=!0:(n.slice(g).forEach(function(e){o.refs[e].circular=!0}),w.circular=!0)))}),u=u.then(function(){function e(i,s,a,u){Object.keys(o.refs).forEach(function(c){var f=o.refs[c];-1===n.indexOf(u)&&-1===r.indexOf(a)&&-1===t.indexOf(u)&&c!==a&&0===c.indexOf(u+"/")&&(s.indexOf(u)>-1?s.forEach(function(e){-1===t.indexOf(u)&&t.push(e)}):e(i.concat(a),s.concat(u),c,f.uri),r.push(a),n.push(u))})}var t=[],r=[],n=[];Object.keys(o.refs).forEach(function(r){var n=o.refs[r];"local"!==n.type||n.circular||-1!==t.indexOf(n.uri)||e([],[],r,n.uri)}),Object.keys(o.refs).forEach(function(e){var r=o.refs[e];t.indexOf(r.uri)>-1&&(r.circular=!0)})}).then(function(){return o})}function c(e,t){var r=e;return t.forEach(function(e){if(e=decodeURI(e),!(e in r))throw Error("JSON Pointer points to missing location: "+S(t));r=r[e]}),r}function f(e){return Object.keys(e).filter(function(e){return"$ref"!==e})}function p(e){var t;switch(e.uriDetails.reference){case"absolute":case"uri":t="remote";break;case"same-document":t="local";break;default:t=e.uriDetails.reference}return t}function h(e,t){var r=k[e],o=Promise.resolve(),i=n(t.loaderOptions||{});return d(r,"Undefined")?(d(i.processContent,"Undefined")&&(i.processContent=function(e,t){t(void 0,JSON.parse(e.text))}),o=I.load(decodeURI(e),i),o=o.then(function(t){return k[e]={value:t},t})["catch"](function(t){throw k[e]={error:t},t})):o=o.then(function(){return r.value}),o=o.then(function(e){return n(e)})}function l(e,t){var r=!0;try{if(!d(e,"Object"))throw new Error("obj is not an Object");if(!d(e.$ref,"String"))throw new Error("obj.$ref is not a String")}catch(n){if(t)throw n;r=!1}return r}function d(e,t){return"Undefined"===t?"undefined"==typeof e:Object.prototype.toString.call(e)==="[object "+t+"]"}function y(e){var t,r;return d(e.filter,"Array")||d(e.filter,"String")?(r=d(e.filter,"String")?[e.filter]:e.filter,t=function(e){return r.indexOf(e.type)>-1||r.indexOf(p(e))>-1}):d(e.filter,"Function")?t=e.filter:d(e.filter,"Undefined")&&(t=function(){return!0}),function(r,n){return("invalid"!==r.type||e.includeInvalid===!0)&&t(r,n)}}function v(e){var t;return d(e.subDocPath,"Array")?t=e.subDocPath:d(e.subDocPath,"String")?t=j(e.subDocPath):d(e.subDocPath,"Undefined")&&(t=[]),t}function m(e){return q.parse(encodeURI(decodeURI(e)))}function g(e,t,r){c(e,t.slice(0,t.length-1))[decodeURI(t[t.length-1])]=r}function b(e,t,r,n){function o(t,o){r.push(o),b(e,t,r,n),r.pop()}var i=!0;d(n,"Function")&&(i=n(e,t,r)),-1===e.indexOf(t)&&(e.push(t),i!==!1&&(d(t,"Array")?t.forEach(function(e,t){o(e,t.toString())}):d(t,"Object")&&Object.keys(t).forEach(function(e){o(t[e],e)}))),e.pop()}function E(e,t){if(e=d(e,"Undefined")?{}:n(e),!d(e,"Object"))throw new TypeError("options must be an Object");if(!(d(e.filter,"Undefined")||d(e.filter,"Array")||d(e.filter,"Function")||d(e.filter,"String")))throw new TypeError("options.filter must be an Array, a Function of a String");if(!d(e.includeInvalid,"Undefined")&&!d(e.includeInvalid,"Boolean"))throw new TypeError("options.includeInvalid must be a Boolean");if(!d(e.refPreProcessor,"Undefined")&&!d(e.refPreProcessor,"Function"))throw new TypeError("options.refPreProcessor must be a Function");if(!d(e.refPostProcessor,"Undefined")&&!d(e.refPostProcessor,"Function"))throw new TypeError("options.refPostProcessor must be a Function");if(!d(e.subDocPath,"Undefined")&&!d(e.subDocPath,"Array")&&!_(e.subDocPath))throw new TypeError("options.subDocPath must be an Array of path segments or a valid JSON Pointer");if(e.filter=y(e),e.subDocPath=v(e),!d(t,"Undefined"))try{c(t,e.subDocPath)}catch(r){throw r.message=r.message.replace("JSON Pointer","options.subDocPath"),r}return e}function w(){k={}}function x(e){if(!d(e,"Array"))throw new TypeError("path must be an array");return e.map(function(e){return d(e,"String")||(e=JSON.stringify(e)),decodeURI(e.replace(/~1/g,"/").replace(/~0/g,"~"))})}function C(e){if(!d(e,"Array"))throw new TypeError("path must be an array");return e.map(function(e){return d(e,"String")||(e=JSON.stringify(e)),e.replace(/~/g,"~0").replace(/\//g,"~1")})}function O(e,t){var r={};if(!d(e,"Array")&&!d(e,"Object"))throw new TypeError("obj must be an Array or an Object");return t=E(t,e),b(s(e,t.subDocPath),c(e,t.subDocPath),n(t.subDocPath),function(e,o,i){var s,a=!0;return l(o)&&(d(t.refPreProcessor,"Undefined")||(o=t.refPreProcessor(n(o),i)),s=T(o),d(t.refPostProcessor,"Undefined")||(s=t.refPostProcessor(s,i)),t.filter(s,i)&&(r[S(i)]=s),f(o).length>0&&(a=!1)),a}),r}function P(e,t){var r=Promise.resolve();return r=r.then(function(){if(!d(e,"String"))throw new TypeError("location must be a string");return t=E(t),e=i(t.relativeBase,e),h(e,t)}).then(function(r){var o=n(k[e]),i=n(t),s=m(e);return d(o.refs,"Undefined")&&(delete i.filter,delete i.subDocPath,i.includeInvalid=!0,k[e].refs=O(r,i)),d(t.filter,"Undefined")||(i.filter=t.filter),d(s.fragment,"Undefined")?d(s.subDocPath,"Undefined")||(i.subDocPath=t.subDocPath):i.subDocPath=j(decodeURI(s.fragment)),{refs:O(r,i),value:r}})}function T(e){var t,r,n,o={def:e};try{l(e,!0)?(t=e.$ref,n=$[t],d(n,"Undefined")&&(n=$[t]=m(t)),o.uri=t,o.uriDetails=n,d(n.error,"Undefined")?o.type=p(o):(o.error=o.uriDetails.error,o.type="invalid"),r=f(e),r.length>0&&(o.warning="Extra JSON Reference properties will be ignored: "+r.join(", "))):o.type="invalid"}catch(i){o.error=i.message,o.type="invalid"}return o}function _(e,t){var r,n=!0;try{if(!d(e,"String"))throw new Error("ptr is not a String");if(""!==e){if(r=e.charAt(0),-1===["#","/"].indexOf(r))throw new Error("ptr must start with a / or #/");if("#"===r&&"#"!==e&&"/"!==e.charAt(1))throw new Error("ptr must start with a / or #/");if(e.match(H))throw new Error("ptr has invalid token(s)")}}catch(o){if(t===!0)throw o;n=!1}return n}function A(e,t){return l(e,t)&&"invalid"!==T(e,t).type}function j(e){if(!_(e))throw new Error("ptr must be a JSON Pointer");var t=e.split("/");return t.shift(),x(t)}function S(e,t){if(!d(e,"Array"))throw new Error("path must be an Array");return(t!==!1?"#":"")+(e.length>0?"/":"")+C(e).join("/")}function D(e,t){var r=Promise.resolve();return r=r.then(function(){if(!d(e,"Array")&&!d(e,"Object"))throw new TypeError("obj must be an Array or an Object");t=E(t,e),e=n(e)}).then(function(){return u(e,t,[],[],{documents:{},refs:{}})}).then(function(t){function r(e,t){return j(e).length-j(t).length}var n={},o={};return Object.keys(t.refs).sort(r).forEach(function(r){var i=t.refs[r];i.indirect||(o[r]=i),delete i.indirect,d(i.error,"Undefined")&&"invalid"!==i.type?(d(i.value,"Undefined")&&i.circular&&(i.value=i.def),d(i.value,"Undefined")?n[r]=i:("#"===r?e=i.value:g(e,j(r),i.value),delete i.ancestorPtrs)):delete i.ancestorPtrs}),Object.keys(n).forEach(function(r){var o=n[r];o.ancestorPtrs.forEach(function(n,i){if(d(o.value,"Undefined"))try{o.value=c(t.documents[n],j(o.uri)),delete o.ancestorPtrs,g(e,j(r),o.value)}catch(s){i===o.ancestorPtrs.length-1&&(o.error=s.message,o.missing=!0,delete o.ancestorPtrs)}})}),{refs:o,resolved:e}})}function R(e,t){var r=Promise.resolve();return r=r.then(function(){if(!d(e,"String"))throw new TypeError("location must be a string");return t=E(t),e=i(t.relativeBase,e),h(e,t)}).then(function(r){var o=n(t),i=m(e);return d(i.fragment,"Undefined")||(o.subDocPath=j(decodeURI(i.fragment))),o.relativeBase=U.dirname(e),D(r,o).then(function(e){return{refs:e.refs,resolved:e.resolved,value:r}})})}var U=e("path"),I=e("path-loader"),N=e("querystring"),F=e("slash"),q=e("uri-js"),H=/~(?:[^01]|$)/g,k={},L=["relative","remote"],M=["absolute","uri"],$={};"undefined"==typeof Promise&&e("native-promise-only"),t.exports.clearCache=w,t.exports.decodePath=x,t.exports.encodePath=C,t.exports.findRefs=O,t.exports.findRefsAt=P,t.exports.getRefDetails=T,t.exports.isPtr=_,t.exports.isRef=A,t.exports.pathFromPtr=j,t.exports.pathToPtr=S,t.exports.resolveRefs=D,t.exports.resolveRefsAt=R},{"native-promise-only":3,path:4,"path-loader":5,querystring:11,slash:13,"uri-js":23}],2:[function(e,t,r){function n(e){return e?o(e):void 0}function o(e){for(var t in n.prototype)e[t]=n.prototype[t];return e}t.exports=n,n.prototype.on=n.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},n.prototype.once=function(e,t){function r(){this.off(e,r),t.apply(this,arguments)}return r.fn=t,this.on(e,r),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var r=this._callbacks["$"+e];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var n,o=0;on;++n)r[n].apply(this,t)}return this},n.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},n.prototype.hasListeners=function(e){return!!this.listeners(e).length}},{}],3:[function(t,r,n){(function(t){!function(t,n,o){n[t]=n[t]||o(),"undefined"!=typeof r&&r.exports?r.exports=n[t]:"function"==typeof e&&e.amd&&e(function(){return n[t]})}("Promise","undefined"!=typeof t?t:this,function(){"use strict";function e(e,t){h.add(e,t),p||(p=d(h.drain))}function t(e){var t,r=typeof e;return null==e||"object"!=r&&"function"!=r||(t=e.then),"function"==typeof t?t:!1}function r(){for(var e=0;e0&&e(r,u))}catch(c){i.call(new a(u),c)}}}function i(t){var n=this;n.triggered||(n.triggered=!0,n.def&&(n=n.def),n.msg=t,n.state=2,n.chain.length>0&&e(r,n))}function s(e,t,r,n){for(var o=0;o=0;n--){var o=e[n];"."===o?e.splice(n,1):".."===o?(e.splice(n,1),r++):r&&(e.splice(n,1),r--)}if(t)for(;r--;r)e.unshift("..");return e}function n(e,t){if(e.filter)return e.filter(t);for(var r=[],n=0;n=-1&&!o;i--){var s=i>=0?arguments[i]:e.cwd();if("string"!=typeof s)throw new TypeError("Arguments to path.resolve must be strings");s&&(r=s+"/"+r,o="/"===s.charAt(0))}return r=t(n(r.split("/"),function(e){return!!e}),!o).join("/"),(o?"/":"")+r||"."},r.normalize=function(e){var o=r.isAbsolute(e),i="/"===s(e,-1);return e=t(n(e.split("/"),function(e){return!!e}),!o).join("/"),e||o||(e="."),e&&i&&(e+="/"),(o?"/":"")+e},r.isAbsolute=function(e){return"/"===e.charAt(0)},r.join=function(){var e=Array.prototype.slice.call(arguments,0);return r.normalize(n(e,function(e,t){if("string"!=typeof e)throw new TypeError("Arguments to path.join must be strings");return e}).join("/"))},r.relative=function(e,t){function n(e){for(var t=0;t=0&&""===e[r];r--);return t>r?[]:e.slice(t,r-t+1)}e=r.resolve(e).substr(1),t=r.resolve(t).substr(1);for(var o=n(e.split("/")),i=n(t.split("/")),s=Math.min(o.length,i.length),a=s,u=0;s>u;u++)if(o[u]!==i[u]){a=u;break}for(var c=[],u=a;ut&&(t=e.length+t),e.substr(t,r)}}).call(this,e("_process"))},{_process:8}],5:[function(e,t,r){"use strict";function n(e){return"undefined"!=typeof e&&(e=-1===e.indexOf("://")?"":e.split("://")[0]),e}function o(e){var t=n(e),r=i[t];if("undefined"==typeof r){if(""!==t)throw new Error("Unsupported scheme: "+t);r=s}return r}var i={file:e("./lib/loaders/file"),http:e("./lib/loaders/http"),https:e("./lib/loaders/http")},s="object"==typeof window||"function"==typeof importScripts?i.http:i.file;"undefined"==typeof Promise&&e("native-promise-only"),t.exports.load=function(e,t){var r=Promise.resolve();return"undefined"==typeof t&&(t={}),r=r.then(function(){if("undefined"==typeof e)throw new TypeError("location is required");if("string"!=typeof e)throw new TypeError("location must be a string");if("undefined"!=typeof t){if("object"!=typeof t)throw new TypeError("options must be an object");if("undefined"!=typeof t.processContent&&"function"!=typeof t.processContent)throw new TypeError("options.processContent must be a function")}}),r=r.then(function(){return new Promise(function(r,n){var i=o(e);i.load(e,t||{},function(e,t){e?n(e):r(t)})})}).then(function(e){return t.processContent?new Promise(function(r,n){t.processContent("object"==typeof e?e:{text:e},function(e,t){e?n(e):r(t)})}):"object"==typeof e?e.text:e})}},{"./lib/loaders/file":6,"./lib/loaders/http":7,"native-promise-only":3}],6:[function(e,t,r){"use strict";var n=new TypeError("The 'file' scheme is not supported in the browser");t.exports.getBase=function(){throw n},t.exports.load=function(){var e=arguments[arguments.length-1];if("function"!=typeof e)throw n;e(n)}},{}],7:[function(e,t,r){"use strict";var n=e("superagent"),o=["delete","get","head","patch","post","put"];t.exports.load=function(e,t,r){function i(e,t){e?r(e):("function"==typeof t.buffer&&t.buffer(!0),t.end(function(e,t){e?r(e):r(void 0,t)}))}var s,a,u=t.method?t.method.toLowerCase():"get";if("undefined"!=typeof t.method?"string"!=typeof t.method?s=new TypeError("options.method must be a string"):-1===o.indexOf(t.method)&&(s=new TypeError("options.method must be one of the following: "+o.slice(0,o.length-1).join(", ")+" or "+o[o.length-1])):"undefined"!=typeof t.prepareRequest&&"function"!=typeof t.prepareRequest&&(s=new TypeError("options.prepareRequest must be a function")),s)r(s);else if(a=n["delete"===u?"del":u](e),t.prepareRequest)try{t.prepareRequest(a,i)}catch(c){r(c)}else i(void 0,a)}},{superagent:14}],8:[function(e,t,r){function n(){f=!1,a.length?c=a.concat(c):p=-1,c.length&&o()}function o(){if(!f){var e=setTimeout(n);f=!0;for(var t=c.length;t;){for(a=c,c=[];++p1)for(var r=1;r0&&c>u&&(c=u);for(var f=0;c>f;++f){var p,h,l,d,y=e[f].replace(a,"%20"),v=y.indexOf(r);v>=0?(p=y.substr(0,v),h=y.substr(v+1)):(p=y,h=""),l=decodeURIComponent(p),d=decodeURIComponent(h),n(s,l)?o(s[l])?s[l].push(d):s[l]=[s[l],d]:s[l]=d}return s};var o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},{}],10:[function(e,t,r){"use strict";function n(e,t){if(e.map)return e.map(t);for(var r=[],n=0;nn;)i=t.call(null,i,e[n],++n,e);return i}},{}],13:[function(e,t,r){"use strict";t.exports=function(e){var t=/^\\\\\?\\/.test(e),r=/[^\x00-\x80]+/.test(e);return t||r?e:e.replace(/\\/g,"/")}},{}],14:[function(e,t,r){function n(){}function o(e){var t={}.toString.call(e);switch(t){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function i(e){if(!b(e))return e;var t=[];for(var r in e)null!=e[r]&&s(t,r,e[r]);return t.join("&")}function s(e,t,r){return Array.isArray(r)?r.forEach(function(r){s(e,t,r)}):void e.push(encodeURIComponent(t)+"="+encodeURIComponent(r))}function a(e){for(var t,r,n={},o=e.split("&"),i=0,s=o.length;s>i;++i)r=o[i],t=r.split("="),n[decodeURIComponent(t[0])]=decodeURIComponent(t[1]);return n}function u(e){var t,r,n,o,i=e.split(/\r?\n/),s={};i.pop();for(var a=0,u=i.length;u>a;++a)r=i[a],t=r.indexOf(":"),n=r.slice(0,t).toLowerCase(),o=w(r.slice(t+1)),s[n]=o;return s}function c(e){return/[\/+]json\b/.test(e)}function f(e){return e.split(/ *; */).shift()}function p(e){return m(e.split(/ *; */),function(e,t){var r=t.split(/ *= */),n=r.shift(),o=r.shift();return n&&o&&(e[n]=o),e},{})}function h(e,t){t=t||{},this.req=e,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method&&(""===this.xhr.responseType||"text"===this.xhr.responseType)||"undefined"==typeof this.xhr.responseType?this.xhr.responseText:null,this.statusText=this.req.xhr.statusText,this.setStatusProperties(this.xhr.status),this.header=this.headers=u(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text?this.text:this.xhr.response):null}function l(e,t){var r=this;this._query=this._query||[],this.method=e,this.url=t,this.header={},this._header={},this.on("end",function(){var e=null,t=null;try{t=new h(r)}catch(n){return e=new Error("Parser is unable to parse the response"),e.parse=!0,e.original=n,e.rawResponse=r.xhr&&r.xhr.responseText?r.xhr.responseText:null,e.statusCode=r.xhr&&r.xhr.status?r.xhr.status:null,r.callback(e)}if(r.emit("response",t),e)return r.callback(e,t);if(t.status>=200&&t.status<300)return r.callback(e,t);var o=new Error(t.statusText||"Unsuccessful HTTP response");o.original=e,o.response=t,o.status=t.status,r.callback(o,t)})}function d(e,t){var r=E("DELETE",e);return t&&r.end(t),r}var y,v=e("emitter"),m=e("reduce"),g=e("./request-base"),b=e("./is-object");y="undefined"!=typeof window?window:"undefined"!=typeof self?self:this;var E=t.exports=e("./request").bind(null,l);E.getXHR=function(){if(!(!y.XMLHttpRequest||y.location&&"file:"==y.location.protocol&&y.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}return!1};var w="".trim?function(e){return e.trim()}:function(e){return e.replace(/(^\s*|\s*$)/g,"")};E.serializeObject=i,E.parseString=a,E.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},E.serialize={"application/x-www-form-urlencoded":i,"application/json":JSON.stringify},E.parse={"application/x-www-form-urlencoded":a,"application/json":JSON.parse},h.prototype.get=function(e){return this.header[e.toLowerCase()]},h.prototype.setHeaderProperties=function(e){var t=this.header["content-type"]||"";this.type=f(t);var r=p(t);for(var n in r)this[n]=r[n]},h.prototype.parseBody=function(e){var t=E.parse[this.type];return!t&&c(this.type)&&(t=E.parse["application/json"]),t&&e&&(e.length||e instanceof Object)?t(e):null},h.prototype.setStatusProperties=function(e){1223===e&&(e=204);var t=e/100|0;this.status=this.statusCode=e,this.statusType=t,this.info=1==t,this.ok=2==t,this.clientError=4==t,this.serverError=5==t,this.error=4==t||5==t?this.toError():!1,this.accepted=202==e,this.noContent=204==e,this.badRequest=400==e,this.unauthorized=401==e,this.notAcceptable=406==e,this.notFound=404==e,this.forbidden=403==e},h.prototype.toError=function(){var e=this.req,t=e.method,r=e.url,n="cannot "+t+" "+r+" ("+this.status+")",o=new Error(n);return o.status=this.status,o.method=t,o.url=r,o},E.Response=h,v(l.prototype);for(var x in g)l.prototype[x]=g[x];l.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},l.prototype.type=function(e){return this.set("Content-Type",E.types[e]||e),this},l.prototype.responseType=function(e){return this._responseType=e,this},l.prototype.accept=function(e){return this.set("Accept",E.types[e]||e),this},l.prototype.auth=function(e,t,r){switch(r||(r={type:"basic"}),r.type){case"basic":var n=btoa(e+":"+t);this.set("Authorization","Basic "+n);break;case"auto":this.username=e,this.password=t}return this},l.prototype.query=function(e){return"string"!=typeof e&&(e=i(e)),e&&this._query.push(e),this},l.prototype.attach=function(e,t,r){return this._getFormData().append(e,t,r||t.name),this},l.prototype._getFormData=function(){return this._formData||(this._formData=new y.FormData),this._formData},l.prototype.send=function(e){var t=b(e),r=this._header["content-type"];if(t&&b(this._data))for(var n in e)this._data[n]=e[n];else"string"==typeof e?(r||this.type("form"),r=this._header["content-type"],"application/x-www-form-urlencoded"==r?this._data=this._data?this._data+"&"+e:e:this._data=(this._data||"")+e):this._data=e;return!t||o(e)?this:(r||this.type("json"),this)},h.prototype.parse=function(e){return y.console&&console.warn("Client-side parse() method has been renamed to serialize(). This method is not compatible with superagent v2.0"),this.serialize(e),this},h.prototype.serialize=function(e){return this._parser=e,this},l.prototype.callback=function(e,t){var r=this._callback;this.clearTimeout(),r(e,t)},l.prototype.crossDomainError=function(){var e=new Error("Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.");e.crossDomain=!0,e.status=this.status,e.method=this.method,e.url=this.url,this.callback(e)},l.prototype.timeoutError=function(){var e=this._timeout,t=new Error("timeout of "+e+"ms exceeded");t.timeout=e,this.callback(t)},l.prototype.withCredentials=function(){return this._withCredentials=!0,this},l.prototype.end=function(e){var t=this,r=this.xhr=E.getXHR(),i=this._query.join("&"),s=this._timeout,a=this._formData||this._data;this._callback=e||n,r.onreadystatechange=function(){if(4==r.readyState){var e;try{e=r.status}catch(n){e=0}if(0==e){if(t.timedout)return t.timeoutError();if(t.aborted)return;return t.crossDomainError()}t.emit("end")}};var u=function(e){e.total>0&&(e.percent=e.loaded/e.total*100),e.direction="download",t.emit("progress",e)};this.hasListeners("progress")&&(r.onprogress=u);try{r.upload&&this.hasListeners("progress")&&(r.upload.onprogress=u)}catch(f){}if(s&&!this._timer&&(this._timer=setTimeout(function(){t.timedout=!0,t.abort()},s)),i&&(i=E.serializeObject(i),this.url+=~this.url.indexOf("?")?"&"+i:"?"+i),this.username&&this.password?r.open(this.method,this.url,!0,this.username,this.password):r.open(this.method,this.url,!0),this._withCredentials&&(r.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof a&&!o(a)){var p=this._header["content-type"],h=this._parser||E.serialize[p?p.split(";")[0]:""];!h&&c(p)&&(h=E.serialize["application/json"]),h&&(a=h(a))}for(var l in this.header)null!=this.header[l]&&r.setRequestHeader(l,this.header[l]);return this._responseType&&(r.responseType=this._responseType),this.emit("request",this),r.send("undefined"!=typeof a?a:null),this},E.Request=l,E.get=function(e,t,r){var n=E("GET",e);return"function"==typeof t&&(r=t,t=null),t&&n.query(t),r&&n.end(r),n},E.head=function(e,t,r){var n=E("HEAD",e);return"function"==typeof t&&(r=t,t=null),t&&n.send(t),r&&n.end(r),n},E.del=d,E["delete"]=d,E.patch=function(e,t,r){var n=E("PATCH",e);return"function"==typeof t&&(r=t,t=null),t&&n.send(t),r&&n.end(r),n},E.post=function(e,t,r){var n=E("POST",e);return"function"==typeof t&&(r=t,t=null),t&&n.send(t),r&&n.end(r),n},E.put=function(e,t,r){var n=E("PUT",e);return"function"==typeof t&&(r=t,t=null),t&&n.send(t),r&&n.end(r),n}},{"./is-object":15,"./request":17,"./request-base":16,emitter:2,reduce:12}],15:[function(e,t,r){function n(e){return null!=e&&"object"==typeof e}t.exports=n},{}],16:[function(e,t,r){var n=e("./is-object");r.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},r.parse=function(e){return this._parser=e,this},r.timeout=function(e){return this._timeout=e,this},r.then=function(e,t){return this.end(function(r,n){r?t(r):e(n)})},r.use=function(e){return e(this),this},r.get=function(e){return this._header[e.toLowerCase()]},r.getHeader=r.get,r.set=function(e,t){if(n(e)){for(var r in e)this.set(r,e[r]);return this}return this._header[e.toLowerCase()]=t,this.header[e]=t,this},r.unset=function(e){return delete this._header[e.toLowerCase()], 2 | delete this.header[e],this},r.field=function(e,t){return this._getFormData().append(e,t),this}},{"./is-object":15}],17:[function(e,t,r){function n(e,t,r){return"function"==typeof r?new e("GET",t).end(r):2==arguments.length?new e("GET",t):new e(t,r)}t.exports=n},{}],18:[function(e,t,r){var n=function(){function e(e){throw new RangeError(P[e])}function t(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function r(e,r){var n=e.split("@"),o="";n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(O,".");var i=e.split("."),s=t(i,r).join(".");return o+s}function n(e){for(var t,r,n=[],o=0,i=e.length;i>o;)t=e.charCodeAt(o++),t>=55296&&56319>=t&&i>o?(r=e.charCodeAt(o++),56320==(64512&r)?n.push(((1023&t)<<10)+(1023&r)+65536):(n.push(t),o--)):n.push(t);return n}function o(e){return t(e,function(e){var t="";return e>65535&&(e-=65536,t+=A(e>>>10&1023|55296),e=56320|1023&e),t+=A(e)}).join("")}function i(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:d}function s(e,t){return e+22+75*(26>e)-((0!=t)<<5)}function a(e,t,r){var n=0;for(e=r?_(e/g):e>>1,e+=_(e/t);e>T*v>>1;n+=d)e=_(e/T);return _(n+(T+1)*e/(e+m))}function u(t){var r,n,s,u,c,f,p,h,m,g,x=[],C=t.length,O=0,P=E,T=b;for(n=t.lastIndexOf(w),0>n&&(n=0),s=0;n>s;++s)t.charCodeAt(s)>=128&&e("not-basic"),x.push(t.charCodeAt(s));for(u=n>0?n+1:0;C>u;){for(c=O,f=1,p=d;u>=C&&e("invalid-input"),h=i(t.charCodeAt(u++)),(h>=d||h>_((l-O)/f))&&e("overflow"),O+=h*f,m=T>=p?y:p>=T+v?v:p-T,!(m>h);p+=d)g=d-m,f>_(l/g)&&e("overflow"),f*=g;r=x.length+1,T=a(O-c,r,0==c),_(O/r)>l-P&&e("overflow"),P+=_(O/r),O%=r,x.splice(O++,0,P)}return o(x)}function c(t){var r,o,i,u,c,f,p,h,m,g,x,C,O,P,T,j=[];for(t=n(t),C=t.length,r=E,o=0,c=b,f=0;C>f;++f)x=t[f],128>x&&j.push(A(x));for(i=u=j.length,u&&j.push(w);C>i;){for(p=l,f=0;C>f;++f)x=t[f],x>=r&&p>x&&(p=x);for(O=i+1,p-r>_((l-o)/O)&&e("overflow"),o+=(p-r)*O,r=p,f=0;C>f;++f)if(x=t[f],r>x&&++o>l&&e("overflow"),x==r){for(h=o,m=d;g=c>=m?y:m>=c+v?v:m-c,!(g>h);m+=d)T=h-g,P=d-g,j.push(A(s(g+T%P,0))),h=_(T/P);j.push(A(s(h,0))),c=a(o,O,i==u),o=0,++i}++o,++r}return j.join("")}function f(e){return r(e,function(e){return x.test(e)?u(e.slice(4).toLowerCase()):e})}function p(e){return r(e,function(e){return C.test(e)?"xn--"+c(e):e})}var h,l=2147483647,d=36,y=1,v=26,m=38,g=700,b=72,E=128,w="-",x=/^xn--/,C=/[^\x20-\x7E]/,O=/[\x2E\u3002\uFF0E\uFF61]/g,P={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},T=d-y,_=Math.floor,A=String.fromCharCode;return h={version:"1.3.2",ucs2:{decode:n,encode:o},decode:u,encode:c,toASCII:p,toUnicode:f}}();"undefined"==typeof COMPILED&&"undefined"!=typeof t&&(t.exports=n)},{}],19:[function(e,t,r){e("./schemes/http"),e("./schemes/urn"),e("./schemes/mailto")},{"./schemes/http":20,"./schemes/mailto":21,"./schemes/urn":22}],20:[function(e,t,r){if("undefined"==typeof COMPILED&&"undefined"==typeof n&&"function"==typeof e)var n=e("../uri");n.SCHEMES.http=n.SCHEMES.https={domainHost:!0,parse:function(e,t){return e.host||(e.error=e.error||"HTTP URIs must have a host."),e},serialize:function(e,t){return e.port!==("https"!==String(e.scheme).toLowerCase()?80:443)&&""!==e.port||(e.port=void 0),e.path||(e.path="/"),e}}},{"../uri":23}],21:[function(e,t,r){if("undefined"==typeof COMPILED&&"undefined"==typeof n&&"function"==typeof e)var n=e("../uri"),o=e("../punycode");!function(){function e(){for(var e=[],t=0;t1){e[0]=e[0].slice(0,-1);for(var r=e.length-1,n=1;r>n;++n)e[n]=e[n].slice(1,-1);return e[r]=e[r].slice(1),e.join("")}return e[0]}function t(e){return"(?:"+e+")"}function r(e){return e.toUpperCase()}function i(e){var t=n.pctDecChars(e);return t.match(D)?t:e}function s(e){return void 0!==e&&null!==e?e instanceof Array&&!e.callee?e:"number"!=typeof e.length||e.split||e.setInterval||e.call?[e]:Array.prototype.slice.call(e):[]}var a={},u=n.IRI_SUPPORT,c="[A-Za-z0-9\\-\\.\\_\\~"+(u?"\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF":"")+"]",f="[0-9A-Fa-f]",p=t(t("%[EFef]"+f+"%"+f+f+"%"+f+f)+"|"+t("%[89A-Fa-f]"+f+"%"+f+f)+"|"+t("%"+f+f)),h="[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]",l="[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]",d=e(l,'[\\"\\\\]'),y=t(h+"+"+t("\\."+h+"+")+"*"),v=t("\\\\"+d),m=t(l+"|"+v),g=t('\\"'+m+'*\\"'),b="[\\x21-\\x5A\\x5E-\\x7E]",E="[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]",w=t(c+"|"+p+"|"+E),x=t(y+"|\\["+b+"*\\]"),C=t(y+"|"+g),O=t(C+"\\@"+x),P=t(O+t("\\,"+O)+"*"),T=t(w+"*"),_=T,A=t(T+"\\="+_),j=t(A+t("\\&"+A)+"*"),S=t("\\?"+j),D=(n.VALIDATE_SUPPORT&&new RegExp("^mailto\\:"+P+"?"+S+"?$"),new RegExp(c,"g")),R=new RegExp(p,"g"),U=new RegExp(e("[^]",h,"[\\.]",'[\\"]',d),"g"),I=new RegExp(e("[^]",h,"[\\.]","[\\[]",b,"[\\]]"),"g"),N=new RegExp(e("[^]",c,E),"g"),F=N,q=n.VALIDATE_SUPPORT&&new RegExp("^"+P+"$"),H=n.VALIDATE_SUPPORT&&new RegExp("^"+j+"$");n.SCHEMES.mailto={parse:function(e,t){n.VALIDATE_SUPPORT&&!e.error&&(e.path&&!q.test(e.path)?e.error="Email address is not valid":e.query&&!H.test(e.query)&&(e.error="Header fields are invalid"));var r=e.to=e.path?e.path.split(","):[];if(e.path=void 0,e.query){for(var i=!1,s={},a=e.query.split("&"),u=0,c=a.length;c>u;++u){var f=a[u].split("=");switch(f[0]){case"to":for(var p=f[1].split(","),h=0,l=p.length;l>h;++h)r.push(p[h]);break;case"subject":e.subject=n.unescapeComponent(f[1],t);break;case"body":e.body=n.unescapeComponent(f[1],t);break;default:i=!0,s[n.unescapeComponent(f[0],t)]=n.unescapeComponent(f[1],t)}}i&&(e.headers=s)}e.query=void 0;for(var u=0,c=r.length;c>u;++u){var d=r[u].split("@");if(d[0]=n.unescapeComponent(d[0]),"undefined"==typeof o||t.unicodeSupport)d[1]=n.unescapeComponent(d[1],t).toLowerCase();else try{d[1]=o.toASCII(n.unescapeComponent(d[1],t).toLowerCase())}catch(y){e.error=e.error||"Email address's domain name can not be converted to ASCII via punycode: "+y}r[u]=d.join("@")}return e},serialize:function(e,t){var u=s(e.to);if(u){for(var c=0,f=u.length;f>c;++c){var p=String(u[c]),h=p.lastIndexOf("@"),l=p.slice(0,h),d=p.slice(h+1);if(l=l.replace(R,i).replace(R,r).replace(U,n.pctEncChar),"undefined"!=typeof o)try{d=t.iri?o.toUnicode(d):o.toASCII(n.unescapeComponent(d,t).toLowerCase())}catch(y){e.error=e.error||"Email address's domain name can not be converted to "+(t.iri?"Unicode":"ASCII")+" via punycode: "+y}else d=d.replace(R,i).toLowerCase().replace(R,r).replace(I,n.pctEncChar);u[c]=l+"@"+d}e.path=u.join(",")}var v=e.headers=e.headers||{};e.subject&&(v.subject=e.subject),e.body&&(v.body=e.body);var m=[];for(var g in v)v[g]!==a[g]&&m.push(g.replace(R,i).replace(R,r).replace(N,n.pctEncChar)+"="+v[g].replace(R,i).replace(R,r).replace(F,n.pctEncChar));return m.length&&(e.query=m.join("&")),e}}}()},{"../punycode":18,"../uri":23}],22:[function(e,t,r){if("undefined"==typeof COMPILED&&"undefined"==typeof n&&"function"==typeof e)var n=e("../uri");!function(){var e=n.pctEncChar,t="(?:[0-9A-Za-z][0-9A-Za-z\\-]{1,31})",r="(?:\\%[0-9A-Fa-f]{2})",o="[0-9A-Za-z\\(\\)\\+\\,\\-\\.\\:\\=\\@\\;\\$\\_\\!\\*\\'\\/\\?\\#]",i="(?:(?:"+r+"|"+o+")+)",s=new RegExp("^urn\\:("+t+")$"),a=new RegExp("^("+t+")\\:("+i+")$"),u=/^([^\:]+)\:(.*)/,c=/[\x00-\x20\\\"\&\<\>\[\]\^\`\{\|\}\~\x7F-\xFF]/g,f=/^[0-9A-Fa-f]{8}(?:\-[0-9A-Fa-f]{4}){3}\-[0-9A-Fa-f]{12}$/;n.SCHEMES.urn={parse:function(e,t){var r,o,i=e.path.match(a);return i||(t.tolerant||(e.error=e.error||"URN is not strictly valid."),i=e.path.match(u)),i?(r="urn:"+i[1].toLowerCase(),o=n.SCHEMES[r],o||(o=n.SCHEMES[r]={parse:function(e,t){return e},serialize:n.SCHEMES.urn.serialize}),e.scheme=r,e.path=i[2],e=o.parse(e,t)):e.error=e.error||"URN can not be parsed.",e},serialize:function(t,r){var n,o=t.scheme||r.scheme;if(o&&"urn"!==o){var n=o.match(s);n||(n=["urn:"+o,o]),t.scheme="urn",t.path=n[1]+":"+(t.path?t.path.replace(c,e):"")}return t}},n.SCHEMES["urn:uuid"]={parse:function(e,t){return t.tolerant||e.path&&e.path.match(f)||(e.error=e.error||"UUID is not valid."),e},serialize:function(e,t){return t.tolerant||e.path&&e.path.match(f)?e.path=(e.path||"").toLowerCase():e.scheme=void 0,n.SCHEMES.urn.serialize(e,t)}}}()},{"../uri":23}],23:[function(e,t,r){var n=!1,o=!0,i=!0,s=function(){function e(){for(var e=[],t=0;t1){e[0]=e[0].slice(0,-1);for(var r=e.length-1,n=1;r>n;++n)e[n]=e[n].slice(1,-1);return e[r]=e[r].slice(1),e.join("")}return e[0]}function t(e){return"(?:"+e+")"}function r(r){var n="[A-Za-z]",o="[0-9]",s=e(o,"[A-Fa-f]"),a=t(t("%[EFef]"+s+"%"+s+s+"%"+s+s)+"|"+t("%[89A-Fa-f]"+s+"%"+s+s)+"|"+t("%"+s+s)),u="[\\:\\/\\?\\#\\[\\]\\@]",c="[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]",f=e(u,c),p=r?"[\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]":"[]",h=r?"[\\uE000-\\uF8FF]":"[]",l=e(n,o,"[\\-\\.\\_\\~]",p),d=t(n+e(n,o,"[\\+\\-\\.]")+"*"),y=t(t(a+"|"+e(l,c,"[\\:]"))+"*"),v=t(t("25[0-5]")+"|"+t("2[0-4]"+o)+"|"+t("1"+o+o)+"|"+t("[1-9]"+o)+"|"+o),m=t(v+"\\."+v+"\\."+v+"\\."+v),g=t(s+"{1,4}"),b=(t(t(g+"\\:"+g)+"|"+m),t(e(l,c,"[\\:]")+"+")),E=t("v"+s+"+\\."+e(l,c,"[\\:]")+"+"),w=t("\\["+t(b+"|"+E)+"\\]"),x=t(t(a+"|"+e(l,c))+"*"),C=t(w+"|"+m+"(?!"+x+")|"+x),O=t(o+"*"),P=t(t(y+"@")+"?"+C+t("\\:"+O)+"?"),T=t(a+"|"+e(l,c,"[\\:\\@]")),_=t(T+"*"),A=t(T+"+"),j=t(t(a+"|"+e(l,c,"[\\@]"))+"+"),S=t(t("\\/"+_)+"*"),D=t("\\/"+t(A+S)+"?"),R=t(j+S),U=t(A+S),I="(?!"+T+")",N=(t(S+"|"+D+"|"+R+"|"+U+"|"+I),t(t(T+"|"+e("[\\/\\?]",h))+"*")),F=t(t(T+"|[\\/\\?]")+"*"),q=t(t("\\/\\/"+P+S)+"|"+D+"|"+U+"|"+I),H=t(d+"\\:"+q+t("\\?"+N)+"?"+t("\\#"+F)+"?"),k=t(t("\\/\\/"+P+S)+"|"+D+"|"+R+"|"+I),L=t(k+t("\\?"+N)+"?"+t("\\#"+F)+"?"),M=(t(H+"|"+L),t(d+"\\:"+q+t("\\?"+N)+"?"),"^("+d+")\\:"+t(t("\\/\\/("+t("("+y+")@")+"?("+C+")"+t("\\:("+O+")")+"?)")+"?("+S+"|"+D+"|"+U+"|"+I+")")+t("\\?("+N+")")+"?"+t("\\#("+F+")")+"?$"),$="^(){0}"+t(t("\\/\\/("+t("("+y+")@")+"?("+C+")"+t("\\:("+O+")")+"?)")+"?("+S+"|"+D+"|"+R+"|"+I+")")+t("\\?("+N+")")+"?"+t("\\#("+F+")")+"?$";"^("+d+")\\:"+t(t("\\/\\/("+t("("+y+")@")+"?("+C+")"+t("\\:("+O+")")+"?)")+"?("+S+"|"+D+"|"+U+"|"+I+")")+t("\\?("+N+")")+"?$","^"+t("\\#("+F+")")+"?$","^"+t("("+y+")@")+"?("+C+")"+t("\\:("+O+")")+"?$";return{URI_REF:i&&new RegExp("("+M+")|("+$+")"),NOT_SCHEME:new RegExp(e("[^]",n,o,"[\\+\\-\\.]"),"g"),NOT_USERINFO:new RegExp(e("[^\\%\\:]",l,c),"g"),NOT_HOST:new RegExp(e("[^\\%]",l,c),"g"),NOT_PATH:new RegExp(e("[^\\%\\/\\:\\@]",l,c),"g"),NOT_PATH_NOSCHEME:new RegExp(e("[^\\%\\/\\@]",l,c),"g"),NOT_QUERY:new RegExp(e("[^\\%]",l,c,"[\\:\\@\\/\\?]",h),"g"),NOT_FRAGMENT:new RegExp(e("[^\\%]",l,c,"[\\:\\@\\/\\?]"),"g"),ESCAPE:new RegExp(e("[^]",l,c),"g"),UNRESERVED:new RegExp(l,"g"),OTHER_CHARS:new RegExp(e("[^\\%]",l,f),"g"),PCT_ENCODED:new RegExp(a,"g")}}function n(e){var t,r=e.charCodeAt(0);return t=16>r?"%0"+r.toString(16).toUpperCase():128>r?"%"+r.toString(16).toUpperCase():2048>r?"%"+(r>>6|192).toString(16).toUpperCase()+"%"+(63&r|128).toString(16).toUpperCase():"%"+(r>>12|224).toString(16).toUpperCase()+"%"+(r>>6&63|128).toString(16).toUpperCase()+"%"+(63&r|128).toString(16).toUpperCase()}function s(e){for(var t,r,n,o="",i=0,s=e.length;s>i;)t=parseInt(e.substr(i+1,2),16),128>t?(o+=String.fromCharCode(t),i+=3):t>=194&&224>t?(s-i>=6?(r=parseInt(e.substr(i+4,2),16),o+=String.fromCharCode((31&t)<<6|63&r)):o+=e.substr(i,6),i+=6):t>=224?(s-i>=9?(r=parseInt(e.substr(i+4,2),16),n=parseInt(e.substr(i+7,2),16),o+=String.fromCharCode((15&t)<<12|(63&r)<<6|63&n)):o+=e.substr(i,9),i+=9):(o+=e.substr(i,3),i+=3);return o}function u(e){return void 0===e?"undefined":null===e?"null":Object.prototype.toString.call(e).split(" ").pop().split("]").shift().toLowerCase()}function c(e){return e.toUpperCase()}function f(e,t){function r(e){var r=s(e);return r.match(t.UNRESERVED)?r:e}return e.scheme&&(e.scheme=String(e.scheme).replace(t.PCT_ENCODED,r).toLowerCase().replace(t.NOT_SCHEME,"")),void 0!==e.userinfo&&(e.userinfo=String(e.userinfo).replace(t.PCT_ENCODED,r).replace(t.NOT_USERINFO,n).replace(t.PCT_ENCODED,c)),void 0!==e.host&&(e.host=String(e.host).replace(t.PCT_ENCODED,r).toLowerCase().replace(t.NOT_HOST,n).replace(t.PCT_ENCODED,c)),void 0!==e.path&&(e.path=String(e.path).replace(t.PCT_ENCODED,r).replace(e.scheme?t.NOT_PATH:t.NOT_PATH_NOSCHEME,n).replace(t.PCT_ENCODED,c)),void 0!==e.query&&(e.query=String(e.query).replace(t.PCT_ENCODED,r).replace(t.NOT_QUERY,n).replace(t.PCT_ENCODED,c)),void 0!==e.fragment&&(e.fragment=String(e.fragment).replace(t.PCT_ENCODED,r).replace(t.NOT_FRAGMENT,n).replace(t.PCT_ENCODED,c)),e}function p(e,t){void 0===t&&(t={});var r,n,u=o&&t.iri!==!1?x:w,c=!1,p={};if("suffix"===t.reference&&(e=(t.scheme?t.scheme+":":"")+"//"+e),i?(r=e.match(u.URI_REF),r&&(r=r[1]?r.slice(1,10):r.slice(10,19)),r||(c=!0,t.tolerant||(p.error=p.error||"URI is not strictly valid."),r=e.match(C))):r=e.match(C),r){if(A?(p.scheme=r[1],p.userinfo=r[3],p.host=r[4],p.port=parseInt(r[5],10),p.path=r[6]||"",p.query=r[7],p.fragment=r[8],isNaN(p.port)&&(p.port=r[5])):(p.scheme=r[1]||void 0,p.userinfo=-1!==e.indexOf("@")?r[3]:void 0,p.host=-1!==e.indexOf("//")?r[4]:void 0,p.port=parseInt(r[5],10),p.path=r[6]||"",p.query=-1!==e.indexOf("?")?r[7]:void 0,p.fragment=-1!==e.indexOf("#")?r[8]:void 0,isNaN(p.port)&&(p.port=e.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/)?r[4]:void 0)),void 0!==p.scheme||void 0!==p.userinfo||void 0!==p.host||void 0!==p.port||p.path||void 0!==p.query?void 0===p.scheme?p.reference="relative":void 0===p.fragment?p.reference="absolute":p.reference="uri":p.reference="same-document",t.reference&&"suffix"!==t.reference&&t.reference!==p.reference&&(p.error=p.error||"URI is not a "+t.reference+" reference."),n=j[(t.scheme||p.scheme||"").toLowerCase()],!o||"undefined"==typeof a||t.unicodeSupport||n&&n.unicodeSupport)f(p,u);else{if(p.host&&(t.domainHost||n&&n.domainHost))try{p.host=a.toASCII(p.host.replace(u.PCT_ENCODED,s).toLowerCase())}catch(h){p.error=p.error||"Host's domain name can not be converted to ASCII via punycode: "+h}f(p,w)}n&&n.parse&&n.parse(p,t)}else c=!0,p.error=p.error||"URI can not be parsed.";return p}function h(e,t){var r=[];return void 0!==e.userinfo&&(r.push(e.userinfo),r.push("@")),void 0!==e.host&&r.push(e.host),"number"==typeof e.port&&(r.push(":"),r.push(e.port.toString(10))),r.length?r.join(""):void 0}function l(e){for(var t,r=[];e.length;)e.match(O)?e=e.replace(O,""):e.match(P)?e=e.replace(P,"/"):e.match(T)?(e=e.replace(T,"/"),r.pop()):"."===e||".."===e?e="":(t=e.match(_)[0],e=e.slice(t.length),r.push(t));return r.join("")}function d(e,t){void 0===t&&(t={});var r,n,i,u=o&&t.iri?x:w,c=[];if(r=j[(t.scheme||e.scheme||"").toLowerCase()],r&&r.serialize&&r.serialize(e,t),o&&"undefined"!=typeof a&&e.host&&(t.domainHost||r&&r.domainHost))try{e.host=t.iri?a.toUnicode(e.host):a.toASCII(e.host.replace(u.PCT_ENCODED,s).toLowerCase())}catch(p){e.error=e.error||"Host's domain name can not be converted to "+(t.iri?"Unicode":"ASCII")+" via punycode: "+p}return f(e,u),"suffix"!==t.reference&&e.scheme&&(c.push(e.scheme),c.push(":")),n=h(e,t),void 0!==n&&("suffix"!==t.reference&&c.push("//"),c.push(n),e.path&&"/"!==e.path.charAt(0)&&c.push("/")),void 0!==e.path&&(i=e.path,t.absolutePath||r&&r.absolutePath||(i=l(i)),void 0===n&&(i=i.replace(/^\/\//,"/%2F")),c.push(i)),void 0!==e.query&&(c.push("?"),c.push(e.query)),void 0!==e.fragment&&(c.push("#"),c.push(e.fragment)),c.join("")}function y(e,t,r,n){void 0===r&&(r={});var o={};return n||(e=p(d(e,r),r),t=p(d(t,r),r)),r=r||{},!r.tolerant&&t.scheme?(o.scheme=t.scheme,o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=l(t.path),o.query=t.query):(void 0!==t.userinfo||void 0!==t.host||void 0!==t.port?(o.userinfo=t.userinfo,o.host=t.host,o.port=t.port,o.path=l(t.path),o.query=t.query):(t.path?("/"===t.path.charAt(0)?o.path=l(t.path):(void 0===e.userinfo&&void 0===e.host&&void 0===e.port||e.path?e.path?o.path=e.path.slice(0,e.path.lastIndexOf("/")+1)+t.path:o.path=t.path:o.path="/"+t.path,o.path=l(o.path)),o.query=t.query):(o.path=e.path,void 0!==t.query?o.query=t.query:o.query=e.query),o.userinfo=e.userinfo,o.host=e.host,o.port=e.port),o.scheme=e.scheme),o.fragment=t.fragment,o}function v(e,t,r){return d(y(p(e,r),p(t,r),r,!0),r)}function m(e,t){return"string"==typeof e?e=d(p(e,t),t):"object"===u(e)&&(e=p(d(e,t),t)),e}function g(e,t,r){return"string"==typeof e?e=d(p(e,r),r):"object"===u(e)&&(e=d(e,r)),"string"==typeof t?t=d(p(t,r),r):"object"===u(t)&&(t=d(t,r)),e===t}function b(e,t){return e&&e.toString().replace(o&&t&&t.iri?x.ESCAPE:w.ESCAPE,n)}function E(e,t){return e&&e.toString().replace(o&&t&&t.iri?x.PCT_ENCODED:w.PCT_ENCODED,s)}var w=r(!1),x=o?r(!0):void 0,C=/^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?([^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n)*))?/i,O=/^\.\.?\//,P=/^\/\.(\/|$)/,T=/^\/\.\.(\/|$)/,_=/^\/?(?:.|\n)*?(?=\/|$)/,A=void 0==="".match(/(){0}/)[1],j={};return{IRI_SUPPORT:o,VALIDATE_SUPPORT:i,pctEncChar:n,pctDecChars:s,SCHEMES:j,parse:p,_recomposeAuthority:h,removeDotSegments:l,serialize:d,resolveComponents:y,resolve:v,normalize:m,equal:g,escapeComponent:b,unescapeComponent:E}}();if(!n&&"undefined"!=typeof t&&"function"==typeof e){var a=e("./punycode");t.exports=s,e("./schemes")}},{"./punycode":18,"./schemes":19}]},{},[1])(1)}); --------------------------------------------------------------------------------