diff --git a/.eslintrc.json b/.eslintrc.json index 5042e54..4c0e9ca 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,9 +7,19 @@ "sourceType": "module" }, "rules": { + "callback-return": "off", "max-params": [ "error", 4 - ] + ], + "no-invalid-this": "off", + "no-unused-vars": [ + "warn", + { + "args": "none", + "vars": "all" + } + ], + "sort-keys": "off" } } diff --git a/Gruntfile.js b/Gruntfile.js index f407d27..6d8e611 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -21,7 +21,6 @@ */ module.exports = function(grunt) { - var commonjs var nodeResolve var semver = require('semver') var uglify @@ -67,7 +66,6 @@ module.exports = function(grunt) { var testTasks = [ 'compile' ] if (semver.satisfies(process.version, '>=0.12')) { - commonjs = require('rollup-plugin-commonjs') nodeResolve = require('rollup-plugin-node-resolve') uglify = require('rollup-plugin-uglify') @@ -93,8 +91,7 @@ module.exports = function(grunt) { browser: true, jsnext: true, main: true - }), - commonjs() + }) ] } }, @@ -117,7 +114,6 @@ module.exports = function(grunt) { jsnext: true, main: true }), - commonjs(), uglify({ output: { comments: function(node, comment) { diff --git a/README.md b/README.md index 1fedaaa..a3ee000 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,6 @@ [YOURLS API](https://github.com/neocotic/yourls-api) is a JavaScript library that provides bindings for [YOURLS](https://yourls.org) URL shortener servers. -> This library is only compatible with YOURLS servers running version **1.5.1** or newer as it requires JSONP support. - [![Build Status](https://img.shields.io/travis/neocotic/yourls-api/develop.svg?style=flat-square)](https://travis-ci.org/neocotic/yourls-api) [![Dev Dependency Status](https://img.shields.io/david/dev/neocotic/yourls-api.svg?style=flat-square)](https://david-dm.org/neocotic/yourls-api#info=devDependencies) [![License](https://img.shields.io/npm/l/yourls-api.svg?style=flat-square)](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md) @@ -69,7 +67,7 @@ responses to see all of the data that is available. ### Connecting ``` javascript -yourls.connect(url[, credentials]) +yourls.connect(url[, credentials][, options]) ``` This is the first step and is where you'll provide the `url` of the YOURLS API that you wish to connect to. It **must** @@ -120,12 +118,48 @@ yourls.connect('https://example.com/yourls-api.php', { }) ``` +> **IMPORTANT:** When sending `GET` requests, by design, all information will be included in the URL of the request +> This includes data as well as *any credentials* used to authenticate with the API. You have been warned. +> This is unavoidable when sending requests in the JSONP format but, when using the JSON format, you can send `POST` +> requests, which means that your data is sent inside the body of the request. Combine this with HTTPS and your data and +> credentials cannot be sniffed over the network. + +As you may have noticed; this method also accepts the following entirely optional `options`: + +Option | Description | Default +------ | ----------------------------------- | --------- +format | Format in which requests are sent | `"jsonp"` +method | HTTP method to be used for requests | `"GET"` + +``` javascript +// Does the same as specifying no options (i.e. using defaults) +yourls.connect('https://example.com/yourls-api.php', null, { + format: 'jsonp', + method: 'GET' +}) + +// Best practice if you want to secure the data you're transmitting and you've setup CORS, if needed +yourls.connect('https://example.com/yourls-api.php', { + signature: '3002a61584' +}, { + format: 'json', + method: 'POST' +}) +``` + +The following formats are supported with the corresponding HTTP methods: + +Format | HTTP Methods +------ | ------------ +json | GET, POST +jsonp | GET + +> **IMPORTANT:** The YOURLS server must be running version **1.5.1** or newer in order to send requests in the JSONP +> format. + Despite the name of this method, no connection or authentication is carried out at this point and this initial method simply stores these values to prevent you from having to specify them with every API call. -> When using JSONP to send requests, by design, all information will be included in the URL of the request. This -> includes data as well as **any credentials** used to authenticate with the API. You have been warned. - ### Disconnecting ``` javascript @@ -271,6 +305,7 @@ yourls.url(url).expand(callback(result, response)) This method expands the shortened `url` into the original (long) URL. ``` javascript +// Get more details for link yourls.url('https://example.com/yourls').expand(function(result, response) { console.log(result.keyword) //=> "yourls" @@ -290,6 +325,7 @@ yourls.url(url).stats(callback(result, response)) This method fetches the statistics for the shortened `url`. ``` javascript +// Get statistics only for this link yourls.url('https://example.com/yourls').stats(function(result, response) { console.log(result.clicks) //=> "123" @@ -309,6 +345,7 @@ yourls.version([db, ]callback(result, response)) This methods fetches the version of YOURLS running on the connected server. ``` javascript +// Get YOURLS version yourls.version(function(result, response) { console.log(result.version) //=> "1.7" @@ -318,6 +355,7 @@ yourls.version(function(result, response) { Optionally, a `db` flag can be enabled for the YOURLS database version to also be included in the result. ``` javascript +// Get YOURLS database version as well yourls.version(true, function(result, response) { console.log(result.version) //=> "1.7" @@ -329,6 +367,7 @@ yourls.version(true, function(result, response) { --- ``` javascript +// Get version of this library console.log(yourls.VERSION) //=> "2.0.0" ``` diff --git a/bower.json b/bower.json index 686f1bf..cfc486e 100644 --- a/bower.json +++ b/bower.json @@ -16,6 +16,7 @@ "url", "shortener", "api", + "json", "jsonp" ], "repository": { diff --git a/dist/yourls.js b/dist/yourls.js index 2b3b4e4..bdb9ba6 100644 --- a/dist/yourls.js +++ b/dist/yourls.js @@ -26,7 +26,7 @@ }(this, (function () { 'use strict'; /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2016 Alasdair Mercer, Skelp * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -48,52 +48,217 @@ */ /** - * The singleton {@link API} instance which is privatized to prevent leaking credentials. + * A bare-bones constructor for surrogate prototype swapping. + * + * @private + * @constructor + */ + var Constructor = function() {}; + /** + * A reference to Object.prototype.hasOwnProperty. * * @private - * @type {API} + * @type {Function} */ - var instance = null; + var hasOwnProperty = Object.prototype.hasOwnProperty; + /** + * A reference to Array.prototype.slice. + * + * @private + * @type {Function} + */ + var slice = Array.prototype.slice; /** - * Sanitizes the specified credentials by ensuring that only valid properties are present and only when - * appropriate. + * Extends the specified target object with the properties in each of the sources provided. * - * This function does not modify credentials and instead creates a new object with the sanitized - * properties. + * Nothing happens if target is null and if any source is null it will be + * ignored. * - * @param {API~Credentials} credentials - the credentials to be sanitized (may be null) - * @return {API~Credentials} A sanitized version of credentials or null if - * credentials is null. + * @param {boolean} own - true to only copy own properties from sources onto + * target; otherwise false + * @param {Object} [target] - the target object which should be extended + * @param {...Object} [sources] - the source objects whose properties are to be copied onto target + * @return {void} * @private */ - function sanitizeCredentials(credentials) { - if (!credentials) { - return null + function extend(own, target, sources) { + if (target == null) { + return + } + + sources = slice.call(arguments, 2); + + var property; + var source; + + for (var i = 0, length = sources.length; i < length; i++) { + source = sources[i]; + + for (property in source) { + if (!own || hasOwnProperty.call(source, property)) { + target[property] = source[property]; + } + } } + } - var result = {}; - if (credentials.signature) { - result.signature = credentials.signature; - result.timestamp = credentials.timestamp; + /** + * Creates an object which inherits the given prototype. + * + * Optionally, the created object can be extended further with the specified properties. + * + * @param {Object} prototype - the prototype to be inherited by the created object + * @param {Object} [properties] - the optional properties to be extended by the created object + * @return {Object} The newly created object. + * @private + */ + function create(prototype, properties) { + var result; + if (typeof Object.create === 'function') { + result = Object.create(prototype); } else { - result.password = credentials.password; - result.username = credentials.username; + Constructor.prototype = prototype; + result = new Constructor(); + Constructor.prototype = null; + } + + if (properties) { + extend(true, result, properties); } return result } + /** + * The base constructor from which all others should extend. + * + * @public + * @constructor + */ + function Oopsy() {} + + /** + * Extends the constructor to which this method is associated with the prototype and/or + * statics provided. + * + * If constructor is provided, it will be used as the constructor for the child, otherwise a simple + * constructor which only calls the super constructor will be used instead. + * + * The super constructor can be accessed via a special super_ property on the child constructor. + * + * @param {Function} [constructor] - the constructor for the child + * @param {Object} [prototype] - the prototype properties to be defined for the child + * @param {Object} [statics] - the static properties to be defined for the child + * @return {Function} The child constructor provided or the one created if none was given. + * @public + * @static + */ + Oopsy.extend = function(constructor, prototype, statics) { + var superConstructor = this; + + if (typeof constructor !== 'function') { + statics = prototype; + prototype = constructor; + constructor = function() { + return superConstructor.apply(this, arguments) + }; + } + + extend(false, constructor, superConstructor, statics); + + constructor.prototype = create(superConstructor.prototype, prototype); + constructor.prototype.constructor = constructor; + + constructor.super_ = superConstructor; + + return constructor + }; + + /* + * Copyright (C) 2016 Alasdair Mercer + * + * 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. + */ + + /** + * Extends the specified target object with the properties in each of the sources provided. + * + * Any of the sources that are null will simply be ignored. + * + * @param {Object} target - the target object which should be extended + * @param {...Object} [sources] - the source objects whose properties are to be copied onto target + * @return {Object} A reference to target. + * @protected + */ + function extend$1(target, sources) { + sources = Array.prototype.slice.call(arguments, 1); + + for (var i = 0, length = sources.length, property, source; i < length; i++) { + source = sources[i]; + + if (source) { + for (property in source) { + if (Object.prototype.hasOwnProperty.call(source, property)) { + target[property] = source[property]; + } + } + } + } + + return target + } + + /* + * Copyright (C) 2016 Alasdair Mercer + * + * 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. + */ + /** * Contains information on how to connect to and authenticate with a YOURLS server. * * @param {string} [url=''] - the URL for the YOURLS server * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be * null) + * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be + * null) * @protected * @constructor */ - function API(url, credentials) { + var API = Oopsy.extend(function(url, credentials, options) { /** * The URL of the YOURLS server. * @@ -109,42 +274,97 @@ * @public * @type {API~Credentials} */ - this.credentials = sanitizeCredentials(credentials); - } + this.credentials = API._sanitizeCredentials(credentials); + /** + * The options to be used to send requests to the YOURLS server. + * + * @public + * @type {API~Options} + */ + this.options = API._sanitizeOptions(options); + }, null, { - /** - * Destroys the singleton instance of {@link API}. - * - * @return {void} - * @public - * @static - */ - API.clear = function() { - instance = null; - }; + /** + * The default options to be used. + * + * @protected + * @static + * @type {API~Options} + */ + defaultOptions: { + format: 'jsonp', + method: 'GET' + }, - /** - * Retrieves the singleton instance of {@link API}. - * - * This function will return null unless an instance is currently stored. - * - * @return {API} The connected {@link API} or null if none exists. - * @public - * @static - */ - API.fetch = function() { - return instance - }; + /** + * The singleton {@link API} instance which is privatized to prevent leaking credentials. + * + * @public + * @static + * @type {API} + */ + instance: null, - /** - * Stores this {@link API} as the singleton, potentially replacing the existing instance. - * - * @return {void} - * @public - */ - API.prototype.store = function() { - instance = this; - }; + /** + * Sanitizes the specified credentials by ensuring that only valid properties are present and only when + * appropriate. + * + * This method does not modify credentials and instead creates a new object with the sanitized + * properties. + * + * @param {API~Credentials} credentials - the credentials to be sanitized (may be null) + * @return {API~Credentials} A sanitized version of credentials or null if + * credentials is null. + * @private + * @static + */ + _sanitizeCredentials: function(credentials) { + if (!credentials) { + return null + } + + var result = {}; + if (credentials.signature) { + result.signature = credentials.signature; + result.timestamp = credentials.timestamp; + } else { + result.password = credentials.password; + result.username = credentials.username; + } + + return result + }, + + /** + * Sanitizes the specified options by ensuring that only valid properties are present and in the correct + * format. + * + * This method does not modify options and instead creates a new object with the sanitized properties and + * default values will be used for missing options. + * + * @param {API~Options} options - the options to be sanitized (may be null) + * @return {API~Options} A sanitized version of options which will contain only default values if + * options is null. + * @private + * @static + */ + _sanitizeOptions: function(options) { + var result = extend$1({}, API.defaultOptions); + if (!options) { + return result + } + + if (options.format) { + result.format = options.format.toLowerCase(); + } + if (options.method) { + result.method = options.method.toUpperCase(); + } + + return result + } + + }); /** * The credentials to be used to authenticate with a private YOURLS API. @@ -164,6 +384,18 @@ * @property {number|string} [timestamp] - The optional timestamp to limit the signature token. */ + /** + * The options that determine how requests are sent to the YOURLS server. + * + * If the request format does not support the HTTP method, requests will not be sent and an + * error will be thrown when such attempts occur. + * + * @typedef {Object} API~Options + * @property {string} [format="jsonp"] - The format in which requests are sent (either "json" or + * "jsonp"). + * @property {string} [method="GET"] - The HTTP method to be used for requests. + */ + /* * Copyright (C) 2016 Alasdair Mercer * @@ -189,7 +421,7 @@ /** * Returns whether the specified obj is an array. * - * This function will use the native Array.isArray, if available. + * This method will use the native Array.isArray, if available. * * @param {*} obj - the object to be checked (may be null) * @return {boolean} true if obj is an array; otherwise false. @@ -222,29 +454,195 @@ */ /** - * Creates a serialized representation of the specified params into a URL query string. + * Contains logic to connect with a YOURLS server and send data to its API. + * + * Due to the nature of HTTP, requests sent using a "GET" HTTP method will include all information in the URL of the + * request. This includes the data that is sent as well as any credentials used to authenticate with the API. You + * have been warned. * - * @param {Object} [params] - the hash of parameter key/value pairs to be serialized - * @return {string} A URL query string representing params. + * @constructor * @protected */ - function paramify(params) { - if (!params) { - return '' + var Request = Oopsy.extend({ + + /** + * Builds the body for this {@link Request} based on the api and data provided. + * + * @param {API} api - the {@link API} to which the request is being sent + * @param {Object} [data] - the data being sent in this request + * @return {Object} The request body. + * @protected + */ + buildBody: function(api, data) { + return extend$1({ format: api.options.format }, api.credentials, data) + }, + + /** + * Returns the list of the HTTP methods that are supported by this {@link Request}. + * + * By default, this method returns null, so implementations must implement this method to ensure + * that {@link Request#isMethodSupported} works correctly. + * + * @return {string[]} The supported HTTP methods. + * @protected + */ + getSupportedHttpMethods: function() { + return null + }, + + /** + * Determines whether this {@link Request} supports the specified HTTP method. + * + * @param {string} method - the HTTP method to be checked + * @return {boolean} true if method is supported; otherwise false. + * @public + */ + isMethodSupported: function(method) { + var supportedMethods = this.getSupportedHttpMethods(); + return supportedMethods && supportedMethods.indexOf(method) !== -1 + }, + + /** + * Determines whether the data that is to be sent to the YOURLS server in this {@link Request} must be serialized as + * query string parameters. + * + * @param {string} method - the HTTP method to be used + * @return {boolean} true if the data needs to be sent as query string parameters; otherwise + * false. + * @protected + */ + isQueryStringRequired: function(method) { + return method === 'GET' + }, + + /** + * Processes this {@link Request} by sending it to the specified target url containing the + * body provided. + * + * callback should be called with the response regardless of whether the it was a success or failure. + * + * This method is called internally by {@link Request#send} and does all of the actual work involved to send the + * request and parse the response. All implementations must implement this method. + * + * @param {string} method - the request HTTP method + * @param {string} url - the request URL + * @param {Object} body - the request body (may be null) + * @param {Function} callback - the function to be called with the response + * @return {void} + * @protected + * @abstract + */ + process: function(method, url, body, callback) { + // Must be implemented + }, + + /** + * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the + * specified callback with the result. + * + * If the request is successful, callback will be passed the value of the named properties from the + * response. If resultNames is a string or only contains a single string, only the value for that named + * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named + * property will be passed as the first argument. The actual response will always be passed as the second argument. + * + * @param {Object} data - the data to be sent + * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to + * callback as the first argument + * @param {Function} callback - the function to be called with the result + * @return {void} + * @public + */ + send: function(data, resultNames, callback) { + var api = API.instance; + var body = Request._serializeParameters(this.buildBody(api, data)); + var method = api.options.method; + var url = api.url; + + if (this.isQueryStringRequired(method)) { + url += '?' + body; + body = null; + } + + this.process(method, url, body, function(response) { + callback(Request._extractResult(resultNames, response), response); + }); } - var results = []; + }, { + + /** + * Extracts the values of the properties with the specified names from the response provided + * and returns them in a single result. + * + * If names is a string or only contains a single string, only the value for that named property will be + * returned. Otherwise, an object containing the key/value pairs for each named property will be returned. + * + * If response is null this method will return null. + * + * @param {string|string[]} names - the names of the response properties whose values are to be returned + * as the result + * @param {Object} response - the YOURLS API response + * @return {*} The result extracted from response. + * @private + * @static + */ + _extractResult: function(names, response) { + names = isArray(names) ? names : [ names ]; + + var i; + var name; + var result = null; + + if (!response) { + return result + } + + if (names.length === 1) { + result = response[names[0]]; + } else { + result = {}; + + for (i = 0; i < names.length; i++) { + name = names[i]; - for (var key in params) { - if (Object.prototype.hasOwnProperty.call(params, key)) { - if (params[key] != null) { - results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); + if (typeof response[name] !== 'undefined') { + result[name] = response[name]; + } } } + + return result + }, + + /** + * Creates a serialized representation of the specified parameters. + * + * All of the parameter names and values are URL-encoded so that they can be safely included in the query string or + * request body. + * + * @param {Object} [params] - the hash of parameter name/value pairs to be serialized + * @return {string} A URL-encoded representing obj or an empty string if obj is + * null. + * @private + * @static + */ + _serializeParameters: function(params) { + if (!params) { + return '' + } + + var results = []; + + for (var name in params) { + if (Object.prototype.hasOwnProperty.call(params, name) && params[name] != null) { + results.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name])); + } + } + + return results.join('&') } - return results.join('&') - } + }); /* * Copyright (C) 2016 Alasdair Mercer @@ -269,126 +667,273 @@ */ /** - * The key of the callback function holder within the global namespace. + * An implementation of {@link Request} that provides support for JSON requests to the YOURLS API. * - * @private - * @type {string} + * JSON requests can only be sent using the "GET" or "POST" HTTP methods. + * + * @constructor + * @extends Request + * @protected */ - var callbackHolderKey = '__yourls' + Date.now() + '_jsonp'; - /** - * Contains the callback functions for active JSONP requests. + var JSONRequest = Request.extend({ + + /** + * @inheritDoc + * @override + */ + getSupportedHttpMethods: function() { + return [ 'GET', 'POST' ] + }, + + /** + * @inheritDoc + * @override + */ + process: function(method, url, body, callback) { + var xhr = new XMLHttpRequest(); + xhr.open(method, url, true); + xhr.onreadystatechange = function() { + var response; + + if (xhr.readyState === 4) { + try { + response = JSON.parse(xhr.responseText); + callback(response); + } catch (e) { + throw new Error('Unable to parse response: ' + e) + } + } + }; + + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + if (body != null) { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + + xhr.send(body); + } + + }); + + /* + * Copyright (C) 2016 Alasdair Mercer * - * Callback references should be removed immediately once they have been called. + * 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: * - * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global). + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * @private - * @type {Object} + * 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. */ - var callbackHolder = window[callbackHolderKey] = {}; /** - * Generates a quick and dirty unique ID for a callback. + * The seed to be used to generate IDs. * - * @return {number} The generated callback ID. * @private + * @type {number} */ - function generateCallbackId() { - var id = Date.now(); - while (callbackHolder[id]) { - id++; - } - - return id - } + var seed = new Date().getTime(); /** - * Extracts the values of the properties with the specified names from the response provided - * and returns them in a single result. + * An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API. * - * If names is a string or only contains a single string, only the value for that named property will be - * returned. Otherwise, an object containing the key/value pairs for each named property will be returned. + * JSONP requests can only be sent using the "GET" HTTP method. * - * If response is null this function will return null. - * - * @param {string|string[]} names - the names of the response properties whose values are to be returned as - * the result - * @param {Object} response - the YOURLS API response - * @return {*} The result extracted from response. - * @private + * @constructor + * @extends Request + * @protected */ - function getResult(names, response) { - names = isArray(names) ? names : [ names ]; + var JSONPRequest = Request.extend(function() { + JSONPRequest.super_.call(this); - var i; - var name; - var result = null; + if (!window[JSONPRequest._callbackHolderKey]) { + window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder; + } - if (!response) { - return result + /** + * The generated ID which is used to store a reference to the callback function within the holder so that the JSONP + * payload can find it in the global namespace. + * + * @private + * @type {number} + */ + this._id = JSONPRequest._generateId(); + }, { + + /** + * @inheritDoc + * @override + */ + getSupportedHttpMethods: function() { + return [ 'GET' ] + }, + + /** + * @inheritDoc + * @override + */ + buildBody: function(api, data) { + var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data); + body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']'; + + return body + }, + + /** + * @inheritDoc + * @override + */ + process: function(method, url, body, callback) { + var script = document.createElement('script'); + + var self = this; + JSONPRequest._callbackHolder[this._id] = function(response) { + delete JSONPRequest._callbackHolder[self._id]; + script.parentNode.removeChild(script); + + callback(response); + }; + + script.setAttribute('src', url); + document.getElementsByTagName('head')[0].appendChild(script); } - if (names.length === 1) { - result = response[names[0]]; - } else { - result = {}; + }, { - for (i = 0; i < names.length; i++) { - name = names[i]; + /** + * The key of the callback function holder within the global namespace. + * + * @private + * @static + * @type {string} + */ + _callbackHolderKey: '__yourls' + seed + '_jsonp', - if (typeof response[name] !== 'undefined') { - result[name] = response[name]; - } - } + /** + * Contains the callback functions for active JSONP requests. + * + * Callback references should be removed immediately once they have been called. + * + * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global). + * + * @private + * @static + * @type {Object} + */ + _callbackHolder: {}, + + /** + * Generates an ID to be used when storing a reference to a callback function. + * + * @return {number} The generated ID. + * @private + */ + _generateId: function() { + do { + seed++; + } while (JSONPRequest._callbackHolder[seed]) + + return seed } - return result - } + }); - /** - * Sends a JSONP request to the connected YOURLS API with the data provided which should, in turn, call the - * specified callback with the result. + /* + * Copyright (C) 2016 Alasdair Mercer * - * If the request is successful, callback will be passed the value of the named properties from the - * response. If resultNames is a string or only contains a single string, only the value for that named - * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named - * property will be passed as the first argument. The actual response will always be passed as the second argument. + * 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. * - * Due to the nature of JSONP, all information will be included in the URL of the request. This includes - * data as well as any credentials used to authenticate with the API. You have been warned. + * 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. + */ + + /** + * Can make requests to the connected YOURLS API. * - * @param {Object} data - the data to be sent - * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to - * callback as the first argument - * @param {Function} callback - the function to be called with the result - * @return {void} + * @constructor * @protected */ - function jsonp(data, resultNames, callback) { - var api = API.fetch(); - var id = generateCallbackId(); - var script = document.createElement('script'); + var Requestor = Oopsy.extend({ + + /** + * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the + * specified callback with the result. + * + * This method is primarily a proxy to {@link Request#send} but does validate the state of the connection information + * to ensure that is is valid before making the request. + * + * @param {Object} data - the data to be sent + * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to + * callback as the first argument + * @param {Function} callback - the function to be called with the result + * @return {void} + * @throws {Error} - If either no connection is present, the request format is not supported, or the configured HTTP + * method is not supported by the {@link Request}. + * @protected + */ + sendRequest: function(data, resultNames, callback) { + var api = API.instance; + + if (!api) { + throw new Error('No connection has been made') + } - callbackHolder[id] = function(response) { - var result = getResult(resultNames, response); + var format = api.options.format; + var method = api.options.method; + var Request = Requestor._requestFormatMap[format]; - delete callbackHolder[id]; - script.parentNode.removeChild(script); + if (!Request) { + throw new Error('Request format not supported: ' + format) + } - callback(result, response); - }; + var request = new Request(); - var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' }); - if (api.credentials) { - target += '&' + paramify(api.credentials); + if (!request.isMethodSupported(method)) { + throw new Error('HTTP method not supported: ' + method) + } + + request.send(data, resultNames, callback); } - if (data) { - target += '&' + paramify(data); + + }, { + + /** + * The mapping of supported request formats to {@link Request} constructors. + * + * @private + * @static + * @type {Object} + */ + _requestFormatMap: { + json: JSONRequest, + jsonp: JSONPRequest } - script.setAttribute('src', target); - document.getElementsByTagName('head')[0].appendChild(script); - } + }); /* * Copyright (C) 2016 Alasdair Mercer @@ -416,26 +961,27 @@ * Provides the ability to lookup information related to the YOURLS database. * * @constructor + * @extends Requestor * @protected */ - function DB() { - // Do nothing - } + var DB = Requestor.extend({ - /** - * Retrieves the statistics for this {@link DB}. - * - * @param {Function} callback - the callback function to be called with the result - * @return {DB} A reference to this {@link DB} for chaining purposes. - * @public - */ - DB.prototype.stats = function(callback) { - var data = { action: 'db-stats' }; + /** + * Retrieves the statistics for this {@link DB}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {DB} A reference to this {@link DB} for chaining purposes. + * @public + */ + stats: function(callback) { + var data = { action: 'db-stats' }; - jsonp(data, 'db-stats', callback); + this.sendRequest(data, 'db-stats', callback); - return this - }; + return this + } + + }); /* * Copyright (C) 2016 Alasdair Mercer @@ -464,9 +1010,12 @@ * * @param {string} url - the shortened URL (or its keyword) to be used * @constructor + * @extends Requestor * @protected */ - function URL(url) { + var URL = Requestor.extend(function(url) { + URL.super_.call(this); + /** * Either the shortened URL or its keyword for this {@link URL}. * @@ -474,7 +1023,7 @@ * @type {string} */ this.url = url; - } + }); /** * Retrieves the original ("long") URL for this shortened {@link URL}. @@ -489,7 +1038,7 @@ shorturl: this.url }; - jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback); + this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback); return this }; @@ -507,7 +1056,7 @@ shorturl: this.url }; - jsonp(data, 'link', callback); + this.sendRequest(data, 'link', callback); return this }; @@ -534,38 +1083,6 @@ * SOFTWARE. */ - /** - * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links - * from an object mapping into an array. - * - * This function simply returns result if it is null, has no links property or one that is - * already an array (future-proofing). - * - * @param {Object} result - the result to be sanitized (may be null) - * @param {Object} [result.links] - the links to be transformed into an array (may be null) - * @return {Object} The modified result or null if result is null. - * @private - */ - function sanitizeStatsResult(result) { - // Future-proofing by sanitizing links *only* when not already an array - if (!result || !result.links || isArray(result.links)) { - return result - } - - var index = 1; - var link; - var links = []; - - while ((link = result.links['link_' + index]) != null) { - links.push(link); - index++; - } - - result.links = links; - - return result - } - /** * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose. * @@ -573,9 +1090,12 @@ * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private). * * @constructor + * @extends Requestor * @protected */ - var YOURLS = function() { + var YOURLS = Requestor.extend(function() { + YOURLS.super_.call(this); + /** * Provides information on the YOURLS {@link DB}. * @@ -587,173 +1107,211 @@ /** * The current version of yourls. * - * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} - * function should be used to provide that information. + * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method + * should be used to provide that information. * * @public * @type {string} */ this.VERSION = '2.0.0'; - }; + }, { - /** - * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. - * - * @param {string} [url=''] - the URL for the YOURLS server - * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be - * null) - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ - YOURLS.prototype.connect = function(url, credentials) { - var api = new API(url, credentials); - api.store(); + /** + * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. + * + * @param {string} [url=''] - the URL for the YOURLS server + * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be + * null) + * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be + * null) + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + connect: function(url, credentials, options) { + API.instance = new API(url, credentials, options); - return this - }; + return this + }, - /** - * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS - * server. - * - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ - YOURLS.prototype.disconnect = function() { - API.clear(); + /** + * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS + * server. + * + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + disconnect: function() { + API.instance = null; - return this - }; + return this + }, - /** - * Creates a short URL for the specified long url. - * - * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is to - * be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a unique - * keyword. If descriptor is a string, it will be treated as the keyword. - * - * @param {string} url - the long URL to be shortened - * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used - * for the short URL - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ - YOURLS.prototype.shorten = function(url, descriptor, callback) { - var data = { - action: 'shorturl', - url: url - }; + /** + * Creates a short URL for the specified long url. + * + * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is + * to be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a + * unique keyword. If descriptor is a string, it will be treated as the keyword. + * + * @param {string} url - the long URL to be shortened + * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be + * used for the short URL + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + shorten: function(url, descriptor, callback) { + var data = { + action: 'shorturl', + url: url + }; + + switch (typeof descriptor) { + case 'function': + callback = descriptor; + descriptor = null; + break + case 'string': + descriptor = { keyword: descriptor }; + break + default: + // Do nothing + } - switch (typeof descriptor) { - case 'function': - callback = descriptor; - descriptor = null; - break - case 'string': - descriptor = { keyword: descriptor }; - break - default: - // Do nothing - } + if (descriptor) { + data.keyword = descriptor.keyword; + data.title = descriptor.title; + } - if (descriptor) { - data.keyword = descriptor.keyword; - data.title = descriptor.title; - } + this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback); - jsonp(data, [ 'shorturl', 'title', 'url' ], callback); + return this + }, - return this - }; + /** + * Retrieves the statistics for all shortened URLs. + * + * Optionally, criteria can be provided to also include a refined set of links in the result. This + * includes filter, which provides limited control over the sorting, as well as limit and start, which allow for + * pagination. If criteria is a number, it will be treated as the limit. + * + * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this + * method would effectively be doing the same as {@link DB#stats}. + * + * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to + * search for links to be included in the result + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + stats: function(criteria, callback) { + var data = { action: 'stats' }; + + switch (typeof criteria) { + case 'function': + callback = criteria; + criteria = null; + break + case 'number': + criteria = { limit: criteria }; + break + default: + // Do nothing + } - /** - * Retrieves the statistics for all shortened URLs. - * - * Optionally, criteria can be provided to also include a refined set of links in the result. This includes - * filter, which provides limited control over the sorting, as well as limit and start, which allow for pagination. If - * criteria is a number, it will be treated as the limit. - * - * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this - * method would effectively be doing the same as {@link DB#stats}. - * - * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to - * search for links to be included in the result - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ - YOURLS.prototype.stats = function(criteria, callback) { - var data = { action: 'stats' }; - - switch (typeof criteria) { - case 'function': - callback = criteria; - criteria = null; - break - case 'number': - criteria = { limit: criteria }; - break - default: - // Do nothing - } + if (criteria) { + data.filter = criteria.filter; + data.limit = criteria.limit; + data.start = criteria.start; + } - if (criteria) { - data.filter = criteria.filter; - data.limit = criteria.limit; - data.start = criteria.start; - } + this.sendRequest(data, [ 'links', 'stats' ], function(result, response) { + callback(YOURLS._sanitizeStatsResult(result), response); + }); - jsonp(data, [ 'links', 'stats' ], function(result, response) { - callback(sanitizeStatsResult(result), response); - }); + return this + }, - return this - }; + /** + * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more + * detailed information relating to it. + * + * No data is fetched just by calling this method; one of the methods on the returned instance need to be called for + * that to happen. + * + * @param {string} url - the shortened URL (or its keyword) + * @return {URL} The {@link URL} created for the shortened url or null if url + * is null. + * @public + */ + url: function(url) { + return url ? new URL(url) : null + }, - /** - * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more - * detailed information relating to it. - * - * No data is fetched just by calling this function; one of the functions on the returned instance need to be called for - * that to happen. - * - * @param {string} url - the shortened URL (or its keyword) - * @return {URL} The {@link URL} created for the shortened url or null if url is - * null. - * @public - */ - YOURLS.prototype.url = function(url) { - return url ? new URL(url) : null - }; + /** + * Retrieves the version of the connected YOURLS API. + * + * Optionally, db can be passed to indicate that the YOURLS database version should also be included in + * the result. + * + * @param {boolean} [db] - true to include the database version; otherwise false + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + version: function(db, callback) { + var data = { action: 'version' }; - /** - * Retrieves the version of the connected YOURLS API. - * - * Optionally, db can be passed to indicate that the YOURLS database version should also be included in the - * result. - * - * @param {boolean} [db] - true to include the database version; otherwise false - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ - YOURLS.prototype.version = function(db, callback) { - var data = { action: 'version' }; + if (typeof db === 'function') { + callback = db; + db = null; + } - if (typeof db === 'function') { - callback = db; - db = null; - } + if (db != null) { + data.db = Number(db); + } + + this.sendRequest(data, [ 'db_version', 'version' ], callback); - if (db != null) { - data.db = Number(db); + return this } - jsonp(data, [ 'db_version', 'version' ], callback); + }, { - return this - }; + /** + * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links + * from an object mapping into an array. + * + * This method simply returns result if it is null, has no links property or one that is + * already an array (future-proofing). + * + * @param {Object} result - the result to be sanitized (may be null) + * @param {Object} [result.links] - the links to be transformed into an array (may be null) + * @return {Object} The modified result or null if result is null. + * @private + * @static + */ + _sanitizeStatsResult: function(result) { + // Future-proofing by sanitizing links *only* when not already an array + if (!result || !result.links || isArray(result.links)) { + return result + } + + var index = 1; + var link; + var links = []; + + while ((link = result.links['link_' + index]) != null) { + links.push(link); + index++; + } + + result.links = links; + + return result + } + + }); /** * The singleton instance of {@link YOURLS}. diff --git a/dist/yourls.js.map b/dist/yourls.js.map index 4dc92d2..a58b4ec 100644 --- a/dist/yourls.js.map +++ b/dist/yourls.js.map @@ -1 +1 @@ -{"version":3,"file":null,"sources":["../src/api.js","../src/util/array.js","../src/request/paramify.js","../src/request/jsonp.js","../src/yourls-db.js","../src/yourls-url.js","../src/yourls.js"],"sourcesContent":["/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * The singleton {@link API} instance which is privatized to prevent leaking credentials.\n *\n * @private\n * @type {API}\n */\nvar instance = null\n\n/**\n * Sanitizes the specified credentials by ensuring that only valid properties are present and only when\n * appropriate.\n *\n * This function does not modify credentials and instead creates a new object with the sanitized\n * properties.\n *\n * @param {API~Credentials} credentials - the credentials to be sanitized (may be null)\n * @return {API~Credentials} A sanitized version of credentials or null if\n * credentials is null.\n * @private\n */\nfunction sanitizeCredentials(credentials) {\n if (!credentials) {\n return null\n }\n\n var result = {}\n if (credentials.signature) {\n result.signature = credentials.signature\n result.timestamp = credentials.timestamp\n } else {\n result.password = credentials.password\n result.username = credentials.username\n }\n\n return result\n}\n\n/**\n * Contains information on how to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @protected\n * @constructor\n */\nexport function API(url, credentials) {\n /**\n * The URL of the YOURLS server.\n *\n * @public\n * @type {string}\n */\n this.url = url ? url.replace(/\\/$/, '') : ''\n /**\n * The credentials to be used to authenticate with the YOURLS API.\n *\n * This may be null if the YOURLS API is public.\n *\n * @public\n * @type {API~Credentials}\n */\n this.credentials = sanitizeCredentials(credentials)\n}\n\n/**\n * Destroys the singleton instance of {@link API}.\n *\n * @return {void}\n * @public\n * @static\n */\nAPI.clear = function() {\n instance = null\n}\n\n/**\n * Retrieves the singleton instance of {@link API}.\n *\n * This function will return null unless an instance is currently stored.\n *\n * @return {API} The connected {@link API} or null if none exists.\n * @public\n * @static\n */\nAPI.fetch = function() {\n return instance\n}\n\n/**\n * Stores this {@link API} as the singleton, potentially replacing the existing instance.\n *\n * @return {void}\n * @public\n */\nAPI.prototype.store = function() {\n instance = this\n}\n\n/**\n * The credentials to be used to authenticate with a private YOURLS API.\n *\n * Authentication can be done with a traditional username and password combination or by using the secret\n * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs\n * and can be found on the \"Tools\" page.\n *\n * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server\n * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and\n * signature token concatenated, and in that order.\n *\n * @typedef {Object} API~Credentials\n * @property {string} [password] - The password of the user to be authenticated.\n * @property {string} [username] - The name of the user to be authenticated.\n * @property {string} [signature] - The signature token to be used for passwordless authentication with the YOURLS API.\n * @property {number|string} [timestamp] - The optional timestamp to limit the signature token.\n */\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Returns whether the specified obj is an array.\n *\n * This function will use the native Array.isArray, if available.\n *\n * @param {*} obj - the object to be checked (may be null)\n * @return {boolean} true if obj is an array; otherwise false.\n * @protected\n */\nexport function isArray(obj) {\n return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]'\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Creates a serialized representation of the specified params into a URL query string.\n *\n * @param {Object} [params] - the hash of parameter key/value pairs to be serialized\n * @return {string} A URL query string representing params.\n * @protected\n */\nexport function paramify(params) {\n if (!params) {\n return ''\n }\n\n var results = []\n\n for (var key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n if (params[key] != null) {\n results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))\n }\n }\n }\n\n return results.join('&')\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from '../api'\nimport { isArray } from '../util/array'\nimport { paramify } from './paramify'\n\n/**\n * The key of the callback function holder within the global namespace.\n *\n * @private\n * @type {string}\n */\nvar callbackHolderKey = '__yourls' + Date.now() + '_jsonp'\n/**\n * Contains the callback functions for active JSONP requests.\n *\n * Callback references should be removed immediately once they have been called.\n *\n * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global).\n *\n * @private\n * @type {Object}\n */\nvar callbackHolder = window[callbackHolderKey] = {}\n\n/**\n * Generates a quick and dirty unique ID for a callback.\n *\n * @return {number} The generated callback ID.\n * @private\n */\nfunction generateCallbackId() {\n var id = Date.now()\n while (callbackHolder[id]) {\n id++\n }\n\n return id\n}\n\n/**\n * Extracts the values of the properties with the specified names from the response provided\n * and returns them in a single result.\n *\n * If names is a string or only contains a single string, only the value for that named property will be\n * returned. Otherwise, an object containing the key/value pairs for each named property will be returned.\n *\n * If response is null this function will return null.\n *\n * @param {string|string[]} names - the names of the response properties whose values are to be returned as\n * the result\n * @param {Object} response - the YOURLS API response\n * @return {*} The result extracted from response.\n * @private\n */\nfunction getResult(names, response) {\n names = isArray(names) ? names : [ names ]\n\n var i\n var name\n var result = null\n\n if (!response) {\n return result\n }\n\n if (names.length === 1) {\n result = response[names[0]]\n } else {\n result = {}\n\n for (i = 0; i < names.length; i++) {\n name = names[i]\n\n if (typeof response[name] !== 'undefined') {\n result[name] = response[name]\n }\n }\n }\n\n return result\n}\n\n/**\n * Sends a JSONP request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * If the request is successful, callback will be passed the value of the named properties from the\n * response. If resultNames is a string or only contains a single string, only the value for that named\n * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named\n * property will be passed as the first argument. The actual response will always be passed as the second argument.\n *\n * Due to the nature of JSONP, all information will be included in the URL of the request. This includes\n * data as well as any credentials used to authenticate with the API. You have been warned.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @protected\n */\nexport function jsonp(data, resultNames, callback) {\n var api = API.fetch()\n var id = generateCallbackId()\n var script = document.createElement('script')\n\n callbackHolder[id] = function(response) {\n var result = getResult(resultNames, response)\n\n delete callbackHolder[id]\n script.parentNode.removeChild(script)\n\n callback(result, response)\n }\n\n var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' })\n if (api.credentials) {\n target += '&' + paramify(api.credentials)\n }\n if (data) {\n target += '&' + paramify(data)\n }\n\n script.setAttribute('src', target)\n document.getElementsByTagName('head')[0].appendChild(script)\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { jsonp } from './request/jsonp'\n\n/**\n * Provides the ability to lookup information related to the YOURLS database.\n *\n * @constructor\n * @protected\n */\nexport function DB() {\n // Do nothing\n}\n\n/**\n * Retrieves the statistics for this {@link DB}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {DB} A reference to this {@link DB} for chaining purposes.\n * @public\n */\nDB.prototype.stats = function(callback) {\n var data = { action: 'db-stats' }\n\n jsonp(data, 'db-stats', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE0\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { jsonp } from './request/jsonp'\n\n/**\n * Provides the ability to lookup information related to the specified shortened url.\n *\n * @param {string} url - the shortened URL (or its keyword) to be used\n * @constructor\n * @protected\n */\nexport function URL(url) {\n /**\n * Either the shortened URL or its keyword for this {@link URL}.\n *\n * @public\n * @type {string}\n */\n this.url = url\n}\n\n/**\n * Retrieves the original (\"long\") URL for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.expand = function(callback) {\n var data = {\n action: 'expand',\n shorturl: this.url\n }\n\n jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.stats = function(callback) {\n var data = {\n action: 'url-stats',\n shorturl: this.url\n }\n\n jsonp(data, 'link', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from './api'\nimport { DB } from './yourls-db'\nimport { isArray } from './util/array'\nimport { jsonp } from './request/jsonp'\nimport { URL } from './yourls-url'\n\n/**\n * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links\n * from an object mapping into an array.\n *\n * This function simply returns result if it is null, has no links property or one that is\n * already an array (future-proofing).\n *\n * @param {Object} result - the result to be sanitized (may be null)\n * @param {Object} [result.links] - the links to be transformed into an array (may be null)\n * @return {Object} The modified result or null if result is null.\n * @private\n */\nfunction sanitizeStatsResult(result) {\n // Future-proofing by sanitizing links *only* when not already an array\n if (!result || !result.links || isArray(result.links)) {\n return result\n }\n\n var index = 1\n var link\n var links = []\n\n while ((link = result.links['link_' + index]) != null) {\n links.push(link)\n index++\n }\n\n result.links = links\n\n return result\n}\n\n/**\n * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose.\n *\n * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure\n * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private).\n *\n * @constructor\n * @protected\n */\nvar YOURLS = function() {\n /**\n * Provides information on the YOURLS {@link DB}.\n *\n * @public\n * @type {DB}\n */\n this.db = new DB()\n\n /**\n * The current version of yourls.\n *\n * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version}\n * function should be used to provide that information.\n *\n * @public\n * @type {string}\n */\n this.VERSION = '2.0.0'\n}\n\n/**\n * Stores the specified information to be used later to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.connect = function(url, credentials) {\n var api = new API(url, credentials)\n api.store()\n\n return this\n}\n\n/**\n * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS\n * server.\n *\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.disconnect = function() {\n API.clear()\n\n return this\n}\n\n/**\n * Creates a short URL for the specified long url.\n *\n * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is to\n * be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword. If descriptor is a string, it will be treated as the keyword.\n *\n * @param {string} url - the long URL to be shortened\n * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used\n * for the short URL\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.shorten = function(url, descriptor, callback) {\n var data = {\n action: 'shorturl',\n url: url\n }\n\n switch (typeof descriptor) {\n case 'function':\n callback = descriptor\n descriptor = null\n break\n case 'string':\n descriptor = { keyword: descriptor }\n break\n default:\n // Do nothing\n }\n\n if (descriptor) {\n data.keyword = descriptor.keyword\n data.title = descriptor.title\n }\n\n jsonp(data, [ 'shorturl', 'title', 'url' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for all shortened URLs.\n *\n * Optionally, criteria can be provided to also include a refined set of links in the result. This includes\n * filter, which provides limited control over the sorting, as well as limit and start, which allow for pagination. If\n * criteria is a number, it will be treated as the limit.\n *\n * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this\n * method would effectively be doing the same as {@link DB#stats}.\n *\n * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to\n * search for links to be included in the result\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.stats = function(criteria, callback) {\n var data = { action: 'stats' }\n\n switch (typeof criteria) {\n case 'function':\n callback = criteria\n criteria = null\n break\n case 'number':\n criteria = { limit: criteria }\n break\n default:\n // Do nothing\n }\n\n if (criteria) {\n data.filter = criteria.filter\n data.limit = criteria.limit\n data.start = criteria.start\n }\n\n jsonp(data, [ 'links', 'stats' ], function(result, response) {\n callback(sanitizeStatsResult(result), response)\n })\n\n return this\n}\n\n/**\n * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more\n * detailed information relating to it.\n *\n * No data is fetched just by calling this function; one of the functions on the returned instance need to be called for\n * that to happen.\n *\n * @param {string} url - the shortened URL (or its keyword)\n * @return {URL} The {@link URL} created for the shortened url or null if url is\n * null.\n * @public\n */\nYOURLS.prototype.url = function(url) {\n return url ? new URL(url) : null\n}\n\n/**\n * Retrieves the version of the connected YOURLS API.\n *\n * Optionally, db can be passed to indicate that the YOURLS database version should also be included in the\n * result.\n *\n * @param {boolean} [db] - true to include the database version; otherwise false\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.version = function(db, callback) {\n var data = { action: 'version' }\n\n if (typeof db === 'function') {\n callback = db\n db = null\n }\n\n if (db != null) {\n data.db = Number(db)\n }\n\n jsonp(data, [ 'db_version', 'version' ], callback)\n\n return this\n}\n\n/**\n * The singleton instance of {@link YOURLS}.\n */\nexport default new YOURLS()\n\n/**\n * Contains criteria which can be used to search for a refined set of shortened URLs.\n *\n * Pagination can be achieved by using limit and start.\n *\n * No links will be returned unless limit is specified and has a value that is greater than zero.\n *\n * @typedef {Object} YOURLS~SearchCriteria\n * @property {string} [filter] - The filter to be applied (either \"top\", \"bottom\",\n * \"rand\", or \"last\").\n * @property {number} [limit] - The maximum number of links whose statistical information is to be counted.\n * null or 0 will result in no links being included in the result.\n * @property {number} [start] - The offset from where the search should begin.\n */\n\n/**\n * Contains additional information which can be used when shortening a URL.\n *\n * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword.\n *\n * @typedef {Object} YOURLS~UrlDescriptor\n * @property {string} [keyword] - The optional keyword to be used for the shortened URL.\n * @property {string} [title] - The optional title to be associated with the shortened URL.\n */\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"yourls.js","sources":["../node_modules/oopsy/src/oopsy.js","../src/util/extend.js","../src/api.js","../src/util/array.js","../src/request/request.js","../src/request/json.js","../src/request/jsonp.js","../src/request/requestor.js","../src/yourls-db.js","../src/yourls-url.js","../src/yourls.js"],"sourcesContent":["/*\n * Copyright (C) 2016 Alasdair Mercer, Skelp\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * A bare-bones constructor for surrogate prototype swapping.\n *\n * @private\n * @constructor\n */\nvar Constructor = function() {}\n/**\n * A reference to Object.prototype.hasOwnProperty.\n *\n * @private\n * @type {Function}\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty\n/**\n * A reference to Array.prototype.slice.\n *\n * @private\n * @type {Function}\n */\nvar slice = Array.prototype.slice\n\n/**\n * Extends the specified target object with the properties in each of the sources provided.\n *\n * Nothing happens if target is null and if any source is null it will be\n * ignored.\n *\n * @param {boolean} own - true to only copy own properties from sources onto\n * target; otherwise false\n * @param {Object} [target] - the target object which should be extended\n * @param {...Object} [sources] - the source objects whose properties are to be copied onto target\n * @return {void}\n * @private\n */\nfunction extend(own, target, sources) {\n if (target == null) {\n return\n }\n\n sources = slice.call(arguments, 2)\n\n var property\n var source\n\n for (var i = 0, length = sources.length; i < length; i++) {\n source = sources[i]\n\n for (property in source) {\n if (!own || hasOwnProperty.call(source, property)) {\n target[property] = source[property]\n }\n }\n }\n}\n\n/**\n * Creates an object which inherits the given prototype.\n *\n * Optionally, the created object can be extended further with the specified properties.\n *\n * @param {Object} prototype - the prototype to be inherited by the created object\n * @param {Object} [properties] - the optional properties to be extended by the created object\n * @return {Object} The newly created object.\n * @private\n */\nfunction create(prototype, properties) {\n var result\n if (typeof Object.create === 'function') {\n result = Object.create(prototype)\n } else {\n Constructor.prototype = prototype\n result = new Constructor()\n Constructor.prototype = null\n }\n\n if (properties) {\n extend(true, result, properties)\n }\n\n return result\n}\n\n/**\n * The base constructor from which all others should extend.\n *\n * @public\n * @constructor\n */\nexport default function Oopsy() {}\n\n/**\n * Extends the constructor to which this method is associated with the prototype and/or\n * statics provided.\n *\n * If constructor is provided, it will be used as the constructor for the child, otherwise a simple\n * constructor which only calls the super constructor will be used instead.\n *\n * The super constructor can be accessed via a special super_ property on the child constructor.\n *\n * @param {Function} [constructor] - the constructor for the child\n * @param {Object} [prototype] - the prototype properties to be defined for the child\n * @param {Object} [statics] - the static properties to be defined for the child\n * @return {Function} The child constructor provided or the one created if none was given.\n * @public\n * @static\n */\nOopsy.extend = function(constructor, prototype, statics) {\n var superConstructor = this\n\n if (typeof constructor !== 'function') {\n statics = prototype\n prototype = constructor\n constructor = function() {\n return superConstructor.apply(this, arguments)\n }\n }\n\n extend(false, constructor, superConstructor, statics)\n\n constructor.prototype = create(superConstructor.prototype, prototype)\n constructor.prototype.constructor = constructor\n\n constructor.super_ = superConstructor\n\n return constructor\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Extends the specified target object with the properties in each of the sources provided.\n *\n * Any of the sources that are null will simply be ignored.\n *\n * @param {Object} target - the target object which should be extended\n * @param {...Object} [sources] - the source objects whose properties are to be copied onto target\n * @return {Object} A reference to target.\n * @protected\n */\nexport function extend(target, sources) {\n sources = Array.prototype.slice.call(arguments, 1)\n\n for (var i = 0, length = sources.length, property, source; i < length; i++) {\n source = sources[i]\n\n if (source) {\n for (property in source) {\n if (Object.prototype.hasOwnProperty.call(source, property)) {\n target[property] = source[property]\n }\n }\n }\n }\n\n return target\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { extend } from './util/extend'\n\n/**\n * Contains information on how to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be\n * null)\n * @protected\n * @constructor\n */\nexport var API = Oopsy.extend(function(url, credentials, options) {\n /**\n * The URL of the YOURLS server.\n *\n * @public\n * @type {string}\n */\n this.url = url ? url.replace(/\\/$/, '') : ''\n /**\n * The credentials to be used to authenticate with the YOURLS API.\n *\n * This may be null if the YOURLS API is public.\n *\n * @public\n * @type {API~Credentials}\n */\n this.credentials = API._sanitizeCredentials(credentials)\n /**\n * The options to be used to send requests to the YOURLS server.\n *\n * @public\n * @type {API~Options}\n */\n this.options = API._sanitizeOptions(options)\n}, null, {\n\n /**\n * The default options to be used.\n *\n * @protected\n * @static\n * @type {API~Options}\n */\n defaultOptions: {\n format: 'jsonp',\n method: 'GET'\n },\n\n /**\n * The singleton {@link API} instance which is privatized to prevent leaking credentials.\n *\n * @public\n * @static\n * @type {API}\n */\n instance: null,\n\n /**\n * Sanitizes the specified credentials by ensuring that only valid properties are present and only when\n * appropriate.\n *\n * This method does not modify credentials and instead creates a new object with the sanitized\n * properties.\n *\n * @param {API~Credentials} credentials - the credentials to be sanitized (may be null)\n * @return {API~Credentials} A sanitized version of credentials or null if\n * credentials is null.\n * @private\n * @static\n */\n _sanitizeCredentials: function(credentials) {\n if (!credentials) {\n return null\n }\n\n var result = {}\n if (credentials.signature) {\n result.signature = credentials.signature\n result.timestamp = credentials.timestamp\n } else {\n result.password = credentials.password\n result.username = credentials.username\n }\n\n return result\n },\n\n /**\n * Sanitizes the specified options by ensuring that only valid properties are present and in the correct\n * format.\n *\n * This method does not modify options and instead creates a new object with the sanitized properties and\n * default values will be used for missing options.\n *\n * @param {API~Options} options - the options to be sanitized (may be null)\n * @return {API~Options} A sanitized version of options which will contain only default values if\n * options is null.\n * @private\n * @static\n */\n _sanitizeOptions: function(options) {\n var result = extend({}, API.defaultOptions)\n if (!options) {\n return result\n }\n\n if (options.format) {\n result.format = options.format.toLowerCase()\n }\n if (options.method) {\n result.method = options.method.toUpperCase()\n }\n\n return result\n }\n\n})\n\n/**\n * The credentials to be used to authenticate with a private YOURLS API.\n *\n * Authentication can be done with a traditional username and password combination or by using the secret\n * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs\n * and can be found on the \"Tools\" page.\n *\n * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server\n * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and\n * signature token concatenated, and in that order.\n *\n * @typedef {Object} API~Credentials\n * @property {string} [password] - The password of the user to be authenticated.\n * @property {string} [username] - The name of the user to be authenticated.\n * @property {string} [signature] - The signature token to be used for passwordless authentication with the YOURLS API.\n * @property {number|string} [timestamp] - The optional timestamp to limit the signature token.\n */\n\n/**\n * The options that determine how requests are sent to the YOURLS server.\n *\n * If the request format does not support the HTTP method, requests will not be sent and an\n * error will be thrown when such attempts occur.\n *\n * @typedef {Object} API~Options\n * @property {string} [format=\"jsonp\"] - The format in which requests are sent (either \"json\" or\n * \"jsonp\").\n * @property {string} [method=\"GET\"] - The HTTP method to be used for requests.\n */\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Returns whether the specified obj is an array.\n *\n * This method will use the native Array.isArray, if available.\n *\n * @param {*} obj - the object to be checked (may be null)\n * @return {boolean} true if obj is an array; otherwise false.\n * @protected\n */\nexport function isArray(obj) {\n return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]'\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { API } from '../api'\nimport { extend } from '../util/extend'\nimport { isArray } from '../util/array'\n\n/**\n * Contains logic to connect with a YOURLS server and send data to its API.\n *\n * Due to the nature of HTTP, requests sent using a \"GET\" HTTP method will include all information in the URL of the\n * request. This includes the data that is sent as well as any credentials used to authenticate with the API. You\n * have been warned.\n *\n * @constructor\n * @protected\n */\nexport var Request = Oopsy.extend({\n\n /**\n * Builds the body for this {@link Request} based on the api and data provided.\n *\n * @param {API} api - the {@link API} to which the request is being sent\n * @param {Object} [data] - the data being sent in this request\n * @return {Object} The request body.\n * @protected\n */\n buildBody: function(api, data) {\n return extend({ format: api.options.format }, api.credentials, data)\n },\n\n /**\n * Returns the list of the HTTP methods that are supported by this {@link Request}.\n *\n * By default, this method returns null, so implementations must implement this method to ensure\n * that {@link Request#isMethodSupported} works correctly.\n *\n * @return {string[]} The supported HTTP methods.\n * @protected\n */\n getSupportedHttpMethods: function() {\n return null\n },\n\n /**\n * Determines whether this {@link Request} supports the specified HTTP method.\n *\n * @param {string} method - the HTTP method to be checked\n * @return {boolean} true if method is supported; otherwise false.\n * @public\n */\n isMethodSupported: function(method) {\n var supportedMethods = this.getSupportedHttpMethods()\n return supportedMethods && supportedMethods.indexOf(method) !== -1\n },\n\n /**\n * Determines whether the data that is to be sent to the YOURLS server in this {@link Request} must be serialized as\n * query string parameters.\n *\n * @param {string} method - the HTTP method to be used\n * @return {boolean} true if the data needs to be sent as query string parameters; otherwise\n * false.\n * @protected\n */\n isQueryStringRequired: function(method) {\n return method === 'GET'\n },\n\n /**\n * Processes this {@link Request} by sending it to the specified target url containing the\n * body provided.\n *\n * callback should be called with the response regardless of whether the it was a success or failure.\n *\n * This method is called internally by {@link Request#send} and does all of the actual work involved to send the\n * request and parse the response. All implementations must implement this method.\n *\n * @param {string} method - the request HTTP method\n * @param {string} url - the request URL\n * @param {Object} body - the request body (may be null)\n * @param {Function} callback - the function to be called with the response\n * @return {void}\n * @protected\n * @abstract\n */\n process: function(method, url, body, callback) {\n // Must be implemented\n },\n\n /**\n * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * If the request is successful, callback will be passed the value of the named properties from the\n * response. If resultNames is a string or only contains a single string, only the value for that named\n * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named\n * property will be passed as the first argument. The actual response will always be passed as the second argument.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @public\n */\n send: function(data, resultNames, callback) {\n var api = API.instance\n var body = Request._serializeParameters(this.buildBody(api, data))\n var method = api.options.method\n var url = api.url\n\n if (this.isQueryStringRequired(method)) {\n url += '?' + body\n body = null\n }\n\n this.process(method, url, body, function(response) {\n callback(Request._extractResult(resultNames, response), response)\n })\n }\n\n}, {\n\n /**\n * Extracts the values of the properties with the specified names from the response provided\n * and returns them in a single result.\n *\n * If names is a string or only contains a single string, only the value for that named property will be\n * returned. Otherwise, an object containing the key/value pairs for each named property will be returned.\n *\n * If response is null this method will return null.\n *\n * @param {string|string[]} names - the names of the response properties whose values are to be returned\n * as the result\n * @param {Object} response - the YOURLS API response\n * @return {*} The result extracted from response.\n * @private\n * @static\n */\n _extractResult: function(names, response) {\n names = isArray(names) ? names : [ names ]\n\n var i\n var name\n var result = null\n\n if (!response) {\n return result\n }\n\n if (names.length === 1) {\n result = response[names[0]]\n } else {\n result = {}\n\n for (i = 0; i < names.length; i++) {\n name = names[i]\n\n if (typeof response[name] !== 'undefined') {\n result[name] = response[name]\n }\n }\n }\n\n return result\n },\n\n /**\n * Creates a serialized representation of the specified parameters.\n *\n * All of the parameter names and values are URL-encoded so that they can be safely included in the query string or\n * request body.\n *\n * @param {Object} [params] - the hash of parameter name/value pairs to be serialized\n * @return {string} A URL-encoded representing obj or an empty string if obj is\n * null.\n * @private\n * @static\n */\n _serializeParameters: function(params) {\n if (!params) {\n return ''\n }\n\n var results = []\n\n for (var name in params) {\n if (Object.prototype.hasOwnProperty.call(params, name) && params[name] != null) {\n results.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name]))\n }\n }\n\n return results.join('&')\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Request } from './request'\n\n/**\n * An implementation of {@link Request} that provides support for JSON requests to the YOURLS API.\n *\n * JSON requests can only be sent using the \"GET\" or \"POST\" HTTP methods.\n *\n * @constructor\n * @extends Request\n * @protected\n */\nexport var JSONRequest = Request.extend({\n\n /**\n * @inheritDoc\n * @override\n */\n getSupportedHttpMethods: function() {\n return [ 'GET', 'POST' ]\n },\n\n /**\n * @inheritDoc\n * @override\n */\n process: function(method, url, body, callback) {\n var xhr = new XMLHttpRequest()\n xhr.open(method, url, true)\n xhr.onreadystatechange = function() {\n var response\n\n if (xhr.readyState === 4) {\n try {\n response = JSON.parse(xhr.responseText)\n callback(response)\n } catch (e) {\n throw new Error('Unable to parse response: ' + e)\n }\n }\n }\n\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')\n if (body != null) {\n xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')\n }\n\n xhr.send(body)\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Request } from './request'\n\n/**\n * The seed to be used to generate IDs.\n *\n * @private\n * @type {number}\n */\nvar seed = new Date().getTime()\n\n/**\n * An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API.\n *\n * JSONP requests can only be sent using the \"GET\" HTTP method.\n *\n * @constructor\n * @extends Request\n * @protected\n */\nexport var JSONPRequest = Request.extend(function() {\n JSONPRequest.super_.call(this)\n\n if (!window[JSONPRequest._callbackHolderKey]) {\n window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder\n }\n\n /**\n * The generated ID which is used to store a reference to the callback function within the holder so that the JSONP\n * payload can find it in the global namespace.\n *\n * @private\n * @type {number}\n */\n this._id = JSONPRequest._generateId()\n}, {\n\n /**\n * @inheritDoc\n * @override\n */\n getSupportedHttpMethods: function() {\n return [ 'GET' ]\n },\n\n /**\n * @inheritDoc\n * @override\n */\n buildBody: function(api, data) {\n var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data)\n body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']'\n\n return body\n },\n\n /**\n * @inheritDoc\n * @override\n */\n process: function(method, url, body, callback) {\n var script = document.createElement('script')\n\n var self = this\n JSONPRequest._callbackHolder[this._id] = function(response) {\n delete JSONPRequest._callbackHolder[self._id]\n script.parentNode.removeChild(script)\n\n callback(response)\n }\n\n script.setAttribute('src', url)\n document.getElementsByTagName('head')[0].appendChild(script)\n }\n\n}, {\n\n /**\n * The key of the callback function holder within the global namespace.\n *\n * @private\n * @static\n * @type {string}\n */\n _callbackHolderKey: '__yourls' + seed + '_jsonp',\n\n /**\n * Contains the callback functions for active JSONP requests.\n *\n * Callback references should be removed immediately once they have been called.\n *\n * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global).\n *\n * @private\n * @static\n * @type {Object}\n */\n _callbackHolder: {},\n\n /**\n * Generates an ID to be used when storing a reference to a callback function.\n *\n * @return {number} The generated ID.\n * @private\n */\n _generateId: function() {\n do {\n seed++\n } while (JSONPRequest._callbackHolder[seed])\n\n return seed\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { API } from '../api'\nimport { JSONRequest } from './json'\nimport { JSONPRequest } from './jsonp'\n\n/**\n * Can make requests to the connected YOURLS API.\n *\n * @constructor\n * @protected\n */\nexport var Requestor = Oopsy.extend({\n\n /**\n * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * This method is primarily a proxy to {@link Request#send} but does validate the state of the connection information\n * to ensure that is is valid before making the request.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @throws {Error} - If either no connection is present, the request format is not supported, or the configured HTTP\n * method is not supported by the {@link Request}.\n * @protected\n */\n sendRequest: function(data, resultNames, callback) {\n var api = API.instance\n\n if (!api) {\n throw new Error('No connection has been made')\n }\n\n var format = api.options.format\n var method = api.options.method\n var Request = Requestor._requestFormatMap[format]\n\n if (!Request) {\n throw new Error('Request format not supported: ' + format)\n }\n\n var request = new Request()\n\n if (!request.isMethodSupported(method)) {\n throw new Error('HTTP method not supported: ' + method)\n }\n\n request.send(data, resultNames, callback)\n }\n\n}, {\n\n /**\n * The mapping of supported request formats to {@link Request} constructors.\n *\n * @private\n * @static\n * @type {Object}\n */\n _requestFormatMap: {\n json: JSONRequest,\n jsonp: JSONPRequest\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Requestor } from './request/requestor'\n\n/**\n * Provides the ability to lookup information related to the YOURLS database.\n *\n * @constructor\n * @extends Requestor\n * @protected\n */\nexport var DB = Requestor.extend({\n\n /**\n * Retrieves the statistics for this {@link DB}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {DB} A reference to this {@link DB} for chaining purposes.\n * @public\n */\n stats: function(callback) {\n var data = { action: 'db-stats' }\n\n this.sendRequest(data, 'db-stats', callback)\n\n return this\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE0\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Requestor } from './request/requestor'\n\n/**\n * Provides the ability to lookup information related to the specified shortened url.\n *\n * @param {string} url - the shortened URL (or its keyword) to be used\n * @constructor\n * @extends Requestor\n * @protected\n */\nexport var URL = Requestor.extend(function(url) {\n URL.super_.call(this)\n\n /**\n * Either the shortened URL or its keyword for this {@link URL}.\n *\n * @public\n * @type {string}\n */\n this.url = url\n})\n\n/**\n * Retrieves the original (\"long\") URL for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.expand = function(callback) {\n var data = {\n action: 'expand',\n shorturl: this.url\n }\n\n this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.stats = function(callback) {\n var data = {\n action: 'url-stats',\n shorturl: this.url\n }\n\n this.sendRequest(data, 'link', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from './api'\nimport { DB } from './yourls-db'\nimport { isArray } from './util/array'\nimport { Requestor } from './request/requestor'\nimport { URL } from './yourls-url'\n\n/**\n * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose.\n *\n * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure\n * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private).\n *\n * @constructor\n * @extends Requestor\n * @protected\n */\nvar YOURLS = Requestor.extend(function() {\n YOURLS.super_.call(this)\n\n /**\n * Provides information on the YOURLS {@link DB}.\n *\n * @public\n * @type {DB}\n */\n this.db = new DB()\n\n /**\n * The current version of yourls.\n *\n * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method\n * should be used to provide that information.\n *\n * @public\n * @type {string}\n */\n this.VERSION = '2.0.0'\n}, {\n\n /**\n * Stores the specified information to be used later to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be\n * null)\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n connect: function(url, credentials, options) {\n API.instance = new API(url, credentials, options)\n\n return this\n },\n\n /**\n * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS\n * server.\n *\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n disconnect: function() {\n API.instance = null\n\n return this\n },\n\n /**\n * Creates a short URL for the specified long url.\n *\n * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is\n * to be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a\n * unique keyword. If descriptor is a string, it will be treated as the keyword.\n *\n * @param {string} url - the long URL to be shortened\n * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be\n * used for the short URL\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n shorten: function(url, descriptor, callback) {\n var data = {\n action: 'shorturl',\n url: url\n }\n\n switch (typeof descriptor) {\n case 'function':\n callback = descriptor\n descriptor = null\n break\n case 'string':\n descriptor = { keyword: descriptor }\n break\n default:\n // Do nothing\n }\n\n if (descriptor) {\n data.keyword = descriptor.keyword\n data.title = descriptor.title\n }\n\n this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback)\n\n return this\n },\n\n /**\n * Retrieves the statistics for all shortened URLs.\n *\n * Optionally, criteria can be provided to also include a refined set of links in the result. This\n * includes filter, which provides limited control over the sorting, as well as limit and start, which allow for\n * pagination. If criteria is a number, it will be treated as the limit.\n *\n * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this\n * method would effectively be doing the same as {@link DB#stats}.\n *\n * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to\n * search for links to be included in the result\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n stats: function(criteria, callback) {\n var data = { action: 'stats' }\n\n switch (typeof criteria) {\n case 'function':\n callback = criteria\n criteria = null\n break\n case 'number':\n criteria = { limit: criteria }\n break\n default:\n // Do nothing\n }\n\n if (criteria) {\n data.filter = criteria.filter\n data.limit = criteria.limit\n data.start = criteria.start\n }\n\n this.sendRequest(data, [ 'links', 'stats' ], function(result, response) {\n callback(YOURLS._sanitizeStatsResult(result), response)\n })\n\n return this\n },\n\n /**\n * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more\n * detailed information relating to it.\n *\n * No data is fetched just by calling this method; one of the methods on the returned instance need to be called for\n * that to happen.\n *\n * @param {string} url - the shortened URL (or its keyword)\n * @return {URL} The {@link URL} created for the shortened url or null if url\n * is null.\n * @public\n */\n url: function(url) {\n return url ? new URL(url) : null\n },\n\n /**\n * Retrieves the version of the connected YOURLS API.\n *\n * Optionally, db can be passed to indicate that the YOURLS database version should also be included in\n * the result.\n *\n * @param {boolean} [db] - true to include the database version; otherwise false\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n version: function(db, callback) {\n var data = { action: 'version' }\n\n if (typeof db === 'function') {\n callback = db\n db = null\n }\n\n if (db != null) {\n data.db = Number(db)\n }\n\n this.sendRequest(data, [ 'db_version', 'version' ], callback)\n\n return this\n }\n\n}, {\n\n /**\n * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links\n * from an object mapping into an array.\n *\n * This method simply returns result if it is null, has no links property or one that is\n * already an array (future-proofing).\n *\n * @param {Object} result - the result to be sanitized (may be null)\n * @param {Object} [result.links] - the links to be transformed into an array (may be null)\n * @return {Object} The modified result or null if result is null.\n * @private\n * @static\n */\n _sanitizeStatsResult: function(result) {\n // Future-proofing by sanitizing links *only* when not already an array\n if (!result || !result.links || isArray(result.links)) {\n return result\n }\n\n var index = 1\n var link\n var links = []\n\n while ((link = result.links['link_' + index]) != null) {\n links.push(link)\n index++\n }\n\n result.links = links\n\n return result\n }\n\n})\n\n/**\n * The singleton instance of {@link YOURLS}.\n */\nexport default new YOURLS()\n\n/**\n * Contains criteria which can be used to search for a refined set of shortened URLs.\n *\n * Pagination can be achieved by using limit and start.\n *\n * No links will be returned unless limit is specified and has a value that is greater than zero.\n *\n * @typedef {Object} YOURLS~SearchCriteria\n * @property {string} [filter] - The filter to be applied (either \"top\", \"bottom\",\n * \"rand\", or \"last\").\n * @property {number} [limit] - The maximum number of links whose statistical information is to be counted.\n * null or 0 will result in no links being included in the result.\n * @property {number} [start] - The offset from where the search should begin.\n */\n\n/**\n * Contains additional information which can be used when shortening a URL.\n *\n * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword.\n *\n * @typedef {Object} YOURLS~UrlDescriptor\n * @property {string} [keyword] - The optional keyword to be used for the shortened URL.\n * @property {string} [title] - The optional title to be associated with the shortened URL.\n */\n"],"names":["extend"],"mappings":"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"} \ No newline at end of file diff --git a/dist/yourls.min.js b/dist/yourls.min.js index b7ec2ce..b0ff3f4 100644 --- a/dist/yourls.min.js +++ b/dist/yourls.min.js @@ -1,4 +1,4 @@ /*! YOURLS API v2.0.0 | (C) 2016 Alasdair Mercer | MIT License */ -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define("yourls-api",n):t.yourls=n()}(this,function(){"use strict";function t(t){if(!t)return null;var n={};return t.signature?(n.signature=t.signature,n.timestamp=t.timestamp):(n.password=t.password,n.username=t.username),n}function n(n,r){this.url=n?n.replace(/\/$/,""):"",this.credentials=t(r)}function r(t){return Array.isArray?Array.isArray(t):"[object Array]"===Object.prototype.toString.call(t)}function e(t){if(!t)return"";var n=[];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&null!=t[r]&&n.push(encodeURIComponent(r)+"="+encodeURIComponent(t[r]));return n.join("&")}function o(){for(var t=Date.now();p[t];)t++;return t}function i(t,n){t=r(t)?t:[t];var e,o,i=null;if(!n)return i;if(1===t.length)i=n[t[0]];else for(i={},e=0;ecredentials by ensuring that only valid properties are present and only when\n * appropriate.\n *\n * This function does not modify credentials and instead creates a new object with the sanitized\n * properties.\n *\n * @param {API~Credentials} credentials - the credentials to be sanitized (may be null)\n * @return {API~Credentials} A sanitized version of credentials or null if\n * credentials is null.\n * @private\n */\nfunction sanitizeCredentials(credentials) {\n if (!credentials) {\n return null\n }\n\n var result = {}\n if (credentials.signature) {\n result.signature = credentials.signature\n result.timestamp = credentials.timestamp\n } else {\n result.password = credentials.password\n result.username = credentials.username\n }\n\n return result\n}\n\n/**\n * Contains information on how to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @protected\n * @constructor\n */\nexport function API(url, credentials) {\n /**\n * The URL of the YOURLS server.\n *\n * @public\n * @type {string}\n */\n this.url = url ? url.replace(/\\/$/, '') : ''\n /**\n * The credentials to be used to authenticate with the YOURLS API.\n *\n * This may be null if the YOURLS API is public.\n *\n * @public\n * @type {API~Credentials}\n */\n this.credentials = sanitizeCredentials(credentials)\n}\n\n/**\n * Destroys the singleton instance of {@link API}.\n *\n * @return {void}\n * @public\n * @static\n */\nAPI.clear = function() {\n instance = null\n}\n\n/**\n * Retrieves the singleton instance of {@link API}.\n *\n * This function will return null unless an instance is currently stored.\n *\n * @return {API} The connected {@link API} or null if none exists.\n * @public\n * @static\n */\nAPI.fetch = function() {\n return instance\n}\n\n/**\n * Stores this {@link API} as the singleton, potentially replacing the existing instance.\n *\n * @return {void}\n * @public\n */\nAPI.prototype.store = function() {\n instance = this\n}\n\n/**\n * The credentials to be used to authenticate with a private YOURLS API.\n *\n * Authentication can be done with a traditional username and password combination or by using the secret\n * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs\n * and can be found on the \"Tools\" page.\n *\n * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server\n * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and\n * signature token concatenated, and in that order.\n *\n * @typedef {Object} API~Credentials\n * @property {string} [password] - The password of the user to be authenticated.\n * @property {string} [username] - The name of the user to be authenticated.\n * @property {string} [signature] - The signature token to be used for passwordless authentication with the YOURLS API.\n * @property {number|string} [timestamp] - The optional timestamp to limit the signature token.\n */\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Returns whether the specified obj is an array.\n *\n * This function will use the native Array.isArray, if available.\n *\n * @param {*} obj - the object to be checked (may be null)\n * @return {boolean} true if obj is an array; otherwise false.\n * @protected\n */\nexport function isArray(obj) {\n return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]'\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Creates a serialized representation of the specified params into a URL query string.\n *\n * @param {Object} [params] - the hash of parameter key/value pairs to be serialized\n * @return {string} A URL query string representing params.\n * @protected\n */\nexport function paramify(params) {\n if (!params) {\n return ''\n }\n\n var results = []\n\n for (var key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n if (params[key] != null) {\n results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))\n }\n }\n }\n\n return results.join('&')\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from '../api'\nimport { isArray } from '../util/array'\nimport { paramify } from './paramify'\n\n/**\n * The key of the callback function holder within the global namespace.\n *\n * @private\n * @type {string}\n */\nvar callbackHolderKey = '__yourls' + Date.now() + '_jsonp'\n/**\n * Contains the callback functions for active JSONP requests.\n *\n * Callback references should be removed immediately once they have been called.\n *\n * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global).\n *\n * @private\n * @type {Object}\n */\nvar callbackHolder = window[callbackHolderKey] = {}\n\n/**\n * Generates a quick and dirty unique ID for a callback.\n *\n * @return {number} The generated callback ID.\n * @private\n */\nfunction generateCallbackId() {\n var id = Date.now()\n while (callbackHolder[id]) {\n id++\n }\n\n return id\n}\n\n/**\n * Extracts the values of the properties with the specified names from the response provided\n * and returns them in a single result.\n *\n * If names is a string or only contains a single string, only the value for that named property will be\n * returned. Otherwise, an object containing the key/value pairs for each named property will be returned.\n *\n * If response is null this function will return null.\n *\n * @param {string|string[]} names - the names of the response properties whose values are to be returned as\n * the result\n * @param {Object} response - the YOURLS API response\n * @return {*} The result extracted from response.\n * @private\n */\nfunction getResult(names, response) {\n names = isArray(names) ? names : [ names ]\n\n var i\n var name\n var result = null\n\n if (!response) {\n return result\n }\n\n if (names.length === 1) {\n result = response[names[0]]\n } else {\n result = {}\n\n for (i = 0; i < names.length; i++) {\n name = names[i]\n\n if (typeof response[name] !== 'undefined') {\n result[name] = response[name]\n }\n }\n }\n\n return result\n}\n\n/**\n * Sends a JSONP request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * If the request is successful, callback will be passed the value of the named properties from the\n * response. If resultNames is a string or only contains a single string, only the value for that named\n * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named\n * property will be passed as the first argument. The actual response will always be passed as the second argument.\n *\n * Due to the nature of JSONP, all information will be included in the URL of the request. This includes\n * data as well as any credentials used to authenticate with the API. You have been warned.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @protected\n */\nexport function jsonp(data, resultNames, callback) {\n var api = API.fetch()\n var id = generateCallbackId()\n var script = document.createElement('script')\n\n callbackHolder[id] = function(response) {\n var result = getResult(resultNames, response)\n\n delete callbackHolder[id]\n script.parentNode.removeChild(script)\n\n callback(result, response)\n }\n\n var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' })\n if (api.credentials) {\n target += '&' + paramify(api.credentials)\n }\n if (data) {\n target += '&' + paramify(data)\n }\n\n script.setAttribute('src', target)\n document.getElementsByTagName('head')[0].appendChild(script)\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { jsonp } from './request/jsonp'\n\n/**\n * Provides the ability to lookup information related to the YOURLS database.\n *\n * @constructor\n * @protected\n */\nexport function DB() {\n // Do nothing\n}\n\n/**\n * Retrieves the statistics for this {@link DB}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {DB} A reference to this {@link DB} for chaining purposes.\n * @public\n */\nDB.prototype.stats = function(callback) {\n var data = { action: 'db-stats' }\n\n jsonp(data, 'db-stats', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE0\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { jsonp } from './request/jsonp'\n\n/**\n * Provides the ability to lookup information related to the specified shortened url.\n *\n * @param {string} url - the shortened URL (or its keyword) to be used\n * @constructor\n * @protected\n */\nexport function URL(url) {\n /**\n * Either the shortened URL or its keyword for this {@link URL}.\n *\n * @public\n * @type {string}\n */\n this.url = url\n}\n\n/**\n * Retrieves the original (\"long\") URL for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.expand = function(callback) {\n var data = {\n action: 'expand',\n shorturl: this.url\n }\n\n jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.stats = function(callback) {\n var data = {\n action: 'url-stats',\n shorturl: this.url\n }\n\n jsonp(data, 'link', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from './api'\nimport { DB } from './yourls-db'\nimport { isArray } from './util/array'\nimport { jsonp } from './request/jsonp'\nimport { URL } from './yourls-url'\n\n/**\n * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links\n * from an object mapping into an array.\n *\n * This function simply returns result if it is null, has no links property or one that is\n * already an array (future-proofing).\n *\n * @param {Object} result - the result to be sanitized (may be null)\n * @param {Object} [result.links] - the links to be transformed into an array (may be null)\n * @return {Object} The modified result or null if result is null.\n * @private\n */\nfunction sanitizeStatsResult(result) {\n // Future-proofing by sanitizing links *only* when not already an array\n if (!result || !result.links || isArray(result.links)) {\n return result\n }\n\n var index = 1\n var link\n var links = []\n\n while ((link = result.links['link_' + index]) != null) {\n links.push(link)\n index++\n }\n\n result.links = links\n\n return result\n}\n\n/**\n * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose.\n *\n * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure\n * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private).\n *\n * @constructor\n * @protected\n */\nvar YOURLS = function() {\n /**\n * Provides information on the YOURLS {@link DB}.\n *\n * @public\n * @type {DB}\n */\n this.db = new DB()\n\n /**\n * The current version of yourls.\n *\n * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version}\n * function should be used to provide that information.\n *\n * @public\n * @type {string}\n */\n this.VERSION = '2.0.0'\n}\n\n/**\n * Stores the specified information to be used later to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.connect = function(url, credentials) {\n var api = new API(url, credentials)\n api.store()\n\n return this\n}\n\n/**\n * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS\n * server.\n *\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.disconnect = function() {\n API.clear()\n\n return this\n}\n\n/**\n * Creates a short URL for the specified long url.\n *\n * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is to\n * be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword. If descriptor is a string, it will be treated as the keyword.\n *\n * @param {string} url - the long URL to be shortened\n * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used\n * for the short URL\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.shorten = function(url, descriptor, callback) {\n var data = {\n action: 'shorturl',\n url: url\n }\n\n switch (typeof descriptor) {\n case 'function':\n callback = descriptor\n descriptor = null\n break\n case 'string':\n descriptor = { keyword: descriptor }\n break\n default:\n // Do nothing\n }\n\n if (descriptor) {\n data.keyword = descriptor.keyword\n data.title = descriptor.title\n }\n\n jsonp(data, [ 'shorturl', 'title', 'url' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for all shortened URLs.\n *\n * Optionally, criteria can be provided to also include a refined set of links in the result. This includes\n * filter, which provides limited control over the sorting, as well as limit and start, which allow for pagination. If\n * criteria is a number, it will be treated as the limit.\n *\n * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this\n * method would effectively be doing the same as {@link DB#stats}.\n *\n * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to\n * search for links to be included in the result\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.stats = function(criteria, callback) {\n var data = { action: 'stats' }\n\n switch (typeof criteria) {\n case 'function':\n callback = criteria\n criteria = null\n break\n case 'number':\n criteria = { limit: criteria }\n break\n default:\n // Do nothing\n }\n\n if (criteria) {\n data.filter = criteria.filter\n data.limit = criteria.limit\n data.start = criteria.start\n }\n\n jsonp(data, [ 'links', 'stats' ], function(result, response) {\n callback(sanitizeStatsResult(result), response)\n })\n\n return this\n}\n\n/**\n * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more\n * detailed information relating to it.\n *\n * No data is fetched just by calling this function; one of the functions on the returned instance need to be called for\n * that to happen.\n *\n * @param {string} url - the shortened URL (or its keyword)\n * @return {URL} The {@link URL} created for the shortened url or null if url is\n * null.\n * @public\n */\nYOURLS.prototype.url = function(url) {\n return url ? new URL(url) : null\n}\n\n/**\n * Retrieves the version of the connected YOURLS API.\n *\n * Optionally, db can be passed to indicate that the YOURLS database version should also be included in the\n * result.\n *\n * @param {boolean} [db] - true to include the database version; otherwise false\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\nYOURLS.prototype.version = function(db, callback) {\n var data = { action: 'version' }\n\n if (typeof db === 'function') {\n callback = db\n db = null\n }\n\n if (db != null) {\n data.db = Number(db)\n }\n\n jsonp(data, [ 'db_version', 'version' ], callback)\n\n return this\n}\n\n/**\n * The singleton instance of {@link YOURLS}.\n */\nexport default new YOURLS()\n\n/**\n * Contains criteria which can be used to search for a refined set of shortened URLs.\n *\n * Pagination can be achieved by using limit and start.\n *\n * No links will be returned unless limit is specified and has a value that is greater than zero.\n *\n * @typedef {Object} YOURLS~SearchCriteria\n * @property {string} [filter] - The filter to be applied (either \"top\", \"bottom\",\n * \"rand\", or \"last\").\n * @property {number} [limit] - The maximum number of links whose statistical information is to be counted.\n * null or 0 will result in no links being included in the result.\n * @property {number} [start] - The offset from where the search should begin.\n */\n\n/**\n * Contains additional information which can be used when shortening a URL.\n *\n * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword.\n *\n * @typedef {Object} YOURLS~UrlDescriptor\n * @property {string} [keyword] - The optional keyword to be used for the shortened URL.\n * @property {string} [title] - The optional title to be associated with the shortened URL.\n */\n"],"names":["sanitizeCredentials","credentials","result","signature","timestamp","password","username","API","url","this","replace","isArray","obj","Array","Object","prototype","toString","call","paramify","params","results","key","hasOwnProperty","push","encodeURIComponent","join","generateCallbackId","id","Date","now","callbackHolder","getResult","names","response","i","name","length","jsonp","data","resultNames","callback","api","fetch","script","document","createElement","parentNode","removeChild","target","callbackHolderKey","format","setAttribute","getElementsByTagName","appendChild","DB","URL","sanitizeStatsResult","links","link","index","instance","clear","store","window","stats","action","expand","shorturl","YOURLS","db","VERSION","connect","disconnect","shorten","descriptor","keyword","title","criteria","limit","filter","start","version","Number"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":null,"sources":["../node_modules/oopsy/src/oopsy.js","../src/util/extend.js","../src/util/array.js","../src/api.js","../src/request/request.js","../src/request/json.js","../src/request/jsonp.js","../src/request/requestor.js","../src/yourls-db.js","../src/yourls-url.js","../src/yourls.js"],"sourcesContent":["/*\n * Copyright (C) 2016 Alasdair Mercer, Skelp\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * A bare-bones constructor for surrogate prototype swapping.\n *\n * @private\n * @constructor\n */\nvar Constructor = function() {}\n/**\n * A reference to Object.prototype.hasOwnProperty.\n *\n * @private\n * @type {Function}\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty\n/**\n * A reference to Array.prototype.slice.\n *\n * @private\n * @type {Function}\n */\nvar slice = Array.prototype.slice\n\n/**\n * Extends the specified target object with the properties in each of the sources provided.\n *\n * Nothing happens if target is null and if any source is null it will be\n * ignored.\n *\n * @param {boolean} own - true to only copy own properties from sources onto\n * target; otherwise false\n * @param {Object} [target] - the target object which should be extended\n * @param {...Object} [sources] - the source objects whose properties are to be copied onto target\n * @return {void}\n * @private\n */\nfunction extend(own, target, sources) {\n if (target == null) {\n return\n }\n\n sources = slice.call(arguments, 2)\n\n var property\n var source\n\n for (var i = 0, length = sources.length; i < length; i++) {\n source = sources[i]\n\n for (property in source) {\n if (!own || hasOwnProperty.call(source, property)) {\n target[property] = source[property]\n }\n }\n }\n}\n\n/**\n * Creates an object which inherits the given prototype.\n *\n * Optionally, the created object can be extended further with the specified properties.\n *\n * @param {Object} prototype - the prototype to be inherited by the created object\n * @param {Object} [properties] - the optional properties to be extended by the created object\n * @return {Object} The newly created object.\n * @private\n */\nfunction create(prototype, properties) {\n var result\n if (typeof Object.create === 'function') {\n result = Object.create(prototype)\n } else {\n Constructor.prototype = prototype\n result = new Constructor()\n Constructor.prototype = null\n }\n\n if (properties) {\n extend(true, result, properties)\n }\n\n return result\n}\n\n/**\n * The base constructor from which all others should extend.\n *\n * @public\n * @constructor\n */\nexport default function Oopsy() {}\n\n/**\n * Extends the constructor to which this method is associated with the prototype and/or\n * statics provided.\n *\n * If constructor is provided, it will be used as the constructor for the child, otherwise a simple\n * constructor which only calls the super constructor will be used instead.\n *\n * The super constructor can be accessed via a special super_ property on the child constructor.\n *\n * @param {Function} [constructor] - the constructor for the child\n * @param {Object} [prototype] - the prototype properties to be defined for the child\n * @param {Object} [statics] - the static properties to be defined for the child\n * @return {Function} The child constructor provided or the one created if none was given.\n * @public\n * @static\n */\nOopsy.extend = function(constructor, prototype, statics) {\n var superConstructor = this\n\n if (typeof constructor !== 'function') {\n statics = prototype\n prototype = constructor\n constructor = function() {\n return superConstructor.apply(this, arguments)\n }\n }\n\n extend(false, constructor, superConstructor, statics)\n\n constructor.prototype = create(superConstructor.prototype, prototype)\n constructor.prototype.constructor = constructor\n\n constructor.super_ = superConstructor\n\n return constructor\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Extends the specified target object with the properties in each of the sources provided.\n *\n * Any of the sources that are null will simply be ignored.\n *\n * @param {Object} target - the target object which should be extended\n * @param {...Object} [sources] - the source objects whose properties are to be copied onto target\n * @return {Object} A reference to target.\n * @protected\n */\nexport function extend(target, sources) {\n sources = Array.prototype.slice.call(arguments, 1)\n\n for (var i = 0, length = sources.length, property, source; i < length; i++) {\n source = sources[i]\n\n if (source) {\n for (property in source) {\n if (Object.prototype.hasOwnProperty.call(source, property)) {\n target[property] = source[property]\n }\n }\n }\n }\n\n return target\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Returns whether the specified obj is an array.\n *\n * This method will use the native Array.isArray, if available.\n *\n * @param {*} obj - the object to be checked (may be null)\n * @return {boolean} true if obj is an array; otherwise false.\n * @protected\n */\nexport function isArray(obj) {\n return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]'\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { extend } from './util/extend'\n\n/**\n * Contains information on how to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be\n * null)\n * @protected\n * @constructor\n */\nexport var API = Oopsy.extend(function(url, credentials, options) {\n /**\n * The URL of the YOURLS server.\n *\n * @public\n * @type {string}\n */\n this.url = url ? url.replace(/\\/$/, '') : ''\n /**\n * The credentials to be used to authenticate with the YOURLS API.\n *\n * This may be null if the YOURLS API is public.\n *\n * @public\n * @type {API~Credentials}\n */\n this.credentials = API._sanitizeCredentials(credentials)\n /**\n * The options to be used to send requests to the YOURLS server.\n *\n * @public\n * @type {API~Options}\n */\n this.options = API._sanitizeOptions(options)\n}, null, {\n\n /**\n * The default options to be used.\n *\n * @protected\n * @static\n * @type {API~Options}\n */\n defaultOptions: {\n format: 'jsonp',\n method: 'GET'\n },\n\n /**\n * The singleton {@link API} instance which is privatized to prevent leaking credentials.\n *\n * @public\n * @static\n * @type {API}\n */\n instance: null,\n\n /**\n * Sanitizes the specified credentials by ensuring that only valid properties are present and only when\n * appropriate.\n *\n * This method does not modify credentials and instead creates a new object with the sanitized\n * properties.\n *\n * @param {API~Credentials} credentials - the credentials to be sanitized (may be null)\n * @return {API~Credentials} A sanitized version of credentials or null if\n * credentials is null.\n * @private\n * @static\n */\n _sanitizeCredentials: function(credentials) {\n if (!credentials) {\n return null\n }\n\n var result = {}\n if (credentials.signature) {\n result.signature = credentials.signature\n result.timestamp = credentials.timestamp\n } else {\n result.password = credentials.password\n result.username = credentials.username\n }\n\n return result\n },\n\n /**\n * Sanitizes the specified options by ensuring that only valid properties are present and in the correct\n * format.\n *\n * This method does not modify options and instead creates a new object with the sanitized properties and\n * default values will be used for missing options.\n *\n * @param {API~Options} options - the options to be sanitized (may be null)\n * @return {API~Options} A sanitized version of options which will contain only default values if\n * options is null.\n * @private\n * @static\n */\n _sanitizeOptions: function(options) {\n var result = extend({}, API.defaultOptions)\n if (!options) {\n return result\n }\n\n if (options.format) {\n result.format = options.format.toLowerCase()\n }\n if (options.method) {\n result.method = options.method.toUpperCase()\n }\n\n return result\n }\n\n})\n\n/**\n * The credentials to be used to authenticate with a private YOURLS API.\n *\n * Authentication can be done with a traditional username and password combination or by using the secret\n * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs\n * and can be found on the \"Tools\" page.\n *\n * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server\n * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and\n * signature token concatenated, and in that order.\n *\n * @typedef {Object} API~Credentials\n * @property {string} [password] - The password of the user to be authenticated.\n * @property {string} [username] - The name of the user to be authenticated.\n * @property {string} [signature] - The signature token to be used for passwordless authentication with the YOURLS API.\n * @property {number|string} [timestamp] - The optional timestamp to limit the signature token.\n */\n\n/**\n * The options that determine how requests are sent to the YOURLS server.\n *\n * If the request format does not support the HTTP method, requests will not be sent and an\n * error will be thrown when such attempts occur.\n *\n * @typedef {Object} API~Options\n * @property {string} [format=\"jsonp\"] - The format in which requests are sent (either \"json\" or\n * \"jsonp\").\n * @property {string} [method=\"GET\"] - The HTTP method to be used for requests.\n */\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { API } from '../api'\nimport { extend } from '../util/extend'\nimport { isArray } from '../util/array'\n\n/**\n * Contains logic to connect with a YOURLS server and send data to its API.\n *\n * Due to the nature of HTTP, requests sent using a \"GET\" HTTP method will include all information in the URL of the\n * request. This includes the data that is sent as well as any credentials used to authenticate with the API. You\n * have been warned.\n *\n * @constructor\n * @protected\n */\nexport var Request = Oopsy.extend({\n\n /**\n * Builds the body for this {@link Request} based on the api and data provided.\n *\n * @param {API} api - the {@link API} to which the request is being sent\n * @param {Object} [data] - the data being sent in this request\n * @return {Object} The request body.\n * @protected\n */\n buildBody: function(api, data) {\n return extend({ format: api.options.format }, api.credentials, data)\n },\n\n /**\n * Returns the list of the HTTP methods that are supported by this {@link Request}.\n *\n * By default, this method returns null, so implementations must implement this method to ensure\n * that {@link Request#isMethodSupported} works correctly.\n *\n * @return {string[]} The supported HTTP methods.\n * @protected\n */\n getSupportedHttpMethods: function() {\n return null\n },\n\n /**\n * Determines whether this {@link Request} supports the specified HTTP method.\n *\n * @param {string} method - the HTTP method to be checked\n * @return {boolean} true if method is supported; otherwise false.\n * @public\n */\n isMethodSupported: function(method) {\n var supportedMethods = this.getSupportedHttpMethods()\n return supportedMethods && supportedMethods.indexOf(method) !== -1\n },\n\n /**\n * Determines whether the data that is to be sent to the YOURLS server in this {@link Request} must be serialized as\n * query string parameters.\n *\n * @param {string} method - the HTTP method to be used\n * @return {boolean} true if the data needs to be sent as query string parameters; otherwise\n * false.\n * @protected\n */\n isQueryStringRequired: function(method) {\n return method === 'GET'\n },\n\n /**\n * Processes this {@link Request} by sending it to the specified target url containing the\n * body provided.\n *\n * callback should be called with the response regardless of whether the it was a success or failure.\n *\n * This method is called internally by {@link Request#send} and does all of the actual work involved to send the\n * request and parse the response. All implementations must implement this method.\n *\n * @param {string} method - the request HTTP method\n * @param {string} url - the request URL\n * @param {Object} body - the request body (may be null)\n * @param {Function} callback - the function to be called with the response\n * @return {void}\n * @protected\n * @abstract\n */\n process: function(method, url, body, callback) {\n // Must be implemented\n },\n\n /**\n * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * If the request is successful, callback will be passed the value of the named properties from the\n * response. If resultNames is a string or only contains a single string, only the value for that named\n * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named\n * property will be passed as the first argument. The actual response will always be passed as the second argument.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @public\n */\n send: function(data, resultNames, callback) {\n var api = API.instance\n var body = Request._serializeParameters(this.buildBody(api, data))\n var method = api.options.method\n var url = api.url\n\n if (this.isQueryStringRequired(method)) {\n url += '?' + body\n body = null\n }\n\n this.process(method, url, body, function(response) {\n callback(Request._extractResult(resultNames, response), response)\n })\n }\n\n}, {\n\n /**\n * Extracts the values of the properties with the specified names from the response provided\n * and returns them in a single result.\n *\n * If names is a string or only contains a single string, only the value for that named property will be\n * returned. Otherwise, an object containing the key/value pairs for each named property will be returned.\n *\n * If response is null this method will return null.\n *\n * @param {string|string[]} names - the names of the response properties whose values are to be returned\n * as the result\n * @param {Object} response - the YOURLS API response\n * @return {*} The result extracted from response.\n * @private\n * @static\n */\n _extractResult: function(names, response) {\n names = isArray(names) ? names : [ names ]\n\n var i\n var name\n var result = null\n\n if (!response) {\n return result\n }\n\n if (names.length === 1) {\n result = response[names[0]]\n } else {\n result = {}\n\n for (i = 0; i < names.length; i++) {\n name = names[i]\n\n if (typeof response[name] !== 'undefined') {\n result[name] = response[name]\n }\n }\n }\n\n return result\n },\n\n /**\n * Creates a serialized representation of the specified parameters.\n *\n * All of the parameter names and values are URL-encoded so that they can be safely included in the query string or\n * request body.\n *\n * @param {Object} [params] - the hash of parameter name/value pairs to be serialized\n * @return {string} A URL-encoded representing obj or an empty string if obj is\n * null.\n * @private\n * @static\n */\n _serializeParameters: function(params) {\n if (!params) {\n return ''\n }\n\n var results = []\n\n for (var name in params) {\n if (Object.prototype.hasOwnProperty.call(params, name) && params[name] != null) {\n results.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name]))\n }\n }\n\n return results.join('&')\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Request } from './request'\n\n/**\n * An implementation of {@link Request} that provides support for JSON requests to the YOURLS API.\n *\n * JSON requests can only be sent using the \"GET\" or \"POST\" HTTP methods.\n *\n * @constructor\n * @extends Request\n * @protected\n */\nexport var JSONRequest = Request.extend({\n\n /**\n * @inheritDoc\n * @override\n */\n getSupportedHttpMethods: function() {\n return [ 'GET', 'POST' ]\n },\n\n /**\n * @inheritDoc\n * @override\n */\n process: function(method, url, body, callback) {\n var xhr = new XMLHttpRequest()\n xhr.open(method, url, true)\n xhr.onreadystatechange = function() {\n var response\n\n if (xhr.readyState === 4) {\n try {\n response = JSON.parse(xhr.responseText)\n callback(response)\n } catch (e) {\n throw new Error('Unable to parse response: ' + e)\n }\n }\n }\n\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')\n if (body != null) {\n xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')\n }\n\n xhr.send(body)\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Request } from './request'\n\n/**\n * The seed to be used to generate IDs.\n *\n * @private\n * @type {number}\n */\nvar seed = new Date().getTime()\n\n/**\n * An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API.\n *\n * JSONP requests can only be sent using the \"GET\" HTTP method.\n *\n * @constructor\n * @extends Request\n * @protected\n */\nexport var JSONPRequest = Request.extend(function() {\n JSONPRequest.super_.call(this)\n\n if (!window[JSONPRequest._callbackHolderKey]) {\n window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder\n }\n\n /**\n * The generated ID which is used to store a reference to the callback function within the holder so that the JSONP\n * payload can find it in the global namespace.\n *\n * @private\n * @type {number}\n */\n this._id = JSONPRequest._generateId()\n}, {\n\n /**\n * @inheritDoc\n * @override\n */\n getSupportedHttpMethods: function() {\n return [ 'GET' ]\n },\n\n /**\n * @inheritDoc\n * @override\n */\n buildBody: function(api, data) {\n var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data)\n body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']'\n\n return body\n },\n\n /**\n * @inheritDoc\n * @override\n */\n process: function(method, url, body, callback) {\n var script = document.createElement('script')\n\n var self = this\n JSONPRequest._callbackHolder[this._id] = function(response) {\n delete JSONPRequest._callbackHolder[self._id]\n script.parentNode.removeChild(script)\n\n callback(response)\n }\n\n script.setAttribute('src', url)\n document.getElementsByTagName('head')[0].appendChild(script)\n }\n\n}, {\n\n /**\n * The key of the callback function holder within the global namespace.\n *\n * @private\n * @static\n * @type {string}\n */\n _callbackHolderKey: '__yourls' + seed + '_jsonp',\n\n /**\n * Contains the callback functions for active JSONP requests.\n *\n * Callback references should be removed immediately once they have been called.\n *\n * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global).\n *\n * @private\n * @static\n * @type {Object}\n */\n _callbackHolder: {},\n\n /**\n * Generates an ID to be used when storing a reference to a callback function.\n *\n * @return {number} The generated ID.\n * @private\n */\n _generateId: function() {\n do {\n seed++\n } while (JSONPRequest._callbackHolder[seed])\n\n return seed\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Oopsy from 'oopsy'\n\nimport { API } from '../api'\nimport { JSONRequest } from './json'\nimport { JSONPRequest } from './jsonp'\n\n/**\n * Can make requests to the connected YOURLS API.\n *\n * @constructor\n * @protected\n */\nexport var Requestor = Oopsy.extend({\n\n /**\n * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the\n * specified callback with the result.\n *\n * This method is primarily a proxy to {@link Request#send} but does validate the state of the connection information\n * to ensure that is is valid before making the request.\n *\n * @param {Object} data - the data to be sent\n * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to\n * callback as the first argument\n * @param {Function} callback - the function to be called with the result\n * @return {void}\n * @throws {Error} - If either no connection is present, the request format is not supported, or the configured HTTP\n * method is not supported by the {@link Request}.\n * @protected\n */\n sendRequest: function(data, resultNames, callback) {\n var api = API.instance\n\n if (!api) {\n throw new Error('No connection has been made')\n }\n\n var format = api.options.format\n var method = api.options.method\n var Request = Requestor._requestFormatMap[format]\n\n if (!Request) {\n throw new Error('Request format not supported: ' + format)\n }\n\n var request = new Request()\n\n if (!request.isMethodSupported(method)) {\n throw new Error('HTTP method not supported: ' + method)\n }\n\n request.send(data, resultNames, callback)\n }\n\n}, {\n\n /**\n * The mapping of supported request formats to {@link Request} constructors.\n *\n * @private\n * @static\n * @type {Object}\n */\n _requestFormatMap: {\n json: JSONRequest,\n jsonp: JSONPRequest\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Requestor } from './request/requestor'\n\n/**\n * Provides the ability to lookup information related to the YOURLS database.\n *\n * @constructor\n * @extends Requestor\n * @protected\n */\nexport var DB = Requestor.extend({\n\n /**\n * Retrieves the statistics for this {@link DB}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {DB} A reference to this {@link DB} for chaining purposes.\n * @public\n */\n stats: function(callback) {\n var data = { action: 'db-stats' }\n\n this.sendRequest(data, 'db-stats', callback)\n\n return this\n }\n\n})\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE0\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Requestor } from './request/requestor'\n\n/**\n * Provides the ability to lookup information related to the specified shortened url.\n *\n * @param {string} url - the shortened URL (or its keyword) to be used\n * @constructor\n * @extends Requestor\n * @protected\n */\nexport var URL = Requestor.extend(function(url) {\n URL.super_.call(this)\n\n /**\n * Either the shortened URL or its keyword for this {@link URL}.\n *\n * @public\n * @type {string}\n */\n this.url = url\n})\n\n/**\n * Retrieves the original (\"long\") URL for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.expand = function(callback) {\n var data = {\n action: 'expand',\n shorturl: this.url\n }\n\n this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback)\n\n return this\n}\n\n/**\n * Retrieves the statistics for this shortened {@link URL}.\n *\n * @param {Function} callback - the callback function to be called with the result\n * @return {URL} A reference to this {@link URL} for chaining purposes.\n * @public\n */\nURL.prototype.stats = function(callback) {\n var data = {\n action: 'url-stats',\n shorturl: this.url\n }\n\n this.sendRequest(data, 'link', callback)\n\n return this\n}\n","/*\n * Copyright (C) 2016 Alasdair Mercer\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { API } from './api'\nimport { DB } from './yourls-db'\nimport { isArray } from './util/array'\nimport { Requestor } from './request/requestor'\nimport { URL } from './yourls-url'\n\n/**\n * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose.\n *\n * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure\n * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private).\n *\n * @constructor\n * @extends Requestor\n * @protected\n */\nvar YOURLS = Requestor.extend(function() {\n YOURLS.super_.call(this)\n\n /**\n * Provides information on the YOURLS {@link DB}.\n *\n * @public\n * @type {DB}\n */\n this.db = new DB()\n\n /**\n * The current version of yourls.\n *\n * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method\n * should be used to provide that information.\n *\n * @public\n * @type {string}\n */\n this.VERSION = '2.0.0'\n}, {\n\n /**\n * Stores the specified information to be used later to connect to and authenticate with a YOURLS server.\n *\n * @param {string} [url=''] - the URL for the YOURLS server\n * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be\n * null)\n * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be\n * null)\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n connect: function(url, credentials, options) {\n API.instance = new API(url, credentials, options)\n\n return this\n },\n\n /**\n * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS\n * server.\n *\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n disconnect: function() {\n API.instance = null\n\n return this\n },\n\n /**\n * Creates a short URL for the specified long url.\n *\n * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is\n * to be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a\n * unique keyword. If descriptor is a string, it will be treated as the keyword.\n *\n * @param {string} url - the long URL to be shortened\n * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be\n * used for the short URL\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n shorten: function(url, descriptor, callback) {\n var data = {\n action: 'shorturl',\n url: url\n }\n\n switch (typeof descriptor) {\n case 'function':\n callback = descriptor\n descriptor = null\n break\n case 'string':\n descriptor = { keyword: descriptor }\n break\n default:\n // Do nothing\n }\n\n if (descriptor) {\n data.keyword = descriptor.keyword\n data.title = descriptor.title\n }\n\n this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback)\n\n return this\n },\n\n /**\n * Retrieves the statistics for all shortened URLs.\n *\n * Optionally, criteria can be provided to also include a refined set of links in the result. This\n * includes filter, which provides limited control over the sorting, as well as limit and start, which allow for\n * pagination. If criteria is a number, it will be treated as the limit.\n *\n * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this\n * method would effectively be doing the same as {@link DB#stats}.\n *\n * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to\n * search for links to be included in the result\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n stats: function(criteria, callback) {\n var data = { action: 'stats' }\n\n switch (typeof criteria) {\n case 'function':\n callback = criteria\n criteria = null\n break\n case 'number':\n criteria = { limit: criteria }\n break\n default:\n // Do nothing\n }\n\n if (criteria) {\n data.filter = criteria.filter\n data.limit = criteria.limit\n data.start = criteria.start\n }\n\n this.sendRequest(data, [ 'links', 'stats' ], function(result, response) {\n callback(YOURLS._sanitizeStatsResult(result), response)\n })\n\n return this\n },\n\n /**\n * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more\n * detailed information relating to it.\n *\n * No data is fetched just by calling this method; one of the methods on the returned instance need to be called for\n * that to happen.\n *\n * @param {string} url - the shortened URL (or its keyword)\n * @return {URL} The {@link URL} created for the shortened url or null if url\n * is null.\n * @public\n */\n url: function(url) {\n return url ? new URL(url) : null\n },\n\n /**\n * Retrieves the version of the connected YOURLS API.\n *\n * Optionally, db can be passed to indicate that the YOURLS database version should also be included in\n * the result.\n *\n * @param {boolean} [db] - true to include the database version; otherwise false\n * @param {Function} callback - the callback function to be called with the result\n * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.\n * @public\n */\n version: function(db, callback) {\n var data = { action: 'version' }\n\n if (typeof db === 'function') {\n callback = db\n db = null\n }\n\n if (db != null) {\n data.db = Number(db)\n }\n\n this.sendRequest(data, [ 'db_version', 'version' ], callback)\n\n return this\n }\n\n}, {\n\n /**\n * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links\n * from an object mapping into an array.\n *\n * This method simply returns result if it is null, has no links property or one that is\n * already an array (future-proofing).\n *\n * @param {Object} result - the result to be sanitized (may be null)\n * @param {Object} [result.links] - the links to be transformed into an array (may be null)\n * @return {Object} The modified result or null if result is null.\n * @private\n * @static\n */\n _sanitizeStatsResult: function(result) {\n // Future-proofing by sanitizing links *only* when not already an array\n if (!result || !result.links || isArray(result.links)) {\n return result\n }\n\n var index = 1\n var link\n var links = []\n\n while ((link = result.links['link_' + index]) != null) {\n links.push(link)\n index++\n }\n\n result.links = links\n\n return result\n }\n\n})\n\n/**\n * The singleton instance of {@link YOURLS}.\n */\nexport default new YOURLS()\n\n/**\n * Contains criteria which can be used to search for a refined set of shortened URLs.\n *\n * Pagination can be achieved by using limit and start.\n *\n * No links will be returned unless limit is specified and has a value that is greater than zero.\n *\n * @typedef {Object} YOURLS~SearchCriteria\n * @property {string} [filter] - The filter to be applied (either \"top\", \"bottom\",\n * \"rand\", or \"last\").\n * @property {number} [limit] - The maximum number of links whose statistical information is to be counted.\n * null or 0 will result in no links being included in the result.\n * @property {number} [start] - The offset from where the search should begin.\n */\n\n/**\n * Contains additional information which can be used when shortening a URL.\n *\n * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique\n * keyword.\n *\n * @typedef {Object} YOURLS~UrlDescriptor\n * @property {string} [keyword] - The optional keyword to be used for the shortened URL.\n * @property {string} [title] - The optional title to be associated with the shortened URL.\n */\n"],"names":["extend","own","target","sources","slice","call","arguments","property","source","i","length","hasOwnProperty","create","prototype","properties","result","Object","Constructor","Oopsy","Array","isArray","obj","toString","constructor","statics","superConstructor","this","apply","super_","API","url","credentials","options","replace","_sanitizeCredentials","_sanitizeOptions","defaultOptions","format","method","instance","signature","timestamp","password","username","toLowerCase","toUpperCase","Request","buildBody","api","data","getSupportedHttpMethods","isMethodSupported","supportedMethods","indexOf","isQueryStringRequired","process","body","callback","send","resultNames","_serializeParameters","response","_extractResult","names","name","params","results","push","encodeURIComponent","join","JSONRequest","xhr","XMLHttpRequest","open","onreadystatechange","readyState","JSON","parse","responseText","e","Error","setRequestHeader","seed","Date","getTime","JSONPRequest","window","_callbackHolderKey","_callbackHolder","_id","_generateId","script","document","createElement","self","parentNode","removeChild","setAttribute","getElementsByTagName","appendChild","Requestor","sendRequest","_requestFormatMap","request","json","jsonp","DB","stats","action","URL","expand","shorturl","YOURLS","db","VERSION","connect","disconnect","shorten","descriptor","keyword","title","criteria","limit","filter","start","_sanitizeStatsResult","version","Number","links","link","index"],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index e27c7f3..1301d45 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "url", "shortener", "api", + "json", "jsonp" ], "repository": { @@ -31,7 +32,7 @@ "grunt-contrib-watch": "^1.0.0", "grunt-eslint": "^19.0.0", "grunt-rollup": "^1.0.1", - "rollup-plugin-commonjs": "^5.0.5", + "oopsy": "^0.2.0", "rollup-plugin-node-resolve": "^2.0.0", "rollup-plugin-uglify": "^1.0.1", "semver": "^5.3.0" diff --git a/src/api.js b/src/api.js index 275469f..a16057e 100644 --- a/src/api.js +++ b/src/api.js @@ -20,42 +20,9 @@ * SOFTWARE. */ -/** - * The singleton {@link API} instance which is privatized to prevent leaking credentials. - * - * @private - * @type {API} - */ -var instance = null - -/** - * Sanitizes the specified credentials by ensuring that only valid properties are present and only when - * appropriate. - * - * This function does not modify credentials and instead creates a new object with the sanitized - * properties. - * - * @param {API~Credentials} credentials - the credentials to be sanitized (may be null) - * @return {API~Credentials} A sanitized version of credentials or null if - * credentials is null. - * @private - */ -function sanitizeCredentials(credentials) { - if (!credentials) { - return null - } - - var result = {} - if (credentials.signature) { - result.signature = credentials.signature - result.timestamp = credentials.timestamp - } else { - result.password = credentials.password - result.username = credentials.username - } +import Oopsy from 'oopsy' - return result -} +import { extend } from './util/extend' /** * Contains information on how to connect to and authenticate with a YOURLS server. @@ -63,10 +30,12 @@ function sanitizeCredentials(credentials) { * @param {string} [url=''] - the URL for the YOURLS server * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be * null) + * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be + * null) * @protected * @constructor */ -export function API(url, credentials) { +export var API = Oopsy.extend(function(url, credentials, options) { /** * The URL of the YOURLS server. * @@ -82,42 +51,97 @@ export function API(url, credentials) { * @public * @type {API~Credentials} */ - this.credentials = sanitizeCredentials(credentials) -} + this.credentials = API._sanitizeCredentials(credentials) + /** + * The options to be used to send requests to the YOURLS server. + * + * @public + * @type {API~Options} + */ + this.options = API._sanitizeOptions(options) +}, null, { -/** - * Destroys the singleton instance of {@link API}. - * - * @return {void} - * @public - * @static - */ -API.clear = function() { - instance = null -} + /** + * The default options to be used. + * + * @protected + * @static + * @type {API~Options} + */ + defaultOptions: { + format: 'jsonp', + method: 'GET' + }, -/** - * Retrieves the singleton instance of {@link API}. - * - * This function will return null unless an instance is currently stored. - * - * @return {API} The connected {@link API} or null if none exists. - * @public - * @static - */ -API.fetch = function() { - return instance -} + /** + * The singleton {@link API} instance which is privatized to prevent leaking credentials. + * + * @public + * @static + * @type {API} + */ + instance: null, -/** - * Stores this {@link API} as the singleton, potentially replacing the existing instance. - * - * @return {void} - * @public - */ -API.prototype.store = function() { - instance = this -} + /** + * Sanitizes the specified credentials by ensuring that only valid properties are present and only when + * appropriate. + * + * This method does not modify credentials and instead creates a new object with the sanitized + * properties. + * + * @param {API~Credentials} credentials - the credentials to be sanitized (may be null) + * @return {API~Credentials} A sanitized version of credentials or null if + * credentials is null. + * @private + * @static + */ + _sanitizeCredentials: function(credentials) { + if (!credentials) { + return null + } + + var result = {} + if (credentials.signature) { + result.signature = credentials.signature + result.timestamp = credentials.timestamp + } else { + result.password = credentials.password + result.username = credentials.username + } + + return result + }, + + /** + * Sanitizes the specified options by ensuring that only valid properties are present and in the correct + * format. + * + * This method does not modify options and instead creates a new object with the sanitized properties and + * default values will be used for missing options. + * + * @param {API~Options} options - the options to be sanitized (may be null) + * @return {API~Options} A sanitized version of options which will contain only default values if + * options is null. + * @private + * @static + */ + _sanitizeOptions: function(options) { + var result = extend({}, API.defaultOptions) + if (!options) { + return result + } + + if (options.format) { + result.format = options.format.toLowerCase() + } + if (options.method) { + result.method = options.method.toUpperCase() + } + + return result + } + +}) /** * The credentials to be used to authenticate with a private YOURLS API. @@ -136,3 +160,15 @@ API.prototype.store = function() { * @property {string} [signature] - The signature token to be used for passwordless authentication with the YOURLS API. * @property {number|string} [timestamp] - The optional timestamp to limit the signature token. */ + +/** + * The options that determine how requests are sent to the YOURLS server. + * + * If the request format does not support the HTTP method, requests will not be sent and an + * error will be thrown when such attempts occur. + * + * @typedef {Object} API~Options + * @property {string} [format="jsonp"] - The format in which requests are sent (either "json" or + * "jsonp"). + * @property {string} [method="GET"] - The HTTP method to be used for requests. + */ diff --git a/src/request/json.js b/src/request/json.js new file mode 100644 index 0000000..59fb230 --- /dev/null +++ b/src/request/json.js @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * 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. + */ + +import { Request } from './request' + +/** + * An implementation of {@link Request} that provides support for JSON requests to the YOURLS API. + * + * JSON requests can only be sent using the "GET" or "POST" HTTP methods. + * + * @constructor + * @extends Request + * @protected + */ +export var JSONRequest = Request.extend({ + + /** + * @inheritDoc + * @override + */ + getSupportedHttpMethods: function() { + return [ 'GET', 'POST' ] + }, + + /** + * @inheritDoc + * @override + */ + process: function(method, url, body, callback) { + var xhr = new XMLHttpRequest() + xhr.open(method, url, true) + xhr.onreadystatechange = function() { + var response + + if (xhr.readyState === 4) { + try { + response = JSON.parse(xhr.responseText) + callback(response) + } catch (e) { + throw new Error('Unable to parse response: ' + e) + } + } + } + + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') + if (body != null) { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') + } + + xhr.send(body) + } + +}) diff --git a/src/request/jsonp.js b/src/request/jsonp.js index ebcb130..106ff1d 100644 --- a/src/request/jsonp.js +++ b/src/request/jsonp.js @@ -20,128 +20,116 @@ * SOFTWARE. */ -import { API } from '../api' -import { isArray } from '../util/array' -import { paramify } from './paramify' +import { Request } from './request' /** - * The key of the callback function holder within the global namespace. + * The seed to be used to generate IDs. * * @private - * @type {string} + * @type {number} */ -var callbackHolderKey = '__yourls' + Date.now() + '_jsonp' -/** - * Contains the callback functions for active JSONP requests. - * - * Callback references should be removed immediately once they have been called. - * - * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global). - * - * @private - * @type {Object} - */ -var callbackHolder = window[callbackHolderKey] = {} - -/** - * Generates a quick and dirty unique ID for a callback. - * - * @return {number} The generated callback ID. - * @private - */ -function generateCallbackId() { - var id = Date.now() - while (callbackHolder[id]) { - id++ - } - - return id -} +var seed = new Date().getTime() /** - * Extracts the values of the properties with the specified names from the response provided - * and returns them in a single result. - * - * If names is a string or only contains a single string, only the value for that named property will be - * returned. Otherwise, an object containing the key/value pairs for each named property will be returned. + * An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API. * - * If response is null this function will return null. + * JSONP requests can only be sent using the "GET" HTTP method. * - * @param {string|string[]} names - the names of the response properties whose values are to be returned as - * the result - * @param {Object} response - the YOURLS API response - * @return {*} The result extracted from response. - * @private + * @constructor + * @extends Request + * @protected */ -function getResult(names, response) { - names = isArray(names) ? names : [ names ] +export var JSONPRequest = Request.extend(function() { + JSONPRequest.super_.call(this) - var i - var name - var result = null - - if (!response) { - return result + if (!window[JSONPRequest._callbackHolderKey]) { + window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder } - if (names.length === 1) { - result = response[names[0]] - } else { - result = {} - - for (i = 0; i < names.length; i++) { - name = names[i] - - if (typeof response[name] !== 'undefined') { - result[name] = response[name] - } + /** + * The generated ID which is used to store a reference to the callback function within the holder so that the JSONP + * payload can find it in the global namespace. + * + * @private + * @type {number} + */ + this._id = JSONPRequest._generateId() +}, { + + /** + * @inheritDoc + * @override + */ + getSupportedHttpMethods: function() { + return [ 'GET' ] + }, + + /** + * @inheritDoc + * @override + */ + buildBody: function(api, data) { + var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data) + body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']' + + return body + }, + + /** + * @inheritDoc + * @override + */ + process: function(method, url, body, callback) { + var script = document.createElement('script') + + var self = this + JSONPRequest._callbackHolder[this._id] = function(response) { + delete JSONPRequest._callbackHolder[self._id] + script.parentNode.removeChild(script) + + callback(response) } - } - return result -} - -/** - * Sends a JSONP request to the connected YOURLS API with the data provided which should, in turn, call the - * specified callback with the result. - * - * If the request is successful, callback will be passed the value of the named properties from the - * response. If resultNames is a string or only contains a single string, only the value for that named - * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named - * property will be passed as the first argument. The actual response will always be passed as the second argument. - * - * Due to the nature of JSONP, all information will be included in the URL of the request. This includes - * data as well as any credentials used to authenticate with the API. You have been warned. - * - * @param {Object} data - the data to be sent - * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to - * callback as the first argument - * @param {Function} callback - the function to be called with the result - * @return {void} - * @protected - */ -export function jsonp(data, resultNames, callback) { - var api = API.fetch() - var id = generateCallbackId() - var script = document.createElement('script') - - callbackHolder[id] = function(response) { - var result = getResult(resultNames, response) - - delete callbackHolder[id] - script.parentNode.removeChild(script) - - callback(result, response) + script.setAttribute('src', url) + document.getElementsByTagName('head')[0].appendChild(script) } - var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' }) - if (api.credentials) { - target += '&' + paramify(api.credentials) - } - if (data) { - target += '&' + paramify(data) +}, { + + /** + * The key of the callback function holder within the global namespace. + * + * @private + * @static + * @type {string} + */ + _callbackHolderKey: '__yourls' + seed + '_jsonp', + + /** + * Contains the callback functions for active JSONP requests. + * + * Callback references should be removed immediately once they have been called. + * + * Due to the nature of JSON, a reference to this object must be publicly available (i.e. global). + * + * @private + * @static + * @type {Object} + */ + _callbackHolder: {}, + + /** + * Generates an ID to be used when storing a reference to a callback function. + * + * @return {number} The generated ID. + * @private + */ + _generateId: function() { + do { + seed++ + } while (JSONPRequest._callbackHolder[seed]) + + return seed } - script.setAttribute('src', target) - document.getElementsByTagName('head')[0].appendChild(script) -} +}) diff --git a/src/request/request.js b/src/request/request.js new file mode 100644 index 0000000..8ea9855 --- /dev/null +++ b/src/request/request.js @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * 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. + */ + +import Oopsy from 'oopsy' + +import { API } from '../api' +import { extend } from '../util/extend' +import { isArray } from '../util/array' + +/** + * Contains logic to connect with a YOURLS server and send data to its API. + * + * Due to the nature of HTTP, requests sent using a "GET" HTTP method will include all information in the URL of the + * request. This includes the data that is sent as well as any credentials used to authenticate with the API. You + * have been warned. + * + * @constructor + * @protected + */ +export var Request = Oopsy.extend({ + + /** + * Builds the body for this {@link Request} based on the api and data provided. + * + * @param {API} api - the {@link API} to which the request is being sent + * @param {Object} [data] - the data being sent in this request + * @return {Object} The request body. + * @protected + */ + buildBody: function(api, data) { + return extend({ format: api.options.format }, api.credentials, data) + }, + + /** + * Returns the list of the HTTP methods that are supported by this {@link Request}. + * + * By default, this method returns null, so implementations must implement this method to ensure + * that {@link Request#isMethodSupported} works correctly. + * + * @return {string[]} The supported HTTP methods. + * @protected + */ + getSupportedHttpMethods: function() { + return null + }, + + /** + * Determines whether this {@link Request} supports the specified HTTP method. + * + * @param {string} method - the HTTP method to be checked + * @return {boolean} true if method is supported; otherwise false. + * @public + */ + isMethodSupported: function(method) { + var supportedMethods = this.getSupportedHttpMethods() + return supportedMethods && supportedMethods.indexOf(method) !== -1 + }, + + /** + * Determines whether the data that is to be sent to the YOURLS server in this {@link Request} must be serialized as + * query string parameters. + * + * @param {string} method - the HTTP method to be used + * @return {boolean} true if the data needs to be sent as query string parameters; otherwise + * false. + * @protected + */ + isQueryStringRequired: function(method) { + return method === 'GET' + }, + + /** + * Processes this {@link Request} by sending it to the specified target url containing the + * body provided. + * + * callback should be called with the response regardless of whether the it was a success or failure. + * + * This method is called internally by {@link Request#send} and does all of the actual work involved to send the + * request and parse the response. All implementations must implement this method. + * + * @param {string} method - the request HTTP method + * @param {string} url - the request URL + * @param {Object} body - the request body (may be null) + * @param {Function} callback - the function to be called with the response + * @return {void} + * @protected + * @abstract + */ + process: function(method, url, body, callback) { + // Must be implemented + }, + + /** + * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the + * specified callback with the result. + * + * If the request is successful, callback will be passed the value of the named properties from the + * response. If resultNames is a string or only contains a single string, only the value for that named + * property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named + * property will be passed as the first argument. The actual response will always be passed as the second argument. + * + * @param {Object} data - the data to be sent + * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to + * callback as the first argument + * @param {Function} callback - the function to be called with the result + * @return {void} + * @public + */ + send: function(data, resultNames, callback) { + var api = API.instance + var body = Request._serializeParameters(this.buildBody(api, data)) + var method = api.options.method + var url = api.url + + if (this.isQueryStringRequired(method)) { + url += '?' + body + body = null + } + + this.process(method, url, body, function(response) { + callback(Request._extractResult(resultNames, response), response) + }) + } + +}, { + + /** + * Extracts the values of the properties with the specified names from the response provided + * and returns them in a single result. + * + * If names is a string or only contains a single string, only the value for that named property will be + * returned. Otherwise, an object containing the key/value pairs for each named property will be returned. + * + * If response is null this method will return null. + * + * @param {string|string[]} names - the names of the response properties whose values are to be returned + * as the result + * @param {Object} response - the YOURLS API response + * @return {*} The result extracted from response. + * @private + * @static + */ + _extractResult: function(names, response) { + names = isArray(names) ? names : [ names ] + + var i + var name + var result = null + + if (!response) { + return result + } + + if (names.length === 1) { + result = response[names[0]] + } else { + result = {} + + for (i = 0; i < names.length; i++) { + name = names[i] + + if (typeof response[name] !== 'undefined') { + result[name] = response[name] + } + } + } + + return result + }, + + /** + * Creates a serialized representation of the specified parameters. + * + * All of the parameter names and values are URL-encoded so that they can be safely included in the query string or + * request body. + * + * @param {Object} [params] - the hash of parameter name/value pairs to be serialized + * @return {string} A URL-encoded representing obj or an empty string if obj is + * null. + * @private + * @static + */ + _serializeParameters: function(params) { + if (!params) { + return '' + } + + var results = [] + + for (var name in params) { + if (Object.prototype.hasOwnProperty.call(params, name) && params[name] != null) { + results.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name])) + } + } + + return results.join('&') + } + +}) diff --git a/src/request/requestor.js b/src/request/requestor.js new file mode 100644 index 0000000..533eb2c --- /dev/null +++ b/src/request/requestor.js @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * 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. + */ + +import Oopsy from 'oopsy' + +import { API } from '../api' +import { JSONRequest } from './json' +import { JSONPRequest } from './jsonp' + +/** + * Can make requests to the connected YOURLS API. + * + * @constructor + * @protected + */ +export var Requestor = Oopsy.extend({ + + /** + * Sends the request to the connected YOURLS API with the data provided which should, in turn, call the + * specified callback with the result. + * + * This method is primarily a proxy to {@link Request#send} but does validate the state of the connection information + * to ensure that is is valid before making the request. + * + * @param {Object} data - the data to be sent + * @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to + * callback as the first argument + * @param {Function} callback - the function to be called with the result + * @return {void} + * @throws {Error} - If either no connection is present, the request format is not supported, or the configured HTTP + * method is not supported by the {@link Request}. + * @protected + */ + sendRequest: function(data, resultNames, callback) { + var api = API.instance + + if (!api) { + throw new Error('No connection has been made') + } + + var format = api.options.format + var method = api.options.method + var Request = Requestor._requestFormatMap[format] + + if (!Request) { + throw new Error('Request format not supported: ' + format) + } + + var request = new Request() + + if (!request.isMethodSupported(method)) { + throw new Error('HTTP method not supported: ' + method) + } + + request.send(data, resultNames, callback) + } + +}, { + + /** + * The mapping of supported request formats to {@link Request} constructors. + * + * @private + * @static + * @type {Object} + */ + _requestFormatMap: { + json: JSONRequest, + jsonp: JSONPRequest + } + +}) diff --git a/src/util/array.js b/src/util/array.js index 06c97e9..6ba36fc 100644 --- a/src/util/array.js +++ b/src/util/array.js @@ -23,7 +23,7 @@ /** * Returns whether the specified obj is an array. * - * This function will use the native Array.isArray, if available. + * This method will use the native Array.isArray, if available. * * @param {*} obj - the object to be checked (may be null) * @return {boolean} true if obj is an array; otherwise false. diff --git a/src/request/paramify.js b/src/util/extend.js similarity index 58% rename from src/request/paramify.js rename to src/util/extend.js index b20c865..c788589 100644 --- a/src/request/paramify.js +++ b/src/util/extend.js @@ -21,26 +21,29 @@ */ /** - * Creates a serialized representation of the specified params into a URL query string. + * Extends the specified target object with the properties in each of the sources provided. * - * @param {Object} [params] - the hash of parameter key/value pairs to be serialized - * @return {string} A URL query string representing params. + * Any of the sources that are null will simply be ignored. + * + * @param {Object} target - the target object which should be extended + * @param {...Object} [sources] - the source objects whose properties are to be copied onto target + * @return {Object} A reference to target. * @protected */ -export function paramify(params) { - if (!params) { - return '' - } +export function extend(target, sources) { + sources = Array.prototype.slice.call(arguments, 1) - var results = [] + for (var i = 0, length = sources.length, property, source; i < length; i++) { + source = sources[i] - for (var key in params) { - if (Object.prototype.hasOwnProperty.call(params, key)) { - if (params[key] != null) { - results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])) + if (source) { + for (property in source) { + if (Object.prototype.hasOwnProperty.call(source, property)) { + target[property] = source[property] + } } } } - return results.join('&') + return target } diff --git a/src/yourls-db.js b/src/yourls-db.js index daae956..988208a 100644 --- a/src/yourls-db.js +++ b/src/yourls-db.js @@ -20,29 +20,30 @@ * SOFTWARE. */ -import { jsonp } from './request/jsonp' +import { Requestor } from './request/requestor' /** * Provides the ability to lookup information related to the YOURLS database. * * @constructor + * @extends Requestor * @protected */ -export function DB() { - // Do nothing -} +export var DB = Requestor.extend({ -/** - * Retrieves the statistics for this {@link DB}. - * - * @param {Function} callback - the callback function to be called with the result - * @return {DB} A reference to this {@link DB} for chaining purposes. - * @public - */ -DB.prototype.stats = function(callback) { - var data = { action: 'db-stats' } + /** + * Retrieves the statistics for this {@link DB}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {DB} A reference to this {@link DB} for chaining purposes. + * @public + */ + stats: function(callback) { + var data = { action: 'db-stats' } + + this.sendRequest(data, 'db-stats', callback) - jsonp(data, 'db-stats', callback) + return this + } - return this -} +}) diff --git a/src/yourls-url.js b/src/yourls-url.js index 0e0159d..0c9ca94 100644 --- a/src/yourls-url.js +++ b/src/yourls-url.js @@ -20,16 +20,19 @@ * SOFTWARE. */ -import { jsonp } from './request/jsonp' +import { Requestor } from './request/requestor' /** * Provides the ability to lookup information related to the specified shortened url. * * @param {string} url - the shortened URL (or its keyword) to be used * @constructor + * @extends Requestor * @protected */ -export function URL(url) { +export var URL = Requestor.extend(function(url) { + URL.super_.call(this) + /** * Either the shortened URL or its keyword for this {@link URL}. * @@ -37,7 +40,7 @@ export function URL(url) { * @type {string} */ this.url = url -} +}) /** * Retrieves the original ("long") URL for this shortened {@link URL}. @@ -52,7 +55,7 @@ URL.prototype.expand = function(callback) { shorturl: this.url } - jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback) + this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback) return this } @@ -70,7 +73,7 @@ URL.prototype.stats = function(callback) { shorturl: this.url } - jsonp(data, 'link', callback) + this.sendRequest(data, 'link', callback) return this } diff --git a/src/yourls.js b/src/yourls.js index a6fb602..2b85605 100644 --- a/src/yourls.js +++ b/src/yourls.js @@ -23,41 +23,9 @@ import { API } from './api' import { DB } from './yourls-db' import { isArray } from './util/array' -import { jsonp } from './request/jsonp' +import { Requestor } from './request/requestor' import { URL } from './yourls-url' -/** - * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links - * from an object mapping into an array. - * - * This function simply returns result if it is null, has no links property or one that is - * already an array (future-proofing). - * - * @param {Object} result - the result to be sanitized (may be null) - * @param {Object} [result.links] - the links to be transformed into an array (may be null) - * @return {Object} The modified result or null if result is null. - * @private - */ -function sanitizeStatsResult(result) { - // Future-proofing by sanitizing links *only* when not already an array - if (!result || !result.links || isArray(result.links)) { - return result - } - - var index = 1 - var link - var links = [] - - while ((link = result.links['link_' + index]) != null) { - links.push(link) - index++ - } - - result.links = links - - return result -} - /** * Provides the ability to connect to YOURLS servers and perform read/write operations via the API that they expose. * @@ -65,9 +33,12 @@ function sanitizeStatsResult(result) { * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private). * * @constructor + * @extends Requestor * @protected */ -var YOURLS = function() { +var YOURLS = Requestor.extend(function() { + YOURLS.super_.call(this) + /** * Provides information on the YOURLS {@link DB}. * @@ -79,173 +50,211 @@ var YOURLS = function() { /** * The current version of yourls. * - * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} - * function should be used to provide that information. + * This is not the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method + * should be used to provide that information. * * @public * @type {string} */ this.VERSION = '2.0.0' -} +}, { -/** - * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. - * - * @param {string} [url=''] - the URL for the YOURLS server - * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be - * null) - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ -YOURLS.prototype.connect = function(url, credentials) { - var api = new API(url, credentials) - api.store() + /** + * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. + * + * @param {string} [url=''] - the URL for the YOURLS server + * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be + * null) + * @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be + * null) + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + connect: function(url, credentials, options) { + API.instance = new API(url, credentials, options) - return this -} + return this + }, -/** - * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS - * server. - * - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ -YOURLS.prototype.disconnect = function() { - API.clear() + /** + * Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS + * server. + * + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + disconnect: function() { + API.instance = null - return this -} + return this + }, -/** - * Creates a short URL for the specified long url. - * - * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is to - * be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a unique - * keyword. If descriptor is a string, it will be treated as the keyword. - * - * @param {string} url - the long URL to be shortened - * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used - * for the short URL - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ -YOURLS.prototype.shorten = function(url, descriptor, callback) { - var data = { - action: 'shorturl', - url: url - } + /** + * Creates a short URL for the specified long url. + * + * Optionally, a descriptor can be provided to specify a keyword and/or title for the short URL that is + * to be created. If a keyword is specified, it must be available and, if not, the YOURLS server will generate a + * unique keyword. If descriptor is a string, it will be treated as the keyword. + * + * @param {string} url - the long URL to be shortened + * @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be + * used for the short URL + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + shorten: function(url, descriptor, callback) { + var data = { + action: 'shorturl', + url: url + } - switch (typeof descriptor) { - case 'function': - callback = descriptor - descriptor = null - break - case 'string': - descriptor = { keyword: descriptor } - break - default: - // Do nothing - } + switch (typeof descriptor) { + case 'function': + callback = descriptor + descriptor = null + break + case 'string': + descriptor = { keyword: descriptor } + break + default: + // Do nothing + } - if (descriptor) { - data.keyword = descriptor.keyword - data.title = descriptor.title - } + if (descriptor) { + data.keyword = descriptor.keyword + data.title = descriptor.title + } - jsonp(data, [ 'shorturl', 'title', 'url' ], callback) + this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback) - return this -} + return this + }, -/** - * Retrieves the statistics for all shortened URLs. - * - * Optionally, criteria can be provided to also include a refined set of links in the result. This includes - * filter, which provides limited control over the sorting, as well as limit and start, which allow for pagination. If - * criteria is a number, it will be treated as the limit. - * - * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this - * method would effectively be doing the same as {@link DB#stats}. - * - * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to - * search for links to be included in the result - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ -YOURLS.prototype.stats = function(criteria, callback) { - var data = { action: 'stats' } - - switch (typeof criteria) { - case 'function': - callback = criteria - criteria = null - break - case 'number': - criteria = { limit: criteria } - break - default: - // Do nothing - } + /** + * Retrieves the statistics for all shortened URLs. + * + * Optionally, criteria can be provided to also include a refined set of links in the result. This + * includes filter, which provides limited control over the sorting, as well as limit and start, which allow for + * pagination. If criteria is a number, it will be treated as the limit. + * + * No links will be included in the result unless a limit is specified that is greater than zero. In that case, this + * method would effectively be doing the same as {@link DB#stats}. + * + * @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to + * search for links to be included in the result + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + stats: function(criteria, callback) { + var data = { action: 'stats' } - if (criteria) { - data.filter = criteria.filter - data.limit = criteria.limit - data.start = criteria.start - } + switch (typeof criteria) { + case 'function': + callback = criteria + criteria = null + break + case 'number': + criteria = { limit: criteria } + break + default: + // Do nothing + } - jsonp(data, [ 'links', 'stats' ], function(result, response) { - callback(sanitizeStatsResult(result), response) - }) + if (criteria) { + data.filter = criteria.filter + data.limit = criteria.limit + data.start = criteria.start + } - return this -} + this.sendRequest(data, [ 'links', 'stats' ], function(result, response) { + callback(YOURLS._sanitizeStatsResult(result), response) + }) -/** - * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more - * detailed information relating to it. - * - * No data is fetched just by calling this function; one of the functions on the returned instance need to be called for - * that to happen. - * - * @param {string} url - the shortened URL (or its keyword) - * @return {URL} The {@link URL} created for the shortened url or null if url is - * null. - * @public - */ -YOURLS.prototype.url = function(url) { - return url ? new URL(url) : null -} + return this + }, -/** - * Retrieves the version of the connected YOURLS API. - * - * Optionally, db can be passed to indicate that the YOURLS database version should also be included in the - * result. - * - * @param {boolean} [db] - true to include the database version; otherwise false - * @param {Function} callback - the callback function to be called with the result - * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. - * @public - */ -YOURLS.prototype.version = function(db, callback) { - var data = { action: 'version' } + /** + * Creates an instance of {@link URL} for the specified shortened url which can be used to lookup more + * detailed information relating to it. + * + * No data is fetched just by calling this method; one of the methods on the returned instance need to be called for + * that to happen. + * + * @param {string} url - the shortened URL (or its keyword) + * @return {URL} The {@link URL} created for the shortened url or null if url + * is null. + * @public + */ + url: function(url) { + return url ? new URL(url) : null + }, - if (typeof db === 'function') { - callback = db - db = null - } + /** + * Retrieves the version of the connected YOURLS API. + * + * Optionally, db can be passed to indicate that the YOURLS database version should also be included in + * the result. + * + * @param {boolean} [db] - true to include the database version; otherwise false + * @param {Function} callback - the callback function to be called with the result + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + version: function(db, callback) { + var data = { action: 'version' } + + if (typeof db === 'function') { + callback = db + db = null + } + + if (db != null) { + data.db = Number(db) + } - if (db != null) { - data.db = Number(db) + this.sendRequest(data, [ 'db_version', 'version' ], callback) + + return this } - jsonp(data, [ 'db_version', 'version' ], callback) +}, { + + /** + * Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links + * from an object mapping into an array. + * + * This method simply returns result if it is null, has no links property or one that is + * already an array (future-proofing). + * + * @param {Object} result - the result to be sanitized (may be null) + * @param {Object} [result.links] - the links to be transformed into an array (may be null) + * @return {Object} The modified result or null if result is null. + * @private + * @static + */ + _sanitizeStatsResult: function(result) { + // Future-proofing by sanitizing links *only* when not already an array + if (!result || !result.links || isArray(result.links)) { + return result + } + + var index = 1 + var link + var links = [] + + while ((link = result.links['link_' + index]) != null) { + links.push(link) + index++ + } + + result.links = links + + return result + } - return this -} +}) /** * The singleton instance of {@link YOURLS}.