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/.travis.yml b/.travis.yml index ba153ad..e4cee86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ +sudo: false language: node_js node_js: - - "0.10" - - "0.12" - "4" - "6" + - "7" script: - - npm test + - npm run ci diff --git a/CHANGES.md b/CHANGES.md index fedc97b..ddb06b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## Version 2.1.0, 2017.03.24 + +* Support JSON requests (GET & POST) [#6](https://github.com/neocotic/yourls-api/issues/6) + ## Version 2.0.0, 2016.11.23 * Support time-limited signature token for [passwordless API](https://github.com/YOURLS/YOURLS/wiki/PasswordlessAPI) (Josh Panter) [#1](https://github.com/neocotic/yourls-api/issues/1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 09b7c49..210c00c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Please [search existing issues](https://github.com/neocotic/yourls-api/issues) f raising a new issue. Commenting on an existing issue is usually preferred over raising duplicate issues. Please ensure that all files conform to the coding standards, using the same coding style as the rest of the code base. -This can be done easily via command-line: +This can easily be checked via command-line: ``` bash # install/update package dependencies @@ -16,15 +16,7 @@ $ npm install $ npm test ``` -This will recompile the distribution files as well. - -If you're using a version of [Node.js](https://nodejs.org) that is older than 0.12, then linting is not performed by -this step. Please consider upgrading to at least 0.12 or your pull request may fail on the CI build. - -Likewise, [Node.js](https://nodejs.org) versions older than 0.10 will also not be able to compile the source code using -[Rollup](http://rollupjs.org) so the test suite will not be executed against any changes you've made to `src/**.js`. For -this reason, 0.10 is not supported from a development perspective, but our CI builds do run the test suite against the -pre-compiled code for this version to ensure that it works. +You must have at least [Node.js](https://nodejs.org) version 4 or newer installed. All pull requests should be made to the `develop` branch. diff --git a/Gruntfile.js b/Gruntfile.js index f407d27..805936f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -21,142 +21,85 @@ */ module.exports = function(grunt) { - var commonjs - var nodeResolve - var semver = require('semver') - var uglify - - var bannerLarge = [ - '/*', - ' * Copyright (C) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>', - ' *', - ' * Permission is hereby granted, free of charge, to any person obtaining a copy', - ' * of this software and associated documentation files (the "Software"), to deal', - ' * in the Software without restriction, including without limitation the rights', - ' * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell', - ' * copies of the Software, and to permit persons to whom the Software is', - ' * furnished to do so, subject to the following conditions:', - ' *', - ' * The above copyright notice and this permission notice shall be included in all', - ' * copies or substantial portions of the Software.', - ' *', - ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR', - ' * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,', - ' * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE', - ' * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER', - ' * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,', - ' * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE', - ' * SOFTWARE.', - ' */' - ].join('\n') - var bannerSmall = '/*! YOURLS API v<%= pkg.version %> | (C) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | MIT License */' + var nodeResolve = require('rollup-plugin-node-resolve') + var uglify = require('rollup-plugin-uglify') grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), - watch: { - all: { - files: [ 'src/**/*.js' ], - tasks: [ 'test' ] - } - } - }) + clean: { + build: [ 'dist/**' ] + }, - var buildTasks = [ 'compile' ] - var compileTasks = [] - 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') - - compileTasks.push('clean', 'rollup') - - grunt.config.merge({ - clean: { - build: [ 'dist/**' ] - }, + eslint: { + target: [ 'src/**/*.js' ] + }, - rollup: { - umdDevelopment: { - options: { - format: 'umd', - moduleId: 'yourls-api', - moduleName: 'yourls', - sourceMap: true, - sourceMapRelativePaths: true, - banner: bannerLarge, - plugins: function() { - return [ - nodeResolve({ - browser: true, - jsnext: true, - main: true - }), - commonjs() - ] - } - }, - files: { - 'dist/yourls.js': 'src/yourls.js' + rollup: { + umdDevelopment: { + options: { + format: 'umd', + moduleId: 'yourls-api', + moduleName: 'yourls', + sourceMap: true, + sourceMapRelativePaths: true, + plugins: function() { + return [ + nodeResolve({ + browser: true, + jsnext: true, + main: true + }) + ] } }, - umdProduction: { - options: { - format: 'umd', - moduleId: 'yourls-api', - moduleName: 'yourls', - sourceMap: true, - sourceMapRelativePaths: true, - banner: bannerSmall, - plugins: function() { - return [ - nodeResolve({ - browser: true, - jsnext: true, - main: true - }), - commonjs(), - uglify({ - output: { - comments: function(node, comment) { - return comment.type === 'comment2' && /^\!/.test(comment.value) - } + files: { + 'dist/yourls.js': 'src/yourls.js' + } + }, + umdProduction: { + options: { + format: 'umd', + moduleId: 'yourls-api', + moduleName: 'yourls', + sourceMap: true, + sourceMapRelativePaths: true, + banner: '/*! YOURLS API v<%= pkg.version %> | (C) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.license %> License */', + plugins: function() { + return [ + nodeResolve({ + browser: true, + jsnext: true, + main: true + }), + uglify({ + output: { + comments: function(node, comment) { + return comment.type === 'comment2' && /^\!/.test(comment.value) } - }) - ] - } - }, - files: { - 'dist/yourls.min.js': 'src/yourls.js' + } + }) + ] } + }, + files: { + 'dist/yourls.min.js': 'src/yourls.js' } } - }) - - grunt.loadNpmTasks('grunt-contrib-clean') - grunt.loadNpmTasks('grunt-rollup') - } else { - grunt.log.writeln('"clean" and "rollup" tasks are disabled because Node.js version is <0.12! Please consider upgrading Node.js...') - } - - if (semver.satisfies(process.version, '>=4')) { - compileTasks.unshift('eslint') - - grunt.config.set('eslint', { - target: [ 'src/**/*.js' ] - }) + }, - grunt.loadNpmTasks('grunt-eslint') - } else { - grunt.log.writeln('"eslint" task is disabled because Node.js version is <4! Please consider upgrading Node.js...') - } + watch: { + all: { + files: [ 'src/**/*.js' ], + tasks: [ 'eslint' ] + } + } + }) - grunt.loadNpmTasks('grunt-contrib-watch') + require('load-grunt-tasks')(grunt) - grunt.registerTask('default', [ 'build' ]) - grunt.registerTask('build', buildTasks) - grunt.registerTask('compile', compileTasks) - grunt.registerTask('test', testTasks) + grunt.registerTask('default', [ 'ci' ]) + grunt.registerTask('build', [ 'eslint', 'clean:build', 'rollup' ]) + grunt.registerTask('ci', [ 'eslint', 'clean', 'rollup' ]) + grunt.registerTask('test', [ 'eslint' ]) } diff --git a/LICENSE.md b/LICENSE.md index 0b9ca94..3678f2b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (C) 2016 Alasdair Mercer +Copyright (C) 2017 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 diff --git a/README.md b/README.md index 1fedaaa..123c636 100644 --- a/README.md +++ b/README.md @@ -15,16 +15,14 @@ d8888888888 888 888 d88P 888 888 8888888 -[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) +[![Dev Dependency Status](https://img.shields.io/david/dev/neocotic/yourls-api.svg?style=flat-square)](https://david-dm.org/neocotic/yourls-api?type=dev) [![License](https://img.shields.io/npm/l/yourls-api.svg?style=flat-square)](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md) [![Release](https://img.shields.io/npm/v/yourls-api.svg?style=flat-square)](https://www.npmjs.com/package/yourls-api) +[YOURLS API](https://github.com/neocotic/yourls-api) is a JavaScript library that provides bindings for +[YOURLS](https://yourls.org) URL shortener servers. + * [Install](#install) * [API](#api) * [Migrating from v1](#migrating-from-v1) @@ -48,8 +46,8 @@ intended for use in the browser. `npm` installations are supported for the many If you want to simply download the file to be used in the browser you can find them below: -* [Development Version](https://github.com/neocotic/yourls-api/blob/master/dist/yourls.js) -* [Production Version](https://github.com/neocotic/yourls-api/blob/master/dist/yourls.min.js) +* [Development Version](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.js) (TODO - [Source Map](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.js.map)) +* [Production Version](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.min.js) (TODO - [Source Map](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.min.js.map)) ## API @@ -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,8 +367,9 @@ yourls.version(true, function(result, response) { --- ``` javascript +// Get version of this library console.log(yourls.VERSION) -//=> "2.0.0" +//=> "2.1.0" ``` The current version of this library. @@ -364,6 +403,6 @@ A list of YOURLS API contributors can be found in ## License -Copyright © 2016 Alasdair Mercer +Copyright © 2017 Alasdair Mercer See [LICENSE.md](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md) for more information on our MIT license. diff --git a/bower.json b/bower.json index c96e755..04f4e6b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "yourls-api", - "version": "2.0.0", + "version": "2.1.0", "description": "Bindings for the YOURLS API", "homepage": "https://github.com/neocotic/yourls-api", "authors": [ @@ -16,6 +16,7 @@ "url", "shortener", "api", + "json", "jsonp" ], "repository": { @@ -23,6 +24,11 @@ "url": "https://github.com/neocotic/yourls-api.git" }, "main": "dist/yourls.js", + "moduleType": [ + "amd", + "globals", + "node" + ], "ignore": [ ".*", "src/", diff --git a/dist/yourls.js b/dist/yourls.js index 2b3b4e4..aeb2a85 100644 --- a/dist/yourls.js +++ b/dist/yourls.js @@ -1,24 +1,3 @@ -/* - * 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. - */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define('yourls-api', factory) : @@ -26,7 +5,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 +27,217 @@ */ /** - * The singleton {@link API} instance which is privatized to prevent leaking credentials. + * A bare-bones constructor for surrogate prototype swapping. * * @private - * @type {API} + * @constructor + */ + var Constructor = function() {}; + /** + * A reference to Object.prototype.hasOwnProperty. + * + * @private + * @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) 2017 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) 2017 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 +253,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,8 +363,20 @@ * @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 + * Copyright (C) 2017 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 @@ -189,7 +400,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. @@ -200,7 +411,7 @@ } /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -222,32 +433,198 @@ */ /** - * 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. * - * @param {Object} [params] - the hash of parameter key/value pairs to be serialized - * @return {string} A URL query string representing params. + * 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 */ - 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 ]; - for (var key in params) { - if (Object.prototype.hasOwnProperty.call(params, key)) { - if (params[key] != null) { - results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); + 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('&') } - return results.join('&') - } + }); /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -269,129 +646,276 @@ */ /** - * 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) 2017 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 = {}; + }, { + + /** + * The key of the callback function holder within the global namespace. + * + * @private + * @static + * @type {string} + */ + _callbackHolderKey: '__yourls' + seed + '_jsonp', - for (i = 0; i < names.length; i++) { - name = names[i]; + /** + * 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: {}, - if (typeof response[name] !== 'undefined') { - result[name] = response[name]; - } - } + /** + * 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) 2017 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: * - * 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 above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * @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} + * 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. + * + * @constructor * @protected */ - function jsonp(data, resultNames, callback) { - var api = API.fetch(); - var id = generateCallbackId(); - var script = document.createElement('script'); + var Requestor = Oopsy.extend({ - callbackHolder[id] = function(response) { - var result = getResult(resultNames, response); + /** + * 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; - delete callbackHolder[id]; - script.parentNode.removeChild(script); + if (!api) { + throw new Error('No connection has been made') + } - callback(result, response); - }; + 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(); - 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 + * Copyright (C) 2017 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 @@ -416,29 +940,30 @@ * 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 + * Copyright (C) 2017 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 @@ -464,9 +989,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 +1002,7 @@ * @type {string} */ this.url = url; - } + }); /** * Retrieves the original ("long") URL for this shortened {@link URL}. @@ -489,7 +1017,7 @@ shorturl: this.url }; - jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback); + this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback); return this }; @@ -507,13 +1035,13 @@ shorturl: this.url }; - jsonp(data, 'link', callback); + this.sendRequest(data, 'link', callback); return this }; /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -534,38 +1062,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 +1069,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 +1086,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'; - }; + this.VERSION = '2.1.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..0f4b41e 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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.1.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..840be71 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":";8LA0CA,SAASA,GAAoBC,GAC3B,IAAKA,EACH,MAAO,KAGT,IAAIC,KASJ,OARID,GAAYE,WACdD,EAAOC,UAAYF,EAAYE,UAC/BD,EAAOE,UAAYH,EAAYG,YAE/BF,EAAOG,SAAWJ,EAAYI,SAC9BH,EAAOI,SAAWL,EAAYK,UAGzBJ,EAYF,QAASK,GAAIC,EAAKP,GAOvBQ,KAAKD,IAAMA,EAAMA,EAAIE,QAAQ,MAAO,IAAM,GAS1CD,KAAKR,YAAcD,EAAoBC,GCrDlC,QAASU,GAAQC,GACtB,MAAOC,OAAMF,QAAUE,MAAMF,QAAQC,GAA+C,mBAAxCE,OAAOC,UAAUC,SAASC,KAAKL,GCHtE,QAASM,GAASC,GACvB,IAAKA,EACH,MAAO,EAGT,IAAIC,KAEJ,KAAK,GAAIC,KAAOF,GACVL,OAAOC,UAAUO,eAAeL,KAAKE,EAAQE,IAC5B,MAAfF,EAAOE,IACTD,EAAQG,KAAKC,mBAAmBH,GAAO,IAAMG,mBAAmBL,EAAOE,IAK7E,OAAOD,GAAQK,KAAK,KCOtB,QAASC,KAEP,IADA,GAAIC,GAAKC,KAAKC,MACPC,EAAeH,IACpBA,GAGF,OAAOA,GAkBT,QAASI,GAAUC,EAAOC,GACxBD,EAAQrB,EAAQqB,GAASA,GAAUA,EAEnC,IAAIE,GACAC,EACAjC,EAAS,IAEb,KAAK+B,EACH,MAAO/B,EAGT,IAAqB,IAAjB8B,EAAMI,OACRlC,EAAS+B,EAASD,EAAM,QAIxB,KAFA9B,KAEKgC,EAAI,EAAGA,EAAIF,EAAMI,OAAQF,IAC5BC,EAAOH,EAAME,GAEiB,mBAAnBD,GAASE,KAClBjC,EAAOiC,GAAQF,EAASE,GAK9B,OAAOjC,GAsBF,QAASmC,GAAMC,EAAMC,EAAaC,GACvC,GAAIC,GAAMlC,EAAImC,QACVf,EAAKD,IACLiB,EAASC,SAASC,cAAc,SAEpCf,GAAeH,GAAM,SAASM,GAC5B,GAAI/B,GAAS6B,EAAUQ,EAAaN,SAE7BH,GAAeH,GACtBgB,EAAOG,WAAWC,YAAYJ,GAE9BH,EAAStC,EAAQ+B,GAGnB,IAAIe,GAASP,EAAIjC,IAAM,IAAMU,GAAWsB,SAAUS,EAAoB,IAAMtB,EAAK,IAAKuB,OAAQ,SAC1FT,GAAIxC,cACN+C,GAAU,IAAM9B,EAASuB,EAAIxC,cAE3BqC,IACFU,GAAU,IAAM9B,EAASoB,IAG3BK,EAAOQ,aAAa,MAAOH,GAC3BJ,SAASQ,qBAAqB,QAAQ,GAAGC,YAAYV,GCnHhD,QAASW,MCCT,QAASC,GAAI/C,GAOlBC,KAAKD,IAAMA,ECEb,QAASgD,GAAoBtD,GAE3B,IAAKA,IAAWA,EAAOuD,OAAS9C,EAAQT,EAAOuD,OAC7C,MAAOvD,EAOT,KAJA,GACIwD,GADAC,EAAQ,EAERF,KAE6C,OAAzCC,EAAOxD,EAAOuD,MAAM,QAAUE,KACpCF,EAAMlC,KAAKmC,GACXC,GAKF,OAFAzD,GAAOuD,MAAQA,EAERvD,KN7BL0D,GAAW,IAkEfrD,GAAIsD,MAAQ,WACVD,EAAW,MAYbrD,EAAImC,MAAQ,WACV,MAAOkB,IASTrD,EAAIQ,UAAU+C,MAAQ,WACpBF,EAAWnD,SGtFTwC,GAAoB,WAAarB,KAAKC,MAAQ,SAW9CC,EAAiBiC,OAAOd,KCF5BK,GAAGvC,UAAUiD,MAAQ,SAASxB,GAC5B,GAAIF,IAAS2B,OAAQ,WAIrB,OAFA5B,GAAMC,EAAM,WAAYE,GAEjB/B,MCET8C,EAAIxC,UAAUmD,OAAS,SAAS1B,GAC9B,GAAIF,IACF2B,OAAQ,SACRE,SAAU1D,KAAKD,IAKjB,OAFA6B,GAAMC,GAAQ,UAAW,UAAW,YAAcE,GAE3C/B,MAUT8C,EAAIxC,UAAUiD,MAAQ,SAASxB,GAC7B,GAAIF,IACF2B,OAAQ,YACRE,SAAU1D,KAAKD,IAKjB,OAFA6B,GAAMC,EAAM,OAAQE,GAEb/B,SCLL2D,GAAS,WAOX3D,KAAK4D,GAAK,GAAIf,GAWd7C,KAAK6D,QAAU,QAYjBF,GAAOrD,UAAUwD,QAAU,SAAS/D,EAAKP,GACvC,GAAIwC,GAAM,GAAIlC,GAAIC,EAAKP,EAGvB,OAFAwC,GAAIqB,QAEGrD,MAUT2D,EAAOrD,UAAUyD,WAAa,WAG5B,MAFAjE,GAAIsD,QAEGpD,MAiBT2D,EAAOrD,UAAU0D,QAAU,SAASjE,EAAKkE,EAAYlC,GACnD,GAAIF,IACF2B,OAAQ,WACRzD,IAAKA,EAGP,cAAekE,IACf,IAAK,WACHlC,EAAWkC,EACXA,EAAa,IACb,MACF,KAAK,SACHA,GAAeC,QAASD,GAa1B,MAPIA,KACFpC,EAAKqC,QAAUD,EAAWC,QAC1BrC,EAAKsC,MAAQF,EAAWE,OAG1BvC,EAAMC,GAAQ,WAAY,QAAS,OAASE,GAErC/B,MAmBT2D,EAAOrD,UAAUiD,MAAQ,SAASa,EAAUrC,GAC1C,GAAIF,IAAS2B,OAAQ,QAErB,cAAeY,IACf,IAAK,WACHrC,EAAWqC,EACXA,EAAW,IACX,MACF,KAAK,SACHA,GAAaC,MAAOD,GAgBtB,MAVIA,KACFvC,EAAKyC,OAASF,EAASE,OACvBzC,EAAKwC,MAAQD,EAASC,MACtBxC,EAAK0C,MAAQH,EAASG,OAGxB3C,EAAMC,GAAQ,QAAS,SAAW,SAASpC,EAAQ+B,GACjDO,EAASgB,EAAoBtD,GAAS+B,KAGjCxB,MAeT2D,EAAOrD,UAAUP,IAAM,SAASA,GAC9B,MAAOA,GAAM,GAAI+C,GAAI/C,GAAO,MAc9B4D,EAAOrD,UAAUkE,QAAU,SAASZ,EAAI7B,GACtC,GAAIF,IAAS2B,OAAQ,UAarB,OAXkB,kBAAPI,KACT7B,EAAW6B,EACXA,EAAK,MAGG,MAANA,IACF/B,EAAK+B,GAAKa,OAAOb,IAGnBhC,EAAMC,GAAQ,aAAc,WAAaE,GAElC/B,YAMM,GAAI2D"} \ No newline at end of file +{"version":3,"file":"yourls.min.js","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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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) 2017 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.1.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..e982aa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yourls-api", - "version": "2.0.0", + "version": "2.1.0", "description": "Bindings for the YOURLS API", "homepage": "https://github.com/neocotic/yourls-api", "bugs": { @@ -17,6 +17,7 @@ "url", "shortener", "api", + "json", "jsonp" ], "repository": { @@ -31,15 +32,17 @@ "grunt-contrib-watch": "^1.0.0", "grunt-eslint": "^19.0.0", "grunt-rollup": "^1.0.1", - "rollup-plugin-commonjs": "^5.0.5", + "load-grunt-tasks": "^3.5.2", + "oopsy": "^0.2.0", "rollup-plugin-node-resolve": "^2.0.0", - "rollup-plugin-uglify": "^1.0.1", - "semver": "^5.3.0" + "rollup-plugin-uglify": "^1.0.1" }, "main": "dist/yourls.js", + "browser": "dist/yourls.js", "jsnext:main": "src/yourls.js", "scripts": { "build": "grunt build", + "ci": "grunt ci", "test": "grunt test" } } diff --git a/src/api.js b/src/api.js index 275469f..b8a20fa 100644 --- a/src/api.js +++ b/src/api.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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..e5de44d --- /dev/null +++ b/src/request/json.js @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 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..7085323 100644 --- a/src/request/jsonp.js +++ b/src/request/jsonp.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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..b075ed9 --- /dev/null +++ b/src/request/request.js @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2017 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..2893b8d --- /dev/null +++ b/src/request/requestor.js @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 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..c894e94 100644 --- a/src/util/array.js +++ b/src/util/array.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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 56% rename from src/request/paramify.js rename to src/util/extend.js index b20c865..b57a8c4 100644 --- a/src/request/paramify.js +++ b/src/util/extend.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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..654da18 100644 --- a/src/yourls-db.js +++ b/src/yourls-db.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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..c20e041 100644 --- a/src/yourls-url.js +++ b/src/yourls-url.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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..3312dbb 100644 --- a/src/yourls.js +++ b/src/yourls.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer + * Copyright (C) 2017 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 @@ -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' -} + this.VERSION = '2.1.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}.