mirror of
https://github.com/aelve/guide.git
synced 2024-11-21 16:03:42 +03:00
Commit the JS bundle for the old frontend into the repo (#396)
This commit is contained in:
parent
7e5612dc35
commit
c9e95986ba
5
.gitignore
vendored
5
.gitignore
vendored
@ -42,10 +42,5 @@ tags
|
||||
# backend config
|
||||
/back/config.json
|
||||
|
||||
# backend JavaScript artifacts
|
||||
/back/guidejs/node_modules/
|
||||
/back/guidejs/package-lock.json
|
||||
/back/static/js/
|
||||
|
||||
# frontend
|
||||
/front/node_modules/
|
||||
|
@ -19,9 +19,6 @@ cache:
|
||||
- $HOME/.stack
|
||||
- .stack-work
|
||||
- back/.stack-work
|
||||
- back/static/js # Needed so that bundle.js would persist to the
|
||||
# "Push Docker image" stage and beyond - without it
|
||||
# the backend doesn't work and the tests don't run.
|
||||
timeout: 1000
|
||||
|
||||
jobs:
|
||||
|
@ -16,7 +16,7 @@ The `back/config.json` file contains the config (it will be created at the first
|
||||
|
||||
# How to install locally
|
||||
|
||||
First install NPM (important!) and `libpq`. Then do:
|
||||
First install `libpq`. Then do:
|
||||
|
||||
$ make back
|
||||
$ make back/run
|
||||
@ -31,7 +31,7 @@ Create a droplet with Ubuntu. Install Stack (this command will import a GPG key,
|
||||
|
||||
$ curl -sSL https://get.haskellstack.org/ | sh
|
||||
|
||||
Install NPM and `libpq`.
|
||||
Install `libpq`.
|
||||
|
||||
Clone and build `guide`:
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
import Distribution.Simple
|
||||
import System.Process
|
||||
import System.Environment (lookupEnv)
|
||||
|
||||
main = do
|
||||
js <- lookupEnv "NO_JS"
|
||||
case js of
|
||||
Just "true" -> defaultMain
|
||||
_ -> do
|
||||
hooks <- buildJS simpleUserHooks
|
||||
defaultMainWithHooks hooks
|
||||
|
||||
buildJS hooks = do
|
||||
let originalPostBuild = postBuild hooks
|
||||
return $ hooks {
|
||||
postBuild = \args flags pkgDesc localBuildInfo -> do
|
||||
let npmbuild = proc "sh" ["./buildjs.sh"]
|
||||
(_, _, _, buildHandle) <- createProcess npmbuild
|
||||
waitForProcess buildHandle
|
||||
originalPostBuild args flags pkgDesc localBuildInfo
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
|
||||
cd guidejs
|
||||
echo "PWD is $PWD"
|
||||
npm install
|
||||
npm run build
|
||||
rm -rf ../static/js
|
||||
cp -r ./dist/ ../static/js
|
||||
cd ..
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": [
|
||||
"last 2 versions"
|
||||
]
|
||||
},
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
]
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
# Developing this module
|
||||
|
||||
To work on this module, it's necessary to use either
|
||||
[yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com/). Yarn is an
|
||||
alternative to npm by Facebook, but they perform the same function here.
|
||||
|
||||
When run in the parent directory, `stack build` will run a build script in the
|
||||
root to install any dependencies and build the output bundle.
|
||||
|
||||
# Motivation for this module
|
||||
|
||||
The situation for handling client-side CSRF token injection was unsatisfying, to
|
||||
say the least. Without performing significant surgery on the types and method
|
||||
the Guide uses to generate JavaScript functions, our best option is to modify
|
||||
the jQuery `$.ajax()` or `$.post()` functions.
|
||||
|
||||
There are a grand total of four packages on [npmjs.com](https://npmjs.com) that
|
||||
show up for "jquery csrf". The most promising is `jquery-csrf-token`. It has two
|
||||
problems, one technical and one contextual.
|
||||
|
||||
1. It does not filter based on the URL, it is a shotgun. Not knowing a lot about
|
||||
how Spock generates and validates CSRF tokens or how that could change, we
|
||||
should defensively program around the worst case: CSRF tokens are valid for a
|
||||
really long time beyond a user's session, and leaking one could be bad.
|
||||
|
||||
2. It gets ~40 downloads a month. Let's not let ourselves be `left-pad`ed.
|
||||
|
||||
So we will include the source (it's relatively short) and add the modifications
|
||||
we need, and _also_ provide a nice path forward for building a
|
||||
single-source-of-truth for client JavaScript for the project. Since
|
||||
`jquery-csrf-token` uses [Rollup](http://rollupjs.org/), we will too.
|
||||
|
||||
We will also use URL parsing to make sure that we only send the CSRF token to
|
||||
the a relative URI. Rollup will come in handy here because IE11 (ugh) and Opera
|
||||
Mini (what?) do not support the URL API and so we'll polyfill it.
|
||||
|
||||
Other features may be added as needed and will be documented here.
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "guidejs",
|
||||
"version": "0.1.0",
|
||||
"description": "Aelve Guide client-side scripts.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "npm run build:prod",
|
||||
"build:dev": "npm run clean && rollup -c",
|
||||
"build:prod": "npm run clean && cross-env NODE_ENV=production rollup -c",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"clean": "rimraf ./dist/*"
|
||||
},
|
||||
"author": "Aaron Friel",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"jquery": "^3.4.1",
|
||||
"url-parse": "^1.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-babili": "^0.0.12",
|
||||
"babel-preset-env": "^1.2.2",
|
||||
"cross-env": "^5.2.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"rollup": "^0.41.6",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-babili": "^1.1.1",
|
||||
"rollup-plugin-commonjs": "^8.0.2",
|
||||
"rollup-plugin-node-resolve": "^2.0.0",
|
||||
"rollup-plugin-replace": "^1.1.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@types/jquery": "^2.0.41"
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import replace from 'rollup-plugin-replace';
|
||||
import babili from 'rollup-plugin-babili';
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
const IS_PRODUCTION = NODE_ENV === 'production';
|
||||
|
||||
let config = {
|
||||
entry: './src/index.js',
|
||||
moduleName: 'guidejs',
|
||||
dest: './dist/bundle.js',
|
||||
format: 'iife',
|
||||
sourceMap: IS_PRODUCTION,
|
||||
plugins: [
|
||||
// TODO: For production, replace with production and minify.
|
||||
nodeResolve({
|
||||
module: true,
|
||||
jsnext: true,
|
||||
main: true,
|
||||
}),
|
||||
commonjs(),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
|
||||
}),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
runtimeHelpers: true,
|
||||
}),
|
||||
]
|
||||
};
|
||||
|
||||
// Reduces the size by about half.
|
||||
if (IS_PRODUCTION) {
|
||||
console.log('Production build');
|
||||
config.plugins.unshift(
|
||||
babili({
|
||||
comments: false,
|
||||
sourceMap: true,
|
||||
mangle: true,
|
||||
evaluate: true
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('Development build');
|
||||
}
|
||||
|
||||
export default config;
|
@ -1,26 +0,0 @@
|
||||
import URL from './url-polyfill';
|
||||
import { csrfPrefilter as oldPrefilter, enable as jqueryCsrfEnable } from './jquery-csrf-token';
|
||||
|
||||
// Now we patch in our own prefilter from url-parse, and layer it with the one from jqueryCsrfToken.
|
||||
function originFilter(options, ...args) {
|
||||
let docOrigin = document.location.origin;
|
||||
let reqOrigin = (new URL(options.url, document.location)).origin;
|
||||
|
||||
// For now, only test to make sure the origins are the same.
|
||||
// TODO: Filter to say, a /api/ prefix?
|
||||
if (docOrigin === reqOrigin) {
|
||||
oldPrefilter(options, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
function enable(csrfKey, csrfValue) {
|
||||
jqueryCsrfEnable(csrfValue, {
|
||||
key: csrfKey,
|
||||
prefilter: originFilter,
|
||||
retry: null,
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
enable
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
import jquery from 'jquery';
|
||||
// export jQuery to the globals.
|
||||
window.$ = window.jquery = window.jQuery = jquery;
|
||||
|
||||
import csrfProtection from './csrfProtection.js';
|
||||
|
||||
export default {
|
||||
csrfProtection
|
||||
};
|
160
back/guidejs/src/jquery-csrf-token/index.js
vendored
160
back/guidejs/src/jquery-csrf-token/index.js
vendored
@ -1,160 +0,0 @@
|
||||
|
||||
import jQuery from 'jquery';
|
||||
|
||||
let $ = jQuery;
|
||||
|
||||
const config = {};
|
||||
let token = null;
|
||||
|
||||
// Function ripped from Django docs.
|
||||
// See: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
|
||||
function csrfSafeMethod(method) {
|
||||
// These HTTP methods do not require CSRF protection.
|
||||
return (/^(GET|HEAD|OPTIONS|TRACE)$/i.test(method));
|
||||
}
|
||||
|
||||
export function csrfPrefilter(options, ...args) {
|
||||
// The header should only be set when the request is local.
|
||||
if (!csrfSafeMethod(options.type) && !options.crossDomain) {
|
||||
const oldBeforeSend = options.beforeSend;
|
||||
options.beforeSend = function (xhr) {
|
||||
// The csrf token is valid for the duration of the session,
|
||||
// so it's safe to use a static token.
|
||||
xhr.setRequestHeader(config.key, token);
|
||||
if (oldBeforeSend) {
|
||||
oldBeforeSend(...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function setToken(newToken) {
|
||||
token = newToken;
|
||||
}
|
||||
|
||||
|
||||
/* Patch $.ajax to support expired CSRF tokens */
|
||||
function addRetrySupport(retryURL, parseResponse, isCSRFFailure) {
|
||||
if (!isCSRFFailure) {
|
||||
isCSRFFailure = xhr => xhr.status === 403;
|
||||
}
|
||||
const originalAjax = $.ajax;
|
||||
|
||||
/**
|
||||
* Copy properties from jqXhrToCopy to fakeJqXhr. This is makes fakeJqXhr
|
||||
* behave properly.
|
||||
*/
|
||||
function fakeJqXhrInheritance(fakeJqXhr, jqXhrToCopy) {
|
||||
Object.keys(jqXhrToCopy).forEach((key) => {
|
||||
if (typeof jqXhrToCopy[key] === 'function') {
|
||||
fakeJqXhr[key] = jqXhrToCopy[key].bind(jqXhrToCopy);
|
||||
} else {
|
||||
fakeJqXhr[key] = jqXhrToCopy[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch $.ajax to support expired csrf tokens. If a request is made and the
|
||||
* token is expired, then a new token is fetched from the server. The original
|
||||
* request will be run again with the new token.
|
||||
*
|
||||
* For the outside world only 1 request is send, but depending on the situation
|
||||
* at most 3 request can be executed.
|
||||
*/
|
||||
$.ajax = function (url, options) {
|
||||
const pResult = $.Deferred(); // eslint-disable-line new-cap
|
||||
const fakeJqXhr = pResult.promise();
|
||||
|
||||
if (typeof url === 'object') {
|
||||
options = url;
|
||||
url = undefined;
|
||||
} else {
|
||||
options.url = url;
|
||||
}
|
||||
|
||||
// The original ajax request might have success or error callbacks. We want
|
||||
// to trigger them manually based on if there is a csrf token mismatch.
|
||||
const success = options.success;
|
||||
const error = options.error;
|
||||
delete options.success;
|
||||
delete options.error;
|
||||
|
||||
// Fire the first try!
|
||||
const xhrFirstTry = originalAjax(options);
|
||||
|
||||
xhrFirstTry.error((jqXHR, textStatus, errorThrown) => {
|
||||
if (isCSRFFailure(jqXHR)) {
|
||||
// We assume that a csrf token mismatch happend, so fetch a new
|
||||
// token and retry with the correct token.
|
||||
originalAjax(retryURL).done((data) => {
|
||||
setToken(parseResponse(data));
|
||||
let xhrSecondTry = null;
|
||||
|
||||
options.success = (dataSecondSuccess, textStatusSecondSuccess, jqXHRSecondSuccess) => {
|
||||
if (typeof success === 'function') success(dataSecondSuccess, textStatusSecondSuccess, jqXHRSecondSuccess);
|
||||
pResult.resolve(dataSecondSuccess, textStatusSecondSuccess, jqXHRSecondSuccess);
|
||||
};
|
||||
|
||||
options.error = (jqXHRSecondError, textStatusSecondError, errorThrownSecondError) => {
|
||||
if (typeof error === 'function') error(jqXHRSecondError, textStatusSecondError, errorThrownSecondError);
|
||||
pResult.reject(jqXHRSecondError, textStatusSecondError, errorThrownSecondError);
|
||||
};
|
||||
|
||||
xhrSecondTry = originalAjax(options);
|
||||
fakeJqXhrInheritance(fakeJqXhr, xhrSecondTry);
|
||||
});
|
||||
} else {
|
||||
// Some other error happend, so just pass it through.
|
||||
fakeJqXhrInheritance(fakeJqXhr, xhrFirstTry);
|
||||
if (typeof error === 'function') error(jqXHR, textStatus, errorThrown);
|
||||
pResult.reject(jqXHR, textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
|
||||
// Upon success, update our fakeJqXhr and trigger the success callback.
|
||||
xhrFirstTry.success((data, textStatus, jqXHR) => {
|
||||
fakeJqXhrInheritance(fakeJqXhr, xhrFirstTry);
|
||||
if (typeof success === 'function') success(data, textStatus, jqXHR);
|
||||
|
||||
pResult.resolve(data, textStatus, jqXHR);
|
||||
});
|
||||
|
||||
fakeJqXhrInheritance(fakeJqXhr, xhrFirstTry);
|
||||
|
||||
return fakeJqXhr;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function enable(newToken, newConfig) {
|
||||
newConfig || (newConfig = {});
|
||||
|
||||
if (!newToken) {
|
||||
console.warn('CSRF token is not set!');
|
||||
}
|
||||
|
||||
if (!newConfig.key) {
|
||||
newConfig.key = 'X-CSRF-TOKEN';
|
||||
}
|
||||
|
||||
if (!newConfig.prefilter) {
|
||||
newConfig.prefilter = csrfPrefilter;
|
||||
}
|
||||
|
||||
config.key = newConfig.key;
|
||||
|
||||
if (newConfig.retry) {
|
||||
addRetrySupport(newConfig.retry.url, newConfig.retry.parseResponse,
|
||||
newConfig.retry.isCSRFFailure);
|
||||
}
|
||||
|
||||
setToken(newToken);
|
||||
|
||||
// Set a header on every request with the current csrf token in it.
|
||||
$.ajaxPrefilter(newConfig.prefilter);
|
||||
}
|
||||
|
||||
export function mockJQuery(mockedJquery) {
|
||||
$ = mockedJquery;
|
||||
}
|
@ -1,623 +0,0 @@
|
||||
/// from https://raw.githubusercontent.com/webcomponents/URL/master/url.js
|
||||
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
var module = {};
|
||||
|
||||
(function(scope) {
|
||||
'use strict';
|
||||
|
||||
// feature detect for URL constructor
|
||||
var hasWorkingUrl = false;
|
||||
if (!scope.forceJURL) {
|
||||
try {
|
||||
var u = new URL('b', 'http://a');
|
||||
u.pathname = 'c%20d';
|
||||
hasWorkingUrl = u.href === 'http://a/c%20d';
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (hasWorkingUrl)
|
||||
return;
|
||||
|
||||
var relative = Object.create(null);
|
||||
relative['ftp'] = 21;
|
||||
relative['file'] = 0;
|
||||
relative['gopher'] = 70;
|
||||
relative['http'] = 80;
|
||||
relative['https'] = 443;
|
||||
relative['ws'] = 80;
|
||||
relative['wss'] = 443;
|
||||
|
||||
var relativePathDotMapping = Object.create(null);
|
||||
relativePathDotMapping['%2e'] = '.';
|
||||
relativePathDotMapping['.%2e'] = '..';
|
||||
relativePathDotMapping['%2e.'] = '..';
|
||||
relativePathDotMapping['%2e%2e'] = '..';
|
||||
|
||||
function isRelativeScheme(scheme) {
|
||||
return relative[scheme] !== undefined;
|
||||
}
|
||||
|
||||
function invalid() {
|
||||
clear.call(this);
|
||||
this._isInvalid = true;
|
||||
}
|
||||
|
||||
function IDNAToASCII(h) {
|
||||
if ('' == h) {
|
||||
invalid.call(this)
|
||||
}
|
||||
// XXX
|
||||
return h.toLowerCase()
|
||||
}
|
||||
|
||||
function percentEscape(c) {
|
||||
var unicode = c.charCodeAt(0);
|
||||
if (unicode > 0x20 &&
|
||||
unicode < 0x7F &&
|
||||
// " # < > ? `
|
||||
[0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
return encodeURIComponent(c);
|
||||
}
|
||||
|
||||
function percentEscapeQuery(c) {
|
||||
// XXX This actually needs to encode c using encoding and then
|
||||
// convert the bytes one-by-one.
|
||||
|
||||
var unicode = c.charCodeAt(0);
|
||||
if (unicode > 0x20 &&
|
||||
unicode < 0x7F &&
|
||||
// " # < > ` (do not escape '?')
|
||||
[0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
return encodeURIComponent(c);
|
||||
}
|
||||
|
||||
var EOF = undefined,
|
||||
ALPHA = /[a-zA-Z]/,
|
||||
ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
|
||||
|
||||
function parse(input, stateOverride, base) {
|
||||
function err(message) {
|
||||
errors.push(message)
|
||||
}
|
||||
|
||||
var state = stateOverride || 'scheme start',
|
||||
cursor = 0,
|
||||
buffer = '',
|
||||
seenAt = false,
|
||||
seenBracket = false,
|
||||
errors = [];
|
||||
|
||||
loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
|
||||
var c = input[cursor];
|
||||
switch (state) {
|
||||
case 'scheme start':
|
||||
if (c && ALPHA.test(c)) {
|
||||
buffer += c.toLowerCase(); // ASCII-safe
|
||||
state = 'scheme';
|
||||
} else if (!stateOverride) {
|
||||
buffer = '';
|
||||
state = 'no scheme';
|
||||
continue;
|
||||
} else {
|
||||
err('Invalid scheme.');
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'scheme':
|
||||
if (c && ALPHANUMERIC.test(c)) {
|
||||
buffer += c.toLowerCase(); // ASCII-safe
|
||||
} else if (':' == c) {
|
||||
this._scheme = buffer;
|
||||
buffer = '';
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
if (isRelativeScheme(this._scheme)) {
|
||||
this._isRelative = true;
|
||||
}
|
||||
if ('file' == this._scheme) {
|
||||
state = 'relative';
|
||||
} else if (this._isRelative && base && base._scheme == this._scheme) {
|
||||
state = 'relative or authority';
|
||||
} else if (this._isRelative) {
|
||||
state = 'authority first slash';
|
||||
} else {
|
||||
state = 'scheme data';
|
||||
}
|
||||
} else if (!stateOverride) {
|
||||
buffer = '';
|
||||
cursor = 0;
|
||||
state = 'no scheme';
|
||||
continue;
|
||||
} else if (EOF == c) {
|
||||
break loop;
|
||||
} else {
|
||||
err('Code point not allowed in scheme: ' + c)
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'scheme data':
|
||||
if ('?' == c) {
|
||||
this._query = '?';
|
||||
state = 'query';
|
||||
} else if ('#' == c) {
|
||||
this._fragment = '#';
|
||||
state = 'fragment';
|
||||
} else {
|
||||
// XXX error handling
|
||||
if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
||||
this._schemeData += percentEscape(c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'no scheme':
|
||||
if (!base || !(isRelativeScheme(base._scheme))) {
|
||||
err('Missing scheme.');
|
||||
invalid.call(this);
|
||||
} else {
|
||||
state = 'relative';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'relative or authority':
|
||||
if ('/' == c && '/' == input[cursor+1]) {
|
||||
state = 'authority ignore slashes';
|
||||
} else {
|
||||
err('Expected /, got: ' + c);
|
||||
state = 'relative';
|
||||
continue
|
||||
}
|
||||
break;
|
||||
|
||||
case 'relative':
|
||||
this._isRelative = true;
|
||||
if ('file' != this._scheme)
|
||||
this._scheme = base._scheme;
|
||||
if (EOF == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = base._query;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
break loop;
|
||||
} else if ('/' == c || '\\' == c) {
|
||||
if ('\\' == c)
|
||||
err('\\ is an invalid code point.');
|
||||
state = 'relative slash';
|
||||
} else if ('?' == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = '?';
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
state = 'query';
|
||||
} else if ('#' == c) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._path = base._path.slice();
|
||||
this._query = base._query;
|
||||
this._fragment = '#';
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
state = 'fragment';
|
||||
} else {
|
||||
var nextC = input[cursor+1]
|
||||
var nextNextC = input[cursor+2]
|
||||
if (
|
||||
'file' != this._scheme || !ALPHA.test(c) ||
|
||||
(nextC != ':' && nextC != '|') ||
|
||||
(EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
this._path = base._path.slice();
|
||||
this._path.pop();
|
||||
}
|
||||
state = 'relative path';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'relative slash':
|
||||
if ('/' == c || '\\' == c) {
|
||||
if ('\\' == c) {
|
||||
err('\\ is an invalid code point.');
|
||||
}
|
||||
if ('file' == this._scheme) {
|
||||
state = 'file host';
|
||||
} else {
|
||||
state = 'authority ignore slashes';
|
||||
}
|
||||
} else {
|
||||
if ('file' != this._scheme) {
|
||||
this._host = base._host;
|
||||
this._port = base._port;
|
||||
this._username = base._username;
|
||||
this._password = base._password;
|
||||
}
|
||||
state = 'relative path';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'authority first slash':
|
||||
if ('/' == c) {
|
||||
state = 'authority second slash';
|
||||
} else {
|
||||
err("Expected '/', got: " + c);
|
||||
state = 'authority ignore slashes';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'authority second slash':
|
||||
state = 'authority ignore slashes';
|
||||
if ('/' != c) {
|
||||
err("Expected '/', got: " + c);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'authority ignore slashes':
|
||||
if ('/' != c && '\\' != c) {
|
||||
state = 'authority';
|
||||
continue;
|
||||
} else {
|
||||
err('Expected authority, got: ' + c);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'authority':
|
||||
if ('@' == c) {
|
||||
if (seenAt) {
|
||||
err('@ already seen.');
|
||||
buffer += '%40';
|
||||
}
|
||||
seenAt = true;
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
var cp = buffer[i];
|
||||
if ('\t' == cp || '\n' == cp || '\r' == cp) {
|
||||
err('Invalid whitespace in authority.');
|
||||
continue;
|
||||
}
|
||||
// XXX check URL code points
|
||||
if (':' == cp && null === this._password) {
|
||||
this._password = '';
|
||||
continue;
|
||||
}
|
||||
var tempC = percentEscape(cp);
|
||||
(null !== this._password) ? this._password += tempC : this._username += tempC;
|
||||
}
|
||||
buffer = '';
|
||||
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
||||
cursor -= buffer.length;
|
||||
buffer = '';
|
||||
state = 'host';
|
||||
continue;
|
||||
} else {
|
||||
buffer += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'file host':
|
||||
if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
||||
if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
|
||||
state = 'relative path';
|
||||
} else if (buffer.length == 0) {
|
||||
state = 'relative path start';
|
||||
} else {
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = '';
|
||||
state = 'relative path start';
|
||||
}
|
||||
continue;
|
||||
} else if ('\t' == c || '\n' == c || '\r' == c) {
|
||||
err('Invalid whitespace in file host.');
|
||||
} else {
|
||||
buffer += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'host':
|
||||
case 'hostname':
|
||||
if (':' == c && !seenBracket) {
|
||||
// XXX host parsing
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = '';
|
||||
state = 'port';
|
||||
if ('hostname' == stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
||||
this._host = IDNAToASCII.call(this, buffer);
|
||||
buffer = '';
|
||||
state = 'relative path start';
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
continue;
|
||||
} else if ('\t' != c && '\n' != c && '\r' != c) {
|
||||
if ('[' == c) {
|
||||
seenBracket = true;
|
||||
} else if (']' == c) {
|
||||
seenBracket = false;
|
||||
}
|
||||
buffer += c;
|
||||
} else {
|
||||
err('Invalid code point in host/hostname: ' + c);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'port':
|
||||
if (/[0-9]/.test(c)) {
|
||||
buffer += c;
|
||||
} else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
|
||||
if ('' != buffer) {
|
||||
var temp = parseInt(buffer, 10);
|
||||
if (temp != relative[this._scheme]) {
|
||||
this._port = temp + '';
|
||||
}
|
||||
buffer = '';
|
||||
}
|
||||
if (stateOverride) {
|
||||
break loop;
|
||||
}
|
||||
state = 'relative path start';
|
||||
continue;
|
||||
} else if ('\t' == c || '\n' == c || '\r' == c) {
|
||||
err('Invalid code point in port: ' + c);
|
||||
} else {
|
||||
invalid.call(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'relative path start':
|
||||
if ('\\' == c)
|
||||
err("'\\' not allowed in path.");
|
||||
state = 'relative path';
|
||||
if ('/' != c && '\\' != c) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'relative path':
|
||||
if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
|
||||
if ('\\' == c) {
|
||||
err('\\ not allowed in relative path.');
|
||||
}
|
||||
var tmp;
|
||||
if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
|
||||
buffer = tmp;
|
||||
}
|
||||
if ('..' == buffer) {
|
||||
this._path.pop();
|
||||
if ('/' != c && '\\' != c) {
|
||||
this._path.push('');
|
||||
}
|
||||
} else if ('.' == buffer && '/' != c && '\\' != c) {
|
||||
this._path.push('');
|
||||
} else if ('.' != buffer) {
|
||||
if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
|
||||
buffer = buffer[0] + ':';
|
||||
}
|
||||
this._path.push(buffer);
|
||||
}
|
||||
buffer = '';
|
||||
if ('?' == c) {
|
||||
this._query = '?';
|
||||
state = 'query';
|
||||
} else if ('#' == c) {
|
||||
this._fragment = '#';
|
||||
state = 'fragment';
|
||||
}
|
||||
} else if ('\t' != c && '\n' != c && '\r' != c) {
|
||||
buffer += percentEscape(c);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'query':
|
||||
if (!stateOverride && '#' == c) {
|
||||
this._fragment = '#';
|
||||
state = 'fragment';
|
||||
} else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
||||
this._query += percentEscapeQuery(c);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fragment':
|
||||
if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
||||
this._fragment += c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
this._scheme = '';
|
||||
this._schemeData = '';
|
||||
this._username = '';
|
||||
this._password = null;
|
||||
this._host = '';
|
||||
this._port = '';
|
||||
this._path = [];
|
||||
this._query = '';
|
||||
this._fragment = '';
|
||||
this._isInvalid = false;
|
||||
this._isRelative = false;
|
||||
}
|
||||
|
||||
// Does not process domain names or IP addresses.
|
||||
// Does not handle encoding for the query parameter.
|
||||
function jURL(url, base /* , encoding */) {
|
||||
if (base !== undefined && !(base instanceof jURL))
|
||||
base = new jURL(String(base));
|
||||
|
||||
this._url = url;
|
||||
clear.call(this);
|
||||
|
||||
var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
|
||||
// encoding = encoding || 'utf-8'
|
||||
|
||||
parse.call(this, input, null, base);
|
||||
}
|
||||
|
||||
jURL.prototype = {
|
||||
toString: function() {
|
||||
return this.href;
|
||||
},
|
||||
get href() {
|
||||
if (this._isInvalid)
|
||||
return this._url;
|
||||
|
||||
var authority = '';
|
||||
if ('' != this._username || null != this._password) {
|
||||
authority = this._username +
|
||||
(null != this._password ? ':' + this._password : '') + '@';
|
||||
}
|
||||
|
||||
return this.protocol +
|
||||
(this._isRelative ? '//' + authority + this.host : '') +
|
||||
this.pathname + this._query + this._fragment;
|
||||
},
|
||||
set href(href) {
|
||||
clear.call(this);
|
||||
parse.call(this, href);
|
||||
},
|
||||
|
||||
get protocol() {
|
||||
return this._scheme + ':';
|
||||
},
|
||||
set protocol(protocol) {
|
||||
if (this._isInvalid)
|
||||
return;
|
||||
parse.call(this, protocol + ':', 'scheme start');
|
||||
},
|
||||
|
||||
get host() {
|
||||
return this._isInvalid ? '' : this._port ?
|
||||
this._host + ':' + this._port : this._host;
|
||||
},
|
||||
set host(host) {
|
||||
if (this._isInvalid || !this._isRelative)
|
||||
return;
|
||||
parse.call(this, host, 'host');
|
||||
},
|
||||
|
||||
get hostname() {
|
||||
return this._host;
|
||||
},
|
||||
set hostname(hostname) {
|
||||
if (this._isInvalid || !this._isRelative)
|
||||
return;
|
||||
parse.call(this, hostname, 'hostname');
|
||||
},
|
||||
|
||||
get port() {
|
||||
return this._port;
|
||||
},
|
||||
set port(port) {
|
||||
if (this._isInvalid || !this._isRelative)
|
||||
return;
|
||||
parse.call(this, port, 'port');
|
||||
},
|
||||
|
||||
get pathname() {
|
||||
return this._isInvalid ? '' : this._isRelative ?
|
||||
'/' + this._path.join('/') : this._schemeData;
|
||||
},
|
||||
set pathname(pathname) {
|
||||
if (this._isInvalid || !this._isRelative)
|
||||
return;
|
||||
this._path = [];
|
||||
parse.call(this, pathname, 'relative path start');
|
||||
},
|
||||
|
||||
get search() {
|
||||
return this._isInvalid || !this._query || '?' == this._query ?
|
||||
'' : this._query;
|
||||
},
|
||||
set search(search) {
|
||||
if (this._isInvalid || !this._isRelative)
|
||||
return;
|
||||
this._query = '?';
|
||||
if ('?' == search[0])
|
||||
search = search.slice(1);
|
||||
parse.call(this, search, 'query');
|
||||
},
|
||||
|
||||
get hash() {
|
||||
return this._isInvalid || !this._fragment || '#' == this._fragment ?
|
||||
'' : this._fragment;
|
||||
},
|
||||
set hash(hash) {
|
||||
if (this._isInvalid)
|
||||
return;
|
||||
this._fragment = '#';
|
||||
if ('#' == hash[0])
|
||||
hash = hash.slice(1);
|
||||
parse.call(this, hash, 'fragment');
|
||||
},
|
||||
|
||||
get origin() {
|
||||
var host;
|
||||
if (this._isInvalid || !this._scheme) {
|
||||
return '';
|
||||
}
|
||||
// javascript: Gecko returns String(""), WebKit/Blink String("null")
|
||||
// Gecko throws error for "data://"
|
||||
// data: Gecko returns "", Blink returns "data://", WebKit returns "null"
|
||||
// Gecko returns String("") for file: mailto:
|
||||
// WebKit/Blink returns String("SCHEME://") for file: mailto:
|
||||
switch (this._scheme) {
|
||||
case 'data':
|
||||
case 'file':
|
||||
case 'javascript':
|
||||
case 'mailto':
|
||||
return 'null';
|
||||
}
|
||||
host = this.host;
|
||||
if (!host) {
|
||||
return '';
|
||||
}
|
||||
return this._scheme + '://' + host;
|
||||
}
|
||||
};
|
||||
|
||||
// Copy over the static methods
|
||||
var OriginalURL = scope.URL;
|
||||
if (OriginalURL) {
|
||||
jURL.createObjectURL = function(blob) {
|
||||
// IE extension allows a second optional options argument.
|
||||
// http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
|
||||
return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
|
||||
};
|
||||
jURL.revokeObjectURL = function(url) {
|
||||
OriginalURL.revokeObjectURL(url);
|
||||
};
|
||||
}
|
||||
|
||||
scope.URL = jURL;
|
||||
|
||||
})(module);
|
||||
|
||||
var urlpoly = module.URL || URL;
|
||||
|
||||
export default urlpoly;
|
2
back/static/js/bundle.js
Normal file
2
back/static/js/bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
back/static/js/bundle.js.map
Normal file
1
back/static/js/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user