mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-04 15:09:16 +02:00
updated polymer example, added urljs (no implemented)
This commit is contained in:
parent
8f06e86d9c
commit
150b6b6050
10 changed files with 2635 additions and 4 deletions
1527
examples/polymer/epub-reader.html
Normal file
1527
examples/polymer/epub-reader.html
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
|||
<script src="../../../build/epub.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<!-- <script type="text/javascript">
|
||||
//-- Setups
|
||||
</script>
|
||||
</script> -->
|
||||
|
||||
<polymer-element name="epub-js" attributes="src chapter width height restore spreads">
|
||||
<template>
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
<meta charset="UTF-8">
|
||||
<script src="../../libs/polymer/polymer.min.js"></script>
|
||||
|
||||
<link rel="import" href="epub-reader/epub-reader.html">
|
||||
<!-- // <script src="../../build/epub.min.js"></script> -->
|
||||
|
||||
<!-- <link rel="import" href="epub-reader/epub-reader.html"> -->
|
||||
<link rel="import" href="epub-reader.html">
|
||||
|
||||
<link rel="stylesheet" href="../../demo/css/normalize.css">
|
||||
<style>
|
||||
|
|
2
examples/polymer/vulcanize/.gitignore
vendored
Executable file
2
examples/polymer/vulcanize/.gitignore
vendored
Executable file
|
@ -0,0 +1,2 @@
|
|||
output.html
|
||||
node_modules
|
86
examples/polymer/vulcanize/README.md
Executable file
86
examples/polymer/vulcanize/README.md
Executable file
|
@ -0,0 +1,86 @@
|
|||
# Vulcan
|
||||
|
||||
### Concatenate a set of Web Components into one file
|
||||
|
||||
>Named for the [Vulcanization](http://en.wikipedia.org/wiki/Vulcanization) process that turns polymers into more durable
|
||||
materials.
|
||||
|
||||
## Getting Started
|
||||
- Install the node dependencies with `npm install`
|
||||
- Depends on [cheerio](https://github.com/MatthewMueller/cheerio) and [nopt](https://github.com/isaacs/nopt)
|
||||
- Give some input html files with the `--input` or `-i` flags
|
||||
- Input html should have `<link rel="import">` tags
|
||||
- Specify an output html file with `--output` or `-o`
|
||||
- Defaults to `output.html` in the current directory
|
||||
- URL paths are adjusted for the new output location automatically (execpt ones set in Javascript)
|
||||
- Once finished, link the final output html into your app page with `<link rel="import">`.
|
||||
|
||||
## Example
|
||||
|
||||
Say we have three html files: `index.html`, `x-app.html`, and `x-dep.html`.
|
||||
|
||||
index.html:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<link rel="import" href="app.html">
|
||||
<x-app></x-app>
|
||||
```
|
||||
|
||||
app.html:
|
||||
|
||||
```html
|
||||
<link rel="import" href="path/to/x-dep.html">
|
||||
<polymer-element name="x-app">
|
||||
<template>
|
||||
<x-dep></x-dep>
|
||||
</template>
|
||||
<script>Polymer('x-app')</script>
|
||||
</polymer-element>
|
||||
```
|
||||
|
||||
x-dep.html:
|
||||
|
||||
```html
|
||||
<polymer-element name="x-dep">
|
||||
<template>
|
||||
<img src="x-dep-icon.jpg">
|
||||
</template>
|
||||
<script>
|
||||
Polymer('x-dep');
|
||||
</script>
|
||||
</polymer-element>
|
||||
```
|
||||
|
||||
Running vulcan on `index.html`, and specifying `build.html` as the output:
|
||||
|
||||
node vulcan.js -i index.html -o build.html
|
||||
|
||||
Will result in `build.html` that appears as so:
|
||||
|
||||
```html
|
||||
<polymer-element name="x-dep" assetpath="path/to/">
|
||||
<template>
|
||||
<img src="path/to/x-dep-icon.jpg">
|
||||
</template>
|
||||
<script>
|
||||
Polymer('x-dep');
|
||||
</script>
|
||||
</polymer-element>
|
||||
<polymer-element name="x-app" assetpath="">
|
||||
<template>
|
||||
<x-dep></x-dep>
|
||||
</template>
|
||||
<script>
|
||||
Polymer('x-app');
|
||||
</script>
|
||||
</polymer-element>
|
||||
```
|
||||
|
||||
To use this, make `build.html` the only import in `index.html`:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<link rel="import" href="build.html">
|
||||
<x-app></x-app>
|
||||
```
|
18
examples/polymer/vulcanize/package.json
Executable file
18
examples/polymer/vulcanize/package.json
Executable file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "vulcanize",
|
||||
"version": "0.0.0",
|
||||
"description": "Concat all the components into one output file, with dependencies in the proper order",
|
||||
"main": "vulcan.js",
|
||||
"dependencies": {
|
||||
"cheerio": "~0.11.0",
|
||||
"nopt": "~2.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": "",
|
||||
"author": "",
|
||||
"license": "BSD",
|
||||
"readmeFilename": "README.md"
|
||||
}
|
183
examples/polymer/vulcanize/vulcan.js
Executable file
183
examples/polymer/vulcanize/vulcan.js
Executable file
|
@ -0,0 +1,183 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var nopt = require('nopt');
|
||||
|
||||
var options = nopt(
|
||||
{
|
||||
'output': path,
|
||||
'input': [path, Array],
|
||||
'verbose': Boolean
|
||||
},
|
||||
{
|
||||
'o': ['--output'],
|
||||
'i': ['--input'],
|
||||
'v': ['--verbose']
|
||||
}
|
||||
);
|
||||
|
||||
if (!options.input) {
|
||||
console.error('No input files given');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!options.output) {
|
||||
console.warn('Default output to output.html');
|
||||
options.output = path.resolve('output.html');
|
||||
}
|
||||
|
||||
var outputDir = path.dirname(options.output);
|
||||
|
||||
var IMPORTS = 'link[rel="import"][href]';
|
||||
var ELEMENTS = 'polymer-element';
|
||||
var URL_ATTR = ['href', 'src', 'action', 'style'];
|
||||
var URL_ATTR_SEL = '[' + URL_ATTR.join('],[') + ']';
|
||||
var ABS_URL = /(^data:)|(^http[s]?:)|(^\/)/;
|
||||
var URL = /url\([^)]*\)/g;
|
||||
var URL_TEMPLATE = '{{.*}}';
|
||||
|
||||
function concatElement(dir, output, e) {
|
||||
e = resolveElementPaths(dir, output, e);
|
||||
buffer.push(e);
|
||||
}
|
||||
|
||||
function resolveElementPaths(input, output, element) {
|
||||
var $ = cheerio.load(element);
|
||||
resolvePaths(input, output, $);
|
||||
return $.html(ELEMENTS);
|
||||
}
|
||||
|
||||
function resolvePaths(input, output, $) {
|
||||
// resolve attributes
|
||||
$(URL_ATTR_SEL).each(function() {
|
||||
var val;
|
||||
URL_ATTR.forEach(function(a) {
|
||||
if (val = this.attr(a)) {
|
||||
if (val.search(URL_TEMPLATE) < 0) {
|
||||
if (a === 'style') {
|
||||
this.attr(a, rewriteURL(input, output, val));
|
||||
} else {
|
||||
this.attr(a, rewriteRelPath(input, output, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
// resolve style elements
|
||||
$('style').each(function() {
|
||||
var val = this.html();
|
||||
this.html(rewriteURL(input, output, val));
|
||||
});
|
||||
}
|
||||
|
||||
function rewriteRelPath(inputPath, outputPath, rel) {
|
||||
if (ABS_URL.test(rel)) {
|
||||
return rel;
|
||||
}
|
||||
var abs = path.resolve(inputPath, rel);
|
||||
return path.relative(outputPath, abs);
|
||||
}
|
||||
|
||||
function rewriteURL(inputPath, outputPath, cssText) {
|
||||
return cssText.replace(URL, function(match) {
|
||||
var path = match.replace(/["']/g, "").slice(4, -1);
|
||||
path = rewriteRelPath(inputPath, outputPath, path);
|
||||
return 'url(' + path + ')';
|
||||
});
|
||||
}
|
||||
|
||||
function readDocument(docname) {
|
||||
if (options.verbose) {
|
||||
console.log('Reading:', docname);
|
||||
}
|
||||
var content = fs.readFileSync(docname, 'utf8');
|
||||
return cheerio.load(content);
|
||||
}
|
||||
|
||||
function extractImports($, dir) {
|
||||
var hrefs = $(IMPORTS).map(function(){ return this.attr('href') });
|
||||
return hrefs.map(function(h) { return path.resolve(dir, h) });
|
||||
}
|
||||
|
||||
function extractScripts($, assetPath) {
|
||||
var scripts = $.root().children('script');
|
||||
|
||||
scripts.each(function(){
|
||||
var src = this.attr('src');
|
||||
|
||||
if(src) {
|
||||
this.attr('src', path.join(assetPath, src));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return scripts;
|
||||
}
|
||||
|
||||
function extractElements($, assetPath) {
|
||||
return $(ELEMENTS).map(function(i, e){ this.attr('assetpath', assetPath); return $.html(e) });
|
||||
}
|
||||
|
||||
function concat(filename) {
|
||||
if (!read[filename]) {
|
||||
read[filename] = true;
|
||||
var $ = readDocument(filename);
|
||||
var dir = path.dirname(filename);
|
||||
var assetPath = path.relative(outputDir, dir);
|
||||
var links = extractImports($, dir);
|
||||
resolve(filename, links);
|
||||
|
||||
var scripts = extractScripts($, assetPath);
|
||||
|
||||
scripts.each(function(){
|
||||
buffer.push($.html(this));
|
||||
});
|
||||
|
||||
|
||||
var es = extractElements($, assetPath);
|
||||
es.forEach(concatElement.bind(this, dir, outputDir));
|
||||
} else {
|
||||
if (options.verbose) {
|
||||
console.log('Dependency deduplicated');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolve(inName, inDependencies) {
|
||||
if (inDependencies.length > 0) {
|
||||
if (options.verbose) {
|
||||
console.log('Dependencies:', inDependencies);
|
||||
}
|
||||
inDependencies.forEach(concat);
|
||||
}
|
||||
}
|
||||
|
||||
// monkey patch addResolvePath for build
|
||||
var monkeyPatch = function(proto, element) {
|
||||
var assetPath = element.getAttribute('assetpath');
|
||||
var url = HTMLImports.getDocumentUrl(element.ownerDocument) || '';
|
||||
if (url) {
|
||||
var parts = url.split('/');
|
||||
parts.pop();
|
||||
if (assetPath) {
|
||||
parts.push(assetPath);
|
||||
}
|
||||
parts.push('');
|
||||
url = parts.join('/');
|
||||
}
|
||||
proto.resolvePath = function(path) {
|
||||
return url + path;
|
||||
}
|
||||
};
|
||||
|
||||
var buffer = [
|
||||
'<!-- Monkey Patch addResolvePath to use assetpath -->',
|
||||
'<script>Polymer.addResolvePath = ' + monkeyPatch + '</script>'
|
||||
];
|
||||
var read = {};
|
||||
|
||||
options.input.forEach(concat);
|
||||
|
||||
if (buffer.length) {
|
||||
fs.writeFileSync(options.output, buffer.join('\n'), 'utf8');
|
||||
}
|
140
libs/urljs/README.md
Executable file
140
libs/urljs/README.md
Executable file
|
@ -0,0 +1,140 @@
|
|||
URL.js
|
||||
======
|
||||
|
||||
**An API for working with URLs in JavaScript.**
|
||||
URL.js can be used in both **server-side** and **client-side** JavaScript environments,
|
||||
it has **no dependencies** on any other libraries, and is easy to use for common URL-related tasks.
|
||||
|
||||
Oh and since I built this to use on [TipTheWeb](http://tiptheweb.org/),
|
||||
you should [**tip this project**](http://tiptheweb.org/tip/?link=https://github.com/ericf/urljs) if you find it useful! :-D
|
||||
|
||||
Design & Approach
|
||||
-----------------
|
||||
|
||||
I had some very specific URL-related programming tasks I needed to do:
|
||||
validate and normalize user input of URLs and URL-like strings within the browser,
|
||||
and resolve URLs against each other on the server (in a YQL table to be specific).
|
||||
Both of these tasks require a very good URL parser, so URL.js centers around parsing.
|
||||
The design of the API and features of URL.js center around these four main concepts:
|
||||
|
||||
* Parsing
|
||||
* Normalization
|
||||
* Resolving
|
||||
* Mutating/Building
|
||||
|
||||
**`URL` is both a namespace for utility methods and a constructor/factory to create instances of URL Objects.**
|
||||
|
||||
The static utility methods make it convenient when you just want to work with Strings since they return Strings.
|
||||
When you want to retain a reference to a parsed URL you can easily create a URL instance;
|
||||
these instances have useful methods, most serve as both an accessor/mutator to a specific part of the URL.
|
||||
|
||||
**Currently URL.js only works with HTTP URLs**, albiet the most popular type of URL;
|
||||
I have plans to extend the functionality to include support [for all URL schemes](http://www.w3.org/Addressing/URL/url-spec.txt).
|
||||
Internal to the library is the distiction between absolute and relative URLs.
|
||||
Absolute URLs are ones which contain a scheme or are scheme-relative, and contain a host.
|
||||
Relative URLs have slightly looser contraints but the relavence is maintained, either host- or path- relative.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
**`URL` is both a namespace for utility methods and a constructor/factory to create instances of URL Objects.**
|
||||
|
||||
### Using Static Utilites
|
||||
|
||||
There are two static methods: `normalize` and `resolve`
|
||||
|
||||
#### `URL.normalize`:
|
||||
|
||||
Takes in a dirty URL and makes it nice and clean.
|
||||
|
||||
URL.normalize('Http://Example.com'); // 'http://example.com/'
|
||||
URL.normalize('Http://Example.com?foo=#bar'); // 'http://example.com/?foo#bar'
|
||||
|
||||
This should be suffient to serve the use-case of want to clean up URLs,
|
||||
especially if were inputted by a user.
|
||||
|
||||
#### `URL.resolve`:
|
||||
|
||||
Given a base URL, this will resolve another URL against it; this method is inspired by what browsers do.
|
||||
Normalizing is part of resolving, so a normalized and resolved URL `String` is returned.
|
||||
|
||||
URL.resolve('http://example.com/foo/bar', 'baz/index.html'); // 'http://example.com/foo/baz/index.html'
|
||||
URL.resolve('https://example.com/foo/, '//example.com/bar.css'); // 'https://example.com/bar.css'
|
||||
URL.resolve('http://example.com/foo/bar/zee/', '../../crazy#whoa'); // 'http://example.com/foo/crazy#whoa'
|
||||
|
||||
Resolving URLs is a pain in the ass, trust me, you don’t want to have to do this by hand.
|
||||
The implementation of `resolve` is using all parts of this library’s API to pull it off.
|
||||
|
||||
### Using URL Instances
|
||||
|
||||
The `URL` `Object` is also a constructor/factory for creating instances of `URL`s.
|
||||
When creating an instance, **the `new` keyword is optional**.
|
||||
|
||||
var url = URL('http://www.example.com');
|
||||
|
||||
// Accessor/Mutator Methods
|
||||
|
||||
url.scheme(); // 'http'
|
||||
url.userInfo(); // undefined
|
||||
url.host(); // 'www.example.com'
|
||||
url.port(); // undefined
|
||||
url.path(); // '/'
|
||||
url.query(); // undefined
|
||||
url.queryString(); // ''
|
||||
url.fragment(); // undefined
|
||||
|
||||
// Convenience Methods
|
||||
|
||||
url.original(); // 'http://www.example.com'
|
||||
url.isValid(); // true
|
||||
url.isAbsolute(); // true
|
||||
url.isRelative(); // false
|
||||
url.isHostRelative(); // false
|
||||
url.type(); // 'absolute' === URL.ABSOLUTE
|
||||
url.domain(); // 'example.com'
|
||||
url.authority(); // 'www.example.com'
|
||||
|
||||
// Output Methods
|
||||
|
||||
url.toString(); // 'http://www.example.com'
|
||||
url.resolve('/foo/').toString(); // 'http://www.example.com/foo/'
|
||||
|
||||
**Yeah, `URL` instances are packed full of useful URL-ly jazz!**
|
||||
|
||||
Here are a few more “complex” examples of what you can do with mutation, chaining, building, and resolving:
|
||||
|
||||
// switch the scheme, resolve a path with a fragment, and navigate to it
|
||||
window.location = URL(window.location.toString()).scheme('https').resolve('/about/#people').toString();
|
||||
|
||||
// turn 'http://example.com' -> 'http://example.com/?foo=bar#baz'
|
||||
URL('http://example.com').query([['foo', 'bar']]).fragment('baz');
|
||||
|
||||
// build up a URL to: http://tiptheweb.org/tip/?link=https://github.com/ericf/urljs
|
||||
URL()
|
||||
.scheme('http')
|
||||
.host('tiptheweb.org')
|
||||
.path('/tip/')
|
||||
.query([['link', 'https://github.com/ericf/urljs']]);
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (c) 2011 Eric Ferraiuolo (http://eric.ferraiuolo.name/)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
7
libs/urljs/url-min.js
vendored
Executable file
7
libs/urljs/url-min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
666
libs/urljs/url.js
Executable file
666
libs/urljs/url.js
Executable file
|
@ -0,0 +1,666 @@
|
|||
/*!
|
||||
* URL.js
|
||||
*
|
||||
* Copyright 2011 Eric Ferraiuolo
|
||||
* https://github.com/ericf/urljs
|
||||
*/
|
||||
|
||||
/**
|
||||
* URL constructor and utility.
|
||||
* Provides support for validating whether something is a URL,
|
||||
* formats and cleans up URL-like inputs into something nice and pretty,
|
||||
* ability to resolve one URL against another and returned the formatted result,
|
||||
* and is a convenient API for working with URL Objects and the various parts of URLs.
|
||||
*
|
||||
* @constructor URL
|
||||
* @param {String | URL} url - the URL String to parse or URL instance to copy
|
||||
* @return {URL} url - instance of a URL all nice and parsed
|
||||
*/
|
||||
var URL = function () {
|
||||
|
||||
var u = this;
|
||||
|
||||
if ( ! (u && u.hasOwnProperty && (u instanceof URL))) {
|
||||
u = new URL();
|
||||
}
|
||||
|
||||
return u._init.apply(u, arguments);
|
||||
};
|
||||
|
||||
(function(){
|
||||
|
||||
var ABSOLUTE = 'absolute',
|
||||
RELATIVE = 'relative',
|
||||
|
||||
HTTP = 'http',
|
||||
HTTPS = 'https',
|
||||
COLON = ':',
|
||||
SLASH_SLASH = '//',
|
||||
AT = '@',
|
||||
DOT = '.',
|
||||
SLASH = '/',
|
||||
DOT_DOT = '..',
|
||||
DOT_DOT_SLASH = '../',
|
||||
QUESTION = '?',
|
||||
EQUALS = '=',
|
||||
AMP = '&',
|
||||
HASH = '#',
|
||||
EMPTY_STRING = '',
|
||||
|
||||
TYPE = 'type',
|
||||
SCHEME = 'scheme',
|
||||
USER_INFO = 'userInfo',
|
||||
HOST = 'host',
|
||||
PORT = 'port',
|
||||
PATH = 'path',
|
||||
QUERY = 'query',
|
||||
FRAGMENT = 'fragment',
|
||||
|
||||
URL_TYPE_REGEX = /^(?:(https?:\/\/|\/\/)|(\/|\?|#)|[^;:@=\.\s])/i,
|
||||
URL_ABSOLUTE_REGEX = /^(?:(https?):\/\/|\/\/)(?:([^:@\s]+:?[^:@\s]+?)@)?((?:[^;:@=\/\?\.\s]+\.)+[A-Za-z0-9\-]{2,})(?::(\d+))?(?=\/|\?|#|$)([^\?#]+)?(?:\?([^#]+))?(?:#(.+))?/i,
|
||||
URL_RELATIVE_REGEX = /^([^\?#]+)?(?:\?([^#]+))?(?:#(.+))?/i,
|
||||
|
||||
OBJECT = 'object',
|
||||
STRING = 'string',
|
||||
TRIM_REGEX = /^\s+|\s+$/g,
|
||||
|
||||
trim, isObject, isString;
|
||||
|
||||
|
||||
// *** Utilities *** //
|
||||
|
||||
trim = String.prototype.trim ? function (s) {
|
||||
return ( s && s.trim ? s.trim() : s );
|
||||
} : function (s) {
|
||||
try {
|
||||
return s.replace(TRIM_REGEX, EMPTY_STRING);
|
||||
} catch (e) { return s; }
|
||||
};
|
||||
|
||||
isObject = function (o) {
|
||||
return ( o && typeof o === OBJECT );
|
||||
};
|
||||
|
||||
isString = function (o) {
|
||||
return typeof o === STRING;
|
||||
};
|
||||
|
||||
|
||||
// *** Static *** //
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
URL.ABSOLUTE = ABSOLUTE;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
URL.RELATIVE = RELATIVE;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
URL.normalize = function (url) {
|
||||
return new URL(url).toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a resolved URL String using the baseUrl to resolve the url against.
|
||||
* This attempts to resolve URLs like a browser would on a web page.
|
||||
*
|
||||
* @static
|
||||
* @method resolve
|
||||
* @param {String | URL} baseUrl - the URL String, or URL instance as the resolving base
|
||||
* @param {String | URL} url - the URL String, or URL instance to resolve
|
||||
* @return {String} resolvedUrl - a resolved URL String
|
||||
*/
|
||||
URL.resolve = function (baseUrl, url) {
|
||||
return new URL(baseUrl).resolve(url).toString();
|
||||
};
|
||||
|
||||
|
||||
// *** Prototype *** //
|
||||
|
||||
URL.prototype = {
|
||||
|
||||
// *** Lifecycle Methods *** //
|
||||
|
||||
/**
|
||||
* Initializes a new URL instance, or re-initializes an existing one.
|
||||
* The URL constructor delegates to this method to do the initializing,
|
||||
* and the mutator instance methods call this to re-initialize when something changes.
|
||||
*
|
||||
* @protected
|
||||
* @method _init
|
||||
* @param {String | URL} url - the URL String, or URL instance
|
||||
* @return {URL} url - instance of a URL all nice and parsed/re-parsed
|
||||
*/
|
||||
_init : function (url) {
|
||||
|
||||
this.constructor = URL;
|
||||
|
||||
url = isString(url) ? url : url instanceof URL ? url.toString() : null;
|
||||
|
||||
this._original = url;
|
||||
this._url = {};
|
||||
this._isValid = this._parse(url);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// *** Object Methods *** //
|
||||
|
||||
/**
|
||||
* Returns the formatted URL String.
|
||||
* Overridden Object toString method to do something useful.
|
||||
*
|
||||
* @public
|
||||
* @method toString
|
||||
* @return {String} url - formatted URL string
|
||||
*/
|
||||
toString : function () {
|
||||
|
||||
var url = this._url,
|
||||
urlParts = [],
|
||||
type = url[TYPE],
|
||||
scheme = url[SCHEME],
|
||||
path = url[PATH],
|
||||
query = url[QUERY],
|
||||
fragment = url[FRAGMENT];
|
||||
|
||||
if (type === ABSOLUTE) {
|
||||
urlParts.push(
|
||||
scheme ? (scheme + COLON + SLASH_SLASH) : SLASH_SLASH,
|
||||
this.authority()
|
||||
);
|
||||
if (path && path.indexOf(SLASH) !== 0) { // this should maybe go in _set
|
||||
path = SLASH + path;
|
||||
}
|
||||
}
|
||||
|
||||
urlParts.push(
|
||||
path,
|
||||
query ? (QUESTION + this.queryString()) : EMPTY_STRING,
|
||||
fragment ? (HASH + fragment) : EMPTY_STRING
|
||||
);
|
||||
|
||||
return urlParts.join(EMPTY_STRING);
|
||||
},
|
||||
|
||||
// *** Accessor/Mutator Methods *** //
|
||||
|
||||
original : function () {
|
||||
return this._original;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether parsing from initialization or re-initialization produced something valid.
|
||||
*
|
||||
* @public
|
||||
* @method isValid
|
||||
* @return {Boolean} valid - whether the URL is valid
|
||||
*/
|
||||
isValid : function () {
|
||||
return this._isValid;
|
||||
},
|
||||
|
||||
/**
|
||||
* URL is absolute if it has a scheme or is scheme-relative (//).
|
||||
*
|
||||
* @public
|
||||
* @method isAbsolute
|
||||
* @return {Boolean} absolute - whether the URL is absolute
|
||||
*/
|
||||
isAbsolute : function () {
|
||||
return this._url[TYPE] === ABSOLUTE;
|
||||
},
|
||||
|
||||
/**
|
||||
* URL is relative if it host or path relative, i.e. doesn't contain a host.
|
||||
*
|
||||
* @public
|
||||
* @method isRelative
|
||||
* @return {Boolean} relative - whether the URL is relative
|
||||
*/
|
||||
isRelative : function () {
|
||||
return this._url[TYPE] === RELATIVE;
|
||||
},
|
||||
|
||||
/**
|
||||
* URL is host relative if it's relative and the path begins with '/'.
|
||||
*
|
||||
* @public
|
||||
* @method isHostRelative
|
||||
* @return {Boolean} hostRelative - whether the URL is host-relative
|
||||
*/
|
||||
isHostRelative : function () {
|
||||
var path = this._url[PATH];
|
||||
return ( this.isRelative() && path && path.indexOf(SLASH) === 0 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the type of the URL, either: URL.ABSOLUTE or URL.RELATIVE.
|
||||
*
|
||||
* @public
|
||||
* @method type
|
||||
* @return {String} type - the type of the URL: URL.ABSOLUTE or URL.RELATIVE
|
||||
*/
|
||||
type : function () {
|
||||
return this._url[TYPE];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the scheme of the URL.
|
||||
* If URL is determined to be absolute (i.e. contains a host) and no scheme is provided,
|
||||
* the scheme will default to http.
|
||||
*
|
||||
* @public
|
||||
* @method scheme
|
||||
* @param {String} scheme - Optional scheme to set on the URL
|
||||
* @return {String | URL} the URL scheme or the URL instance
|
||||
*/
|
||||
scheme : function (scheme) {
|
||||
return ( arguments.length ? this._set(SCHEME, scheme) : this._url[SCHEME] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or set the user info of the URL.
|
||||
* The user info can optionally contain a password and is only valid for absolute URLs.
|
||||
*
|
||||
* @public
|
||||
* @method userInfo
|
||||
* @param {String} userInfo - Optional userInfo to set on the URL
|
||||
* @return {String | URL} the URL userInfo or the URL instance
|
||||
*/
|
||||
userInfo : function (userInfo) {
|
||||
return ( arguments.length ? this._set(USER_INFO, userInfo) : this._url[USER_INFO] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the host of the URL.
|
||||
* The host name, if set, must be something valid otherwise the URL will become invalid.
|
||||
*
|
||||
* @public
|
||||
* @method host
|
||||
* @param {String} host - Optional host to set on the URL
|
||||
* @return {String | URL} the URL host or the URL instance
|
||||
*/
|
||||
host : function (host) {
|
||||
return ( arguments.length ? this._set(HOST, host) : this._url[HOST] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the URL's domain, where the domain is the TLD and SLD of the host.
|
||||
* e.g. foo.example.com -> example.com
|
||||
*
|
||||
* @public
|
||||
* @method domain
|
||||
* @return {String} domain - the URL domain
|
||||
*/
|
||||
domain : function () {
|
||||
var host = this._url[HOST];
|
||||
return ( host ? host.split(DOT).slice(-2).join(DOT) : undefined );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the port of the URL.
|
||||
*
|
||||
* @public
|
||||
* @method port
|
||||
* @param {Number} port - Optional port to set on the URL
|
||||
* @return {Number | URL} the URL port or the URL instance
|
||||
*/
|
||||
port : function (port) {
|
||||
return ( arguments.length ? this._set(PORT, port) : this._url[PORT] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the URL's authority which is the userInfo, host, and port combined.
|
||||
* This only makes sense for absolute URLs
|
||||
*
|
||||
* @public
|
||||
* @method authority
|
||||
* @return {String} authority - the URL's authority (userInfo, host, and port)
|
||||
*/
|
||||
authority : function () {
|
||||
|
||||
var url = this._url,
|
||||
userInfo = url[USER_INFO],
|
||||
host = url[HOST],
|
||||
port = url[PORT];
|
||||
|
||||
return [
|
||||
|
||||
userInfo ? (userInfo + AT) : EMPTY_STRING,
|
||||
host,
|
||||
port ? (COLON + port) : EMPTY_STRING,
|
||||
|
||||
].join(EMPTY_STRING);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the path of the URL.
|
||||
*
|
||||
* @public
|
||||
* @method path
|
||||
* @param {String} path - Optional path to set on the URL
|
||||
* @return {String | URL} the URL path or the URL instance
|
||||
*/
|
||||
path : function (path) {
|
||||
return ( arguments.length ? this._set(PATH, path) : this._url[PATH] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the query of the URL.
|
||||
* This takes or returns the parsed query as an Array of Arrays.
|
||||
*
|
||||
* @public
|
||||
* @method query
|
||||
* @param {Array} query - Optional query to set on the URL
|
||||
* @return {Array | URL} the URL query or the URL instance
|
||||
*/
|
||||
query : function (query) {
|
||||
return ( arguments.length ? this._set(QUERY, query) : this._url[QUERY] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the query of the URL.
|
||||
* This takes or returns the query as a String; doesn't include the '?'
|
||||
*
|
||||
* @public
|
||||
* @method queryString
|
||||
* @param {String} queryString - Optional queryString to set on the URL
|
||||
* @return {String | URL} the URL queryString or the URL instance
|
||||
*/
|
||||
queryString : function (queryString) {
|
||||
|
||||
// parse and set queryString
|
||||
if (arguments.length) {
|
||||
return this._set(QUERY, this._parseQuery(queryString));
|
||||
}
|
||||
|
||||
queryString = EMPTY_STRING;
|
||||
|
||||
var query = this._url[QUERY],
|
||||
i, len;
|
||||
|
||||
if (query) {
|
||||
for (i = 0, len = query.length; i < len; i++) {
|
||||
queryString += query[i].join(EQUALS);
|
||||
if (i < len - 1) {
|
||||
queryString += AMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queryString;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns or sets the fragment on the URL.
|
||||
* The fragment does not contain the '#'.
|
||||
*
|
||||
* @public
|
||||
* @method fragment
|
||||
* @param {String} fragment - Optional fragment to set on the URL
|
||||
* @return {String | URL} the URL fragment or the URL instance
|
||||
*/
|
||||
fragment : function (fragment) {
|
||||
return ( arguments.length ? this._set(FRAGMENT, fragment) : this._url[FRAGMENT] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a new, resolved URL instance using this as the baseUrl.
|
||||
* The URL passed in will be resolved against the baseUrl.
|
||||
*
|
||||
* @public
|
||||
* @method resolve
|
||||
* @param {String | URL} url - the URL String, or URL instance to resolve
|
||||
* @return {URL} url - a resolved URL instance
|
||||
*/
|
||||
resolve : function (url) {
|
||||
|
||||
url = (url instanceof URL) ? url : new URL(url);
|
||||
|
||||
var resolved, path;
|
||||
|
||||
if ( ! (this.isValid() && url.isValid())) { return this; } // not sure what to do???
|
||||
|
||||
// the easy way
|
||||
if (url.isAbsolute()) {
|
||||
return ( this.isAbsolute() ? url.scheme() ? url : new URL(url).scheme(this.scheme()) : url );
|
||||
}
|
||||
|
||||
// the hard way
|
||||
resolved = new URL(this.isAbsolute() ? this : null);
|
||||
|
||||
if (url.path()) {
|
||||
|
||||
if (url.isHostRelative() || ! this.path()) {
|
||||
path = url.path();
|
||||
} else {
|
||||
path = this.path().substring(0, this.path().lastIndexOf(SLASH) + 1) + url.path();
|
||||
}
|
||||
|
||||
resolved.path(this._normalizePath(path)).query(url.query()).fragment(url.fragment());
|
||||
|
||||
} else if (url.query()) {
|
||||
resolved.query(url.query()).fragment(url.fragment());
|
||||
} else if (url.fragment()) {
|
||||
resolved.fragment(url.fragment());
|
||||
}
|
||||
|
||||
return resolved;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a new, reduced relative URL instance using this as the baseUrl.
|
||||
* The URL passed in will be compared to the baseUrl with the goal of
|
||||
* returning a reduced-down URL to one that’s relative to the base (this).
|
||||
* This method is basically the opposite of resolve.
|
||||
*
|
||||
* @public
|
||||
* @method reduce
|
||||
* @param {String | URL} url - the URL String, or URL instance to resolve
|
||||
* @return {URL} url - the reduced URL instance
|
||||
*/
|
||||
reduce : function (url) {
|
||||
|
||||
url = (url instanceof URL) ? url : new URL(url);
|
||||
|
||||
var reduced = this.resolve(url);
|
||||
|
||||
if (this.isAbsolute() && reduced.isAbsolute()) {
|
||||
if (reduced.scheme() === this.scheme() && reduced.authority() === this.authority()) {
|
||||
reduced.scheme(null).userInfo(null).host(null).port(null);
|
||||
}
|
||||
}
|
||||
|
||||
return reduced;
|
||||
},
|
||||
|
||||
// *** Private Methods *** //
|
||||
|
||||
/**
|
||||
* Parses a URL into usable parts.
|
||||
* Reasonable defaults are applied to parts of the URL which weren't present in the input,
|
||||
* e.g. 'http://example.com' -> { type: 'absolute', scheme: 'http', host: 'example.com', path: '/' }
|
||||
* If nothing or a falsy value is returned, the URL wasn't something valid.
|
||||
*
|
||||
* @private
|
||||
* @method _parse
|
||||
* @param {String} url - the URL string to parse
|
||||
* @param {String} type - Optional type to seed parsing: URL.ABSOLUTE or URL.RELATIVE
|
||||
* @return {Boolean} parsed - whether or not the URL string was parsed
|
||||
*/
|
||||
_parse : function (url, type) {
|
||||
|
||||
// make sure we have a good string
|
||||
url = trim(url);
|
||||
if ( ! (isString(url) && url.length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var urlParts, parsed;
|
||||
|
||||
// figure out type, absolute or relative, or quit
|
||||
if ( ! type) {
|
||||
type = url.match(URL_TYPE_REGEX);
|
||||
type = type ? type[1] ? ABSOLUTE : type[2] ? RELATIVE : null : null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case ABSOLUTE:
|
||||
urlParts = url.match(URL_ABSOLUTE_REGEX);
|
||||
if (urlParts) {
|
||||
parsed = {};
|
||||
parsed[TYPE] = ABSOLUTE;
|
||||
parsed[SCHEME] = urlParts[1] ? urlParts[1].toLowerCase() : undefined;
|
||||
parsed[USER_INFO] = urlParts[2];
|
||||
parsed[HOST] = urlParts[3].toLowerCase();
|
||||
parsed[PORT] = urlParts[4] ? parseInt(urlParts[4], 10) : undefined;
|
||||
parsed[PATH] = urlParts[5] || SLASH;
|
||||
parsed[QUERY] = this._parseQuery(urlParts[6]);
|
||||
parsed[FRAGMENT] = urlParts[7];
|
||||
}
|
||||
break;
|
||||
|
||||
case RELATIVE:
|
||||
urlParts = url.match(URL_RELATIVE_REGEX);
|
||||
if (urlParts) {
|
||||
parsed = {};
|
||||
parsed[TYPE] = RELATIVE;
|
||||
parsed[PATH] = urlParts[1];
|
||||
parsed[QUERY] = this._parseQuery(urlParts[2]);
|
||||
parsed[FRAGMENT] = urlParts[3];
|
||||
}
|
||||
break;
|
||||
|
||||
// try to parse as absolute, if that fails then as relative
|
||||
default:
|
||||
return ( this._parse(url, ABSOLUTE) || this._parse(url, RELATIVE) );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (parsed) {
|
||||
this._url = parsed;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse a URL query string into an array of arrays.
|
||||
* Order of the query paramerters is maintained, an example structure would be:
|
||||
* queryString: 'foo=bar&baz' -> [['foo', 'bar'], ['baz']]
|
||||
*
|
||||
* @private
|
||||
* @method _parseQuery
|
||||
* @param {String} queryString - the query string to parse, should not include '?'
|
||||
* @return {Array} parsedQuery - array of arrays representing the query parameters and values
|
||||
*/
|
||||
_parseQuery : function (queryString) {
|
||||
|
||||
if ( ! isString(queryString)) { return; }
|
||||
|
||||
queryString = trim(queryString);
|
||||
|
||||
var query = [],
|
||||
queryParts = queryString.split(AMP),
|
||||
queryPart, i, len;
|
||||
|
||||
for (i = 0, len = queryParts.length; i < len; i++) {
|
||||
if (queryParts[i]) {
|
||||
queryPart = queryParts[i].split(EQUALS);
|
||||
query.push(queryPart[1] ? queryPart : [queryPart[0]]);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for mutators to set a new URL-part value.
|
||||
* After the URL-part is updated, the URL will be toString'd and re-parsed.
|
||||
* This is a brute, but will make sure the URL stays in sync and is re-validated.
|
||||
*
|
||||
* @private
|
||||
* @method _set
|
||||
* @param {String} urlPart - the _url Object member String name
|
||||
* @param {Object} val - the new value for the URL-part, mixed type
|
||||
* @return {URL} this - returns this URL instance, chainable
|
||||
*/
|
||||
_set : function (urlPart, val) {
|
||||
|
||||
this._url[urlPart] = val;
|
||||
|
||||
if (val && (
|
||||
urlPart === SCHEME ||
|
||||
urlPart === USER_INFO ||
|
||||
urlPart === HOST ||
|
||||
urlPart === PORT )){
|
||||
this._url[TYPE] = ABSOLUTE; // temp, set this to help clue parsing
|
||||
}
|
||||
if ( ! val && urlPart === HOST) {
|
||||
this._url[TYPE] = RELATIVE; // temp, no host means relative
|
||||
}
|
||||
|
||||
this._isValid = this._parse(this.toString());
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a normalized path String, by removing ../'s.
|
||||
*
|
||||
* @private
|
||||
* @method _normalizePath
|
||||
* @param {String} path — the path String to normalize
|
||||
* @return {String} normalizedPath — the normalized path String
|
||||
*/
|
||||
_normalizePath : function (path) {
|
||||
|
||||
var pathParts, pathPart, pathStack, normalizedPath, i, len;
|
||||
|
||||
if (path.indexOf(DOT_DOT_SLASH) > -1) {
|
||||
|
||||
pathParts = path.split(SLASH);
|
||||
pathStack = [];
|
||||
|
||||
for ( i = 0, len = pathParts.length; i < len; i++ ) {
|
||||
pathPart = pathParts[i];
|
||||
if (pathPart === DOT_DOT) {
|
||||
pathStack.pop();
|
||||
} else if (pathPart) {
|
||||
pathStack.push(pathPart);
|
||||
}
|
||||
}
|
||||
|
||||
normalizedPath = pathStack.join(SLASH);
|
||||
|
||||
// prepend slash if needed
|
||||
if (path[0] === SLASH) {
|
||||
normalizedPath = SLASH + normalizedPath;
|
||||
}
|
||||
|
||||
// append slash if needed
|
||||
if (path[path.length - 1] === SLASH && normalizedPath.length > 1) {
|
||||
normalizedPath += SLASH;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
normalizedPath = path;
|
||||
|
||||
}
|
||||
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}());
|
Loading…
Add table
Add a link
Reference in a new issue